fix: 실제 행동 기준으로 접속 중 국가를 집계

This commit is contained in:
2026-08-19 14:14:12 +00:00
parent daf98722de
commit 830833ebc6
11 changed files with 295 additions and 69 deletions
@@ -16,7 +16,8 @@ const accessToken = `ga_${randomUUID()}`;
let postgres: ReturnType<typeof createGamePostgresConnector>;
let redis: RedisConnector;
let prisma: GamePrismaClient;
let fixture: { generalId: number; nationId: number; userId: string; voteId: number } | null = null;
let fixture: { generalId: number; freeGeneralId: number; nationId: number; userId: string; voteId: number } | null =
null;
const accessKey = (token: string) => `sammo:game:access:che:default:${token}`;
const response = (data: unknown) => ({ result: { data } });
@@ -49,7 +50,9 @@ test.beforeAll(async () => {
]);
const nationId = (nationMax._max.id ?? 0) + 10_000;
const generalId = (generalMax._max.id ?? 0) + 10_000;
const freeGeneralId = generalId + 1;
const userId = `main-front-status-${randomUUID()}`;
const freeUserId = `main-front-status-free-${randomUUID()}`;
await prisma.$executeRaw(
GamePrisma.sql`
INSERT INTO nation (id, name, color, meta)
@@ -64,19 +67,35 @@ test.beforeAll(async () => {
await prisma.$executeRaw(
GamePrisma.sql`
INSERT INTO general (id, user_id, name, nation_id, city_id, turn_time)
VALUES (${generalId}, ${userId}, ${'현황검증장수'}, ${nationId}, ${0}, ${new Date()})
VALUES
(${generalId}, ${userId}, ${'현황검증장수'}, ${nationId}, ${0}, ${new Date()}),
(${freeGeneralId}, ${freeUserId}, ${'재야행동검증장수'}, ${0}, ${0}, ${new Date()})
`
);
await prisma.generalAccessLog.create({
data: {
generalId,
userId,
lastRefresh: new Date(),
refresh: 1,
refreshTotal: 1,
refreshScore: 1,
refreshScoreTotal: 1,
},
const actionAt = new Date();
await prisma.generalAccessLog.createMany({
data: [
{
generalId,
userId,
lastRefresh: actionAt,
lastActionAt: actionAt,
refresh: 1,
refreshTotal: 1,
refreshScore: 1,
refreshScoreTotal: 1,
},
{
generalId: freeGeneralId,
userId: freeUserId,
lastRefresh: actionAt,
lastActionAt: actionAt,
refresh: 1,
refreshTotal: 1,
refreshScore: 1,
refreshScoreTotal: 1,
},
],
});
const poll = await prisma.votePoll.create({
data: {
@@ -92,7 +111,7 @@ test.beforeAll(async () => {
},
select: { id: true },
});
fixture = { generalId, nationId, userId, voteId: poll.id };
fixture = { generalId, freeGeneralId, nationId, userId, voteId: poll.id };
const issuedAt = new Date();
await redis.client.set(
@@ -118,14 +137,15 @@ test.beforeAll(async () => {
test.afterAll(async () => {
if (fixture) {
await prisma.votePoll.deleteMany({ where: { id: fixture.voteId } });
await prisma.generalAccessLog.deleteMany({ where: { generalId: fixture.generalId } });
await prisma.$executeRaw(GamePrisma.sql`DELETE FROM general WHERE id = ${fixture.generalId}`);
const generalIds = [fixture.generalId, fixture.freeGeneralId];
await prisma.generalAccessLog.deleteMany({ where: { generalId: { in: generalIds } } });
await prisma.general.deleteMany({ where: { id: { in: generalIds } } });
await prisma.$executeRaw(GamePrisma.sql`DELETE FROM nation WHERE id = ${fixture.nationId}`);
const [generalCount, nationCount, voteCount, accessCount] = await Promise.all([
prisma.general.count({ where: { id: fixture.generalId } }),
prisma.general.count({ where: { id: { in: generalIds } } }),
prisma.nation.count({ where: { id: fixture.nationId } }),
prisma.votePoll.count({ where: { id: fixture.voteId } }),
prisma.generalAccessLog.count({ where: { generalId: fixture.generalId } }),
prisma.generalAccessLog.count({ where: { generalId: { in: generalIds } } }),
]);
expect([generalCount, nationCount, voteCount, accessCount]).toEqual([0, 0, 0, 0]);
}
@@ -146,6 +166,33 @@ const installMainFixture = async (
},
failStatus: () => boolean
) => {
const generalContext = {
general: {
id: fixture?.generalId ?? 1,
name: '현황검증장수',
npcState: 0,
nationId: fixture?.nationId ?? 1,
cityId: 1,
troopId: 0,
picture: null,
imageServer: 0,
officerLevel: 0,
stats: { leadership: 55, strength: 55, intelligence: 55 },
gold: 1000,
rice: 1000,
crew: 0,
train: 0,
atmos: 0,
injury: 0,
experience: 0,
dedication: 0,
items: { horse: null, weapon: null, book: null, item: null },
},
city: null,
nation: null,
settings: {},
penalties: {},
};
await page.addInitScript(
({ token }) => {
window.localStorage.setItem('sammo-game-token', token);
@@ -155,35 +202,28 @@ const installMainFixture = async (
);
await page.route('**/che/api/trpc/**', async (route) => {
const results = operationNames(route).map((operation) => {
if (operation === 'general.me') {
if (operation === 'dashboard.getContextBundleDelta') {
return response({
general: {
id: fixture?.generalId ?? 1,
name: '현황검증장수',
npcState: 0,
nationId: fixture?.nationId ?? 1,
cityId: 1,
troopId: 0,
picture: null,
imageServer: 0,
officerLevel: 0,
stats: { leadership: 55, strength: 55, intelligence: 55 },
gold: 1000,
rice: 1000,
crew: 0,
train: 0,
atmos: 0,
injury: 0,
experience: 0,
dedication: 0,
items: { horse: null, weapon: null, book: null, item: null },
context: {
kind: 'snapshot',
revision: 'AAAAAAAAAAAAAAAAAAAAAA',
data: generalContext,
},
commandTable: {
kind: 'snapshot',
revision: 'BBBBBBBBBBBBBBBBBBBBBB',
data: { general: [], nation: [] },
},
boardAccess: {
kind: 'snapshot',
revision: 'CCCCCCCCCCCCCCCCCCCCCC',
data: { canMeeting: false, canSecret: false, permission: 0 },
},
city: null,
nation: null,
settings: {},
penalties: {},
});
}
if (operation === 'general.me') {
return response(generalContext);
}
if (operation === 'general.getFrontStatus') {
return failStatus()
? errorResponse(operation, '상단 현황을 불러오지 못했습니다.')
@@ -214,6 +254,15 @@ const installMainFixture = async (
levelMap: { 7: '수도' },
});
}
if (operation === 'world.getState') {
return response({
currentYear: 190,
currentMonth: 1,
tickSeconds: 60,
config: { npcMode: 0, const: {}, environment: {} },
meta: {},
});
}
if (operation === 'world.getMap') {
return response({
year: 190,
@@ -280,6 +329,9 @@ test('renders actual online, nation policy, and survey data with ref geometry an
};
}
).result.data;
expect(liveStatus.onlineUserCount).toBe(2);
expect(liveStatus.onlineNations).toContain('【검증국】');
expect(liveStatus.onlineNations).not.toContain('재야');
expect(liveStatus.onlineGenerals).toContain('현황검증장수');
expect(liveStatus.nationNotice).toContain(marker);
expect(liveStatus.latestVote).toMatchObject({ title: '검증 설문', hasVoted: false });
@@ -337,7 +389,7 @@ test('renders actual online, nation policy, and survey data with ref geometry an
expect(desktop.voteRow.x).toBeCloseTo(666.67, 0);
expect(desktop.voteRow.width).toBeCloseTo(333.33, 0);
expect(desktop.voteRow.height).toBeCloseTo(36, 0);
expect(desktop.backgroundImage).toContain('/image/game/back_walnut.jpg');
expect(desktop.backgroundImage).toContain('/game/back_walnut.jpg');
await page.setViewportSize({ width: 500, height: 900 });
await expect(status).toHaveCSS('width', '500px');
@@ -349,7 +401,7 @@ test('renders actual online, nation policy, and survey data with ref geometry an
for (const width of mobileActivity.widths) expect(width).toBeCloseTo(166.67, 0);
failStatus = true;
await page.getByRole('button', { name: '새로고침', exact: true }).click();
await page.getByRole('button', { name: '갱 신', exact: true }).click();
await expect(page.getByRole('alert').filter({ hasText: '상단 현황을 불러오지 못했습니다.' })).toBeVisible();
await expect(status).toContainText(marker);