Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
@@ -5,34 +5,87 @@ import type { ProvisioningDetail, RecoveryObservation } from '../../src/lib/prov
|
||||
import { provisioningNeedsRefresh } from '../../src/lib/ui/provisioning-presentation.js';
|
||||
import ProvisioningPage from '../../src/routes/provisioning/[id]/+page.svelte';
|
||||
|
||||
vi.mock('$app/stores', () => ({ page: writable({
|
||||
url: new URL('http://localhost/provisioning/operation-a'), data: { user: null }
|
||||
}) }));
|
||||
vi.mock('$app/stores', () => ({
|
||||
page: writable({
|
||||
url: new URL('http://localhost/provisioning/operation-a'),
|
||||
data: { user: null }
|
||||
})
|
||||
}));
|
||||
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
|
||||
|
||||
const start = '2026-10-09T12:00:00Z';
|
||||
const finish = '2026-10-09T12:01:00Z';
|
||||
function fixture(state = 'done', recovery: RecoveryObservation | null = null): ProvisioningDetail {
|
||||
return {
|
||||
deployment: { id: 'operation-a', server: 't5_zombie_theater_1', action: 'restart', state,
|
||||
stage: 'Completed', server_deleted_at: null, server_exists: 1, started_at: start, finished_at: finish,
|
||||
triggered_by: 'watchdog:incident-a', requested_by: 'Maxis', error: null, warnings: '[]' },
|
||||
generatedAt: finish, events: [], activity: { total: 0, latestId: null, failureExcerpt: null },
|
||||
sources: [{ kind: 'watchdog', source_id: 'incident-a', label: 'Deleted watchdog incident', href: null }],
|
||||
recipe: null, blueprintName: null, canResumeCleanup: false, canResumeRebuild: false,
|
||||
canResumeVerification: false, canCancelRemoval: false,
|
||||
deployment: {
|
||||
id: 'operation-a',
|
||||
server: 't5_zombie_theater_1',
|
||||
action: 'restart',
|
||||
state,
|
||||
stage: 'Completed',
|
||||
server_deleted_at: null,
|
||||
server_exists: 1,
|
||||
started_at: start,
|
||||
finished_at: finish,
|
||||
triggered_by: 'watchdog:incident-a',
|
||||
requested_by: 'Maxis',
|
||||
error: null,
|
||||
warnings: '[]'
|
||||
},
|
||||
generatedAt: finish,
|
||||
events: [],
|
||||
activity: { total: 0, latestId: null, failureExcerpt: null },
|
||||
sources: [
|
||||
{
|
||||
kind: 'watchdog',
|
||||
source_id: 'incident-a',
|
||||
label: 'Deleted watchdog incident',
|
||||
href: null
|
||||
}
|
||||
],
|
||||
recipe: null,
|
||||
blueprintName: null,
|
||||
canResumeCleanup: false,
|
||||
canResumeRebuild: false,
|
||||
canResumeVerification: false,
|
||||
canCancelRemoval: false,
|
||||
collection: { id: 'collection-a', name: 'Evening lobbies' },
|
||||
progress: { version: 1, phase: 'verify-game', phaseStartedAt: '2026-10-09T12:00:50Z', observedAt: finish,
|
||||
blocker: null, checks: [], checkpoints: [], command: null, recovery, recipeStatus: null },
|
||||
agentOperation: { id: 'command-a', hostId: 'host-a', state: 'completed', createdAt: start, updatedAt: finish, error: null },
|
||||
capacity: { evaluatedAt: finish, pools: [] }, warningMessages: []
|
||||
progress: {
|
||||
version: 1,
|
||||
phase: 'verify-game',
|
||||
phaseStartedAt: '2026-10-09T12:00:50Z',
|
||||
observedAt: finish,
|
||||
blocker: null,
|
||||
checks: [],
|
||||
checkpoints: [],
|
||||
command: null,
|
||||
recovery,
|
||||
recipeStatus: null
|
||||
},
|
||||
agentOperation: {
|
||||
id: 'command-a',
|
||||
hostId: 'host-a',
|
||||
state: 'completed',
|
||||
createdAt: start,
|
||||
updatedAt: finish,
|
||||
error: null
|
||||
},
|
||||
capacity: { evaluatedAt: finish, pools: [] },
|
||||
warningMessages: []
|
||||
};
|
||||
}
|
||||
function pageBody(detail: ProvisioningDetail) {
|
||||
return render(ProvisioningPage, { props: { data: { detail, user: null, setupRequired: false } } }).body;
|
||||
return render(ProvisioningPage, {
|
||||
props: { data: { detail, user: null, setupRequired: false } }
|
||||
}).body;
|
||||
}
|
||||
function recovery(state: RecoveryObservation['state']): RecoveryObservation {
|
||||
return { state, at: finish, message: 'Recovery status updated', ...(state === 'transferred' ? { operationId: 'rebuild-a' } : {}) };
|
||||
return {
|
||||
state,
|
||||
at: finish,
|
||||
message: 'Recovery status updated',
|
||||
...(state === 'transferred' ? { operationId: 'rebuild-a' } : {})
|
||||
};
|
||||
}
|
||||
|
||||
describe('provisioning detail summary and technical details', () => {
|
||||
@@ -53,14 +106,18 @@ describe('provisioning detail summary and technical details', () => {
|
||||
|
||||
it('keeps operation, command and source IDs and copy controls in closed Technical details', () => {
|
||||
const body = pageBody(fixture());
|
||||
const technical = body.match(/<details\b[^>]*class="technical-details[^>]*>[\s\S]*?<\/details>/)?.[0];
|
||||
const technical = body.match(
|
||||
/<details\b[^>]*class="technical-details[^>]*>[\s\S]*?<\/details>/
|
||||
)?.[0];
|
||||
expect(technical).toBeDefined();
|
||||
expect(technical).toContain('Technical details</summary>');
|
||||
expect(technical!.split('>')[0]).not.toMatch(/\bopen\b/);
|
||||
const outside = body.replace(technical!, '');
|
||||
for (const [value, label] of [
|
||||
['operation-a', 'operation ID'], ['command-a', 'agent command ID'],
|
||||
['collection-a', 'collection launch ID'], ['incident-a', 'Deleted watchdog incident source ID']
|
||||
['operation-a', 'operation ID'],
|
||||
['command-a', 'agent command ID'],
|
||||
['collection-a', 'collection launch ID'],
|
||||
['incident-a', 'Deleted watchdog incident source ID']
|
||||
]) {
|
||||
const code = new RegExp(`<code\\b[^>]*>${value}</code>`);
|
||||
expect(technical).toMatch(code);
|
||||
@@ -100,38 +157,49 @@ describe('provisioning detail summary and technical details', () => {
|
||||
});
|
||||
|
||||
describe('provisioning refresh policy', () => {
|
||||
it.each(['queued', 'waiting', 'running', 'awaiting-health'])('refreshes %s operations', state => {
|
||||
expect(provisioningNeedsRefresh(fixture(state))).toBe(true);
|
||||
});
|
||||
it.each(['queued', 'waiting', 'running', 'awaiting-health'])(
|
||||
'refreshes %s operations',
|
||||
(state) => {
|
||||
expect(provisioningNeedsRefresh(fixture(state))).toBe(true);
|
||||
}
|
||||
);
|
||||
|
||||
it.each(['done', 'failed', 'interrupted', 'warning', 'cancelled'])('stops refreshing %s operations without held recovery', state => {
|
||||
const detail = fixture(state);
|
||||
expect(provisioningNeedsRefresh(detail)).toBe(false);
|
||||
for (const value of ['released', 'transferred', 'unknown'] as const) {
|
||||
detail.progress!.recovery = recovery(value);
|
||||
it.each(['done', 'failed', 'interrupted', 'warning', 'cancelled'])(
|
||||
'stops refreshing %s operations without held recovery',
|
||||
(state) => {
|
||||
const detail = fixture(state);
|
||||
expect(provisioningNeedsRefresh(detail)).toBe(false);
|
||||
for (const value of ['released', 'transferred', 'unknown'] as const) {
|
||||
detail.progress!.recovery = recovery(value);
|
||||
expect(provisioningNeedsRefresh(detail)).toBe(false);
|
||||
}
|
||||
detail.progress = null;
|
||||
expect(provisioningNeedsRefresh(detail)).toBe(false);
|
||||
}
|
||||
detail.progress = null;
|
||||
expect(provisioningNeedsRefresh(detail)).toBe(false);
|
||||
});
|
||||
);
|
||||
|
||||
it.each(['released', 'transferred'] as const)('keeps failed restart recovery refreshing until %s without marking it live', state => {
|
||||
const detail = fixture('failed', recovery('held'));
|
||||
expect(provisioningNeedsRefresh(detail)).toBe(true);
|
||||
const held = pageBody(detail);
|
||||
expect(held).toContain('Recovery capacity held');
|
||||
expect(held).not.toContain('Updates every 5 seconds');
|
||||
expect(held).not.toContain('View recovery operation');
|
||||
it.each(['released', 'transferred'] as const)(
|
||||
'keeps failed restart recovery refreshing until %s without marking it live',
|
||||
(state) => {
|
||||
const detail = fixture('failed', recovery('held'));
|
||||
expect(provisioningNeedsRefresh(detail)).toBe(true);
|
||||
const held = pageBody(detail);
|
||||
expect(held).toContain('Recovery capacity held');
|
||||
expect(held).not.toContain('Updates every 5 seconds');
|
||||
expect(held).not.toContain('View recovery operation');
|
||||
|
||||
detail.progress!.recovery = recovery(state);
|
||||
expect(provisioningNeedsRefresh(detail)).toBe(false);
|
||||
const settled = pageBody(detail);
|
||||
expect(settled).not.toContain('Recovery capacity held');
|
||||
expect(settled).not.toContain('Updates every 5 seconds');
|
||||
expect(settled).toContain(state === 'released' ? 'Recovery capacity released' : 'Recovery transferred');
|
||||
if (state === 'transferred') {
|
||||
expect(settled).toContain('href="/provisioning/rebuild-a"');
|
||||
expect(settled).toContain('View recovery operation');
|
||||
detail.progress!.recovery = recovery(state);
|
||||
expect(provisioningNeedsRefresh(detail)).toBe(false);
|
||||
const settled = pageBody(detail);
|
||||
expect(settled).not.toContain('Recovery capacity held');
|
||||
expect(settled).not.toContain('Updates every 5 seconds');
|
||||
expect(settled).toContain(
|
||||
state === 'released' ? 'Recovery capacity released' : 'Recovery transferred'
|
||||
);
|
||||
if (state === 'transferred') {
|
||||
expect(settled).toContain('href="/provisioning/rebuild-a"');
|
||||
expect(settled).toContain('View recovery operation');
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user