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); }); });