Show match demand in metrics (Refs #138)
CI / checks (push) Successful in 4m44s

This commit is contained in:
2026-10-10 16:26:32 -04:00
parent ea932ec563
commit 616b1937ed
15 changed files with 775 additions and 42 deletions
+63
View File
@@ -3,12 +3,20 @@ import { createMetricsRefresh } from '../../src/lib/ui/metrics-refresh.js';
afterEach(() => vi.useRealTimers());
const totals = { observed: 0, active: 0, idle: 0, unknown: 0, playerHours: 0 };
const emptyMatches = {
started: 0,
withRecordedPlayers: 0,
averageGroupSize: null,
solo: 0,
soloShare: null
};
const report = (days: number) => ({
days,
now: 1000,
since: 0,
summary: totals,
running: { average: null, peak: 0, capturedHours: 0 },
matches: emptyMatches,
points: [],
maps: [],
servers: [],
@@ -134,6 +142,60 @@ describe('metrics background refresh', () => {
expect(refresh.onLoading).toHaveBeenLastCalledWith(false);
});
it('preserves successful match reports when fleet or map match data is malformed', async () => {
const matches = {
started: 3,
withRecordedPlayers: 2,
averageGroupSize: 1.5,
solo: 1,
soloShare: 50
};
const map = {
...totals,
game: 't5',
map: 'Moon',
matches,
noEmpty: 0,
availabilityObserved: 0,
availabilityUnknown: 0,
noEmptyAtMaximum: 0,
noEmptyBelowMaximum: 0,
policy: null
};
const value = { ...report(7), matches, maps: [map] };
const invalidMatches = [
undefined,
null,
{ ...matches, withRecordedPlayers: undefined },
{ ...matches, started: -1 },
{ ...matches, solo: 0.5 },
{ ...matches, averageGroupSize: '1.5' },
{ ...matches, soloShare: 101 }
];
const invalid = invalidMatches.flatMap((incomplete) => [
{ ...value, matches: incomplete },
{ ...value, maps: [{ ...map, matches: incomplete }] }
]);
const fetcher = vi.fn().mockResolvedValueOnce(Response.json(value));
for (const incomplete of invalid) fetcher.mockResolvedValueOnce(Response.json(incomplete));
const empty = { ...report(7), maps: [{ ...map, matches: emptyMatches }] };
fetcher.mockResolvedValueOnce(Response.json(empty));
const refresh = setup(fetcher);
await refresh.refresh();
expect(refresh.onData).toHaveBeenLastCalledWith(value);
for (const _ of invalid) {
await refresh.refresh();
expect(refresh.onData).toHaveBeenCalledTimes(1);
expect(refresh.onError).toHaveBeenLastCalledWith(
'The metrics response was incomplete.'
);
}
await refresh.refresh();
expect(refresh.onData).toHaveBeenLastCalledWith(empty);
expect(refresh.onData).toHaveBeenCalledTimes(2);
expect(refresh.onError).toHaveBeenLastCalledWith(null);
});
it('accepts availability reports and rejects incomplete running or policy evidence', async () => {
const value = {
...report(7),
@@ -142,6 +204,7 @@ describe('metrics background refresh', () => {
...totals,
game: 't5',
map: 'Moon',
matches: emptyMatches,
noEmpty: 0,
availabilityObserved: 0,
availabilityUnknown: 1,