Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
@@ -14,37 +14,87 @@ beforeEach(async () => {
|
||||
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
|
||||
await seedServerRegistry();
|
||||
});
|
||||
afterEach(async () => { await fixture.cleanup(); });
|
||||
afterEach(async () => {
|
||||
await fixture.cleanup();
|
||||
});
|
||||
|
||||
async function promotion(kind: 'script' | 'plugin' | 'baseline', staged: boolean) {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { createScript, uploadNewVersion } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { createScript, uploadNewVersion } =
|
||||
await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { destOfKind } = await import('../../src/lib/server/config.js');
|
||||
const { sha256Buffer } = await import('../../src/lib/server/fleet/files.js');
|
||||
const { queueFileChange } = await import('../../src/lib/server/fleet/staged-files.js');
|
||||
const before = Buffer.from(kind === 'baseline' ? 'sv_voice 0\n' : 'original');
|
||||
const after = Buffer.from(kind === 'baseline' ? 'sv_voice 1\n' : 'promoted');
|
||||
const relPath = kind === 'baseline' ? 'dedicated_sp.cfg' : kind === 'plugin' ? 'managed.dll' : 'managed.gsc';
|
||||
const { script, version: prior } = createScript({ name: 'managed', kind, targetRelPath: relPath, scopeType: 'game', scopeValue: 't5', content: before });
|
||||
const relPath =
|
||||
kind === 'baseline'
|
||||
? 'dedicated_sp.cfg'
|
||||
: kind === 'plugin'
|
||||
? 'managed.dll'
|
||||
: 'managed.gsc';
|
||||
const { script, version: prior } = createScript({
|
||||
name: 'managed',
|
||||
kind,
|
||||
targetRelPath: relPath,
|
||||
scopeType: 'game',
|
||||
scopeValue: 't5',
|
||||
content: before
|
||||
});
|
||||
const version = uploadNewVersion(script.id, after);
|
||||
db().prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?').run(version.id, script.id);
|
||||
db().prepare("INSERT INTO deployments (id, action, started_at, triggered_by) VALUES ('promote', 'promote', ?, 'ui')").run(new Date().toISOString());
|
||||
db().prepare("INSERT INTO deployment_servers (deployment_id, server, files_changed, restart_state) VALUES ('promote', ?, 1, 'pending')").run(server);
|
||||
db().prepare("INSERT INTO deployment_scripts (deployment_id, script_id, script_version_id, prior_version_id, kind) VALUES ('promote', ?, ?, ?, ?)")
|
||||
db()
|
||||
.prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?')
|
||||
.run(version.id, script.id);
|
||||
db()
|
||||
.prepare(
|
||||
"INSERT INTO deployments (id, action, started_at, triggered_by) VALUES ('promote', 'promote', ?, 'ui')"
|
||||
)
|
||||
.run(new Date().toISOString());
|
||||
db()
|
||||
.prepare(
|
||||
"INSERT INTO deployment_servers (deployment_id, server, files_changed, restart_state) VALUES ('promote', ?, 1, 'pending')"
|
||||
)
|
||||
.run(server);
|
||||
db()
|
||||
.prepare(
|
||||
"INSERT INTO deployment_scripts (deployment_id, script_id, script_version_id, prior_version_id, kind) VALUES ('promote', ?, ?, ?, ?)"
|
||||
)
|
||||
.run(script.id, version.id, prior.id, kind);
|
||||
const file = db().prepare(`INSERT INTO deployment_files (deployment_id, server, verb, rel_path, sha256_before, sha256_after, content_before_hash, content_after_hash)
|
||||
VALUES ('promote', ?, 'updated', ?, ?, ?, ?, ?)`).run(server, relPath, sha256Buffer(before), sha256Buffer(after), putFileBlob(before, db()), putFileBlob(after, db()));
|
||||
const file = db()
|
||||
.prepare(
|
||||
`INSERT INTO deployment_files (deployment_id, server, verb, rel_path, sha256_before, sha256_after, content_before_hash, content_after_hash)
|
||||
VALUES ('promote', ?, 'updated', ?, ?, ?, ?, ?)`
|
||||
)
|
||||
.run(
|
||||
server,
|
||||
relPath,
|
||||
sha256Buffer(before),
|
||||
sha256Buffer(after),
|
||||
putFileBlob(before, db()),
|
||||
putFileBlob(after, db())
|
||||
);
|
||||
const path = join(destOfKind(server, kind), relPath);
|
||||
await mkdir(dirname(path), { recursive: true });
|
||||
await writeFile(path, staged ? before : after);
|
||||
if (staged) queueFileChange({ sourceKind: 'campaign', sourceId: 'promote', server, kind, relPath, content: after,
|
||||
scriptVersionId: version.id, inventorySource: 'promote:promote', deploymentFileId: Number(file.lastInsertRowid) });
|
||||
if (staged)
|
||||
queueFileChange({
|
||||
sourceKind: 'campaign',
|
||||
sourceId: 'promote',
|
||||
server,
|
||||
kind,
|
||||
relPath,
|
||||
content: after,
|
||||
scriptVersionId: version.id,
|
||||
inventorySource: 'promote:promote',
|
||||
deploymentFileId: Number(file.lastInsertRowid)
|
||||
});
|
||||
return { path, before, after, script, prior };
|
||||
}
|
||||
|
||||
async function applyCampaign(id: string) {
|
||||
const { requestRestart } = await import('../../src/lib/server/provisioning/requests.js');
|
||||
const { applyStagedFilesForOperation } = await import('../../src/lib/server/fleet/staged-files.js');
|
||||
const { applyStagedFilesForOperation } =
|
||||
await import('../../src/lib/server/fleet/staged-files.js');
|
||||
const operation = requestRestart(server, { kind: 'campaign', id });
|
||||
applyStagedFilesForOperation(operation.id, server);
|
||||
return operation;
|
||||
@@ -55,45 +105,86 @@ describe('deferred promotion rollback', () => {
|
||||
const { path, before } = await promotion('script', true);
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { requestRestart } = await import('../../src/lib/server/provisioning/requests.js');
|
||||
const { applyStagedFilesForOperation, isFileApplied } = await import('../../src/lib/server/fleet/staged-files.js');
|
||||
const { rollbackPromoteDeployment } = await import('../../src/lib/server/fleet/rollback.js');
|
||||
const { applyStagedFilesForOperation, isFileApplied } =
|
||||
await import('../../src/lib/server/fleet/staged-files.js');
|
||||
const { rollbackPromoteDeployment } =
|
||||
await import('../../src/lib/server/fleet/rollback.js');
|
||||
const operation = requestRestart(server, { kind: 'campaign', id: 'promote' });
|
||||
const rollback = await rollbackPromoteDeployment('promote');
|
||||
expect(rollback.affectedServers).toEqual([]);
|
||||
expect(await readFile(path)).toEqual(before);
|
||||
expect(isFileApplied('campaign', 'promote', server, 'script', 'managed.gsc')).toBe(false);
|
||||
expect(db().prepare("SELECT restart_state FROM deployment_servers WHERE deployment_id = 'promote'").get()).toEqual({ restart_state: 'skipped' });
|
||||
expect(db().prepare('SELECT * FROM deployment_servers WHERE deployment_id = ?').all(rollback.deploymentId)).toEqual([]);
|
||||
expect(
|
||||
db()
|
||||
.prepare(
|
||||
"SELECT restart_state FROM deployment_servers WHERE deployment_id = 'promote'"
|
||||
)
|
||||
.get()
|
||||
).toEqual({ restart_state: 'skipped' });
|
||||
expect(
|
||||
db()
|
||||
.prepare('SELECT * FROM deployment_servers WHERE deployment_id = ?')
|
||||
.all(rollback.deploymentId)
|
||||
).toEqual([]);
|
||||
applyStagedFilesForOperation(operation.id, server);
|
||||
expect(await readFile(path)).toEqual(before);
|
||||
});
|
||||
|
||||
it.each(['script', 'plugin', 'baseline'] as const)('queues restoration of applied %s bytes until rollback activation', async (kind) => {
|
||||
const { path, before, after, prior } = await promotion(kind, true);
|
||||
await applyCampaign('promote');
|
||||
const { rollbackPromoteDeployment } = await import('../../src/lib/server/fleet/rollback.js');
|
||||
const { getServerScript } = await import('../../src/lib/server/fleet/inventory.js');
|
||||
const result = await rollbackPromoteDeployment('promote');
|
||||
expect(result.affectedServers).toEqual([server]);
|
||||
expect(await readFile(path)).toEqual(after);
|
||||
await applyCampaign(result.deploymentId);
|
||||
expect(await readFile(path)).toEqual(before);
|
||||
expect(getServerScript(server, kind === 'baseline' ? 'dedicated_sp.cfg' : kind === 'plugin' ? 'managed.dll' : 'managed.gsc')?.script_version_id).toBe(prior.id);
|
||||
});
|
||||
it.each(['script', 'plugin', 'baseline'] as const)(
|
||||
'queues restoration of applied %s bytes until rollback activation',
|
||||
async (kind) => {
|
||||
const { path, before, after, prior } = await promotion(kind, true);
|
||||
await applyCampaign('promote');
|
||||
const { rollbackPromoteDeployment } =
|
||||
await import('../../src/lib/server/fleet/rollback.js');
|
||||
const { getServerScript } = await import('../../src/lib/server/fleet/inventory.js');
|
||||
const result = await rollbackPromoteDeployment('promote');
|
||||
expect(result.affectedServers).toEqual([server]);
|
||||
expect(await readFile(path)).toEqual(after);
|
||||
await applyCampaign(result.deploymentId);
|
||||
expect(await readFile(path)).toEqual(before);
|
||||
expect(
|
||||
getServerScript(
|
||||
server,
|
||||
kind === 'baseline'
|
||||
? 'dedicated_sp.cfg'
|
||||
: kind === 'plugin'
|
||||
? 'managed.dll'
|
||||
: 'managed.gsc'
|
||||
)?.script_version_id
|
||||
).toBe(prior.id);
|
||||
}
|
||||
);
|
||||
|
||||
it('restores only applied files when a promotion was partly installed', async () => {
|
||||
const { path, before } = await promotion('script', true);
|
||||
await applyCampaign('promote');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { queueFileChange } = await import('../../src/lib/server/fleet/staged-files.js');
|
||||
const { rollbackPromoteDeployment } = await import('../../src/lib/server/fleet/rollback.js');
|
||||
const pending = db().prepare(`INSERT INTO deployment_files (deployment_id, server, verb, rel_path, content_after_hash)
|
||||
VALUES ('promote', ?, 'added', 'never.gsc', ?)`).run(server, putFileBlob(Buffer.from('queued'), db()));
|
||||
queueFileChange({ sourceKind: 'campaign', sourceId: 'promote', server, kind: 'script', relPath: 'never.gsc',
|
||||
content: Buffer.from('queued'), inventorySource: 'promote:promote', deploymentFileId: Number(pending.lastInsertRowid) });
|
||||
const { rollbackPromoteDeployment } =
|
||||
await import('../../src/lib/server/fleet/rollback.js');
|
||||
const pending = db()
|
||||
.prepare(
|
||||
`INSERT INTO deployment_files (deployment_id, server, verb, rel_path, content_after_hash)
|
||||
VALUES ('promote', ?, 'added', 'never.gsc', ?)`
|
||||
)
|
||||
.run(server, putFileBlob(Buffer.from('queued'), db()));
|
||||
queueFileChange({
|
||||
sourceKind: 'campaign',
|
||||
sourceId: 'promote',
|
||||
server,
|
||||
kind: 'script',
|
||||
relPath: 'never.gsc',
|
||||
content: Buffer.from('queued'),
|
||||
inventorySource: 'promote:promote',
|
||||
deploymentFileId: Number(pending.lastInsertRowid)
|
||||
});
|
||||
const result = await rollbackPromoteDeployment('promote');
|
||||
expect(db().prepare('SELECT rel_path FROM deployment_files WHERE deployment_id = ?').all(result.deploymentId))
|
||||
.toEqual([{ rel_path: 'managed.gsc' }]);
|
||||
expect(
|
||||
db()
|
||||
.prepare('SELECT rel_path FROM deployment_files WHERE deployment_id = ?')
|
||||
.all(result.deploymentId)
|
||||
).toEqual([{ rel_path: 'managed.gsc' }]);
|
||||
await applyCampaign(result.deploymentId);
|
||||
expect(await readFile(path)).toEqual(before);
|
||||
expect(existsSync(join(dirname(path), 'never.gsc'))).toBe(false);
|
||||
@@ -104,11 +195,27 @@ describe('deferred promotion rollback', () => {
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { queueFileChange } = await import('../../src/lib/server/fleet/staged-files.js');
|
||||
const { requestRestart } = await import('../../src/lib/server/provisioning/requests.js');
|
||||
const { rollbackPromoteDeployment } = await import('../../src/lib/server/fleet/rollback.js');
|
||||
db().prepare("INSERT INTO deployments (id, action, started_at, triggered_by) VALUES ('other', 'drift', ?, 'ui')").run(new Date().toISOString());
|
||||
db().prepare("INSERT INTO deployment_servers (deployment_id, server, files_changed, restart_state) VALUES ('other', ?, 1, 'pending')").run(server);
|
||||
queueFileChange({ sourceKind: 'campaign', sourceId: 'other', server, kind: 'script', relPath: 'other.gsc',
|
||||
content: Buffer.from('other campaign'), inventorySource: 'drift:other' });
|
||||
const { rollbackPromoteDeployment } =
|
||||
await import('../../src/lib/server/fleet/rollback.js');
|
||||
db()
|
||||
.prepare(
|
||||
"INSERT INTO deployments (id, action, started_at, triggered_by) VALUES ('other', 'drift', ?, 'ui')"
|
||||
)
|
||||
.run(new Date().toISOString());
|
||||
db()
|
||||
.prepare(
|
||||
"INSERT INTO deployment_servers (deployment_id, server, files_changed, restart_state) VALUES ('other', ?, 1, 'pending')"
|
||||
)
|
||||
.run(server);
|
||||
queueFileChange({
|
||||
sourceKind: 'campaign',
|
||||
sourceId: 'other',
|
||||
server,
|
||||
kind: 'script',
|
||||
relPath: 'other.gsc',
|
||||
content: Buffer.from('other campaign'),
|
||||
inventorySource: 'drift:other'
|
||||
});
|
||||
const shared = requestRestart(server, { kind: 'campaign', id: 'promote' });
|
||||
expect(requestRestart(server, { kind: 'campaign', id: 'other' }).id).not.toBe(shared.id);
|
||||
await rollbackPromoteDeployment('promote');
|
||||
@@ -119,7 +226,8 @@ describe('deferred promotion rollback', () => {
|
||||
|
||||
it('stages rollback for legacy promotions with no deferred file records', async () => {
|
||||
const { path, before, after } = await promotion('plugin', false);
|
||||
const { rollbackPromoteDeployment } = await import('../../src/lib/server/fleet/rollback.js');
|
||||
const { rollbackPromoteDeployment } =
|
||||
await import('../../src/lib/server/fleet/rollback.js');
|
||||
const result = await rollbackPromoteDeployment('promote');
|
||||
expect(result.affectedServers).toEqual([server]);
|
||||
expect(await readFile(path)).toEqual(after);
|
||||
|
||||
Reference in New Issue
Block a user