Compare commits

...
Author SHA1 Message Date
nessarandClaude Sonnet 5.5 036b13613c test(calendar): pin the clock for the Paris today marker
CI / policy (push) Successful in 26s
CI / validate (push) Successful in 28s
CI / build (push) Successful in 23s
CI / benchmark (push) Successful in 1m26s
CI / security (push) Successful in 1m14s
CI / test (push) Successful in 1m58s
CI / images (push) Successful in 57s
CI / required (push) Successful in 3s
CI / policy (pull_request) Failing after 10s
CI / validate (pull_request) Successful in 26s
CI / build (pull_request) Successful in 21s
CI / benchmark (pull_request) Successful in 1m0s
CI / security (pull_request) Successful in 58s
CI / test (pull_request) Successful in 1m52s
CI / images (pull_request) Skipped
CI / required (pull_request) Failing after 1s
The test of the today marker read the wall clock, so it only exercised
the Paris/local date mismatch between 22:00 and 24:00 UTC and passed
with or without the fix the rest of the day. Pin the clock to 22:16 UTC,
already the next day in Paris, so a UTC runner fails without the fix at
any time.

Refs #301

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 10:21:54 +02:00
nessarandClaude Sonnet 5.5 af20c96f01 test(planning): hold the worker until the preparation cancel arrives
CI / policy (push) Successful in 16s
CI / validate (push) Successful in 29s
CI / build (push) Successful in 20s
CI / benchmark (push) Successful in 1m27s
CI / security (push) Successful in 1m0s
CI / test (push) Successful in 1m57s
CI / images (push) Successful in 1m20s
CI / required (push) Successful in 1s
CI / policy (pull_request) Successful in 8s
CI / validate (pull_request) Successful in 23s
CI / test (pull_request) Successful in 1m51s
CI / build (pull_request) Successful in 38s
CI / security (pull_request) Successful in 1m1s
CI / benchmark (pull_request) Successful in 46s
CI / images (pull_request) Successful in 51s
CI / required (pull_request) Successful in 2s
The worker reads its queue only between event-loop turns, so the
calendar preparation could finish before the cancel sent on its second
progress update arrived, and the test received a complete plan. Use the
real-worker harness hold already applied to the checkpoint cancellation
test, and cancel on the first preparation update so the hold is released.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 09:52:15 +02:00
nessarandClaude Sonnet 5.5 f458a83599 fix(calendar): mark today on the Europe/Paris date
The calendar highlighted today from the browser's local date while the
default planning horizon starts on the Europe/Paris date, so the two
disagreed for a few hours a day (and always, for users in another time
zone). The horizon's first day was then not marked as today. This also
failed the CI test run in UTC containers between 22:00 and 24:00 UTC.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 09:52:03 +02:00
3 changed files with 19 additions and 9 deletions

No files matched your search

+12
View File
@@ -8,6 +8,7 @@ import {
SelectedDates,
} from './calendar-service';
import { DateRange } from '@angular/material/datepicker';
import { vi } from '../../testing/vitest-compat';
describe('CalendarService', () => {
let service: CalendarService;
@@ -197,6 +198,8 @@ describe('CalendarService', () => {
service.project({ placements: [], horizon });
const day = (key: string) => new Date(`${key}T00:00:00`);
afterEach(() => vi.useRealTimers());
it('projects weekends, holidays and today for the default planning horizon', () => {
const { selections } = project({ start: '2026-01-01', end: '2027-01-01' });
expect(selections.dayTypes(day('2026-05-09'))).toContain(DayType.WEEKEND);
@@ -207,6 +210,15 @@ describe('CalendarService', () => {
);
});
it('marks today on the Europe/Paris date when the runtime date is still the day before', () => {
vi.setSystemTime(new Date('2026-10-17T22:16:00Z'));
const horizon = service.getDefaultPlanningHorizon();
expect(horizon.start).toBe('2026-10-18');
const { selections } = project(horizon);
expect(selections.getClasses(day('2026-10-18'))).toContain(DayType.TODAY);
expect(selections.getClasses(day('2026-10-17'))).not.toContain(DayType.TODAY);
});
it('lists the covered months, including partial boundary months and year changes', () => {
expect(project({ start: '2024-11-20', end: '2025-02-02' }).months).toEqual([
{ monthIndex: 10, year: 2024 },
+4 -6
View File
@@ -85,11 +85,9 @@ export class CalendarService {
const holidays = new Set(
this.getHolidays(first.getFullYear(), last.getFullYear()).map((h) => h.getTime()),
);
const {
date: todayDate,
month: todayMonth,
year: todayYear,
} = this.dateCache.getTodayComponents();
const [todayYear, todayMonth, todayDate] = planningDateInParis(new Date())
.split('-')
.map(Number);
const months: CalendarProjection['months'] = [];
const ranges: SelectedDate[] = [];
const cursor = new Date(first);
@@ -124,7 +122,7 @@ export class CalendarService {
);
if (
day.getDate() === todayDate &&
day.getMonth() === todayMonth &&
day.getMonth() + 1 === todayMonth &&
day.getFullYear() === todayYear
)
ranges.push(new SelectedDate(DayType.TODAY, new DateRange<Date>(day, day)));
@@ -587,8 +587,8 @@ parentPort.on('message', (data) => globalThis.self.onmessage({ data }));
);
it('stops during calendar preparation without a plan or an obligation conflict', async () => {
holdForCancelAtStage = 'preparation';
const stages: string[] = [];
let preparationUpdates = 0;
const calculation = createPlanningCalculation({
horizon: { start: '2024-01-01', end: '2027-01-01' },
balances: [],
@@ -596,7 +596,7 @@ parentPort.on('message', (data) => globalThis.self.onmessage({ data }));
declaredDays: [{ date: '2024-01-02', type: 'other' }],
progress: (stage) => {
stages.push(stage);
if (stage === 'preparation' && ++preparationUpdates === 2) calculation.cancel();
if (stage === 'preparation') calculation.cancel();
},
});
const outcome = await calculation.outcome;
@@ -606,7 +606,7 @@ parentPort.on('message', (data) => globalThis.self.onmessage({ data }));
});
if (outcome.kind !== 'PlanningIncomplete') return;
expect(outcome.incumbent).toBeUndefined();
expect(preparationUpdates).toBeGreaterThanOrEqual(2);
expect(stages).toContain('preparation');
expect(stages).not.toContain('obligations');
expect(await calculation.explanationError).toBeUndefined();
expect(heartbeats).toBeGreaterThan(0);