Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
@@ -16,7 +16,8 @@ afterEach(async () => {
|
||||
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 { 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');
|
||||
@@ -35,15 +36,28 @@ describe('provisioning reads with retained lifecycle history', () => {
|
||||
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(
|
||||
`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 (?, ?, ?)')
|
||||
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));
|
||||
handle
|
||||
.prepare('INSERT INTO restart_rollout(server, requested_at) VALUES (?, ?)')
|
||||
.run('server-1', iso(-2000));
|
||||
})();
|
||||
const submissions = latestGameSubmissions();
|
||||
expect(submissions.get('empty')).toBeNull();
|
||||
@@ -55,34 +69,58 @@ describe('provisioning reads with retained lifecycle history', () => {
|
||||
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'));
|
||||
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');
|
||||
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'));
|
||||
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(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).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 { 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();
|
||||
@@ -91,9 +129,13 @@ describe('provisioning reads with retained lifecycle history', () => {
|
||||
(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
|
||||
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();
|
||||
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
|
||||
@@ -101,44 +143,73 @@ describe('provisioning reads with retained lifecycle history', () => {
|
||||
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'));
|
||||
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');
|
||||
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(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']));
|
||||
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(
|
||||
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(); }
|
||||
} 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' }]);
|
||||
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(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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user