test(planning): verify integrated planning after trace removal
CI / validate (push) Successful in 44s
CI / build (push) Successful in 40s
CI / policy (push) Successful in 6s
CI / policy (pull_request) Successful in 7s
CI / validate (pull_request) Successful in 1m19s
CI / test (push) Successful in 1m52s
CI / benchmark (push) Failing after 5m29s
CI / test (pull_request) Successful in 3m22s
CI / security (pull_request) Failing after 12m15s
CI / build (pull_request) Failing after 14m33s
CI / benchmark (pull_request) Failing after 17m21s
CI / security (push) Failing after 19m29s
CI / images (pull_request) Skipped
CI / images (push) Skipped
CI / required (push) Failing after 1s
CI / required (pull_request) Failing after 1s
CI / validate (push) Successful in 44s
CI / build (push) Successful in 40s
CI / policy (push) Successful in 6s
CI / policy (pull_request) Successful in 7s
CI / validate (pull_request) Successful in 1m19s
CI / test (push) Successful in 1m52s
CI / benchmark (push) Failing after 5m29s
CI / test (pull_request) Successful in 3m22s
CI / security (pull_request) Failing after 12m15s
CI / build (pull_request) Failing after 14m33s
CI / benchmark (pull_request) Failing after 17m21s
CI / security (push) Failing after 19m29s
CI / images (pull_request) Skipped
CI / images (push) Skipped
CI / required (push) Failing after 1s
CI / required (pull_request) Failing after 1s
This commit is contained in:
1 parent
2f4af14869
commit
56577d23b5
3 files changed
+217
-1
No files matched your search
@@ -33,6 +33,9 @@ describe('real planning worker cancellation', () => {
|
||||
let simulateLongCalculation = false;
|
||||
let simulatedElapsed = 0;
|
||||
const threads = new Set<NodeWorker>();
|
||||
const sentTypes: string[] = [];
|
||||
const receivedTypes: string[] = [];
|
||||
let terminationCount = 0;
|
||||
|
||||
beforeAll(async () => {
|
||||
directory = mkdtempSync(join(tmpdir(), 'vac-optimizer-worker-'));
|
||||
@@ -65,6 +68,9 @@ parentPort.on('message', (data) => globalThis.self.onmessage({ data }));
|
||||
heartbeats = 0;
|
||||
simulateLongCalculation = false;
|
||||
simulatedElapsed = 0;
|
||||
sentTypes.length = 0;
|
||||
receivedTypes.length = 0;
|
||||
terminationCount = 0;
|
||||
class RealWorker {
|
||||
onmessage: ((event: MessageEvent) => void) | null = null;
|
||||
onerror: (() => void) | null = null;
|
||||
@@ -76,6 +82,7 @@ parentPort.on('message', (data) => globalThis.self.onmessage({ data }));
|
||||
simulatedElapsed = data.elapsed;
|
||||
return;
|
||||
}
|
||||
receivedTypes.push(data.type);
|
||||
if (holdArtifacts && ['explanation', 'artifacts'].includes(data.type)) {
|
||||
heldArtifactMessages.push(() => this.onmessage?.({ data } as MessageEvent));
|
||||
return;
|
||||
@@ -86,10 +93,12 @@ parentPort.on('message', (data) => globalThis.self.onmessage({ data }));
|
||||
});
|
||||
this.thread.on('error', () => this.onerror?.());
|
||||
}
|
||||
postMessage(message: unknown) {
|
||||
postMessage(message: { type: string }) {
|
||||
sentTypes.push(message.type);
|
||||
this.thread.postMessage(message);
|
||||
}
|
||||
terminate() {
|
||||
terminationCount++;
|
||||
void this.thread.terminate();
|
||||
}
|
||||
}
|
||||
@@ -104,6 +113,59 @@ parentPort.on('message', (data) => globalThis.self.onmessage({ data }));
|
||||
});
|
||||
afterAll(() => rmSync(directory, { recursive: true, force: true }));
|
||||
|
||||
it('delivers the outcome and explanation and releases the worker without acknowledgements', async () => {
|
||||
const calculation = createPlanningCalculation({
|
||||
horizon: { start: '2024-01-02', end: '2024-01-04' },
|
||||
balances: [{ id: 'cp', type: 'cp', quantity: 1, expiryDate: '2024-01-03' }],
|
||||
rules: [presets[0]],
|
||||
});
|
||||
const outcome = await calculation.outcome;
|
||||
expect(outcome).toMatchObject({
|
||||
kind: 'Plan',
|
||||
placements: [{ date: '2024-01-02', balanceId: 'cp', type: 'cp' }],
|
||||
lastCompletedStage: 'dates',
|
||||
});
|
||||
expect(await calculation.explanation).toMatchObject({
|
||||
kind: 'PlanExplanation',
|
||||
placements: [{ placement: { date: '2024-01-02', balanceId: 'cp' } }],
|
||||
});
|
||||
expect(await calculation.explanationError).toBeUndefined();
|
||||
expect(await calculation.timings).toEqual({ planningWorkMs: expect.any(Number) });
|
||||
expect(sentTypes).toEqual(['plan']);
|
||||
expect(receivedTypes.slice(-3)).toEqual(['result', 'explanation', 'artifacts']);
|
||||
expect(new Set(receivedTypes)).toEqual(
|
||||
new Set(['heartbeat', 'progress', 'checkpoint', 'result', 'explanation', 'artifacts']),
|
||||
);
|
||||
expect(terminationCount).toBe(1);
|
||||
});
|
||||
|
||||
it('ignores a replaced real worker’s late explanation without losing either outcome', async () => {
|
||||
const request = {
|
||||
horizon: { start: '2024-01-02', end: '2024-01-04' },
|
||||
balances: [{ id: 'cp', type: 'cp', quantity: 1, expiryDate: '2024-01-03' }],
|
||||
rules: [presets[0]],
|
||||
};
|
||||
holdArtifacts = true;
|
||||
const first = createPlanningCalculation(request);
|
||||
const original = await first.outcome;
|
||||
expect(original.kind).toBe('Plan');
|
||||
await vi.waitFor(() => expect(heldArtifactMessages).toHaveLength(2));
|
||||
holdArtifacts = false;
|
||||
const latest = createPlanningCalculation({ ...request, rules: [] });
|
||||
const outcome = await latest.outcome;
|
||||
expect(outcome).toMatchObject({ kind: 'Plan', placements: [] });
|
||||
const explanation = await latest.explanation;
|
||||
expect(explanation).toMatchObject({ kind: 'PlanExplanation', placements: [] });
|
||||
await latest.timings;
|
||||
for (const deliver of heldArtifactMessages) deliver();
|
||||
expect(await first.outcome).toEqual(original);
|
||||
expect(await first.explanation).toBeUndefined();
|
||||
expect(await latest.outcome).toEqual(outcome);
|
||||
expect(await latest.explanation).toEqual(explanation);
|
||||
expect(await latest.explanationError).toBeUndefined();
|
||||
expect(terminationCount).toBe(2);
|
||||
});
|
||||
|
||||
it('preserves a complete optimization configuration and active order in the real worker snapshot', async () => {
|
||||
const configuration: OptimizationConfiguration = structuredClone(
|
||||
OPTIMIZATION_PRESETS.unplacedAtRisk,
|
||||
|
||||
Reference in new issue
Block a user