This commit is contained in:
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);
|
||||
|
||||
Reference in new issue
Block a user