Public source validation / validate (push) Failing after 3m8s
113 lines
5.7 KiB
TypeScript
113 lines
5.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import { aggregateStatus, BACKUP_STALE_AFTER_SECONDS, backupPresentation, overviewTitle, STALE_AFTER_MS, statusProblems, type SystemStatus, type SystemStatusSnapshot } from '../../src/systemStatus';
|
|
|
|
const observedAt = Date.parse('2026-08-10T04:00:00Z');
|
|
|
|
function status(overrides: Partial<SystemStatus> = {}): SystemStatus {
|
|
return {
|
|
version: '1',
|
|
generatedAt: new Date(observedAt).toISOString(),
|
|
overallState: 'healthy',
|
|
components: [],
|
|
backup: { state: 'disabled', reason: 'not_configured' },
|
|
sourceLag: [],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function ready(value: SystemStatus): SystemStatusSnapshot {
|
|
return { state: 'ready', status: value, fetchedAt: observedAt };
|
|
}
|
|
|
|
describe('aggregateStatus ADR-0008 invariant', () => {
|
|
it.each([
|
|
{ state: 'loading', status: null, fetchedAt: 0 },
|
|
{ state: 'error', status: null, fetchedAt: observedAt },
|
|
{ state: 'unauthorized', status: null, fetchedAt: observedAt },
|
|
{ state: 'forbidden', status: null, fetchedAt: observedAt },
|
|
] satisfies SystemStatusSnapshot[])('maps $state without telemetry to Unknown', (snapshot) => {
|
|
expect(aggregateStatus(snapshot, observedAt)).toMatchObject({ state: 'unknown', tone: 'unknown', stale: false });
|
|
});
|
|
|
|
it('allows healthy only for a fresh ready payload that explicitly says healthy', () => {
|
|
expect(aggregateStatus(ready(status()), observedAt + 1_000)).toMatchObject({ state: 'healthy', tone: 'ready', stale: false });
|
|
});
|
|
|
|
it.each(['unknown', 'degraded', 'disabled'])('never maps %s to a ready tone', (overallState) => {
|
|
expect(aggregateStatus(ready(status({ overallState })), observedAt + 1_000).tone).toBe('unknown');
|
|
});
|
|
|
|
it('expires a formerly healthy payload to Unknown', () => {
|
|
expect(aggregateStatus(ready(status()), observedAt + STALE_AFTER_MS + 1)).toMatchObject({ state: 'unknown', tone: 'unknown', stale: true });
|
|
});
|
|
});
|
|
|
|
describe('statusProblems', () => {
|
|
it('keeps non-healthy signals bounded and omits disabled components', () => {
|
|
const value = status({
|
|
components: [
|
|
{ id: 'database', state: 'healthy', reason: 'ok' },
|
|
{ id: 'prometheus', state: 'unknown', reason: 'stale' },
|
|
{ id: 'optional', state: 'disabled', reason: 'not_configured' },
|
|
],
|
|
sourceLag: Array.from({ length: 12 }, (_, index) => ({ sourceId: `source-${index}`, state: 'unknown', reason: 'missing' })),
|
|
});
|
|
const problems = statusProblems(value);
|
|
expect(problems).toHaveLength(10);
|
|
expect(problems[0]).toEqual({ id: 'component:prometheus', label: 'Prometheus', reason: 'De laatste meting is verouderd; controleer de bronverbinding en collector.' });
|
|
expect(problems.some((problem) => problem.label === 'optional')).toBe(false);
|
|
});
|
|
|
|
it('does not repeat a source that is already represented by its component', () => {
|
|
const value = status({
|
|
components: [{ id: 'prometheus', state: 'unknown', reason: 'stale' }],
|
|
sourceLag: [{ sourceId: 'prometheus', state: 'unknown', reason: 'stale' }],
|
|
});
|
|
expect(statusProblems(value)).toEqual([{ id: 'component:prometheus', label: 'Prometheus', reason: 'De laatste meting is verouderd; controleer de bronverbinding en collector.' }]);
|
|
});
|
|
|
|
it('keeps a disabled required source actionable while omitting optional disabled features', () => {
|
|
const value = status({ components: [
|
|
{ id: 'unraid', state: 'disabled', reason: 'not_configured' },
|
|
{ id: 'notifications', state: 'disabled', reason: 'not_configured' },
|
|
] });
|
|
expect(statusProblems(value)).toEqual([{ id: 'component:unraid', label: 'Unraid', reason: 'Dit onderdeel is nog niet geconfigureerd; open de instellingen om het te activeren.' }]);
|
|
});
|
|
|
|
it('promotes an old allegedly healthy backup to an actionable problem', () => {
|
|
const value = status({ backup: { state: 'healthy', reason: 'backup_verified', ageSeconds: BACKUP_STALE_AFTER_SECONDS + 1 } });
|
|
expect(statusProblems(value)).toEqual([{ id: 'backup', label: 'Backupstatus', reason: 'De laatste geverifieerde backup is verlopen; maak en verifieer een nieuwe backup.' }]);
|
|
});
|
|
});
|
|
|
|
describe('backupPresentation', () => {
|
|
it('allows healthy only while a verified backup is within the 24-hour boundary', () => {
|
|
expect(backupPresentation({ state: 'healthy', reason: 'backup_verified', ageSeconds: 3600 })).toMatchObject({ state: 'healthy', reason: 'backup_verified' });
|
|
expect(backupPresentation({ state: 'healthy', reason: 'backup_verified', ageSeconds: BACKUP_STALE_AFTER_SECONDS + 1 })).toMatchObject({ state: 'degraded', reason: 'backup_stale' });
|
|
});
|
|
|
|
it('fails closed when a healthy claim has no age or timestamp', () => {
|
|
expect(backupPresentation({ state: 'healthy', reason: 'backup_verified' })).toMatchObject({ state: 'unknown', reason: 'no_verified_backup' });
|
|
});
|
|
});
|
|
|
|
describe('overviewTitle', () => {
|
|
it('reserves the optimistic title for a healthy issue-free snapshot', () => {
|
|
const healthy = aggregateStatus(ready(status()), observedAt + 1_000);
|
|
expect(overviewTitle(healthy, 0)).toBe('Alles onder controle');
|
|
expect(overviewTitle(healthy, 1)).toBe('Aandacht vereist');
|
|
expect(overviewTitle(healthy, 0, true)).toBe('Aandacht vereist');
|
|
});
|
|
|
|
it('uses a conservative title for unknown telemetry', () => {
|
|
const unknown = aggregateStatus(ready(status({ overallState: 'unknown' })), observedAt + 1_000);
|
|
expect(overviewTitle(unknown, 0)).toBe('Status nog niet bevestigd');
|
|
});
|
|
|
|
it('does not claim that no sources exist when an unknown status has live sources', () => {
|
|
const unknown = aggregateStatus(ready(status({ overallState: 'unknown', sourceLag: [{ sourceId: 'unraid', state: 'healthy', reason: 'fresh' }] })), observedAt + 1_000);
|
|
expect(unknown.detail).toContain('Databronnen zijn verbonden');
|
|
});
|
|
});
|