Files
core2026/app/game-api/test/publicTraffic.test.ts
2026-07-27 11:32:48 +00:00

150 lines
4.9 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { RedisConnector } from '@sammo-ts/infra';
import { RedisAccessTokenStore } from '../src/auth/accessTokenStore.js';
import { InMemoryFlushStore } from '../src/auth/flushStore.js';
import { InMemoryBattleSimTransport } from '../src/battleSim/inMemoryTransport.js';
import type { DatabaseClient, GameApiContext, GameProfile } from '../src/context.js';
import { InMemoryTurnDaemonTransport } from '../src/daemon/inMemoryTransport.js';
import { appRouter } from '../src/router.js';
const profile: GameProfile = {
id: 'che',
scenario: 'default',
name: 'che:default',
};
const buildContext = (): GameApiContext => {
const db = {
worldState: {
findFirst: async () => ({
id: 1,
currentYear: 185,
currentMonth: 3,
tickSeconds: 600,
config: {},
meta: {
lastTurnTime: '2026-07-26T03:00:00.000Z',
refresh: 12,
maxrefresh: 30,
maxonline: 5,
recentTraffic: [
{
year: 185,
month: 2,
refresh: 30,
online: 5,
date: '2026-07-26 02:50:00',
},
],
},
}),
},
generalAccessLog: {
aggregate: async () => ({
_sum: {
refreshScoreTotal: 21,
},
}),
findMany: async () => [
{ generalId: 7, refreshScoreTotal: 15 },
{ generalId: 8, refreshScoreTotal: 6 },
],
},
trafficPeriod: {
findUnique: async () => ({
id: 12,
worldStateId: 1,
year: 185,
month: 3,
startedAt: new Date('2026-07-26T03:00:00.000Z'),
lastRefresh: new Date('2026-07-26T03:05:00.000Z'),
refresh: 12,
online: 0,
_count: { generals: 2 },
}),
findMany: async () => [
{
id: 11,
worldStateId: 1,
year: 185,
month: 2,
startedAt: new Date('2026-07-26T02:50:00.000Z'),
lastRefresh: new Date('2026-07-26T02:50:00.000Z'),
refresh: 30,
online: 5,
_count: { generals: 0 },
},
],
aggregate: async () => ({
_max: {
refresh: 30,
online: 5,
},
}),
},
trafficPeriodGeneral: {
findMany: async () => [
{ generalId: 7, refresh: 9 },
{ generalId: 8, refresh: 3 },
],
},
general: {
findMany: async () => [
{ id: 7, name: '갑' },
{ id: 8, name: '을' },
],
},
};
const redis = {
get: async () => null,
set: async () => null,
} as unknown as RedisConnector['client'];
return {
db: db as unknown as DatabaseClient,
turnDaemon: new InMemoryTurnDaemonTransport(),
battleSim: new InMemoryBattleSimTransport(),
profile,
auth: null,
uploadDir: 'uploads',
uploadPath: '/uploads',
uploadPublicUrl: null,
redis,
accessTokenStore: new RedisAccessTokenStore(redis, profile.name),
flushStore: new InMemoryFlushStore(),
gameTokenSecret: 'test-secret',
};
};
describe('public.getTraffic', () => {
it('is public and returns only aggregate traffic plus allowlisted general names', async () => {
const result = await appRouter.createCaller(buildContext()).public.getTraffic();
expect(result.history).toHaveLength(2);
expect(result.history[0]).toEqual({
year: 185,
month: 2,
refresh: 30,
online: 5,
date: '2026-07-26T02:50:00.000Z',
});
expect(result.history[1]).toMatchObject({
year: 185,
month: 3,
refresh: 12,
online: 2,
date: '2026-07-26T03:05:00.000Z',
});
expect(result.maxRefresh).toBe(30);
expect(result.maxOnline).toBe(5);
expect(result.suspects).toEqual([
{ generalId: null, name: '접속자 총합', refresh: 12, refreshScoreTotal: 21 },
{ generalId: 7, name: '갑', refresh: 9, refreshScoreTotal: 15 },
{ generalId: 8, name: '을', refresh: 3, refreshScoreTotal: 6 },
]);
expect(JSON.stringify(result)).not.toContain('userId');
});
});