80 lines
4.8 KiB
TypeScript
80 lines
4.8 KiB
TypeScript
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('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,komodo_name) VALUES('existing','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'
|
|
]));
|
|
expect(handle.pragma('foreign_key_check')).toEqual([]);
|
|
closeDb();
|
|
}
|
|
});
|
|
});
|