Allow revising active betas with fresh tests and history

This commit is contained in:
2026-10-05 21:02:58 -04:00
parent 9d4ff992a4
commit f8d0ba3936
21 changed files with 713 additions and 41 deletions
+35
View File
@@ -1276,6 +1276,41 @@ describe('native provisioning cancellation and rollback', () => {
});
describe('cancelled native mutation bookkeeping', () => {
it.each([null, 'before beta'])('keeps the original host receipt across beta revisions (original=%s)', async original => {
const job = await queue(); await advance(); await acknowledge(job.deploymentId);
const { db } = await import('../../src/lib/server/db/index.js');
const { createScript } = await import('../../src/lib/server/fleet/scripts.js');
const { openBeta, reviseBeta, rollbackBeta } = await import('../../src/lib/server/fleet/beta.js');
const { requestRestart } = await import('../../src/lib/server/provisioning/requests.js');
const { reportHostOperation, getHostOperation } = await import('../../src/lib/server/hosts/index.js');
const { script, version } = createScript({ name: 'Revised beta', kind: 'script', targetRelPath: 'revision.gsc',
content: Buffer.from('revision one'), scopeType: 'game', scopeValue: 't5' });
const { betaId } = await openBeta({ scripts: [{ scriptId: script.id, scriptVersionId: version.id }],
servers: [job.name], waitForHealth: true, waitForMatch: false });
async function activate(before: string | null) {
const restart = requestRestart(job.name, { kind: 'beta', id: String(betaId) });
await advance();
const staged = db().prepare('SELECT stage_operation_id FROM native_provisioning WHERE deployment_id = ?').get(restart.id) as { stage_operation_id: string };
const files = getHostOperation(staged.stage_operation_id)!.payload.files as Array<{ id: number; path: string }>;
reportHostOperation(hostId, staged.stage_operation_id, { state: 'completed' });
await advance();
// A dispatched activation must settle before revision submission.
await expect(reviseBeta(betaId, 1, [{ scriptId: script.id, content: Buffer.from('premature update') }])).rejects.toThrow();
const activation = (await operationFor(restart.id))!;
reportHostOperation(hostId, activation.id, { state: 'completed', result: {
before: files.map(file => ({ ...file, contentBase64: before === null ? null : Buffer.from(before).toString('base64') }))
} });
await advance();
expect((await detail(restart.id)).state).toBe('done');
}
await activate(original);
await reviseBeta(betaId, 1, [{ scriptId: script.id, content: Buffer.from('revision two') }]);
await activate('revision one');
const rollback = await rollbackBeta(betaId);
expect(db().prepare("SELECT content FROM staged_file_changes WHERE source_kind = 'campaign' AND source_id = ?").get(rollback.deploymentId))
.toEqual({ content: original === null ? null : Buffer.from(original) });
});
it('keeps explicit cleanup available after cancelling an unsubmitted creation', async () => {
const job = await queue();
const { cancelProvisioningJob } = await import('../../src/lib/server/provisioning/cancellation.js');