141 lines
6.2 KiB
TypeScript
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);
|
|
});
|
|
});
|