import { describe, expect, it } from 'vitest'; import { defaultPlan, parseBetaPlan, requirement, type BetaPlan } from '../../src/lib/beta-plan.js'; import { applySample, applySignal, coveragePassed, emptyMetrics, evaluateRequirements, type TestSignal } from '../../src/lib/server/fleet/beta-evaluation.js'; const signal = (event: TestSignal['event'], extra: Partial = {}): TestSignal => ({ event, bootId: 'boot-1', sessionId: 'session-1', sequence: 0, round: 0, players: 4, name: '', ...extra }); const planWith = (...kinds: Parameters[0][]): BetaPlan => ({ ...defaultPlan(), requirements: kinds.map((k, i) => requirement(k, `r${i}`)) }); describe('beta requirement evaluation', () => { it('copies a reactive preset without cloning proxies or mutating the saved requirements', () => { const source = defaultPlan(); const proxied = new Proxy(source, { get(target, key, receiver) { return key === 'requirements' ? new Proxy(target.requirements, {}) : Reflect.get(target, key, receiver); } }); const selected = parseBetaPlan(proxied); expect(selected).toEqual(source); selected.requirements[0].target = 15; selected.requirements.push(requirement('round', 'round')); expect(source.requirements).toHaveLength(1); expect(source.requirements[0].target).toBe(1); }); it('validates plans without accepting unknown kinds, duplicate IDs, fractional counts or unsafe numbers', () => { expect(parseBetaPlan(defaultPlan())).toEqual(defaultPlan()); expect(() => parseBetaPlan({ ...defaultPlan(), requirements: [] })).toThrow(); expect(() => parseBetaPlan({ ...defaultPlan(), timeoutHours: NaN })).toThrow(); expect(() => parseBetaPlan({ ...defaultPlan(), requirements: [requirement('round', 'same'), requirement('round', 'same')] })).toThrow(); expect(() => parseBetaPlan({ ...defaultPlan(), requirements: [{ ...requirement('round', 'r'), target: 1.5 }] })).toThrow(); expect(() => parseBetaPlan({ ...defaultPlan(), requirements: [requirement('manual', 'constructor')] })).toThrow(); }); it('counts only qualifying rounds and completed matches from the same session', () => { const plan = planWith('round', 'matches'); plan.requirements[0].minPlayers = 4; plan.requirements[1] = { ...plan.requirements[1], target: 2, minRound: 25, minPlayers: 4, minMinutes: 1 }; const m = emptyMetrics(); expect(applySignal(plan, m, signal('round_change', { sequence: 1, round: 50 }), 0)).toBe(false); applySignal(plan, m, signal('boot'), 0); applySignal(plan, m, signal('session_start'), 0); applySignal(plan, m, signal('round_change', { sequence: 1, round: 30, players: 1 }), 1000); applySignal(plan, m, signal('match_end', { sequence: 2 }), 61_000); expect(m.values.r0 ?? 0).toBe(0); expect(m.values.r1 ?? 0).toBe(0); applySignal(plan, m, signal('session_start', { sessionId: 'session-2' }), 62_000); applySignal(plan, m, signal('round_change', { sessionId: 'session-2', sequence: 1, round: 25 }), 63_000); applySignal(plan, m, signal('match_end', { sessionId: 'session-2', sequence: 2 }), 123_000); expect(m.values).toMatchObject({ r0: 25, r1: 1 }); expect(applySignal(plan, m, signal('match_end', { sessionId: 'session-2', sequence: 3 }), 124_000)).toBe(false); }); it('rejects out-of-order events and retired boots, while counting actual boot changes once', () => { const plan = planWith('feature', 'restart-budget'); plan.requirements[0].event = 'perk.purchased'; const m = emptyMetrics(); applySignal(plan, m, signal('boot'), 0); applySignal(plan, m, signal('session_start'), 0); applySignal(plan, m, signal('feature', { sequence: 2, name: 'perk.purchased' }), 1); expect(applySignal(plan, m, signal('feature', { sequence: 1, name: 'perk.purchased' }), 2)).toBe(false); applySignal(plan, m, signal('boot', { bootId: 'boot-2' }), 3); expect(applySignal(plan, m, signal('boot'), 4)).toBe(false); expect(m.values.r0).toBe(1); expect(evaluateRequirements(plan, m, 0)[1].failed).toBe(true); expect(m.restarts).toBe(1); }); it('does not double count unqualified matches across log and telemetry sources', () => { const plan = planWith('matches'); const m = emptyMetrics(); applySignal(plan, m, signal('boot'), 0); applySignal(plan, m, signal('session_start'), 0); applySignal(plan, m, signal('match_end', { sequence: 1 }), 1000); expect(m.values.r0).toBeUndefined(); // these requirements use incremental match logs }); it('integrates populated time and player-hours conservatively and excludes monitoring gaps', () => { const plan = planWith('populated-minutes', 'player-hours', 'healthy-minutes', 'population', 'sustained-population', 'failure-budget'); plan.requirements[4].minPlayers = 4; const m = emptyMetrics(); applySample(plan, m, 0, true, 4); applySample(plan, m, 30_000, true, 4); applySample(plan, m, 60_000, true, 4); expect(m.values).toMatchObject({ r0: 1, r1: 4 / 60, r2: 1, r3: 4, r4: 1 }); applySample(plan, m, 300_000, true, 4); expect(m.values.r0).toBe(1); expect(m.values.r2).toBe(0); applySample(plan, m, 330_000, false, 0); applySample(plan, m, 360_000, false, 0); expect(m.outages).toBe(1); expect(evaluateRequirements(plan, m, 0)[5].failed).toBe(true); }); it('requires readable logs, boot evidence and explicit manual sign-off', () => { const plan = planWith('clean-logs', 'restart-budget', 'manual'); const m = emptyMetrics(); expect(evaluateRequirements(plan, m, 0).every(r => !r.passed)).toBe(true); m.logAvailable = true; m.bootId = 'boot'; m.manual.r2 = 'Josh: HUD checked'; expect(evaluateRequirements(plan, m, 0).every(r => r.passed)).toBe(true); m.logErrors = 1; expect(evaluateRequirements(plan, m, 0)[0].failed).toBe(true); }); it('requires each selected coverage group even when many servers on one map pass', () => { const servers = [{ game: 't5', map: 'a', passed: true }, { game: 't5', map: 'a', passed: true }, { game: 't6', map: 'b', passed: false }]; expect(coveragePassed({ ...defaultPlan(), scope: 'quorum', quorum: 2 }, servers)).toBe(true); for (const scope of ['all', 'map', 'game', 'map-game'] as const) expect(coveragePassed({ ...defaultPlan(), scope }, servers)).toBe(false); servers[2].passed = true; expect(coveragePassed({ ...defaultPlan(), scope: 'map-game' }, servers)).toBe(true); }); });