Sort active provisioning jobs by progress
This commit is contained in:
@@ -70,6 +70,69 @@ describe('bounded provisioning lists', () => {
|
||||
expect(JSON.stringify(deploymentListPage({ view: 'active' }))).not.toContain('private snapshot');
|
||||
});
|
||||
|
||||
it('puts verification and executing work ahead of preflight waits and capacity queues', async () => {
|
||||
const { add, handle, listDeployments, deploymentListPage } = await setup();
|
||||
const addProgress = (id: string, state: string, second: number, phase: string,
|
||||
waitCode: string | null = null, stage = 'Recorded', action = 'rebuild') => {
|
||||
add(id, state, action, 't5_zombie_theater_1', null, stage, `2026-01-01T00:00:${String(second).padStart(2, '0')}.000Z`);
|
||||
handle.prepare('UPDATE server_deployments SET phase_key=?, observation_json=? WHERE id=?')
|
||||
.run(phase, JSON.stringify({ blocker: waitCode ? { code: waitCode } : null }), id);
|
||||
};
|
||||
// Request order is deliberately different from progress order.
|
||||
addProgress('queued', 'queued', 1, 'requested');
|
||||
addProgress('cleanup', 'waiting', 12, 'cleanup', null, 'Removing server files and registry', 'destroy');
|
||||
addProgress('verify-newer', 'awaiting-health', 21, 'verify-map');
|
||||
addProgress('capacity', 'running', 2, 'startup-queue', 'startup-capacity');
|
||||
addProgress('operation-conflict', 'running', 6, 'container-action', 'operation-conflict');
|
||||
addProgress('agent-wait', 'running', 11, 'container-action', 'native-host', 'Agent operation pending');
|
||||
addProgress('host-wait', 'waiting', 5, 'eligibility', 'native-host', 'Host is unavailable; waiting for reconnection.');
|
||||
addProgress('staging', 'waiting', 7, 'eligibility', 'native-host', 'Staging and verifying files on the host.');
|
||||
addProgress('staging-wait', 'waiting', 8, 'eligibility', 'native-host', 'Waiting for verified remote file staging.');
|
||||
addProgress('legacy-capacity', 'waiting', 3, 'eligibility', 'native-host', 'Queued to rebuild; waiting for another server to recover');
|
||||
addProgress('verify-older', 'awaiting-health', 20, 'verify-container', 'native-host', 'Waiting for container readiness: starting');
|
||||
addProgress('preparing-queued', 'queued', 9, 'prepare');
|
||||
addProgress('executing', 'running', 10, 'prepare');
|
||||
addProgress('eligibility', 'running', 4, 'eligibility', 'eligibility');
|
||||
addProgress('interrupted', 'interrupted', 30, 'container-action', 'native-host');
|
||||
addProgress('failed', 'failed', 31, 'verify-map', 'native-host');
|
||||
|
||||
const expected = ['verify-older', 'verify-newer', 'staging', 'staging-wait', 'preparing-queued', 'executing', 'agent-wait', 'cleanup',
|
||||
'eligibility', 'host-wait', 'operation-conflict', 'queued', 'capacity', 'legacy-capacity'];
|
||||
expect(listDeployments({ view: 'active' }).map(row => row.id)).toEqual(expected);
|
||||
const { GET } = await import('../../src/routes/api/provisioning/+server.js');
|
||||
const active = await GET({ url: new URL('http://localhost/api/provisioning?view=active&limit=1') }).json();
|
||||
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.
|
||||
expect(deploymentListPage({ view: 'history', limit: 1 }).deployments.map(row => row.id)).toEqual(['failed']);
|
||||
expect(deploymentListPage({ view: 'history', limit: 1, offset: 1 }).deployments.map(row => row.id)).toEqual(['interrupted']);
|
||||
expect(listDeployments({ view: 'all', limit: 3 }).map(row => row.id)).toEqual(['failed', 'interrupted', 'verify-newer']);
|
||||
});
|
||||
|
||||
it('keeps oldest requests and insertion ties stable while active progress is polled', async () => {
|
||||
const { add, handle, listDeployments } = await setup();
|
||||
add('z-first', 'awaiting-health', 'rebuild', 't5_zombie_theater_1', null, 'Checking game status', '2026-01-02');
|
||||
add('a-second', 'awaiting-health', 'rebuild', 't5_zombie_theater_1', null, 'Checking selected engine map', '2026-01-02');
|
||||
add('old-inserted-last', 'awaiting-health', 'rebuild', 't5_zombie_theater_1', null, 'Checking container state', '2026-01-01');
|
||||
add('queued', 'queued', 'rebuild', 't5_zombie_theater_1', null, 'Request recorded', '2025-12-31');
|
||||
const ids = () => listDeployments({ view: 'active' }).map(row => row.id);
|
||||
const expected = ['old-inserted-last', 'z-first', 'a-second', 'queued'];
|
||||
expect(ids()).toEqual(expected);
|
||||
expect(ids()).toEqual(expected);
|
||||
|
||||
handle.prepare(`UPDATE server_deployments SET phase_key='verify-container', phase_started_at='2026-01-03',
|
||||
observed_at='2026-01-03', observation_json=? WHERE id='z-first'`)
|
||||
.run(JSON.stringify({ blocker: { code: 'native-host', message: 'Waiting for container readiness: starting' } }));
|
||||
handle.prepare("UPDATE server_deployments SET phase_key='verify-plutonium', phase_started_at='2026-01-04' WHERE id='a-second'").run();
|
||||
expect(ids()).toEqual(expected);
|
||||
|
||||
handle.prepare("UPDATE server_deployments SET state='done', finished_at='2026-01-05' WHERE id='z-first'").run();
|
||||
handle.prepare("UPDATE server_deployments SET state='awaiting-health', phase_key='verify-game' WHERE id='queued'").run();
|
||||
expect(ids()).toEqual(['queued', 'old-inserted-last', 'a-second']);
|
||||
expect(listDeployments({ view: 'all' }).map(row => row.id)).toEqual(['a-second', 'z-first', 'old-inserted-last', 'queued']);
|
||||
});
|
||||
|
||||
it('polls every active row and counts review outcomes without transferring retained history', async () => {
|
||||
const { handle, add, REMOVING_SERVER } = await setup();
|
||||
handle.transaction(() => {
|
||||
|
||||
Reference in New Issue
Block a user