Pace collection startups and clarify deployment queue progress

This commit is contained in:
2026-09-19 20:33:08 -04:00
parent 1d073779fe
commit f171889dfb
18 changed files with 693 additions and 81 deletions
+13 -3
View File
@@ -36,6 +36,7 @@ beforeEach(async () => {
vi.stubEnv('RCON_PASSWORD', 'private-rcon-password');
vi.stubEnv('SERVER_PORT_MIN', '28960');
vi.stubEnv('SERVER_PORT_MAX', '28980');
vi.stubEnv('DEPLOYMENT_CREATE_CONCURRENCY', '1');
});
afterEach(async () => { await fixture.cleanup(); vi.unstubAllEnvs(); });
@@ -60,6 +61,7 @@ describe('multi-server blueprints', () => {
const stored = db().prepare('SELECT recipe FROM server_blueprints WHERE id = ?').get(blueprint.id) as { recipe: string };
expect(JSON.parse(stored.recipe)).toEqual({ format: 3, entries });
expect(preview.ready).toBe(true);
expect(preview.launchConcurrency).toBe(1);
expect(preview.servers.map(server => server.name)).toEqual(['t5_zombie_theater_1', 't5_zombie_theater_2', 't5_zombie_moon_1', 't6_zombie_town_1', 't6_zombie_town_2']);
expect(preview.servers.map(server => server.port)).toEqual([28960, 28961, 28962, 28963, 28964]);
expect(db().prepare('SELECT * FROM server_creation_jobs').all()).toEqual([]);
@@ -112,7 +114,7 @@ describe('multi-server blueprints', () => {
expect(preview.entries[0].recipe.files).toContainEqual(expect.objectContaining({ path: 'telemetry.gsc', versionId: current.id }));
expect(preview.entries[2].recipe.mapSlug).toBe('town');
const queued = queueCollection({ previewId: preview.id, keys: keysFor(preview) });
await processCreations();
for (let pass = 0; pass <= preview.servers.length; pass++) await processCreations();
expect(getCollection(queued.collectionId!)?.collection).toMatchObject({ total: 5, done: 5 });
for (const name of ['t5_zombie_theater_1', 't5_zombie_theater_2', 't5_zombie_moon_1']) {
expect(await readFile(join(fixture.t5, name, 'runtime/plutonium/storage/t5/raw/scripts/sp/telemetry.gsc'), 'utf8')).toBe('current telemetry');
@@ -221,9 +223,14 @@ describe('atomic collection launches', () => {
const { komodoCreateDeployment } = await import('../../src/lib/server/integrations/komodo.js');
const queued = queueCollection({ previewId: preview.id, keys: keysFor(preview) });
expect(komodoCreateDeployment).not.toHaveBeenCalled();
expect(getCollection(queued.collectionId!)?.collection).toMatchObject({ total: 5, active: 5, done: 0, failed: 0 });
expect(getCollection(queued.collectionId!)?.collection).toMatchObject({ total: 5, active: 5, done: 0, failed: 0, queued: 5, provisioning: 0, verifying: 0 });
expect(getCollection(queued.collectionId!)?.servers.map(server => server.queuePosition)).toEqual([1, 2, 3, 4, 5]);
closeDb();
await processCreations();
expect(getCollection(queued.collectionId!)?.collection).toMatchObject({ queued: 4, provisioning: 0, verifying: 1, done: 0 });
expect(getCollection(queued.collectionId!)?.queue).toMatchObject({ concurrency: 1, queued: 4, active: [{ server: preview.servers[0].name, state: 'awaiting-health' }] });
expect(getCollection(queued.collectionId!)?.servers.map(server => server.queuePosition)).toEqual([null, 1, 2, 3, 4]);
for (let pass = 0; pass < preview.servers.length; pass++) await processCreations();
const detail = getCollection(queued.collectionId!)!;
expect(detail.collection).toMatchObject({ total: 5, active: 0, done: 5, failed: 0 });
expect(listCollections()).toHaveLength(1);
@@ -308,7 +315,7 @@ describe('atomic collection launches', () => {
const newest = uploadNewVersion(script.id, Buffer.from('promoted while queued'));
db().prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?').run(newest.id, script.id);
uploadNewVersion(script.id, Buffer.from('unpromoted draft'));
await processCreations();
for (let pass = 0; pass <= preview.servers.length; pass++) await processCreations();
expect(getCollection(queued.collectionId!)?.collection).toMatchObject({ name: 'Friday Zombies', total: 5, done: 5 });
for (const name of ['t5_zombie_theater_1', 't5_zombie_theater_2', 't5_zombie_moon_1']) {
expect(await readFile(join(fixture.t5, name, 'runtime/plutonium/storage/t5/raw/scripts/sp/perks.gsc'), 'utf8')).toBe('promoted while queued');
@@ -323,6 +330,9 @@ describe('atomic collection launches', () => {
vi.mocked(komodoCreateDeployment).mockResolvedValueOnce({ ok: false, kind: 'http-error', message: 'Host rejected this container' });
const queued = queueCollection({ previewId: preview.id, keys: keysFor(preview) });
await processCreations();
expect(getCollection(queued.collectionId!)?.collection).toMatchObject({ queued: 4, failed: 1, done: 0 });
expect(komodoCreateDeployment).toHaveBeenCalledTimes(1);
for (let pass = 0; pass < preview.servers.length; pass++) await processCreations();
const detail = getCollection(queued.collectionId!)!;
expect(detail.collection).toMatchObject({ total: 5, active: 0, done: 4, failed: 1 });
expect(detail.servers[0]).toMatchObject({ state: 'failed', error: expect.stringContaining('Host rejected') });