Cover beta health continuity and readiness boundaries (Refs #149)
CI / checks (push) Successful in 5m29s
CI / checks (push) Successful in 5m29s
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user