test scheduled state rollback boundary
This commit is contained in:
@@ -2,6 +2,7 @@ import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
InMemoryControlQueue,
|
||||
EngineStateManager,
|
||||
ManualClock,
|
||||
TurnDaemonLifecycle,
|
||||
getNextTickTime,
|
||||
@@ -13,6 +14,67 @@ import {
|
||||
const addMinutes = (time: Date, minutes: number): Date => new Date(time.getTime() + minutes * 60_000);
|
||||
|
||||
describe('TurnDaemonLifecycle', () => {
|
||||
it('restores engine state when a scheduled calculation throws', async () => {
|
||||
const now = new Date('2026-01-01T00:10:00.000Z');
|
||||
const queue = new InMemoryControlQueue();
|
||||
let engineState = { value: 'before' };
|
||||
const stateManager = new EngineStateManager();
|
||||
stateManager.register('test', {
|
||||
capture: () => structuredClone(engineState),
|
||||
restore: (snapshot) => {
|
||||
engineState = snapshot;
|
||||
},
|
||||
});
|
||||
let resolveError: (() => void) | undefined;
|
||||
const errorObserved = new Promise<void>((resolve) => {
|
||||
resolveError = resolve;
|
||||
});
|
||||
const lifecycle = new TurnDaemonLifecycle(
|
||||
{
|
||||
clock: new ManualClock(now.getTime()),
|
||||
controlQueue: queue,
|
||||
getNextTickTime: () => new Date('2026-01-01T00:05:00.000Z'),
|
||||
stateStore: {
|
||||
loadLastTurnTime: async () => new Date('2026-01-01T00:00:00.000Z'),
|
||||
loadNextGeneralTurnTime: async () => null,
|
||||
saveLastTurnTime: async () => {},
|
||||
loadCheckpoint: async () => undefined,
|
||||
saveCheckpoint: async () => {},
|
||||
},
|
||||
processor: {
|
||||
run: async () => {
|
||||
engineState.value = 'partial';
|
||||
throw new Error('scheduled calculation failed');
|
||||
},
|
||||
},
|
||||
stateManager,
|
||||
hooks: {
|
||||
onRunError: async () => {
|
||||
resolveError?.();
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
profile: 'test',
|
||||
defaultBudget: { budgetMs: 100, maxGenerals: 1, catchUpCap: 1 },
|
||||
}
|
||||
);
|
||||
|
||||
const loop = lifecycle.start();
|
||||
await errorObserved;
|
||||
|
||||
expect(engineState).toEqual({ value: 'before' });
|
||||
expect(stateManager.getRevision()).toBe(0);
|
||||
expect(lifecycle.getStatus()).toMatchObject({
|
||||
state: 'paused',
|
||||
paused: true,
|
||||
lastError: 'scheduled calculation failed',
|
||||
});
|
||||
|
||||
await lifecycle.stop('done');
|
||||
await loop;
|
||||
});
|
||||
|
||||
it('runs scheduled turn based on queue front and checkpoint context', async () => {
|
||||
const turnTermMinutes = 10;
|
||||
const lastTurnTime = new Date(2026, 0, 2, 2, 0, 0, 0);
|
||||
|
||||
Reference in New Issue
Block a user