Add Prettier and ESLint tooling (Refs #77)
This commit is contained in:
@@ -1,49 +1,83 @@
|
||||
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';
|
||||
|
||||
vi.mock('../../src/lib/server/integrations/rcon.js', () => ({
|
||||
rconGetPlayerCount: vi.fn(async () => 0),
|
||||
rconPlutoniumVersion: vi.fn(async () => ({ revision: 5354, error: null })),
|
||||
rconStatusResult: vi.fn(async () => ({ ok: true, endpoint: { host: 'localhost', port: 28960 },
|
||||
status: { players: 0, map: 'zombie_theater', gametype: null, roster: [] } }))
|
||||
rconStatusResult: vi.fn(async () => ({
|
||||
ok: true,
|
||||
endpoint: { host: 'localhost', port: 28960 },
|
||||
status: { players: 0, map: '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 selection = { game: 't5' as const, mapSlug: 'theater' };
|
||||
const input = { ...selection, gameMode: 'zombie' as const, plutoKey: 'private-pluto-key' };
|
||||
|
||||
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');
|
||||
});
|
||||
afterEach(async () => { await fixture.cleanup(); vi.unstubAllEnvs(); vi.unstubAllGlobals(); });
|
||||
afterEach(async () => {
|
||||
await fixture.cleanup();
|
||||
vi.unstubAllEnvs();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
async function seedArtifacts() {
|
||||
const { createScript } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const baseline = createScript({ name: 'Classic settings', kind: 'baseline', scopeType: 'game', scopeValue: 't5', content: Buffer.from('set sv_voice "1"\n') });
|
||||
const script = createScript({ name: 'Classic perks', kind: 'script', scopeType: 'game', scopeValue: 't5', targetRelPath: 'perks.gsc', content: Buffer.from('main(){ old_perks(); }') });
|
||||
for (const item of [baseline, script]) db().prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?').run(item.version.id, item.script.id);
|
||||
const baseline = createScript({
|
||||
name: 'Classic settings',
|
||||
kind: 'baseline',
|
||||
scopeType: 'game',
|
||||
scopeValue: 't5',
|
||||
content: Buffer.from('set sv_voice "1"\n')
|
||||
});
|
||||
const script = createScript({
|
||||
name: 'Classic perks',
|
||||
kind: 'script',
|
||||
scopeType: 'game',
|
||||
scopeValue: 't5',
|
||||
targetRelPath: 'perks.gsc',
|
||||
content: Buffer.from('main(){ old_perks(); }')
|
||||
});
|
||||
for (const item of [baseline, script])
|
||||
db()
|
||||
.prepare('UPDATE scripts SET current_version_id = ? WHERE id = ?')
|
||||
.run(item.version.id, item.script.id);
|
||||
return { baseline, script };
|
||||
}
|
||||
|
||||
describe('launch recipes and previews', () => {
|
||||
|
||||
it('previews without reserving capacity, saving keys, exposing bytes, or submitting host commands', async () => {
|
||||
await seedArtifacts();
|
||||
const { previewCreation } = await import('../../src/lib/server/fleet/create.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const first = previewCreation(selection);
|
||||
const second = previewCreation(selection);
|
||||
expect(first).toMatchObject({ name: 't5_zombie_theater_1', port: 28960, hasSavedKey: false, ready: true });
|
||||
expect(first).toMatchObject({
|
||||
name: 't5_zombie_theater_1',
|
||||
port: 28960,
|
||||
hasSavedKey: false,
|
||||
ready: true
|
||||
});
|
||||
expect(second.name).toBe(first.name);
|
||||
expect(second.port).toBe(first.port);
|
||||
expect(first.recipe.files).toHaveLength(3);
|
||||
@@ -54,42 +88,74 @@ describe('launch recipes and previews', () => {
|
||||
});
|
||||
|
||||
it('returns the same operation for repeated submission, even after expiry and restart', async () => {
|
||||
const { previewCreation, queueCreateServer } = await import('../../src/lib/server/fleet/create.js');
|
||||
const { previewCreation, queueCreateServer } =
|
||||
await import('../../src/lib/server/fleet/create.js');
|
||||
const { db, closeDb } = await import('../../src/lib/server/db/index.js');
|
||||
const { getServerKey } = await import('../../src/lib/server/fleet/server-keys.js');
|
||||
const preview = previewCreation(selection);
|
||||
const queued = queueCreateServer({ ...input, previewId: preview.id });
|
||||
expect(queued.ok).toBe(true);
|
||||
db().prepare("UPDATE server_launch_previews SET expires_at = '2000-01-01T00:00:00Z' WHERE id = ?").run(preview.id);
|
||||
db()
|
||||
.prepare(
|
||||
"UPDATE server_launch_previews SET expires_at = '2000-01-01T00:00:00Z' WHERE id = ?"
|
||||
)
|
||||
.run(preview.id);
|
||||
closeDb();
|
||||
expect(queueCreateServer({ ...input, previewId: preview.id, plutoKey: 'replacement-key' })).toEqual(queued);
|
||||
expect(
|
||||
queueCreateServer({ ...input, previewId: preview.id, plutoKey: 'replacement-key' })
|
||||
).toEqual(queued);
|
||||
expect(db().prepare('SELECT * FROM server_creation_jobs').all()).toHaveLength(1);
|
||||
expect(getServerKey(preview.name!)).toBe('private-pluto-key');
|
||||
});
|
||||
|
||||
it('carries preview identity through the public creation API and rejects malformed requests', async () => {
|
||||
const { POST: preview } = await import('../../src/routes/api/deployments/preview/+server.js');
|
||||
const { POST: preview } =
|
||||
await import('../../src/routes/api/deployments/preview/+server.js');
|
||||
const { POST: launch } = await import('../../src/routes/api/servers/+server.js');
|
||||
const request = (body: unknown) => ({ request: new Request('http://localhost/api', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }) });
|
||||
for (const body of [null, [], { game: 't7', mapSlug: 'theater' }]) expect((await preview(request(body))).status).toBe(400);
|
||||
const request = (body: unknown) => ({
|
||||
request: new Request('http://localhost/api', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
});
|
||||
for (const body of [null, [], { game: 't7', mapSlug: 'theater' }])
|
||||
expect((await preview(request(body))).status).toBe(400);
|
||||
const response = await preview(request(selection));
|
||||
const plan = await response.json();
|
||||
expect(response.status).toBe(200);
|
||||
expect((await launch(request({ ...input, previewId: '' }))).status).toBe(400);
|
||||
const first = await launch(request({ ...input, previewId: plan.id, expectedName: plan.name }));
|
||||
const first = await launch(
|
||||
request({ ...input, previewId: plan.id, expectedName: plan.name })
|
||||
);
|
||||
expect(first.status).toBe(202);
|
||||
const retry = await launch(request({ ...input, previewId: plan.id, expectedName: plan.name }));
|
||||
const retry = await launch(
|
||||
request({ ...input, previewId: plan.id, expectedName: plan.name })
|
||||
);
|
||||
expect(await retry.json()).toEqual(await first.json());
|
||||
});
|
||||
|
||||
it('rejects expired, missing, and mismatched previews', async () => {
|
||||
const { previewCreation, queueCreateServer } = await import('../../src/lib/server/fleet/create.js');
|
||||
const { previewCreation, queueCreateServer } =
|
||||
await import('../../src/lib/server/fleet/create.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const preview = previewCreation(selection);
|
||||
expect(queueCreateServer({ ...input, mapSlug: 'moon', previewId: preview.id })).toMatchObject({ ok: false, kind: 'bad-request' });
|
||||
db().prepare("UPDATE server_launch_previews SET expires_at = '2000-01-01T00:00:00Z' WHERE id = ?").run(preview.id);
|
||||
expect(queueCreateServer({ ...input, previewId: preview.id })).toMatchObject({ ok: false, kind: 'bad-request' });
|
||||
expect(queueCreateServer({ ...input, previewId: 'missing' })).toMatchObject({ ok: false, kind: 'bad-request' });
|
||||
expect(
|
||||
queueCreateServer({ ...input, mapSlug: 'moon', previewId: preview.id })
|
||||
).toMatchObject({ ok: false, kind: 'bad-request' });
|
||||
db()
|
||||
.prepare(
|
||||
"UPDATE server_launch_previews SET expires_at = '2000-01-01T00:00:00Z' WHERE id = ?"
|
||||
)
|
||||
.run(preview.id);
|
||||
expect(queueCreateServer({ ...input, previewId: preview.id })).toMatchObject({
|
||||
ok: false,
|
||||
kind: 'bad-request'
|
||||
});
|
||||
expect(queueCreateServer({ ...input, previewId: 'missing' })).toMatchObject({
|
||||
ok: false,
|
||||
kind: 'bad-request'
|
||||
});
|
||||
expect(db().prepare('SELECT * FROM server_keys').all()).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -98,9 +164,13 @@ describe('launch recipes and previews', () => {
|
||||
const { saveBlueprint } = await import('../../src/lib/server/fleet/launch-recipes.js');
|
||||
const previewId = previewCreation(selection).id;
|
||||
expect(() => saveBlueprint({ name: '', previewId })).toThrow(/name/);
|
||||
expect(() => saveBlueprint({ name: 'Valid', previewId, deploymentId: 'also' })).toThrow(/exactly one/);
|
||||
expect(() => saveBlueprint({ name: 'Valid', previewId, deploymentId: 'also' })).toThrow(
|
||||
/exactly one/
|
||||
);
|
||||
expect(() => saveBlueprint({ name: 'Valid', previewId: 'missing' })).toThrow(/unavailable/);
|
||||
saveBlueprint({ name: 'Classic Night', previewId });
|
||||
expect(() => saveBlueprint({ name: ' classic night ', previewId })).toThrow(/already exists/);
|
||||
expect(() => saveBlueprint({ name: ' classic night ', previewId })).toThrow(
|
||||
/already exists/
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user