test(clock): prevent terminal lifecycle starvation

This commit is contained in:
2026-09-03 21:08:48 +00:00
parent 51f8be492f
commit 424f0b66ec
3 changed files with 37 additions and 11 deletions
@@ -202,18 +202,28 @@ describe('HWE-shaped unification invader resume', () => {
actorUserId: recipient.userId, actorUserId: recipient.userId,
target: 'ENGINE', target: 'ENGINE',
eventType: 'messageRespond', eventType: 'messageRespond',
createdAt: acceptedAt, processingGameTick: 19_980_000_000n,
})), })),
}, },
$queryRaw: vi.fn(async () => [ messageAction: { updateMany: vi.fn(async () => ({ count: 1 })) },
{ message: { updateMany: vi.fn(async () => ({ count: 1 })) },
id: 1014, $queryRaw: vi
mailbox: recipient.id, .fn()
type: 'private', .mockResolvedValueOnce([
validUntil: new Date('9999-12-31T00:00:00.000Z'), {
message, id: 1014,
}, mailbox: recipient.id,
]), type: 'private',
time: world.gameTickToDate(19_980_000_000),
validUntil: new Date('9999-12-31T00:00:00.000Z'),
actionType: 'raiseInvader',
actionStatus: 'PENDING',
createdGameTick: 19_980_000_000n,
expiresGameTick: null,
message,
},
])
.mockResolvedValueOnce([{ id: 1014 }]),
} as unknown as GamePrisma.TransactionClient; } as unknown as GamePrisma.TransactionClient;
const queue = new InMemoryControlQueue(); const queue = new InMemoryControlQueue();
queue.enqueue({ queue.enqueue({
@@ -294,7 +304,11 @@ describe('HWE-shaped unification invader resume', () => {
await lifecycle.start(); await lifecycle.start();
expect(commandDb.inputEvent.findUnique).toHaveBeenCalledWith(expect.objectContaining({ where: { requestId } })); expect(commandDb.inputEvent.findUnique).toHaveBeenCalledWith(expect.objectContaining({ where: { requestId } }));
expect(commandDb.$queryRaw).toHaveBeenCalledOnce(); expect(commandDb.$queryRaw).toHaveBeenCalledTimes(2);
expect(commandDb.messageAction.updateMany).toHaveBeenCalledWith({
where: { messageId: { in: [1014] }, status: 'PENDING' },
data: { status: 'RESOLVED', resolvedGameTick: 20_088_000_000n },
});
expect(world.getState()).toMatchObject({ expect(world.getState()).toMatchObject({
currentYear: 226, currentYear: 226,
currentMonth: 4, currentMonth: 4,
+2
View File
@@ -34,6 +34,7 @@ export class ManualClock implements Clock {
return; return;
} }
this.currentMs += ms; this.currentMs += ms;
await new Promise<void>((resolve) => setTimeout(resolve, 0));
} }
advanceMs(ms: number): void { advanceMs(ms: number): void {
@@ -71,6 +72,7 @@ export class StepClock implements Clock {
return; return;
} }
this.currentMs += ms; this.currentMs += ms;
await new Promise<void>((resolve) => setTimeout(resolve, 0));
} }
advanceMs(ms: number): void { advanceMs(ms: number): void {
+10
View File
@@ -11,8 +11,13 @@ describe('ManualClock', () => {
it('advances with sleep and manual advance', async () => { it('advances with sleep and manual advance', async () => {
const clock = new ManualClock(0); const clock = new ManualClock(0);
let eventLoopTurnObserved = false;
setTimeout(() => {
eventLoopTurnObserved = true;
}, 0);
await clock.sleepMs(250); await clock.sleepMs(250);
expect(clock.nowMs()).toBe(250); expect(clock.nowMs()).toBe(250);
expect(eventLoopTurnObserved).toBe(true);
clock.advanceMs(750); clock.advanceMs(750);
expect(clock.nowMs()).toBe(1000); expect(clock.nowMs()).toBe(1000);
}); });
@@ -34,8 +39,13 @@ describe('StepClock', () => {
it('advances with sleep', async () => { it('advances with sleep', async () => {
const clock = new StepClock(50, 1000); const clock = new StepClock(50, 1000);
let eventLoopTurnObserved = false;
setTimeout(() => {
eventLoopTurnObserved = true;
}, 0);
await clock.sleepMs(200); await clock.sleepMs(200);
expect(clock.nowMs()).toBe(1250); expect(clock.nowMs()).toBe(1250);
expect(eventLoopTurnObserved).toBe(true);
}); });
it('advances manually', () => { it('advances manually', () => {