Track traffic by game month

This commit is contained in:
2026-07-27 11:32:48 +00:00
parent 9f402a3d67
commit 2b15410c5c
9 changed files with 595 additions and 146 deletions
@@ -6,30 +6,54 @@ import { upsertGeneralAccess } from '../src/services/generalAccess.js';
const databaseUrl = process.env.INPUT_EVENT_DATABASE_URL;
const integration = describe.skipIf(!databaseUrl);
const generalId = 9_980_071;
const secondGeneralId = generalId + 1;
const rollbackGeneralId = generalId + 2;
const scenarioCode = `traffic-period-${generalId}`;
integration('general access tracking persistence', () => {
let db: GamePrismaClient;
let closeDb: (() => Promise<void>) | undefined;
let worldStateId: number;
beforeAll(async () => {
const connector = createGamePostgresConnector({ url: databaseUrl! });
await connector.connect();
db = connector.prisma;
closeDb = () => connector.disconnect();
await db.generalAccessLog.deleteMany({ where: { generalId } });
await db.generalAccessLog.deleteMany({
where: { generalId: { in: [generalId, secondGeneralId, rollbackGeneralId] } },
});
await db.worldState.deleteMany({ where: { scenarioCode } });
const world = await db.worldState.create({
data: {
scenarioCode,
currentYear: 185,
currentMonth: 3,
tickSeconds: 600,
config: {},
meta: {},
},
});
worldStateId = world.id;
});
afterAll(async () => {
await db.generalAccessLog.deleteMany({ where: { generalId } });
await db.generalAccessLog.deleteMany({
where: { generalId: { in: [generalId, secondGeneralId, rollbackGeneralId] } },
});
await db.worldState.deleteMany({ where: { id: worldStateId } });
await closeDb?.();
});
it('atomically increments concurrent requests and resets only windowed counters', async () => {
it('atomically increments one game-month bucket and opens a new bucket at the next month', async () => {
const firstWindow = {
worldStateId,
year: 185,
month: 3,
generalId,
userId: 'access-user-a',
now: new Date('2026-07-26T03:05:00.000Z'),
dayStartedAt: new Date('2026-07-26T00:00:00.000Z'),
periodStartedAt: new Date('2026-07-26T03:00:00.000Z'),
scoreStartedAt: new Date('2026-07-26T03:00:00.000Z'),
};
await upsertGeneralAccess(db, { ...firstWindow, weight: 2 });
@@ -50,13 +74,37 @@ integration('general access tracking persistence', () => {
refreshScore: 22,
refreshScoreTotal: 22,
});
const firstPeriod = await db.trafficPeriod.findUniqueOrThrow({
where: {
worldStateId_year_month: {
worldStateId,
year: 185,
month: 3,
},
},
include: { generals: { orderBy: { generalId: 'asc' } } },
});
expect(firstPeriod).toMatchObject({
refresh: 22,
online: 1,
generals: [
{
generalId,
userId: 'access-user-a',
refresh: 22,
},
],
});
await upsertGeneralAccess(db, {
worldStateId,
year: 185,
month: 4,
generalId,
userId: 'access-user-b',
now: new Date('2026-07-27T00:05:00.000Z'),
dayStartedAt: new Date('2026-07-27T00:00:00.000Z'),
scoreStartedAt: new Date('2026-07-27T00:00:00.000Z'),
now: new Date('2026-07-26T03:15:00.000Z'),
periodStartedAt: new Date('2026-07-26T03:10:00.000Z'),
scoreStartedAt: new Date('2026-07-26T03:10:00.000Z'),
weight: 1,
});
@@ -67,5 +115,89 @@ integration('general access tracking persistence', () => {
refreshScore: 1,
refreshScoreTotal: 23,
});
await upsertGeneralAccess(db, {
worldStateId,
year: 185,
month: 4,
generalId: secondGeneralId,
userId: 'access-user-c',
now: new Date('2026-07-26T03:15:01.000Z'),
periodStartedAt: new Date('2026-07-26T03:10:00.000Z'),
scoreStartedAt: new Date('2026-07-26T03:10:00.000Z'),
weight: 1,
});
await expect(
db.trafficPeriod.findUniqueOrThrow({
where: {
worldStateId_year_month: {
worldStateId,
year: 185,
month: 4,
},
},
include: { generals: { orderBy: { generalId: 'asc' } } },
})
).resolves.toMatchObject({
refresh: 2,
online: 2,
generals: [
{
generalId,
userId: 'access-user-b',
refresh: 1,
},
{
generalId: secondGeneralId,
userId: 'access-user-c',
refresh: 1,
},
],
});
});
it('rolls back the period and member rows when the legacy access update fails', async () => {
await db.generalAccessLog.create({
data: {
generalId: rollbackGeneralId,
userId: 'rollback-user',
lastRefresh: new Date('2026-07-26T03:19:00.000Z'),
refresh: 1,
refreshTotal: 2_147_483_647,
refreshScore: 1,
refreshScoreTotal: 1,
},
});
await expect(
upsertGeneralAccess(db, {
worldStateId,
year: 186,
month: 1,
generalId: rollbackGeneralId,
userId: 'rollback-user',
now: new Date('2026-07-26T03:20:00.000Z'),
periodStartedAt: new Date('2026-07-26T03:20:00.000Z'),
scoreStartedAt: new Date('2026-07-26T03:20:00.000Z'),
weight: 1,
})
).rejects.toThrow();
await expect(
db.trafficPeriod.findUnique({
where: {
worldStateId_year_month: {
worldStateId,
year: 186,
month: 1,
},
},
})
).resolves.toBeNull();
await expect(
db.generalAccessLog.findUniqueOrThrow({ where: { generalId: rollbackGeneralId } })
).resolves.toMatchObject({
refreshTotal: 2_147_483_647,
});
});
});