Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
@@ -1,57 +1,117 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { newFixture, type Fixture } from '../helpers/fixture.js';
|
||||
|
||||
vi.mock('../../src/lib/server/pipeline/worker.js', () => ({ kickProvisioning: vi.fn(async () => {}) }));
|
||||
vi.mock('../../src/lib/server/pipeline/worker.js', () => ({
|
||||
kickProvisioning: vi.fn(async () => {})
|
||||
}));
|
||||
let fixture: Fixture;
|
||||
beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); });
|
||||
afterEach(async () => { await fixture.cleanup(); });
|
||||
beforeEach(async () => {
|
||||
vi.resetModules();
|
||||
fixture = await newFixture([]);
|
||||
});
|
||||
afterEach(async () => {
|
||||
await fixture.cleanup();
|
||||
});
|
||||
|
||||
describe('provisioning cancellation', () => {
|
||||
it.each(['create', 'start', 'restart', 'rebuild', 'stop', 'destroy'])('cancels %s in every active phase and keeps cancellation terminal', async action => {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { cancelProvisioningJob, canCancelProvisioningJob } = await import('../../src/lib/server/provisioning/cancellation.js');
|
||||
const { finishDeployment, getDeployment, listDeployments } = await import('../../src/lib/server/provisioning/history.js');
|
||||
for (const state of ['queued', 'waiting', 'running', 'awaiting-health', 'interrupted']) {
|
||||
const id = `${action}-${state}`;
|
||||
db().prepare(`INSERT INTO server_deployments(id,server,action,state,stage,started_at,submitted_at,triggered_by)
|
||||
VALUES(?,'server',?,?, 'In progress','2026-10-05',?,'test')`).run(id, action, state, state === 'waiting' ? null : '2026-10-05');
|
||||
expect(canCancelProvisioningJob(id)).toBe(true);
|
||||
expect(getDeployment(id)?.canCancel).toBe(true);
|
||||
expect(listDeployments().find(row => row.id === id)?.canCancel).toBe(1);
|
||||
expect(cancelProvisioningJob(id)).toBe(true);
|
||||
expect(cancelProvisioningJob(id)).toBe(true);
|
||||
finishDeployment(id, { ok: true });
|
||||
expect(getDeployment(id)?.deployment).toMatchObject({ state: 'cancelled', stage: 'Cancelled by operator' });
|
||||
expect(getDeployment(id)?.canCancel).toBe(false);
|
||||
expect(db().prepare("SELECT COUNT(*) n FROM server_deployment_events WHERE deployment_id=? AND kind='outcome'").get(id)).toEqual({ n: 1 });
|
||||
it.each(['create', 'start', 'restart', 'rebuild', 'stop', 'destroy'])(
|
||||
'cancels %s in every active phase and keeps cancellation terminal',
|
||||
async (action) => {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { cancelProvisioningJob, canCancelProvisioningJob } =
|
||||
await import('../../src/lib/server/provisioning/cancellation.js');
|
||||
const { finishDeployment, getDeployment, listDeployments } =
|
||||
await import('../../src/lib/server/provisioning/history.js');
|
||||
for (const state of [
|
||||
'queued',
|
||||
'waiting',
|
||||
'running',
|
||||
'awaiting-health',
|
||||
'interrupted'
|
||||
]) {
|
||||
const id = `${action}-${state}`;
|
||||
db()
|
||||
.prepare(
|
||||
`INSERT INTO server_deployments(id,server,action,state,stage,started_at,submitted_at,triggered_by)
|
||||
VALUES(?,'server',?,?, 'In progress','2026-10-05',?,'test')`
|
||||
)
|
||||
.run(id, action, state, state === 'waiting' ? null : '2026-10-05');
|
||||
expect(canCancelProvisioningJob(id)).toBe(true);
|
||||
expect(getDeployment(id)?.canCancel).toBe(true);
|
||||
expect(listDeployments().find((row) => row.id === id)?.canCancel).toBe(1);
|
||||
expect(cancelProvisioningJob(id)).toBe(true);
|
||||
expect(cancelProvisioningJob(id)).toBe(true);
|
||||
finishDeployment(id, { ok: true });
|
||||
expect(getDeployment(id)?.deployment).toMatchObject({
|
||||
state: 'cancelled',
|
||||
stage: 'Cancelled by operator'
|
||||
});
|
||||
expect(getDeployment(id)?.canCancel).toBe(false);
|
||||
expect(
|
||||
db()
|
||||
.prepare(
|
||||
"SELECT COUNT(*) n FROM server_deployment_events WHERE deployment_id=? AND kind='outcome'"
|
||||
)
|
||||
.get(id)
|
||||
).toEqual({ n: 1 });
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
it('releases only the cancelled job’s recovery slot and cancels its pending file changes', async () => {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { beginDeployment } = await import('../../src/lib/server/provisioning/history.js');
|
||||
const { queueFileChange } = await import('../../src/lib/server/fleet/staged-files.js');
|
||||
const { cancelProvisioningJob } = await import('../../src/lib/server/provisioning/cancellation.js');
|
||||
const { cancelProvisioningJob } =
|
||||
await import('../../src/lib/server/provisioning/cancellation.js');
|
||||
db().prepare("INSERT INTO servers(name) VALUES('one'),('two')").run();
|
||||
const id = beginDeployment('one','restart');
|
||||
const other = beginDeployment('two','restart');
|
||||
db().prepare("INSERT INTO restart_rollout(server,requested_at,operation_id) VALUES('one','2026-10-05',?),('two','2026-10-05',?)").run(id, other);
|
||||
db().prepare("INSERT INTO provisioning_sources(operation_id,kind,source_id,server) VALUES(?,'campaign','source','one')").run(id);
|
||||
queueFileChange({ sourceKind: 'campaign', sourceId: 'source', server: 'one', kind: 'script', relPath: 'test.gsc', content: Buffer.from('queued'), inventorySource: 'test' });
|
||||
const id = beginDeployment('one', 'restart');
|
||||
const other = beginDeployment('two', 'restart');
|
||||
db()
|
||||
.prepare(
|
||||
"INSERT INTO restart_rollout(server,requested_at,operation_id) VALUES('one','2026-10-05',?),('two','2026-10-05',?)"
|
||||
)
|
||||
.run(id, other);
|
||||
db()
|
||||
.prepare(
|
||||
"INSERT INTO provisioning_sources(operation_id,kind,source_id,server) VALUES(?,'campaign','source','one')"
|
||||
)
|
||||
.run(id);
|
||||
queueFileChange({
|
||||
sourceKind: 'campaign',
|
||||
sourceId: 'source',
|
||||
server: 'one',
|
||||
kind: 'script',
|
||||
relPath: 'test.gsc',
|
||||
content: Buffer.from('queued'),
|
||||
inventorySource: 'test'
|
||||
});
|
||||
expect(cancelProvisioningJob(id)).toBe(true);
|
||||
expect(db().prepare('SELECT operation_id FROM restart_rollout').all()).toEqual([{ operation_id: other }]);
|
||||
expect(db().prepare('SELECT state FROM staged_file_changes').get()).toEqual({ state: 'cancelled' });
|
||||
expect(db().prepare('SELECT operation_id FROM restart_rollout').all()).toEqual([
|
||||
{ operation_id: other }
|
||||
]);
|
||||
expect(db().prepare('SELECT state FROM staged_file_changes').get()).toEqual({
|
||||
state: 'cancelled'
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects missing, completed and deleted-server jobs through the cancellation API', async () => {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { POST } = await import('../../src/routes/api/deployments/[id]/cancel/+server.js');
|
||||
for (const state of ['done','warning','failed']) {
|
||||
db().prepare(`INSERT INTO server_deployments(id,server,action,state,stage,started_at,triggered_by) VALUES(?,'server','restart',?,'Finished','2026-10-05','test')`).run(state,state);
|
||||
for (const state of ['done', 'warning', 'failed']) {
|
||||
db()
|
||||
.prepare(
|
||||
`INSERT INTO server_deployments(id,server,action,state,stage,started_at,triggered_by) VALUES(?,'server','restart',?,'Finished','2026-10-05','test')`
|
||||
)
|
||||
.run(state, state);
|
||||
expect(POST({ params: { id: state } }).status).toBe(409);
|
||||
}
|
||||
expect(POST({ params: { id: 'missing' } }).status).toBe(409);
|
||||
db().prepare(`INSERT INTO server_deployments(id,server,action,state,stage,started_at,triggered_by,server_deleted_at) VALUES('deleted','server','restart','running','Running','2026-10-05','test','2026-10-05')`).run();
|
||||
db()
|
||||
.prepare(
|
||||
`INSERT INTO server_deployments(id,server,action,state,stage,started_at,triggered_by,server_deleted_at) VALUES('deleted','server','restart','running','Running','2026-10-05','test','2026-10-05')`
|
||||
)
|
||||
.run();
|
||||
expect(POST({ params: { id: 'deleted' } }).status).toBe(409);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user