import { afterEach, beforeEach, expect, it, vi } from 'vitest'; import { newFixture, type Fixture } from '../helpers/fixture.js'; import { parseMetricsQuery } from '../../src/lib/metrics.js'; let fixture: Fixture; beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); }); afterEach(async () => { vi.restoreAllMocks(); await fixture.cleanup(); }); it('runs reports off the request thread and invalidates cached history after writes', async () => { const { db } = await import('../../src/lib/server/db/index.js'); const { recordMetrics, readMetrics } = await import('../../src/lib/server/fleet/metrics.js'); const { metricsReport } = await import('../../src/lib/server/reports/index.js'); const now = Date.UTC(2026, 9, 4, 12); vi.spyOn(Date, 'now').mockReturnValue(now); const sample = { server: 'one', game: 't5', map: 'Moon', players: 1, capacity: 4 }; recordMetrics([sample], now - 30_000); const query = parseMetricsQuery(new URLSearchParams('days=1')); const first = metricsReport(query); const winner = await Promise.race([ first.then(() => 'report'), new Promise((resolve) => setTimeout(() => resolve('timer'), 0)) ]); expect(winner).toBe('timer'); expect(await first).toEqual(readMetrics(query, now)); expect(await metricsReport(query)).toEqual(await first); db().prepare('UPDATE metric_samples SET players=3').run(); const changed = await metricsReport(query); expect(changed).toEqual(readMetrics(query, now)); expect(changed.summary.playerHours).toBeCloseTo((await first).summary.playerHours * 3); });