Improve operational UX, recovery flows, and action safety
Implement the approved UX review across Fleet, Betas, Library, deployments, and Settings. Add reviewed promotion and reconciliation plans, atomic queued-removal cancellation, recoverable beta drafts and launch inputs, accessible controls, and mobile improvements. Validated with 473 tests, a clean typecheck, production build, and isolated desktop/mobile browser walkthroughs. Include the review and implementation coverage notes.
This commit is contained in:
@@ -21,6 +21,57 @@ afterEach(async () => {
|
||||
await fixture.cleanup();
|
||||
});
|
||||
|
||||
describe('reviewed drift plans', () => {
|
||||
it('rejects changed file bytes before any deletion and returns a fresh review', async () => {
|
||||
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
|
||||
const { previewDrift, runDriftDeployment, DriftPlanChanged } = await import('../../src/lib/server/fleet/drift.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
await seedServerRegistry();
|
||||
const path = join(fixture.t5, 't5_zombie_theater_1', 'configs', 'stray.cfg');
|
||||
await writeFile(path, 'original');
|
||||
const plan = await previewDrift(['t5_zombie_theater_1']);
|
||||
await writeFile(path, 'changed');
|
||||
await expect(runDriftDeployment({ servers: plan.servers, expectedFingerprint: plan.fingerprint })).rejects.toBeInstanceOf(DriftPlanChanged);
|
||||
expect(await readFile(path, 'utf8')).toBe('changed');
|
||||
expect(db().prepare('SELECT * FROM deployments').all()).toEqual([]);
|
||||
const refreshed = await previewDrift(plan.servers);
|
||||
expect(refreshed.fingerprint).not.toBe(plan.fingerprint);
|
||||
expect((await runDriftDeployment({ servers: refreshed.servers, expectedFingerprint: refreshed.fingerprint })).totalRemoved).toBe(1);
|
||||
});
|
||||
|
||||
it('rejects changed replacement bytes and preserves the reviewed existing file', async () => {
|
||||
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
|
||||
const { createScript } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { previewDrift, runDriftDeployment, DriftPlanChanged } = await import('../../src/lib/server/fleet/drift.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
await seedServerRegistry();
|
||||
const { script, version } = createScript({ name: 'Managed config', kind: 'baseline', scopeType: 'game', scopeValue: 't5', content: Buffer.from('set sv_voice "1"\n') });
|
||||
db().prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?').run(version.id, script.id);
|
||||
const path = join(fixture.t5, 't5_zombie_theater_1', 'configs', 'dedicated_sp.cfg');
|
||||
await writeFile(path, 'existing settings');
|
||||
const plan = await previewDrift(['t5_zombie_theater_1']);
|
||||
db().prepare('UPDATE script_versions SET content = ? WHERE id = ?').run(Buffer.from('set sv_voice "0"\n'), version.id);
|
||||
await expect(runDriftDeployment({ servers: plan.servers, expectedFingerprint: plan.fingerprint })).rejects.toBeInstanceOf(DriftPlanChanged);
|
||||
expect(await readFile(path, 'utf8')).toBe('existing settings');
|
||||
});
|
||||
|
||||
it('keeps selected reconciliation within its reviewed server identities', async () => {
|
||||
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
|
||||
const { previewDrift, runDriftDeployment, DriftPlanChanged } = await import('../../src/lib/server/fleet/drift.js');
|
||||
const second = join(fixture.t5, 't5_zombie_theater_2', 'configs');
|
||||
await mkdir(second, { recursive: true });
|
||||
await seedServerRegistry();
|
||||
const firstPath = join(fixture.t5, 't5_zombie_theater_1', 'configs', 'stray.cfg');
|
||||
await writeFile(firstPath, 'one');
|
||||
await writeFile(join(second, 'stray.cfg'), 'two');
|
||||
const plan = await previewDrift(['t5_zombie_theater_1']);
|
||||
await expect(runDriftDeployment({ expectedFingerprint: plan.fingerprint })).rejects.toBeInstanceOf(DriftPlanChanged);
|
||||
await runDriftDeployment({ servers: plan.servers, expectedFingerprint: plan.fingerprint });
|
||||
expect(existsSync(firstPath)).toBe(false);
|
||||
expect(await readFile(join(second, 'stray.cfg'), 'utf8')).toBe('two');
|
||||
});
|
||||
});
|
||||
|
||||
describe('drift scanning', () => {
|
||||
it('replaces a seeded config once instead of planning both removal and installation', async () => {
|
||||
const { seedServerRegistry } = await import('../../src/lib/server/fleet/servers.js');
|
||||
|
||||
Reference in New Issue
Block a user