Clarify provisioning source labels and filters
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { newFixture, type Fixture } from '../helpers/fixture.js';
|
||||
import { provisioningListSources, provisioningMatchesSource, provisioningSourceLabel, type ProvisioningListSource } from '../../src/lib/provisioning-list.js';
|
||||
|
||||
let fixture: Fixture;
|
||||
beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); });
|
||||
@@ -19,6 +20,79 @@ async function setup() {
|
||||
}
|
||||
|
||||
describe('bounded provisioning lists', () => {
|
||||
it('keeps recovery sources concise as incident summaries change, including after retention', async () => {
|
||||
const { add, handle, deploymentListPage } = await setup();
|
||||
add('recovery', 'waiting', 'rebuild');
|
||||
handle.prepare("INSERT INTO provisioning_sources VALUES ('recovery','watchdog','incident','t5_zombie_theater_1')").run();
|
||||
handle.prepare(`INSERT INTO watchdog_incidents (id,server,kind,state,summary,first_seen_at,last_seen_at)
|
||||
VALUES ('incident','t5_zombie_theater_1','health','recovering',?,'2026-01-01','2026-01-01')`)
|
||||
.run('Rebuild queued with the latest promoted setup');
|
||||
|
||||
for (const summary of ['Rebuild queued with the latest promoted setup', 'Recovered game service']) {
|
||||
handle.prepare('UPDATE watchdog_incidents SET summary=? WHERE id=?').run(summary, 'incident');
|
||||
const row = deploymentListPage({ view: 'active' }).deployments[0];
|
||||
expect(provisioningSourceLabel(row)).toBe('Recovery');
|
||||
expect(row.sources).toEqual([{ kind: 'watchdog', source_id: 'incident', label: summary, href: '/maintenance/incident' }]);
|
||||
}
|
||||
handle.prepare("DELETE FROM watchdog_incidents WHERE id='incident'").run();
|
||||
const retained = deploymentListPage({ view: 'active' }).deployments[0];
|
||||
expect(provisioningSourceLabel(retained)).toBe('Recovery');
|
||||
expect(retained.sources?.[0].href).toBeNull();
|
||||
});
|
||||
|
||||
it.each(['active', 'history'] as const)('keeps %s source categories and client filters aligned with API filters', async view => {
|
||||
const { add, handle, deploymentListPage } = await setup();
|
||||
const categories: [ProvisioningListSource, string, string][] = [
|
||||
['campaign', 'campaign', 'Campaign'], ['beta', 'beta', 'Beta'], ['maintenance', 'maintenance', 'Maintenance'],
|
||||
['recovery', 'watchdog', 'Recovery'], ['autoscaling', 'autoscaling', 'Autoscaling'], ['rebalance', 'rebalance', 'Rebalance']
|
||||
];
|
||||
const state = view === 'active' ? 'waiting' : 'done';
|
||||
for (const [source, kind] of categories) {
|
||||
// Rebalance ownership is recorded only in triggered_by.
|
||||
if (source !== 'rebalance') {
|
||||
add(`${source}-linked`, state);
|
||||
handle.prepare('INSERT INTO provisioning_sources VALUES (?,?,?,?)').run(`${source}-linked`, kind, 'expired', 't5_zombie_theater_1');
|
||||
}
|
||||
add(`${source}-legacy`, state);
|
||||
handle.prepare('UPDATE server_deployments SET triggered_by=? WHERE id=?').run(`${kind}:expired`, `${source}-legacy`);
|
||||
}
|
||||
add('manual', state);
|
||||
handle.prepare("UPDATE server_deployments SET triggered_by='ui' WHERE id='manual'").run();
|
||||
add('system', state);
|
||||
// A shared operation must be discoverable through each of its sources, even when initiated manually.
|
||||
add('shared', state);
|
||||
handle.prepare("UPDATE server_deployments SET triggered_by='ui' WHERE id='shared'").run();
|
||||
handle.exec(`INSERT INTO provisioning_sources VALUES ('shared','campaign','expired','t5_zombie_theater_1');
|
||||
INSERT INTO provisioning_sources VALUES ('shared','watchdog','expired','t5_zombie_theater_1');`);
|
||||
|
||||
const rows = deploymentListPage({ view }).deployments;
|
||||
expect(rows).toHaveLength(14);
|
||||
for (const [source, , label] of categories) {
|
||||
for (const suffix of source === 'rebalance' ? ['legacy'] : ['linked', 'legacy']) {
|
||||
expect(provisioningSourceLabel(rows.find(row => row.id === `${source}-${suffix}`)!)).toBe(label);
|
||||
}
|
||||
}
|
||||
expect(provisioningSourceLabel(rows.find(row => row.id === 'manual')!)).toBe('Manual');
|
||||
expect(provisioningSourceLabel(rows.find(row => row.id === 'system')!)).toBe('System');
|
||||
|
||||
const { GET } = await import('../../src/routes/api/provisioning/+server.js');
|
||||
for (const source of provisioningListSources) {
|
||||
const expected = source === 'manual' || source === 'system' ? [source]
|
||||
: source === 'rebalance' ? ['rebalance-legacy']
|
||||
: [`${source}-linked`, `${source}-legacy`, ...(['campaign', 'recovery'].includes(source) ? ['shared'] : [])];
|
||||
expect(rows.filter(row => provisioningMatchesSource(row, source)).map(row => row.id).sort()).toEqual(expected.sort());
|
||||
const result = await GET({ url: new URL(`http://localhost/api/provisioning?view=${view}&source=${source}`) }).json();
|
||||
expect(result.total).toBe(expected.length);
|
||||
expect(result.deployments.map((row: { id: string }) => row.id).sort()).toEqual(expected);
|
||||
if (view === 'history') {
|
||||
const page = deploymentListPage({ view, source, limit: 1, offset: expected.length - 1 });
|
||||
expect(page.total).toBe(expected.length);
|
||||
expect(page.deployments).toHaveLength(1);
|
||||
expect(provisioningMatchesSource(page.deployments[0], source)).toBe(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('filters readable sources before pagination and retains expired source references without broken links', async () => {
|
||||
const { add, handle, deploymentListPage } = await setup();
|
||||
handle.exec(`INSERT INTO scripts(id,name,target_rel_path) VALUES(1,'Common scripts','common.gsc');
|
||||
@@ -41,6 +115,8 @@ describe('bounded provisioning lists', () => {
|
||||
expect(second.deployments[0]).toMatchObject({ id: 'old-campaign', sources: [{ label: 'Promotion · Beta #75', href: '/campaigns/campaign' }] });
|
||||
const beta = deploymentListPage({ view: 'active', source: 'beta', status: 'pending' });
|
||||
expect(beta.deployments).toEqual([expect.objectContaining({ id: 'linked-beta', sources: [{ kind: 'beta', source_id: '75', label: 'Beta #75: Gameplay checks', href: '/betas/75' }] })]);
|
||||
expect(provisioningSourceLabel(second.deployments[0])).toBe('Campaign');
|
||||
expect(provisioningSourceLabel(beta.deployments[0])).toBe('Beta');
|
||||
expect(deploymentListPage({ view: 'history', source: 'manual' }).deployments.map(row => row.id)).toEqual(['manual']);
|
||||
expect(deploymentListPage({ view: 'history', source: 'system' }).total).toBe(30);
|
||||
const { GET } = await import('../../src/routes/api/provisioning/+server.js');
|
||||
|
||||
Reference in New Issue
Block a user