Simplify beta lists and combine server test details
This commit is contained in:
@@ -46,3 +46,41 @@ it('coalesces beta progress notifications after synchronous database work', asyn
|
||||
expect(events).toEqual([{ type: 'beta-changed', betaId: 1 }, { type: 'beta-changed', betaId: 2 }]);
|
||||
} finally { stop(); }
|
||||
});
|
||||
|
||||
it('filters by displayed status and searches servers and file paths before pagination', async () => {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { defaultPlan } = await import('../../src/lib/beta-plan.js');
|
||||
const { betaListPage } = await import('../../src/lib/server/fleet/beta-pages.js');
|
||||
const { betaStatus } = await import('../../src/lib/ui/betas.js');
|
||||
const { GET } = await import('../../src/routes/api/betas/+server.js');
|
||||
db().exec(`INSERT INTO scripts(id,name,target_rel_path) VALUES(1,'Plugin','special-file.dll');
|
||||
INSERT INTO script_versions(id,script_id,version,sha256,size) VALUES(1,1,1,'hash',0);
|
||||
INSERT INTO servers(name) VALUES('t5_zombie_theater_3');`);
|
||||
const states = [
|
||||
['running','active','awaiting-empty'], ['ready-promote','passed','ready'], ['running','failed','failed'],
|
||||
['running','paused','observing'], ['promoted','passed','ready'], ['rolled-back','paused','ready'], ['running','inconclusive','observing']
|
||||
];
|
||||
for (let index = 0; index < 35; index++) {
|
||||
const [state, outcome, stage] = states[index % states.length];
|
||||
const id = index + 1;
|
||||
db().prepare('INSERT INTO betas(id,script_id,script_version_id,trigger_kind,state,notes) VALUES(?,1,1,?,?,?)').run(id, 'ui', state, `Beta test ${id}`);
|
||||
db().prepare('INSERT INTO beta_scripts(beta_id,script_id,script_version_id) VALUES(?,1,1)').run(id);
|
||||
db().prepare('INSERT INTO beta_servers(beta_id,server,stage,had_original) VALUES(?,?,?,0)').run(id, 't5_zombie_theater_3', stage);
|
||||
db().prepare('INSERT INTO beta_test_plans(beta_id,plan,outcome) VALUES(?,?,?)').run(id, JSON.stringify(defaultPlan()), outcome);
|
||||
}
|
||||
const body = await (await GET({ url: new URL('http://localhost/api/betas?limit=100') })).json();
|
||||
for (const status of ['ready', 'testing', 'attention', 'paused', 'promoted', 'rolled-back']) {
|
||||
const expected = body.betas.filter((beta: Parameters<typeof betaStatus>[0]) => betaStatus(beta).key === status);
|
||||
const result = betaListPage(new URLSearchParams({ status, limit: '100' }));
|
||||
expect(result.total).toBe(expected.length);
|
||||
expect(result.betas.map(beta => beta.id)).toEqual(expected.map((beta: { id: number }) => beta.id));
|
||||
}
|
||||
expect(betaListPage(new URLSearchParams('q=Kino%20Der%20Toten')).total).toBe(35);
|
||||
expect(betaListPage(new URLSearchParams('q=special-file.dll')).total).toBe(35);
|
||||
expect(betaListPage(new URLSearchParams('q=%2335')).betas.map(beta => beta.id)).toEqual([35]);
|
||||
const first = betaListPage(new URLSearchParams('view=active&status=attention&q=Kino&limit=3'));
|
||||
const second = betaListPage(new URLSearchParams('view=active&status=attention&q=Kino&limit=3&page=2'));
|
||||
expect(first.total).toBe(10);
|
||||
expect(first.betas).toHaveLength(3);
|
||||
expect(second.betas.some(beta => first.betas.some(other => other.id === beta.id))).toBe(false);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { expect, it, vi } from 'vitest';
|
||||
import { render } from 'svelte/server';
|
||||
import { writable } from 'svelte/store';
|
||||
import { defaultPlan, requirement, type BetaTestView } from '../../src/lib/beta-plan.js';
|
||||
import { betaStatus, type BetaDeploymentServer } from '../../src/lib/ui/betas.js';
|
||||
import BetaTestProgress from '../../src/lib/ui/components/BetaTestProgress.svelte';
|
||||
|
||||
vi.mock('$app/stores', () => ({ page: writable({ url: new URL('http://localhost/betas/75') }) }));
|
||||
|
||||
it('prioritizes lifecycle, failures and pauses over readiness without treating stage text as failure', () => {
|
||||
expect(betaStatus({ state: 'promoted', testOutcome: 'paused', servers: [] })).toMatchObject({ key: 'promoted', label: 'Promoted' });
|
||||
expect(betaStatus({ state: 'rolled-back', testOutcome: 'failed', servers: [] })).toMatchObject({ key: 'rolled-back' });
|
||||
expect(betaStatus({ state: 'ready-promote', testOutcome: 'failed', servers: [] })).toMatchObject({ key: 'attention', tone: 'danger' });
|
||||
expect(betaStatus({ state: 'ready-promote', testOutcome: 'paused', servers: [] })).toMatchObject({ key: 'paused' });
|
||||
expect(betaStatus({ state: 'ready-promote', testOutcome: 'passed', servers: [{ stage: 'ready' }] })).toMatchObject({ key: 'ready' });
|
||||
expect(betaStatus({ state: 'running', servers: [{ stage: 'awaiting-empty' }] })).toMatchObject({ key: 'testing' });
|
||||
expect(betaStatus({ state: 'running', servers: [{ stage: 'ready', server_deleted_at: '2026-10-06' }] })).toMatchObject({ key: 'attention' });
|
||||
});
|
||||
|
||||
const deployment = (server: string, stage = 'ready'): BetaDeploymentServer => ({ server, stage, error: null, deployed_at: '2026-10-06', restarted_at: '2026-10-06', healthy_at: '2026-10-06', match_seen_at: null, provisioning_id: null });
|
||||
const testRun = (server: string, passed = true, failed = false): BetaTestView['servers'][number] => ({
|
||||
server, server_deleted_at: null, phase: 1, passed, observingAt: '2026-10-06',
|
||||
progress: [{ id: 'health', label: 'Health', current: passed ? 1 : 0, target: 1, passed, failed, detail: failed ? 'Health check failed' : 'Health observation' }]
|
||||
});
|
||||
|
||||
it('combines deployment and tests once per server, expands failures first, and keeps passing details collapsed', () => {
|
||||
const test: BetaTestView = { plan: defaultPlan(), outcome: 'failed', reason: null, servers: [testRun('passed'), testRun('failed', false, true)], evidence: [] };
|
||||
const body = render(BetaTestProgress, { props: { test, betaId: 75, closed: false, deployments: [deployment('passed'), deployment('waiting', 'awaiting-empty'), deployment('failed')], onUpdate: async () => {} } }).body;
|
||||
expect(body).toContain('Deployment</th>');
|
||||
expect(body).toContain('Test result</th>');
|
||||
expect(body).toContain('Awaiting test run');
|
||||
expect(body.indexOf('id="beta-server-75-failed"')).toBeLessThan(body.indexOf('id="beta-server-75-passed"'));
|
||||
expect(body).toMatch(/<tr hidden[^>]*id="beta-server-75-passed"/);
|
||||
expect(body).toMatch(/<tr id="beta-server-75-failed"/);
|
||||
expect(body.match(/id="beta-server-75-passed"/g)).toHaveLength(1);
|
||||
expect(body).toContain('View relevant evidence');
|
||||
expect(body).not.toContain('Search evidence');
|
||||
});
|
||||
|
||||
it('keeps manual sign-offs in server details and disables them on closed or deleted tests', () => {
|
||||
const test: BetaTestView = { plan: { ...defaultPlan(), requirements: [requirement('manual', 'health')] }, outcome: 'active', reason: null, servers: [testRun('manual', false)], evidence: [] };
|
||||
const props = { test, betaId: 75, closed: false, deployments: [deployment('manual')], onUpdate: async () => {} };
|
||||
expect(render(BetaTestProgress, { props }).body).toContain('Tester name and observation');
|
||||
expect(render(BetaTestProgress, { props: { ...props, closed: true } }).body).not.toContain('Tester name and observation');
|
||||
const deleted = render(BetaTestProgress, { props: { ...props, deletedParticipants: true, deployments: [{ ...deployment('manual'), server_deleted_at: '2026-10-06' }] } }).body;
|
||||
expect(deleted).not.toContain('Tester name and observation');
|
||||
expect(deleted).not.toContain('href="/servers/manual');
|
||||
});
|
||||
|
||||
it('shows missing historical evidence as not recorded, without claiming zero servers passed', () => {
|
||||
const test: BetaTestView = { plan: defaultPlan(), outcome: 'passed', reason: null, servers: [], evidence: [] };
|
||||
const body = render(BetaTestProgress, { props: { test, betaId: 75, closed: true, deployments: [deployment('historical')], onUpdate: async () => {} } }).body;
|
||||
expect(body).toContain('Not recorded');
|
||||
expect(body).not.toContain('Awaiting test run');
|
||||
expect(body).not.toContain('0 / 0');
|
||||
});
|
||||
Reference in New Issue
Block a user