Files
electric-cherry/tests/unit/script-versions.test.ts
2026-10-12 18:46:57 -04:00

141 lines
6.2 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { newFixture, type Fixture } from '../helpers/fixture.js';
import {
manifestVersion,
parseScriptVersions,
scriptVersionError
} from '../../src/lib/script-version.js';
import { artifactVersionLabel } from '../../src/lib/artifact-version.js';
it('validates numeric release versions and exact manifest filenames', () => {
for (const value of [
undefined,
null,
'',
' ',
'v1.0.0',
'01.0.0',
'1.0',
'1.0.0-beta',
'1.0.0\nextra',
'1.'.repeat(50)
])
expect(scriptVersionError(value)).not.toBe('');
for (const value of ['0.0.0', '1.20.3', ' 1.2.0 ']) expect(scriptVersionError(value)).toBe('');
expect(manifestVersion({}, 'constructor')).toBe('');
expect(parseScriptVersions('{"hud.gsc":"1.2.0","HUD.gsc":"2.0.0","t5-plugin":"tag"}')).toEqual({
'hud.gsc': '1.2.0',
'HUD.gsc': '2.0.0'
});
for (const text of [
'oops',
'[]',
'null',
'{}',
'{"hud.gsc":1}',
'{"hud.gsc":"1"}',
'{"path/hud.gsc":"1.0.0"}'
])
expect(() => parseScriptVersions(text)).toThrow();
});
it('distinguishes historical script uploads from publisher versions', () => {
expect(artifactVersionLabel({ version: 3 }, 'script')).toBe('Unversioned (upload 3)');
expect(artifactVersionLabel({ version: 3, release_version: '1.2.0' }, 'script')).toBe(
'1.2.0 (upload 3)'
);
expect(artifactVersionLabel({ version: 3 }, 'config')).toBe('v3');
});
let fixture: Fixture;
beforeEach(async () => {
vi.resetModules();
fixture = await newFixture();
});
afterEach(async () => {
await fixture.cleanup();
});
const script = { name: 'HUD', targetRelPath: 'hud.gsc', content: Buffer.from('main(){}') };
describe('mandatory script releases', () => {
it('rejects missing and invalid versions without creating records', async () => {
const { createScript, uploadNewVersion, listScripts, listVersions } =
await import('../../src/lib/server/fleet/scripts.js');
for (const releaseVersion of [undefined, '', 'v1.0.0', '1.0', '1.0.0-rc1']) {
expect(() => createScript({ ...script, releaseVersion })).toThrow(/release version/);
}
expect(listScripts()).toHaveLength(0);
const saved = createScript({ ...script, releaseVersion: ' 1.0.0 ' });
expect(saved.version.release_version).toBe('1.0.0');
expect(() => uploadNewVersion(saved.script.id, script.content)).toThrow(/required/);
expect(listVersions(saved.script.id)).toHaveLength(1);
});
it('reuses identical releases, rejects changed contents and accepts older versions', async () => {
const { createScript, uploadNewVersion, listVersions } =
await import('../../src/lib/server/fleet/scripts.js');
const saved = createScript({ ...script, releaseVersion: '2.0.0' });
expect(uploadNewVersion(saved.script.id, script.content, 'retry', '2.0.0')).toEqual(
saved.version
);
expect(() =>
uploadNewVersion(saved.script.id, Buffer.from('changed'), undefined, '2.0.0')
).toThrow(/Bump/);
const older = uploadNewVersion(
saved.script.id,
Buffer.from('old release'),
undefined,
'1.0.0'
);
expect(older).toMatchObject({ version: 2, release_version: '1.0.0' });
expect(listVersions(saved.script.id)).toHaveLength(2);
expect(
createScript({ ...script, name: 'Other', releaseVersion: '2.0.0' }).version
.release_version
).toBe('2.0.0');
});
it('preserves legacy content and does not infer a release from matching bytes', async () => {
const { createScript, uploadNewVersion, getVersion, getVersionContent } =
await import('../../src/lib/server/fleet/scripts.js');
const { db } = await import('../../src/lib/server/db/index.js');
const saved = createScript({ ...script, releaseVersion: '1.0.0' });
db()
.prepare('UPDATE script_versions SET release_version = NULL WHERE id = ?')
.run(saved.version.id);
const next = uploadNewVersion(saved.script.id, script.content, undefined, '1.0.0');
expect(next).toMatchObject({ version: 2, release_version: '1.0.0' });
expect(getVersion(saved.version.id)!.release_version).toBeNull();
expect(getVersionContent(saved.version.id)).toEqual(script.content);
});
it('enforces the API contract even when reuseExisting is requested', async () => {
const create = await import('../../src/routes/api/scripts/+server.js');
const upload = await import('../../src/routes/api/scripts/[id]/versions/+server.js');
const { listScripts, listVersions } = await import('../../src/lib/server/fleet/scripts.js');
const url = new URL('http://test/api/scripts');
const form = new FormData();
form.set('name', 'HUD');
form.set('content', new File(['main(){}'], 'hud.gsc'));
const request = () => new Request(url, { method: 'POST', body: form });
expect((await create.POST({ url, request: request() })).status).toBe(400);
expect(listScripts()).toHaveLength(0);
form.set('releaseVersion', '1.0.0');
const saved = await (await create.POST({ url, request: request() })).json();
const params = { id: String(saved.script.id) };
form.set('reuseExisting', 'true');
form.delete('releaseVersion');
expect((await upload.POST({ params, request: request() })).status).toBe(400);
form.set('releaseVersion', '1.0.0');
expect(await (await upload.POST({ params, request: request() })).json()).toMatchObject({
reused: true,
version: { id: saved.versionId, release_version: '1.0.0' }
});
form.set('content', new File(['different'], 'hud.gsc'));
expect((await upload.POST({ params, request: request() })).status).toBe(409);
expect(listVersions(saved.script.id)).toHaveLength(1);
form.set('releaseVersion', '0.9.0');
expect((await upload.POST({ params, request: request() })).status).toBe(200);
});
});