From 272055a1ed6ef6ce1fbd537fcc8bcdc276179361 Mon Sep 17 00:00:00 2001 From: Josh Wright Date: Mon, 12 Oct 2026 19:09:27 -0400 Subject: [PATCH] Cover beta health continuity and readiness boundaries (Refs #149) --- tests/unit/beta-evaluation.test.ts | 38 +++++++++++++++++++++++ tests/unit/beta-plans.test.ts | 48 ++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/tests/unit/beta-evaluation.test.ts b/tests/unit/beta-evaluation.test.ts index 29d9c8f..37729a4 100644 --- a/tests/unit/beta-evaluation.test.ts +++ b/tests/unit/beta-evaluation.test.ts @@ -152,6 +152,44 @@ describe('beta requirement evaluation', () => { expect(m.values.r0).toBe(0); }); + it.each([ + { minutes: 2, gap: 60_005 }, + { minutes: 2.5, gap: 60_048 }, + { minutes: 2.5, gap: 90_000 } + ])( + 'preserves a satisfied healthy-minute requirement after a $gap ms gap', + ({ minutes, gap }) => { + const plan = planWith('healthy-minutes', 'failure-budget'); + const m = emptyMetrics(); + const previous = minutes * 60_000; + for (let at = 0; at <= previous; at += 30_000) applySample(plan, m, at, true); + expect(m.values.r0).toBe(minutes); + expect(evaluateRequirements(plan, m, 0).every((r) => r.passed)).toBe(true); + + applySample(plan, m, previous + gap, true); + + expect(m.values.r0).toBeCloseTo(minutes + gap / 60_000); + expect(m.wasHealthy).toBe(true); + expect(m.outages).toBe(0); + expect(evaluateRequirements(plan, m, 0).every((r) => r.passed)).toBe(true); + } + ); + + it('resets a satisfied healthy-minute requirement on an unhealthy sample within the freshness window', () => { + const plan = planWith('healthy-minutes', 'failure-budget'); + const m = emptyMetrics(); + applySample(plan, m, 0, true); + applySample(plan, m, 60_000, true); + expect(evaluateRequirements(plan, m, 0)[0].passed).toBe(true); + + applySample(plan, m, 90_000, false); + + expect(m.values.r0).toBe(0); + expect(m.wasHealthy).toBe(false); + expect(m.outages).toBe(1); + expect(evaluateRequirements(plan, m, 0)[0].passed).toBe(false); + }); + it('excludes monitoring gaps from healthy time and requires explicit manual sign-off', () => { const plan = planWith('healthy-minutes', 'failure-budget', 'manual'); const m = emptyMetrics(); diff --git a/tests/unit/beta-plans.test.ts b/tests/unit/beta-plans.test.ts index f21fb80..f3438f3 100644 --- a/tests/unit/beta-plans.test.ts +++ b/tests/unit/beta-plans.test.ts @@ -367,6 +367,54 @@ describe('durable beta test plans', () => { await expect(promoteBeta(id)).rejects.toThrow('Test plan has not passed'); }); + it.each(['expired', 'unhealthy', 'gap'] as const)( + 'preserves healthy-minute readiness across polling variation until evidence is %s', + async (failure) => { + const id = await openPlan(defaultPlan()); + const { getBeta, listBetaServers, promoteBeta } = + await import('../../src/lib/server/fleet/beta.js'); + const { currentRun, readMetrics, testView } = + await import('../../src/lib/server/fleet/beta-tests.js'); + await tick(); + await tick(); + await tick(30); + await tick(30); + const metrics = () => readMetrics(currentRun(id, names[0])!); + const expectReady = () => { + expect(getBeta(id)?.state).toBe('ready-promote'); + expect(listBetaServers(id)[0].stage).toBe('ready'); + expect(testView(id)?.outcome).toBe('passed'); + expect(metrics()).toMatchObject({ wasHealthy: true, outages: 0 }); + }; + expectReady(); + let minutes = metrics().values.health; + for (const seconds of [60.005, 60.048, 90]) { + await tick(seconds); + minutes += seconds / 60; + expect(metrics().values.health).toBeCloseTo(minutes); + expectReady(); + } + if (failure === 'expired') { + const { betaTick } = await import('../helpers/provisioning.js'); + // Re-reading the same sample cannot extend its original lifetime. + vi.setSystemTime(Date.now() + 90_000); + await betaTick(); + expectReady(); + expect(metrics().values.health).toBeCloseTo(minutes); + vi.setSystemTime(Date.now() + 1); + await betaTick(); + } else { + await tick(failure === 'gap' ? 90.001 : 15, 0, failure === 'gap'); + } + expect(metrics().values.health).toBe(0); + expect(metrics().outages).toBe(failure === 'gap' ? 0 : 1); + expect(getBeta(id)?.state).toBe('running'); + expect(listBetaServers(id)[0].stage).toBe('observing'); + expect(testView(id)?.outcome).toBe('active'); + await expect(promoteBeta(id)).rejects.toThrow('Test plan has not passed'); + } + ); + it('revokes readiness if health fails while awaiting manual promotion', async () => { const id = await openPlan({ ...defaultPlan(),