This commit is contained in:
nessar committed 2026-10-07 00:28:29 +02:00
1 parent 03841c6e3d
commit 036a8c0e1e
32 files changed
+1805 -89

No files matched your search

@@ -46,6 +46,65 @@ async function runInNonBrowser(request: Parameters<typeof planLeave>[0]) {
}
describe('planLeave', () => {
it('keeps declared non-working days fixed without consuming their remaining balances', async () => {
const declaredDays = [
{ date: '2024-01-02', type: 'cp' as const },
{ date: '2024-01-03', type: 'rtt' as const },
{ date: '2024-01-05', type: 'other' as const },
{ date: '2026-10-06', type: 'other' as const },
];
const result = isPlan(
await runInNonBrowser({
horizon: { start: '2024-01-02', end: '2024-01-06' },
balances: [balance('remaining-cp', 'cp', 4, '2024-01-05')],
rules: [],
declaredDays,
}),
);
expect(result).toMatchObject({ declaredDays });
expect(result.placements.map(({ date }) => date)).toEqual(['2024-01-04']);
expect(result.remainingBalances).toMatchObject([{ id: 'remaining-cp', quantity: 3 }]);
});
it('accepts declarations without a source balance or a new placement', async () => {
const result = isPlan(
await runInNonBrowser({
horizon,
balances: [],
rules: [],
declaredDays: [{ date: '2024-01-02', type: 'other' }],
}),
);
expect(result.declaredDays).toEqual([{ date: '2024-01-02', type: 'other' }]);
expect(result.placements).toEqual([]);
expect(result.remainingBalances).toEqual([]);
});
it.each([
null,
{},
[null],
[{ date: '2024-02-30', type: 'cp' }],
[{ date: '2024-01-06', type: 'cp' }],
[{ date: '2024-05-08', type: 'cp' }],
[{ date: '2024-01-02', type: 'any' }],
[
{ date: '2024-01-02', type: 'cp' },
{ date: '2024-01-02', type: 'rtt' },
],
])('reports malformed declarations as invalid planning input: %j', async (declaredDays) => {
const outcome = await runInNonBrowser({
horizon,
balances: [],
rules: [],
declaredDays,
} as unknown as PlanningRequest);
expect(outcome).toMatchObject({
kind: 'InvalidInput',
reasons: [{ code: 'declared-days' }],
});
});
it.each([
['one leap day', '2024-02-29', '2024-03-01', '2024-02-29', '2024-02-29', '2024-02-29'],
['historical day', '2021-05-14', '2021-05-15', '2021-05-14', '2021-05-13', '2021-05-16'],
@@ -1597,6 +1656,54 @@ describe('planLeave transport', () => {
});
describe('planning calculation context', () => {
it('detaches declared dates and types across results, explanations, trace and comparisons', async () => {
const browserWindow = globalThis.window;
vi.stubGlobal('window', undefined);
try {
const submitted: PlanningRequest = {
horizon: { start: '2024-01-02', end: '2024-02-01' },
balances: [balance('cp', 'cp', 4, '2024-01-05')],
rules: [],
declaredDays: [
{ date: '2024-01-02', type: 'cp' },
{ date: '2024-01-03', type: 'rtt' },
{ date: '2024-01-05', type: 'other' },
],
};
const original = structuredClone(submitted.declaredDays);
const calculation = createPlanningCalculation(submitted);
submitted.declaredDays![0].type = 'other';
submitted.horizon.end = '2024-01-03';
expect(calculation.snapshot.declaredDays).toEqual(original);
expect(Object.isFrozen(calculation.snapshot.declaredDays![0])).toBe(true);
const outcome = isPlan(await calculation.outcome);
expect(outcome.declaredDays).toEqual(original);
expect(outcome.placements.map(({ date }) => date)).toEqual(['2024-01-04']);
expect(await calculation.explanation).toMatchObject({
kind: 'PlanExplanation',
declaredDays: original,
placements: [{ placement: { date: '2024-01-04' } }],
});
const trace = await calculation.trace;
expect(trace?.initialState.declaredDays).toEqual(original);
expect(trace?.nonWorkingDates).toContainEqual({
date: '2024-01-02',
reason: expect.objectContaining({ code: 'declared-day' }),
});
const frozenOutcome = structuredClone(outcome);
const analysis = await calculation.analyzeCounterfactual(outcome.placements[0]);
for (const { date } of original!)
expect(analysis.results.find((result) => result.date === date)).toMatchObject({
status: 'ineligible',
reason: { code: 'declared-day' },
historicalStatus: 'ineligible',
});
expect(outcome).toEqual(frozenOutcome);
} finally {
vi.stubGlobal('window', browserWindow);
}
});
it('keeps a multi-year horizon attached to its immutable outcome, explanation and original trace', async () => {
const browserWindow = globalThis.window;
vi.stubGlobal('window', undefined);