feat(autopilot): /autopilot/today liefert Saison-Zusätze für das Bienenjahr-Rad (T5b B5)
- seasons[] bekommt segmentSince, reportedOn und note (NO_COORDINATES | WEATHER_GAPS | WEATHER_STALE). - Neues seasonCalendar: calendarFallback (8 Phasen, MM-DD), swarmSeason und winterVarroaWindow aus den Regel-Configs, auch bei inaktiver Regel. - Nur Zusätze, keine Migration, Recht bleibt colonies:read; SEASON_AUTOPILOT wird je Anfrage einmal gelesen. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
90d1c5244f
commit
bcc7cbfd13
4 files changed
+260
-11
No files matched your search
@@ -38,7 +38,18 @@ interface TodayBody {
|
||||
nextStep: { colonyId: string } | null;
|
||||
inspectionDue: { apiaryId: string }[];
|
||||
suggestionsOpen: number;
|
||||
seasons: { apiaryId: string; segment: string }[];
|
||||
seasons: {
|
||||
apiaryId: string;
|
||||
segment: string;
|
||||
segmentSince: string;
|
||||
reportedOn: string | null;
|
||||
note: string | null;
|
||||
}[];
|
||||
seasonCalendar?: {
|
||||
calendarFallback: Record<string, string>;
|
||||
swarmSeason: { from: string; until: string };
|
||||
winterVarroaWindow: { from: string; to: string };
|
||||
};
|
||||
tasksVisible: boolean;
|
||||
}
|
||||
|
||||
@@ -454,4 +465,65 @@ describe('Next steps (e2e)', () => {
|
||||
expect(body.suggestionsOpen).toBeGreaterThanOrEqual(4);
|
||||
expect(body.nextStep).not.toBeNull();
|
||||
});
|
||||
|
||||
it('GET /autopilot/today carries seasonCalendar and segmentSince per apiary', async () => {
|
||||
const example = await nextSteps.today(users.full.id, EXAMPLE_DAY);
|
||||
expect(example.seasons).toContainEqual({
|
||||
apiaryId,
|
||||
apiaryName: `Next steps ${suffix}`,
|
||||
segment: 'SCHWARMZEIT',
|
||||
phase: 'APFEL',
|
||||
phaseSource: 'CALENDAR',
|
||||
segmentSince: '2026-04-15',
|
||||
reportedOn: null,
|
||||
note: null,
|
||||
});
|
||||
|
||||
const response = await get(users.noTasks.token, '/autopilot/today').expect(
|
||||
200,
|
||||
);
|
||||
const body = response.body as TodayBody;
|
||||
const own = body.seasons.find((season) => season.apiaryId === apiaryId);
|
||||
expect(own?.segmentSince).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
||||
expect(own).toHaveProperty('reportedOn');
|
||||
expect(own).toHaveProperty('note');
|
||||
expect(body.seasonCalendar).toEqual({
|
||||
calendarFallback: expect.objectContaining({
|
||||
SALWEIDE: expect.stringMatching(/^\d{2}-\d{2}$/) as string,
|
||||
WINTER: expect.stringMatching(/^\d{2}-\d{2}$/) as string,
|
||||
}) as Record<string, string>,
|
||||
swarmSeason: {
|
||||
from: expect.any(String) as string,
|
||||
until: expect.any(String) as string,
|
||||
},
|
||||
winterVarroaWindow: {
|
||||
from: expect.stringMatching(/^\d{2}-\d{2}$/) as string,
|
||||
to: expect.stringMatching(/^\d{2}-\d{2}$/) as string,
|
||||
},
|
||||
});
|
||||
expect(Object.keys(body.seasonCalendar?.calendarFallback ?? {})).toEqual([
|
||||
'SALWEIDE',
|
||||
'KIRSCHE',
|
||||
'APFEL',
|
||||
'HOLUNDER',
|
||||
'LINDE',
|
||||
'TRACHTENDE',
|
||||
'HERBST',
|
||||
'WINTER',
|
||||
]);
|
||||
});
|
||||
|
||||
// A changed PHENOLOGY config is covered by the unit test: mutating the shared
|
||||
// rule row here would race the other e2e suites that read it in parallel.
|
||||
|
||||
it('a user without colonies:read still gets 403 and no calendar', async () => {
|
||||
const noColonies = await createUser('nocalendar', ['tasks:read']);
|
||||
|
||||
const response = await get(noColonies.token, '/autopilot/today').expect(
|
||||
403,
|
||||
);
|
||||
|
||||
expect(response.body).not.toHaveProperty('seasonCalendar');
|
||||
expect(JSON.stringify(response.body)).not.toMatch(/calendarFallback/);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user