갱신 행의 ID를 보존하고 예약턴의 불필요한 시퀀스를 제거한다
This commit is contained in:
1 parent
bc54b157c1
commit
81e101ffef
43 files changed
+587
-113
No files matched your search
@@ -198,6 +198,23 @@ integration('general batch persistence', () => {
|
||||
);
|
||||
});
|
||||
expect(writes).toBe(2);
|
||||
const originalIds = (
|
||||
await db.generalAccessLog.findMany({
|
||||
where: { generalId: { gte: firstId, lte: firstId + GENERAL_UPDATE_BATCH_SIZE } },
|
||||
orderBy: { generalId: 'asc' },
|
||||
})
|
||||
).map((row) => row.id);
|
||||
const sequence = await db.$queryRaw`SELECT last_value FROM general_access_log_id_seq`;
|
||||
await db.$transaction((tx) => persistGeneralAccessScores(tx, updates));
|
||||
expect(
|
||||
(
|
||||
await db.generalAccessLog.findMany({
|
||||
where: { generalId: { gte: firstId, lte: firstId + GENERAL_UPDATE_BATCH_SIZE } },
|
||||
orderBy: { generalId: 'asc' },
|
||||
})
|
||||
).map((row) => row.id)
|
||||
).toEqual(originalIds);
|
||||
expect(await db.$queryRaw`SELECT last_value FROM general_access_log_id_seq`).toEqual(sequence);
|
||||
expect(await db.generalAccessLog.findUniqueOrThrow({ where: { generalId: firstId } })).toMatchObject({
|
||||
userId: 'original-owner',
|
||||
lastRefresh: at,
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { GamePrisma } from '@sammo-ts/infra';
|
||||
import { GamePrisma } from '@sammo-ts/infra';
|
||||
import { LogCategory, LogScope } from '@sammo-ts/logic';
|
||||
|
||||
import { persistGeneralLifecycleEvents } from '../src/turn/generalTurnLifecyclePersistence.js';
|
||||
import type { GeneralLifecycleEvent } from '../src/turn/inMemoryWorld.js';
|
||||
import type { TurnGeneral } from '../src/turn/types.js';
|
||||
|
||||
// This harness begins without the durable archive/point row.
|
||||
const missingRecordUpdate = async (): Promise<never> => {
|
||||
throw new GamePrisma.PrismaClientKnownRequestError('fixture record missing', {
|
||||
code: 'P2025',
|
||||
clientVersion: 'fixture',
|
||||
});
|
||||
};
|
||||
|
||||
const archivedGeneral = (): TurnGeneral => ({
|
||||
id: 91,
|
||||
userId: null,
|
||||
@@ -65,12 +73,13 @@ describe('general lifecycle archive history', () => {
|
||||
]),
|
||||
},
|
||||
rankData: {
|
||||
update: missingRecordUpdate,
|
||||
findMany: vi.fn(async () => [
|
||||
{ type: 'warnum', value: 2 },
|
||||
{ type: 'killnum', value: 1 },
|
||||
]),
|
||||
},
|
||||
oldGeneral: { upsert },
|
||||
oldGeneral: { update: missingRecordUpdate, upsert },
|
||||
} as unknown as GamePrisma.TransactionClient;
|
||||
const event: GeneralLifecycleEvent = {
|
||||
generalId: general.id,
|
||||
@@ -137,6 +146,7 @@ describe('general lifecycle archive history', () => {
|
||||
updateMany: vi.fn(async () => ({ count: 1 })),
|
||||
},
|
||||
rankData: {
|
||||
update: missingRecordUpdate,
|
||||
findMany: vi.fn(async () => [
|
||||
{ type: 'warnum', value: 10 },
|
||||
{ type: 'inherit_earned', value: 123 },
|
||||
@@ -156,6 +166,7 @@ describe('general lifecycle archive history', () => {
|
||||
update: vi.fn(async () => undefined),
|
||||
},
|
||||
inheritancePoint: {
|
||||
update: missingRecordUpdate,
|
||||
findMany: vi.fn(async () => [{ key: 'previous', value: 0 }]),
|
||||
upsert: vi.fn(async () => undefined),
|
||||
deleteMany: vi.fn(async () => ({ count: 0 })),
|
||||
|
||||
@@ -68,8 +68,6 @@ describe.skipIf(!databaseUrl)('rank data bigint PostgreSQL persistence', () => {
|
||||
|
||||
it('reloads bigint IDs for high churn queues, logs, upserts and the traffic foreign key', async () => {
|
||||
const tables = [
|
||||
'general_turn',
|
||||
'nation_turn',
|
||||
'general_access_log',
|
||||
'diplomacy',
|
||||
'inheritance_point',
|
||||
@@ -93,10 +91,6 @@ describe.skipIf(!databaseUrl)('rank data bigint PostgreSQL persistence', () => {
|
||||
},
|
||||
});
|
||||
const rows = await Promise.all([
|
||||
tx.generalTurn.create({ data: { generalId, turnIdx: 0, actionCode: 'fixture' } }),
|
||||
tx.nationTurn.create({
|
||||
data: { nationId: 3, officerLevel: 12, turnIdx: 0, actionCode: 'fixture' },
|
||||
}),
|
||||
tx.generalAccessLog.create({ data: { generalId } }),
|
||||
tx.diplomacy.create({ data: { srcNationId: 3, destNationId: 4, stateCode: 0 } }),
|
||||
tx.inheritancePoint.create({ data: { userId: 'bigint-fixture', key: 'previous', value: 123 } }),
|
||||
@@ -109,6 +103,14 @@ describe.skipIf(!databaseUrl)('rank data bigint PostgreSQL persistence', () => {
|
||||
}),
|
||||
]);
|
||||
expect(rows.every((row) => row.id > 2_147_483_647n)).toBe(true);
|
||||
const generalQueue = await tx.generalTurn.create({
|
||||
data: { generalId, turnIdx: 0, actionCode: 'fixture' },
|
||||
});
|
||||
const nationQueue = await tx.nationTurn.create({
|
||||
data: { nationId: 3, officerLevel: 12, turnIdx: 0, actionCode: 'fixture' },
|
||||
});
|
||||
expect(generalQueue).not.toHaveProperty('id');
|
||||
expect(nationQueue).not.toHaveProperty('id');
|
||||
const period = await tx.trafficPeriod.create({
|
||||
data: {
|
||||
worldStateId: world.id,
|
||||
@@ -122,7 +124,7 @@ describe.skipIf(!databaseUrl)('rank data bigint PostgreSQL persistence', () => {
|
||||
});
|
||||
expect(period.id).toBeGreaterThan(2_147_483_647n);
|
||||
expect(period.generals[0]?.periodId).toBe(period.id);
|
||||
expect((await tx.logEntry.findUniqueOrThrow({ where: { id: rows[5]!.id } })).text).toBe('fixture');
|
||||
expect((await tx.logEntry.findUniqueOrThrow({ where: { id: rows[3]!.id } })).text).toBe('fixture');
|
||||
throw new Error('high churn fixture rollback');
|
||||
})
|
||||
).rejects.toThrow('high churn fixture rollback');
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { normalizeArchivedGeneral, type ArchivedJsonValue } from '@sammo-ts/common';
|
||||
import type { GamePrisma } from '@sammo-ts/infra';
|
||||
import { GamePrisma } from '@sammo-ts/infra';
|
||||
import { LogCategory, LogScope, type City, type Nation } from '@sammo-ts/logic';
|
||||
|
||||
import { InMemoryTurnWorld } from '../src/turn/inMemoryWorld.js';
|
||||
import { persistUnificationFinalization, resolveStoredInheritancePoint } from '../src/turn/unificationPersistence.js';
|
||||
import type { TurnGeneral, TurnWorldSnapshot, TurnWorldState } from '../src/turn/types.js';
|
||||
|
||||
// This harness begins without the durable archive/point row.
|
||||
const missingRecordUpdate = async (): Promise<never> => {
|
||||
throw new GamePrisma.PrismaClientKnownRequestError('fixture record missing', {
|
||||
code: 'P2025',
|
||||
clientVersion: 'fixture',
|
||||
});
|
||||
};
|
||||
|
||||
const buildWorld = (): InMemoryTurnWorld => {
|
||||
const turnTime = new Date('0190-07-01T00:00:00.000Z');
|
||||
const general: TurnGeneral = {
|
||||
@@ -206,6 +214,7 @@ describe('persistUnificationFinalization', () => {
|
||||
create: vi.fn().mockResolvedValue({}),
|
||||
},
|
||||
inheritancePoint: {
|
||||
update: missingRecordUpdate,
|
||||
findMany: vi.fn().mockResolvedValue([
|
||||
{ userId: 'user-1', key: 'previous', value: 100 },
|
||||
{ userId: 'user-1', key: 'unifier', value: 7 },
|
||||
@@ -216,6 +225,7 @@ describe('persistUnificationFinalization', () => {
|
||||
inheritanceResult: { create: inheritanceResultCreate },
|
||||
inheritanceLog: { create: inheritanceLogCreate },
|
||||
rankData: {
|
||||
update: missingRecordUpdate,
|
||||
findMany: vi.fn().mockResolvedValue([
|
||||
{ generalId: 1, type: 'warnum', value: 15 },
|
||||
{ generalId: 1, type: 'killnum', value: 9 },
|
||||
@@ -251,10 +261,11 @@ describe('persistUnificationFinalization', () => {
|
||||
),
|
||||
},
|
||||
oldNation: {
|
||||
update: missingRecordUpdate,
|
||||
upsert: oldNationUpsert,
|
||||
findMany: vi.fn().mockResolvedValue([]),
|
||||
},
|
||||
oldGeneral: { upsert: oldGeneralUpsert },
|
||||
oldGeneral: { update: missingRecordUpdate, upsert: oldGeneralUpsert },
|
||||
emperor: { create: emperorCreate },
|
||||
});
|
||||
await expect(persistUnificationFinalization(transaction, input, buildWorld())).resolves.toEqual({
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { GamePrisma } from '@sammo-ts/infra';
|
||||
import { GamePrisma } from '@sammo-ts/infra';
|
||||
import { LogCategory, LogScope } from '@sammo-ts/logic';
|
||||
|
||||
import { persistYearbookSnapshot } from '../src/turn/yearbookPersistence.js';
|
||||
|
||||
// This harness begins without the durable archive/point row.
|
||||
const missingRecordUpdate = async (): Promise<never> => {
|
||||
throw new GamePrisma.PrismaClientKnownRequestError('fixture record missing', {
|
||||
code: 'P2025',
|
||||
clientVersion: 'fixture',
|
||||
});
|
||||
};
|
||||
|
||||
describe('persistYearbookSnapshot', () => {
|
||||
it('archives canonical summary logs and compatible action logs together in descending ID order', async () => {
|
||||
const findMany = vi
|
||||
@@ -14,7 +22,7 @@ describe('persistYearbookSnapshot', () => {
|
||||
const upsert = vi.fn().mockResolvedValue(undefined);
|
||||
const transaction = {
|
||||
logEntry: { findMany },
|
||||
yearbookHistory: { upsert },
|
||||
yearbookHistory: { update: missingRecordUpdate, upsert },
|
||||
} as unknown as GamePrisma.TransactionClient;
|
||||
|
||||
await persistYearbookSnapshot(transaction, {
|
||||
|
||||
Reference in new issue
Block a user