Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import { prepareNativeHost } from '../helpers/native-host.js';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { newFixture, type Fixture } from '../helpers/fixture.js';
|
||||
import type { CollectionPreview } from '../../src/lib/deployment-plan.js';
|
||||
|
||||
@@ -9,13 +7,28 @@ vi.mock('../../src/lib/server/integrations/rcon.js', () => ({
|
||||
rconGetPlayerCount: vi.fn(async () => 0),
|
||||
rconPlutoniumVersion: vi.fn(async () => {
|
||||
// Version evidence must be observed strictly after the launch timestamp.
|
||||
await new Promise(resolve => setTimeout(resolve, 2));
|
||||
await new Promise((resolve) => setTimeout(resolve, 2));
|
||||
return { revision: 5354, error: null };
|
||||
}),
|
||||
rconStatusResult: vi.fn(async (server: string) => ({ ok: true, endpoint: { host: 'localhost', port: 28960 },
|
||||
status: { players: 0, map: server.startsWith('t6_') ? 'zm_transit' : server.includes('_moon_') ? 'zombie_moon' : 'zombie_theater', gametype: null, roster: [] } }))
|
||||
rconStatusResult: vi.fn(async (server: string) => ({
|
||||
ok: true,
|
||||
endpoint: { host: 'localhost', port: 28960 },
|
||||
status: {
|
||||
players: 0,
|
||||
map: server.startsWith('t6_')
|
||||
? 'zm_transit'
|
||||
: server.includes('_moon_')
|
||||
? 'zombie_moon'
|
||||
: 'zombie_theater',
|
||||
gametype: null,
|
||||
roster: []
|
||||
}
|
||||
}))
|
||||
}));
|
||||
vi.mock('../../src/lib/server/pipeline/worker.js', () => ({
|
||||
kick: vi.fn(async () => {}),
|
||||
kickProvisioning: vi.fn(async () => {})
|
||||
}));
|
||||
vi.mock('../../src/lib/server/pipeline/worker.js', () => ({ kick: vi.fn(async () => {}), kickProvisioning: vi.fn(async () => {}) }));
|
||||
|
||||
let fixture: Fixture;
|
||||
const entries = [
|
||||
@@ -24,15 +37,25 @@ const entries = [
|
||||
{ game: 't6', mapSlug: 'town', count: 2 }
|
||||
];
|
||||
beforeEach(async () => {
|
||||
vi.resetModules(); vi.clearAllMocks();
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
fixture = await newFixture([]);
|
||||
await prepareNativeHost();
|
||||
vi.stubGlobal('fetch', vi.fn(async () => Response.json({ product: 'plutonium-core-prod', revision: 5354, files: [] })));
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn(async () =>
|
||||
Response.json({ product: 'plutonium-core-prod', revision: 5354, files: [] })
|
||||
)
|
||||
);
|
||||
|
||||
vi.stubEnv('RCON_PASSWORD', 'private-rcon-password');
|
||||
vi.stubEnv('DEPLOYMENT_CREATE_CONCURRENCY', '1');
|
||||
});
|
||||
afterEach(async () => { await fixture.cleanup(); vi.unstubAllEnvs(); vi.unstubAllGlobals(); });
|
||||
afterEach(async () => {
|
||||
await fixture.cleanup();
|
||||
vi.unstubAllEnvs();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
async function previewLineup() {
|
||||
const { saveBlueprint } = await import('../../src/lib/server/fleet/launch-recipes.js');
|
||||
@@ -41,67 +64,114 @@ async function previewLineup() {
|
||||
return { blueprint, preview: previewCollection(blueprint.id) };
|
||||
}
|
||||
function keysFor(preview: CollectionPreview) {
|
||||
return Object.fromEntries(preview.servers.filter(server => server.name).map(server => [server.name!, 'key-' + server.name]));
|
||||
return Object.fromEntries(
|
||||
preview.servers
|
||||
.filter((server) => server.name)
|
||||
.map((server) => [server.name!, 'key-' + server.name])
|
||||
);
|
||||
}
|
||||
|
||||
describe('multi-server blueprints', () => {
|
||||
it('shows the saved startup limit in new previews and existing collection progress', async () => {
|
||||
const { blueprint, preview } = await previewLineup();
|
||||
const { queueCollection, getCollection, previewCollection } = await import('../../src/lib/server/fleet/collections.js');
|
||||
const { queueCollection, getCollection, previewCollection } =
|
||||
await import('../../src/lib/server/fleet/collections.js');
|
||||
const result = queueCollection({ previewId: preview.id, keys: keysFor(preview) });
|
||||
const { fleetSettings, saveFleetSettings } = await import('../../src/lib/server/settings.js');
|
||||
const { fleetSettings, saveFleetSettings } =
|
||||
await import('../../src/lib/server/settings.js');
|
||||
saveFleetSettings({ ...fleetSettings(), createConcurrency: 3 });
|
||||
expect(previewCollection(blueprint.id).launchConcurrency).toBe(3);
|
||||
expect(getCollection(result.collectionId!)?.queue.concurrency).toBe(3);
|
||||
});
|
||||
|
||||
it('reads a legacy one-server blueprint as a map selection and saves edits without snapshots', async () => {
|
||||
const { captureRecipe, listBlueprints, updateBlueprint } = await import('../../src/lib/server/fleet/launch-recipes.js');
|
||||
const { createScript, uploadNewVersion } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { captureRecipe, listBlueprints, updateBlueprint } =
|
||||
await import('../../src/lib/server/fleet/launch-recipes.js');
|
||||
const { createScript, uploadNewVersion } =
|
||||
await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { previewCreation } = await import('../../src/lib/server/fleet/create.js');
|
||||
const { db, closeDb } = await import('../../src/lib/server/db/index.js');
|
||||
const artifact = createScript({ name: 'Telemetry', targetRelPath: 'telemetry.gsc', scopeType: 'game', scopeValue: 't5', content: Buffer.from('legacy telemetry') });
|
||||
db().prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?').run(artifact.version.id, artifact.script.id);
|
||||
const artifact = createScript({
|
||||
name: 'Telemetry',
|
||||
targetRelPath: 'telemetry.gsc',
|
||||
scopeType: 'game',
|
||||
scopeValue: 't5',
|
||||
content: Buffer.from('legacy telemetry')
|
||||
});
|
||||
db()
|
||||
.prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?')
|
||||
.run(artifact.version.id, artifact.script.id);
|
||||
const recipe = captureRecipe('t5', 'theater');
|
||||
db().prepare('INSERT INTO server_blueprints (id,name,created_at,recipe) VALUES (?, ?, ?, ?)').run('legacy', 'Old favorite', new Date().toISOString(), JSON.stringify(recipe));
|
||||
db()
|
||||
.prepare(
|
||||
'INSERT INTO server_blueprints (id,name,created_at,recipe) VALUES (?, ?, ?, ?)'
|
||||
)
|
||||
.run('legacy', 'Old favorite', new Date().toISOString(), JSON.stringify(recipe));
|
||||
const current = uploadNewVersion(artifact.script.id, Buffer.from('current telemetry'));
|
||||
db().prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?').run(current.id, artifact.script.id);
|
||||
db()
|
||||
.prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?')
|
||||
.run(current.id, artifact.script.id);
|
||||
closeDb();
|
||||
const old = listBlueprints()[0];
|
||||
expect(old.totalServers).toBe(1);
|
||||
expect(old.entries).toEqual([{ count: 1, game: 't5', mapSlug: 'theater', mapName: 'Kino Der Toten' }]);
|
||||
expect(old.entries).toEqual([
|
||||
{ count: 1, game: 't5', mapSlug: 'theater', mapName: 'Kino Der Toten' }
|
||||
]);
|
||||
expect(old).not.toHaveProperty('recipe');
|
||||
expect(previewCreation({ blueprintId: 'legacy' }).recipe.files).toContainEqual(expect.objectContaining({ path: 'telemetry.gsc', versionId: current.id }));
|
||||
expect(previewCreation({ blueprintId: 'legacy' }).recipe.files).toContainEqual(
|
||||
expect.objectContaining({ path: 'telemetry.gsc', versionId: current.id })
|
||||
);
|
||||
const updatedEntries = [{ ...entries[0], count: 15 }, ...entries.slice(1)];
|
||||
const edited = updateBlueprint('legacy', { name: 'Whole lineup', entries: updatedEntries });
|
||||
expect(edited.totalServers).toBe(18);
|
||||
expect(edited.entries[0]).toEqual({ ...old.entries[0], count: 15 });
|
||||
const stored = db().prepare('SELECT recipe FROM server_blueprints WHERE id = ?').get('legacy') as { recipe: string };
|
||||
const stored = db()
|
||||
.prepare('SELECT recipe FROM server_blueprints WHERE id = ?')
|
||||
.get('legacy') as { recipe: string };
|
||||
expect(JSON.parse(stored.recipe)).toEqual({ format: 3, entries: updatedEntries });
|
||||
expect(listBlueprints()).toHaveLength(1);
|
||||
});
|
||||
|
||||
it.each([[], [{ game: 't5', mapSlug: 'theater', count: 0 }], [{ game: 't5', mapSlug: 'theater', count: 16 }],
|
||||
[{ game: 't5', mapSlug: 'theater', count: 1.5 }], [{ game: 't5', mapSlug: 'theater', count: '2' }],
|
||||
[{ game: 't5', mapSlug: 'town', count: 1 }], [entries[0], entries[0]]].map(input => ({ input })))('rejects an invalid lineup: $input', async ({ input }) => {
|
||||
it.each(
|
||||
[
|
||||
[],
|
||||
[{ game: 't5', mapSlug: 'theater', count: 0 }],
|
||||
[{ game: 't5', mapSlug: 'theater', count: 16 }],
|
||||
[{ game: 't5', mapSlug: 'theater', count: 1.5 }],
|
||||
[{ game: 't5', mapSlug: 'theater', count: '2' }],
|
||||
[{ game: 't5', mapSlug: 'town', count: 1 }],
|
||||
[entries[0], entries[0]]
|
||||
].map((input) => ({ input }))
|
||||
)('rejects an invalid lineup: $input', async ({ input }) => {
|
||||
const { saveBlueprint } = await import('../../src/lib/server/fleet/launch-recipes.js');
|
||||
expect(() => saveBlueprint({ name: 'Invalid', entries: input })).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('atomic collection launches', () => {
|
||||
|
||||
it('counts available keys once per game across all maps without reserving them in a preview', async () => {
|
||||
const { blueprint } = await previewLineup();
|
||||
const { saveServerKey, reserveServerKey } = await import('../../src/lib/server/fleet/server-keys.js');
|
||||
const { previewCollection, queueCollection } = await import('../../src/lib/server/fleet/collections.js');
|
||||
const { saveServerKey, reserveServerKey } =
|
||||
await import('../../src/lib/server/fleet/server-keys.js');
|
||||
const { previewCollection, queueCollection } =
|
||||
await import('../../src/lib/server/fleet/collections.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
saveServerKey('t5', 'pool-one'); saveServerKey('t5', 'pool-two'); saveServerKey('t6', 'pool-three');
|
||||
saveServerKey('t5', 'pool-one');
|
||||
saveServerKey('t5', 'pool-two');
|
||||
saveServerKey('t6', 'pool-three');
|
||||
const preview = previewCollection(blueprint.id);
|
||||
expect(preview.servers.map(server => server.hasSavedKey)).toEqual([true, true, false, true, false]);
|
||||
expect(preview.servers.map((server) => server.hasSavedKey)).toEqual([
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]);
|
||||
expect(db().prepare('SELECT * FROM server_key_leases').all()).toEqual([]);
|
||||
reserveServerKey('t5_zombie_pentagon_1', 't5');
|
||||
expect(() => queueCollection({ previewId: preview.id })).toThrow(/No available T5 server keys/);
|
||||
expect(() => queueCollection({ previewId: preview.id })).toThrow(
|
||||
/No available T5 server keys/
|
||||
);
|
||||
expect(db().prepare('SELECT * FROM server_creation_jobs').all()).toEqual([]);
|
||||
expect(db().prepare('SELECT * FROM server_key_leases').all()).toHaveLength(1);
|
||||
});
|
||||
@@ -110,62 +180,105 @@ describe('atomic collection launches', () => {
|
||||
const { preview } = await previewLineup();
|
||||
const { queueCollection } = await import('../../src/lib/server/fleet/collections.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { saveServerKey, getServerKey } = await import('../../src/lib/server/fleet/server-keys.js');
|
||||
const { saveServerKey, getServerKey } =
|
||||
await import('../../src/lib/server/fleet/server-keys.js');
|
||||
saveServerKey('t5', 'original-key');
|
||||
const keys = keysFor(preview);
|
||||
delete keys[preview.servers[4].name!];
|
||||
expect(() => queueCollection({ previewId: preview.id, keys })).toThrow(/No available .* server keys/);
|
||||
for (const table of ['server_launch_collections', 'server_collection_members', 'server_creation_jobs', 'server_deployments', 'server_launch_previews', 'server_launch_recipes']) {
|
||||
expect(() => queueCollection({ previewId: preview.id, keys })).toThrow(
|
||||
/No available .* server keys/
|
||||
);
|
||||
for (const table of [
|
||||
'server_launch_collections',
|
||||
'server_collection_members',
|
||||
'server_creation_jobs',
|
||||
'server_deployments',
|
||||
'server_launch_previews',
|
||||
'server_launch_recipes'
|
||||
]) {
|
||||
expect(db().prepare(`SELECT * FROM ${table}`).all()).toEqual([]);
|
||||
}
|
||||
expect(getServerKey(preview.servers[0].name!)).toBeUndefined();
|
||||
expect(db().prepare('SELECT pluto_key FROM server_keys').get()).toEqual({ pluto_key: 'original-key' });
|
||||
expect(db().prepare('SELECT pluto_key FROM server_keys').get()).toEqual({
|
||||
pluto_key: 'original-key'
|
||||
});
|
||||
expect(db().prepare('SELECT * FROM server_key_leases').all()).toEqual([]);
|
||||
expect(db().prepare('SELECT * FROM server_keys').all()).toHaveLength(1);
|
||||
keys[preview.servers[4].name!] = 'invalid key';
|
||||
expect(() => queueCollection({ previewId: preview.id, keys })).toThrow(/without whitespace/);
|
||||
expect(() => queueCollection({ previewId: preview.id, keys })).toThrow(
|
||||
/without whitespace/
|
||||
);
|
||||
expect(db().prepare('SELECT * FROM server_creation_jobs').all()).toEqual([]);
|
||||
expect(queueCollection({ previewId: preview.id, keys: keysFor(preview) }).ok).toBe(true);
|
||||
});
|
||||
|
||||
it('reuses saved keys and returns the same collection on resubmission after restart and expiry', async () => {
|
||||
const { preview, blueprint } = await previewLineup();
|
||||
const { queueCollection, previewCollection, getCollection } = await import('../../src/lib/server/fleet/collections.js');
|
||||
const { queueCollection, previewCollection, getCollection } =
|
||||
await import('../../src/lib/server/fleet/collections.js');
|
||||
const { saveServerKey } = await import('../../src/lib/server/fleet/server-keys.js');
|
||||
const { db, closeDb } = await import('../../src/lib/server/db/index.js');
|
||||
for (const server of preview.servers) saveServerKey(server.name!.slice(0, 2), `saved-${server.name}`);
|
||||
for (const server of preview.servers)
|
||||
saveServerKey(server.name!.slice(0, 2), `saved-${server.name}`);
|
||||
const ready = previewCollection(blueprint.id);
|
||||
expect(ready.servers.every(server => server.hasSavedKey)).toBe(true);
|
||||
expect(ready.servers.every((server) => server.hasSavedKey)).toBe(true);
|
||||
const queued = queueCollection({ previewId: ready.id });
|
||||
db().prepare("UPDATE server_collection_previews SET expires_at = '2000-01-01T00:00:00Z' WHERE id = ?").run(ready.id);
|
||||
db()
|
||||
.prepare(
|
||||
"UPDATE server_collection_previews SET expires_at = '2000-01-01T00:00:00Z' WHERE id = ?"
|
||||
)
|
||||
.run(ready.id);
|
||||
closeDb();
|
||||
expect(queueCollection({ previewId: ready.id })).toEqual(queued);
|
||||
expect(getCollection(queued.collectionId!)?.servers).toHaveLength(5);
|
||||
expect(db().prepare('SELECT * FROM server_creation_jobs').all()).toHaveLength(5);
|
||||
const next = previewCollection(blueprint.id);
|
||||
expect(next.servers).toEqual([]);
|
||||
expect(next.entries.map(entry => entry.existing)).toEqual([2, 1, 2]);
|
||||
expect(next.entries.map((entry) => entry.existing)).toEqual([2, 1, 2]);
|
||||
expect(queueCollection({ previewId: next.id })).toEqual({ ok: true, collectionId: null });
|
||||
expect(db().prepare('SELECT * FROM server_launch_collections').all()).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('validates collection API payloads and rejects an unused expired preview', async () => {
|
||||
const { preview, blueprint } = await previewLineup();
|
||||
const { POST: singlePreview } = await import('../../src/routes/api/deployments/preview/+server.js');
|
||||
const oldEndpoint = await singlePreview({ request: new Request('http://localhost/api/deployments/preview', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ blueprintId: blueprint.id })
|
||||
}) });
|
||||
const { POST: singlePreview } =
|
||||
await import('../../src/routes/api/deployments/preview/+server.js');
|
||||
const oldEndpoint = await singlePreview({
|
||||
request: new Request('http://localhost/api/deployments/preview', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ blueprintId: blueprint.id })
|
||||
})
|
||||
});
|
||||
expect(oldEndpoint.status).toBe(400);
|
||||
expect((await oldEndpoint.json()).error).toContain('target counts');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const { queueCollection } = await import('../../src/lib/server/fleet/collections.js');
|
||||
const { POST } = await import('../../src/routes/api/deployment-collections/+server.js');
|
||||
for (const body of [null, [], {}, { previewId: preview.id, keys: [] }, { previewId: preview.id, keys: { wrong: 'key' } }]) {
|
||||
const response = await POST({ request: new Request('http://localhost/api/deployment-collections', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }) });
|
||||
for (const body of [
|
||||
null,
|
||||
[],
|
||||
{},
|
||||
{ previewId: preview.id, keys: [] },
|
||||
{ previewId: preview.id, keys: { wrong: 'key' } }
|
||||
]) {
|
||||
const response = await POST({
|
||||
request: new Request('http://localhost/api/deployment-collections', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
});
|
||||
expect(response.status).toBe(400);
|
||||
}
|
||||
db().prepare("UPDATE server_collection_previews SET expires_at = '2000-01-01T00:00:00Z' WHERE id = ?").run(preview.id);
|
||||
expect(() => queueCollection({ previewId: preview.id, keys: keysFor(preview) })).toThrow(/expired/);
|
||||
db()
|
||||
.prepare(
|
||||
"UPDATE server_collection_previews SET expires_at = '2000-01-01T00:00:00Z' WHERE id = ?"
|
||||
)
|
||||
.run(preview.id);
|
||||
expect(() => queueCollection({ previewId: preview.id, keys: keysFor(preview) })).toThrow(
|
||||
/expired/
|
||||
);
|
||||
expect(db().prepare('SELECT * FROM server_creation_jobs').all()).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -178,33 +291,72 @@ describe('matching a blueprint exactly', () => {
|
||||
|
||||
it('cancels unstarted excess creations without booting them and retains their saved keys', async () => {
|
||||
const { queueCreateServer } = await import('../../src/lib/server/fleet/create.js');
|
||||
const first = queueCreateServer({ gameMode: 'zombie', mapSlug: 'theater', plutoKey: 'first' });
|
||||
const second = queueCreateServer({ gameMode: 'zombie', mapSlug: 'theater', plutoKey: 'second' });
|
||||
const first = queueCreateServer({
|
||||
gameMode: 'zombie',
|
||||
mapSlug: 'theater',
|
||||
plutoKey: 'first'
|
||||
});
|
||||
const second = queueCreateServer({
|
||||
gameMode: 'zombie',
|
||||
mapSlug: 'theater',
|
||||
plutoKey: 'second'
|
||||
});
|
||||
if (!first.ok || !second.ok) throw new Error('Fixture queue failed');
|
||||
const plan = await blueprint([{ game: 't5', mapSlug: 'theater', count: 1 }]);
|
||||
const { previewCollection, queueCollection, getCollection } = await import('../../src/lib/server/fleet/collections.js');
|
||||
const { previewCollection, queueCollection, getCollection } =
|
||||
await import('../../src/lib/server/fleet/collections.js');
|
||||
const queued = queueCollection({ previewId: previewCollection(plan.id, 'exact').id });
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
expect(db().prepare('SELECT state FROM server_deployments WHERE id = ?').get(second.deploymentId)).toEqual({ state: 'cancelled' });
|
||||
expect(db().prepare('SELECT state FROM server_deployments WHERE id = ?').get(first.deploymentId)).toEqual({ state: 'queued' });
|
||||
expect(getCollection(queued.collectionId!)?.collection).toMatchObject({ done: 1, total: 1, active: 0 });
|
||||
expect(
|
||||
db()
|
||||
.prepare('SELECT state FROM server_deployments WHERE id = ?')
|
||||
.get(second.deploymentId)
|
||||
).toEqual({ state: 'cancelled' });
|
||||
expect(
|
||||
db()
|
||||
.prepare('SELECT state FROM server_deployments WHERE id = ?')
|
||||
.get(first.deploymentId)
|
||||
).toEqual({ state: 'queued' });
|
||||
expect(getCollection(queued.collectionId!)?.collection).toMatchObject({
|
||||
done: 1,
|
||||
total: 1,
|
||||
active: 0
|
||||
});
|
||||
const { getServerKey } = await import('../../src/lib/server/fleet/server-keys.js');
|
||||
expect(getServerKey(second.name)).toBeUndefined();
|
||||
expect(db().prepare("SELECT pluto_key FROM server_keys WHERE pluto_key='second'").get()).toEqual({ pluto_key: 'second' });
|
||||
expect(
|
||||
db().prepare("SELECT pluto_key FROM server_keys WHERE pluto_key='second'").get()
|
||||
).toEqual({ pluto_key: 'second' });
|
||||
});
|
||||
|
||||
it('rolls back removals and creation cancellations when the game pool is exhausted', async () => {
|
||||
const { queueCreateServer } = await import('../../src/lib/server/fleet/create.js');
|
||||
const old = queueCreateServer({ gameMode: 'zombie', mapSlug: 'moon', plutoKey: 'existing' });
|
||||
const old = queueCreateServer({
|
||||
gameMode: 'zombie',
|
||||
mapSlug: 'moon',
|
||||
plutoKey: 'existing'
|
||||
});
|
||||
if (!old.ok) throw new Error(old.message);
|
||||
const plan = await blueprint([{ game: 't5', mapSlug: 'theater', count: 2 }]);
|
||||
const { previewCollection, queueCollection } = await import('../../src/lib/server/fleet/collections.js');
|
||||
const { previewCollection, queueCollection } =
|
||||
await import('../../src/lib/server/fleet/collections.js');
|
||||
const preview = previewCollection(plan.id, 'exact');
|
||||
expect(() => queueCollection({ previewId: preview.id })).toThrow(/No available .* server keys/);
|
||||
expect(() => queueCollection({ previewId: preview.id })).toThrow(
|
||||
/No available .* server keys/
|
||||
);
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
expect(db().prepare('SELECT state FROM server_deployments WHERE id = ?').get(old.deploymentId)).toEqual({ state: 'queued' });
|
||||
for (const table of ['server_collection_removals', 'server_launch_collections', 'server_creation_dependencies']) expect(db().prepare(`SELECT * FROM ${table}`).all()).toEqual([]);
|
||||
expect(db().prepare("SELECT id FROM server_deployments WHERE action = 'destroy'").all()).toEqual([]);
|
||||
expect(
|
||||
db().prepare('SELECT state FROM server_deployments WHERE id = ?').get(old.deploymentId)
|
||||
).toEqual({ state: 'queued' });
|
||||
for (const table of [
|
||||
'server_collection_removals',
|
||||
'server_launch_collections',
|
||||
'server_creation_dependencies'
|
||||
])
|
||||
expect(db().prepare(`SELECT * FROM ${table}`).all()).toEqual([]);
|
||||
expect(
|
||||
db().prepare("SELECT id FROM server_deployments WHERE action = 'destroy'").all()
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('defaults old previews and collections to fill and validates the preview mode API', async () => {
|
||||
@@ -219,9 +371,15 @@ describe('matching a blueprint exactly', () => {
|
||||
closeDb();
|
||||
const { previewCollection } = await import('../../src/lib/server/fleet/collections.js');
|
||||
expect(previewCollection(plan.id).mode).toBe('fill');
|
||||
const { POST } = await import('../../src/routes/api/deployment-collections/preview/+server.js');
|
||||
const { POST } =
|
||||
await import('../../src/routes/api/deployment-collections/preview/+server.js');
|
||||
for (const mode of ['exact', 'fill', 'invalid', null, true]) {
|
||||
const response = await POST({ request: new Request('http://localhost/api/deployment-collections/preview', { method: 'POST', body: JSON.stringify({ blueprintId: plan.id, mode }) }) });
|
||||
const response = await POST({
|
||||
request: new Request('http://localhost/api/deployment-collections/preview', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ blueprintId: plan.id, mode })
|
||||
})
|
||||
});
|
||||
expect(response.status).toBe(mode === 'exact' || mode === 'fill' ? 200 : 400);
|
||||
if (response.ok) expect((await response.json()).mode).toBe(mode);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user