Align provisioning UI with lifecycle states
CI / checks (push) Successful in 5m37s

This commit is contained in:
2026-10-12 15:52:34 -04:00
parent 6059ebf2a5
commit c296769b33
15 changed files with 294 additions and 237 deletions
+35 -11
View File
@@ -47,6 +47,26 @@ async function setup() {
}
describe('bounded provisioning lists', () => {
it('filters Queued and Waiting independently while preserving combined pending links', async () => {
const { add, handle } = await setup();
add('queued', 'queued');
add('waiting', 'waiting', 'restart');
add('running', 'running', 'restart');
handle.prepare("UPDATE server_deployments SET phase_key='prepare' WHERE id='queued'").run();
const { GET } = await import('../../src/routes/api/provisioning/+server.js');
for (const [status, expected] of [
['queued', ['queued']],
['waiting', ['waiting']],
['running', ['running']],
['pending', ['waiting', 'queued']]
] as const) {
const result = await GET({
url: new URL(`http://localhost/api/provisioning?view=active&status=${status}`)
}).json();
expect(result.deployments.map((row: { id: string }) => row.id)).toEqual(expected);
}
});
it('keeps recovery sources concise as incident summaries change, including after retention', async () => {
const { add, handle, deploymentListPage } = await setup();
add('recovery', 'waiting', 'rebuild');
@@ -293,13 +313,17 @@ describe('bounded provisioning lists', () => {
)
.run(
JSON.stringify({
blocker: { code: 'restart-capacity' },
blocker: {
code: 'restart-capacity',
message: 'Waiting for another server to recover'
},
checks: ['private snapshot'],
checkpoints: []
})
);
expect(deploymentListPage({ view: 'active' }).deployments[0]).toMatchObject({
waitCode: 'restart-capacity',
waitMessage: 'Waiting for another server to recover',
phase: 'eligibility',
phaseStartedAt: '2026-01-01'
});
@@ -308,7 +332,7 @@ describe('bounded provisioning lists', () => {
);
});
it('puts verification and executing work ahead of preflight waits and capacity queues', async () => {
it('orders active jobs by lifecycle state regardless of phases and blockers', async () => {
const { add, handle, listDeployments, deploymentListPage } = await setup();
const addProgress = (
id: string,
@@ -403,18 +427,18 @@ describe('bounded provisioning lists', () => {
const expected = [
'verify-older',
'verify-newer',
'staging',
'staging-wait',
'preparing-queued',
'capacity',
'eligibility',
'operation-conflict',
'executing',
'agent-wait',
'cleanup',
'eligibility',
'legacy-capacity',
'host-wait',
'operation-conflict',
'staging',
'staging-wait',
'cleanup',
'queued',
'capacity',
'legacy-capacity'
'preparing-queued'
];
expect(listDeployments({ view: 'active' }).map((row) => row.id)).toEqual(expected);
const { GET } = await import('../../src/routes/api/provisioning/+server.js');
@@ -424,7 +448,7 @@ describe('bounded provisioning lists', () => {
expect(active.deployments.map((row: { id: string }) => row.id)).toEqual(expected);
expect(active).toMatchObject({ total: expected.length, needsReview: 2 });
// Progress priority applies only to Active; retained outcomes and mixed reads stay newest first.
// State priority applies only to Active; retained outcomes and mixed reads stay newest first.
expect(
deploymentListPage({ view: 'history', limit: 1 }).deployments.map((row) => row.id)
).toEqual(['failed']);