import Database from 'better-sqlite3'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import schema from '../../src/lib/server/db/schema.sql?raw'; import { newFixture, type Fixture } from '../helpers/fixture.js'; let fixture: Fixture; beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); }); afterEach(async () => { vi.restoreAllMocks(); await fixture.cleanup(); }); describe('provisioning reads with retained lifecycle history', () => { it('uses indexed per-server submissions and separate lifecycle indexes for fleet reads', async () => { const { db } = await import('../../src/lib/server/db/index.js'); const { latestGameSubmission, latestGameSubmissions } = await import('../../src/lib/server/fleet/lifecycle-evidence.js'); const { observationReader } = await import('../../src/lib/server/fleet/status.js'); const { runningVersions } = await import('../../src/lib/server/integrations/plutonium.js'); const { fleetOperations } = await import('../../src/lib/server/fleet/overview.js'); const handle = db(); const now = Date.parse('2026-10-05T12:00:00Z'); const iso = (offset: number) => new Date(now + offset).toISOString(); handle.transaction(() => { const server = handle.prepare('INSERT INTO servers(name) VALUES (?)'); for (let i = 0; i < 16; i++) server.run(`server-${i}`); server.run('empty'); const insert = handle.prepare(`INSERT INTO server_deployments (id, server, action, state, stage, started_at, finished_at, submitted_at, server_deleted_at, triggered_by) VALUES (?, ?, ?, 'done', 'Complete', ?, ?, ?, ?, 'test')`); for (let i = 0; i < 6_000; i++) { const at = iso(-100_000_000 - i); insert.run(`history-${i}`, `server-${i % 16}`, 'restart', at, at, at, null); } for (const [i, action] of ['create', 'start', 'restart', 'rebuild'].entries()) { insert.run( `current-${i}`, `server-${i}`, action, iso(-1000), iso(-1000), iso(-1000), null ); } insert.run('stop', 'server-0', 'stop', iso(0), iso(0), iso(0), null); insert.run('destroy', 'server-1', 'destroy', iso(0), iso(0), iso(0), null); insert.run('deleted', 'server-2', 'restart', iso(0), iso(0), iso(0), iso(0)); insert.run('unsubmitted', 'server-3', 'rebuild', iso(0), null, null, null); handle .prepare( 'INSERT INTO restart_rollout(server, requested_at, healthy_at) VALUES (?, ?, ?)' ) .run('server-0', iso(-500), iso(0)); handle .prepare('INSERT INTO restart_rollout(server, requested_at) VALUES (?, ?)') .run('server-1', iso(-2000)); })(); const submissions = latestGameSubmissions(); expect(submissions.get('empty')).toBeNull(); expect(submissions.get('server-0')).toBe(iso(-500)); for (const i of [1, 2, 3]) expect(submissions.get(`server-${i}`)).toBe(iso(-1000)); for (const [server, at] of submissions) expect(at).toBe(latestGameSubmission(server)); // Capture real snapshot queries so plan checks catch regressions at call sites. for (const read of [observationReader, runningVersions]) { const prepare = vi.spyOn(handle, 'prepare'); read(); const queries = prepare.mock.calls .map(([sql]) => sql) .filter((sql) => sql.includes('submitted_at')); prepare.mockRestore(); expect(queries).toHaveLength(1); const plan = handle.prepare(`EXPLAIN QUERY PLAN ${queries[0]}`).all() as { detail: string; }[]; const details = plan.map((row) => row.detail).join('\n'); expect(details).toContain( 'SEARCH server_deployments USING COVERING INDEX idx_server_deployments_submission' ); expect(details).not.toMatch(/SCAN server_deployments\b/); } const prepare = vi.spyOn(handle, 'prepare'); fleetOperations(now); const queries = prepare.mock.calls .map(([sql]) => sql) .filter((sql) => sql.includes('server_deployments')); prepare.mockRestore(); expect(queries).toHaveLength(1); const statement = handle.prepare(queries[0]); expect(statement.columns().map((column) => column.name)).toEqual([ 'id', 'server', 'action', 'state', 'stage', 'error', 'started_at' ]); const plan = handle.prepare(`EXPLAIN QUERY PLAN ${queries[0]}`).all() as { detail: string; }[]; const details = plan.map((row) => row.detail).join('\n'); expect(details).toContain( 'SEARCH server_deployments USING INDEX idx_server_deployments_state' ); expect(queries[0]).not.toContain('finished_at >='); expect(details).toContain('SEARCH server_deployments USING INTEGER PRIMARY KEY (rowid=?)'); expect(details).toContain( 'SCAN server_deployments USING COVERING INDEX idx_server_deployments_server' ); expect(details).not.toMatch(/SCAN server_deployments(?:\n|$)/); }); it('indexes the server and queue reads used by collection matching and health polling', async () => { const { db } = await import('../../src/lib/server/db/index.js'); const { latestGameSubmission } = await import('../../src/lib/server/fleet/lifecycle-evidence.js'); const { hasActiveProvisioning } = await import('../../src/lib/server/fleet/provisioning.js'); const { housePartyTargets } = await import('../../src/lib/server/fleet/house-party.js'); const { creationQueue } = await import('../../src/lib/server/provisioning/instances.js'); const handle = db(); handle.transaction(() => { const insert = handle.prepare(`INSERT INTO server_deployments (id,server,action,state,stage,started_at,triggered_by,server_deleted_at) VALUES(?,?,'create','done','Complete','2025-01-01','fixture','2025-01-02')`); for (let i = 0; i < 2_000; i++) insert.run(`history-${i}`, `retired-${i}`); handle .prepare( `INSERT INTO server_deployments (id,server,action,state,stage,started_at,triggered_by,submitted_at) VALUES('current','current','create','queued','Queued','2026-01-01','fixture','2026-01-01')` ) .run(); })(); // Inspect the SQL issued by the public reads, so the regression follows // those implementations rather than keeping separate copies of them. const expectIndexed = (read: () => unknown, bindings: string[] = []) => { const prepare = vi.spyOn(handle, 'prepare'); read(); const queries = prepare.mock.calls .map(([sql]) => sql) .filter((sql) => sql.includes('server_deployments')); prepare.mockRestore(); expect(queries).toHaveLength(1); const plan = handle.prepare(`EXPLAIN QUERY PLAN ${queries[0]}`).all(...bindings) as { detail: string; }[]; const details = plan.map((row) => row.detail).join('\n'); expect(details).toMatch(/SEARCH server_deployments USING (?:COVERING )?INDEX/); expect(details).not.toMatch(/SCAN server_deployments\b/); }; expectIndexed( () => expect(latestGameSubmission('current')).toBe('2026-01-01'), ['current', 'current'] ); expectIndexed(() => expect(hasActiveProvisioning('current')).toBe(true), ['current', '']); expectIndexed(() => expect(housePartyTargets()).toEqual(['current'])); expectIndexed(() => expect(creationQueue().queued.map((job) => job.id)).toEqual(['current']) ); }); it('installs the indexes after legacy table rebuilds and keeps them on reopen', async () => { const legacy = new Database(fixture.dbPath); try { legacy.exec( schema.replace( /CREATE TABLE IF NOT EXISTS server_deployments \([\s\S]*?\n\);/, (table) => table .replace( "'create', 'start', 'restart', 'rebuild', 'stop', 'destroy'", "'create', 'destroy'" ) .replace(' server_deleted_at TEXT,\n', '') ) ); legacy.exec(`INSERT INTO servers(name) VALUES('existing'); INSERT INTO server_deployments(id,server,action,state,stage,started_at,triggered_by) VALUES('old','existing','create','done','Complete','2025-01-01','fixture'); INSERT INTO server_deployment_events(deployment_id,created_at,message) VALUES('old','2025-01-01','Creation complete');`); } finally { legacy.close(); } const { db, closeDb } = await import('../../src/lib/server/db/index.js'); for (let open = 0; open < 2; open++) { const handle = db(); expect(handle.prepare('SELECT rowid,id,state FROM server_deployments').all()).toEqual([ { rowid: 1, id: 'old', state: 'done' } ]); expect(handle.prepare('SELECT message FROM server_deployment_events').all()).toEqual([ { message: 'Creation complete' } ]); const indexes = handle.pragma('index_list(server_deployments)') as { name: string }[]; expect(indexes.map((index) => index.name)).toEqual( expect.arrayContaining([ 'idx_server_deployments_server', 'idx_server_deployments_state', 'idx_server_deployments_started_at', 'idx_server_deployments_submission', 'idx_server_deployments_finished_at', 'idx_server_deployments_server_state', 'idx_server_deployments_unsettled' ]) ); expect(handle.pragma('foreign_key_check')).toEqual([]); closeDb(); } }); });