import { describe, expect, test } from 'bun:test'; import { mkdtemp, readFile, writeFile, cp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { Database } from 'bun:sqlite'; import { ReferenceCompiler, SqliteReferenceRepository, readReferenceManifest } from '../src/lib/server/reference'; import { fixtureSources } from '../src/lib/server/reference/sources'; import type { ReferenceSourceAdapter } from '../src/lib/server/reference/types'; async function fixtureDirectory(): Promise<{ root: string; sources: string }> { const root = await mkdtemp(join(tmpdir(), 'eden-reference-')); const sources = join(root, 'fixtures'); await cp('fixtures/reference', sources, { recursive: true }); return { root, sources }; } describe('fixture reference compiler', () => { test('builds normalized schema and read-only lookup, including unknown IDs', async () => { const { root, sources } = await fixtureDirectory(); try { const result = await new ReferenceCompiler({ directory: join(root, 'reference'), profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(result.status).toBe('success'); if (result.status !== 'success' || !result.version) throw new Error('fixture build did not publish a version'); const resultVersion = result.version; const repository = new SqliteReferenceRepository(join(root, 'reference')); expect(await repository.types([34, 35, 587, 999999])).toEqual([ { id: 34, name: 'Tritanium', categoryId: 4, volume: 0.01 }, { id: 35, name: 'Pyerite', categoryId: 4, volume: 0.01 }, { id: 587, name: 'Rifter', categoryId: 6, volume: 2500 }, ]); expect(await repository.version()).toBe(resultVersion); repository.close(); const db = new Database(join(root, 'reference', 'databases', `${resultVersion}.sqlite`), { readonly: true }); for (const table of ['categories', 'groups', 'types', 'market_groups', 'blueprints', 'activities', 'materials', 'products', 'dogma_attributes', 'systems', 'regions', 'constellations', 'gates', 'stations']) expect(db.query(`SELECT 1 FROM sqlite_master WHERE type='table' AND name=?`).get(table)).toBeTruthy(); expect(db.query('SELECT COUNT(*) AS count FROM materials').get()).toEqual({ count: 2 }); expect(db.query('SELECT x,y,z FROM systems WHERE system_id=30000142').get()).toEqual({ x: -129064861735000000, y: 60755306910000000, z: 117469227060000000 }); db.close(); } finally { await rm(root, { recursive: true, force: true }); } }); test('unchanged inputs are a no-op and a failed independent source cannot change ACTIVE', async () => { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); try { const adapters = fixtureSources(sources); const first = await new ReferenceCompiler({ directory, profile: 'fixture', sources: adapters }).compile(); expect(first.status).toBe('success'); if (first.status !== 'success' || !first.version) throw new Error('initial fixture build did not publish a version'); const firstVersion = first.version; const firstManifest = await readReferenceManifest(directory); if (!firstManifest.active) throw new Error('missing active fixture manifest'); const hoboleaksCachePath = join(directory, firstManifest.active.sourceInputs.hoboleaks.cacheFile); const referenceCachePath = join(directory, firstManifest.active.sourceInputs['reference-data'].cacheFile); const hoboleaksCache = await readFile(hoboleaksCachePath); const referenceCache = await readFile(referenceCachePath); const noop = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(noop.status).toBe('noop'); expect(noop.version).toBe(firstVersion); const failedHoboleaks: ReferenceSourceAdapter = { source: 'hoboleaks', async check() { throw new Error('Hoboleaks unavailable'); }, async load() { throw new Error('not reached'); } }; const failed = await new ReferenceCompiler({ directory, profile: 'fixture', sources: { ...fixtureSources(sources), hoboleaks: failedHoboleaks } }).compile(); expect(failed.status).toBe('noop'); expect(failed.activeVersion).toBe(firstVersion); const repository = new SqliteReferenceRepository(directory); expect(await repository.version()).toBe(firstVersion); repository.close(); expect(failed.sourceResults.hoboleaks?.status).toBe('failed'); expect(await readFile(hoboleaksCachePath)).toEqual(hoboleaksCache); expect(await readFile(referenceCachePath)).toEqual(referenceCache); const invalidReference: ReferenceSourceAdapter = { source: 'reference-data', async check() { return { source: 'reference-data', checkedAt: new Date().toISOString(), fingerprint: 'invalid-reference-fingerprint' }; }, async load(check) { return { source: 'reference-data', raw: { categories: {} }, bytes: new TextEncoder().encode('{"categories":{}}'), check }; }, }; const invalid = await new ReferenceCompiler({ directory, profile: 'fixture', sources: { ...fixtureSources(sources), 'reference-data': invalidReference } }).compile(); expect(invalid.status).toBe('failed'); expect(invalid.activeVersion).toBe(firstVersion); expect(invalid.sourceResults['reference-data']?.status).toBe('failed'); expect(await readFile(referenceCachePath)).toEqual(referenceCache); } finally { await rm(root, { recursive: true, force: true }); } }); test('advances one source with the other source failure and records fallback provenance', async () => { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); try { const initial = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(initial.status).toBe('success'); if (initial.status !== 'success' || !initial.version) throw new Error('initial build missing version'); const failHoboleaks: ReferenceSourceAdapter = { source: 'hoboleaks', async check() { throw new Error('Hoboleaks unavailable'); }, async load() { throw new Error('not reached'); } }; await writeFile(join(sources, 'reference-data.json'), `${await readFile(join(sources, 'reference-data.json'), 'utf8')}\n`); const refAdvances = await new ReferenceCompiler({ directory, profile: 'fixture', sources: { ...fixtureSources(sources), hoboleaks: failHoboleaks } }).compile(); expect(refAdvances.status).toBe('success'); if (refAdvances.status !== 'success' || !refAdvances.version) throw new Error('reference source did not advance'); expect(refAdvances.version).not.toBe(initial.version); expect(refAdvances.sourceResults.hoboleaks?.status).toBe('failed'); expect(refAdvances.sourceResults.hoboleaks?.reused).toBe(true); const afterReference = await readReferenceManifest(directory); expect(afterReference.active?.sourceInputs.hoboleaks.fingerprint).toBe(afterReference.previous?.sourceInputs.hoboleaks.fingerprint); const failReference: ReferenceSourceAdapter = { source: 'reference-data', async check() { throw new Error('Reference Data unavailable'); }, async load() { throw new Error('not reached'); } }; await writeFile(join(sources, 'hoboleaks-industryactivities.json'), `${await readFile(join(sources, 'hoboleaks-industryactivities.json'), 'utf8')}\n`); const hoboAdvances = await new ReferenceCompiler({ directory, profile: 'fixture', sources: { ...fixtureSources(sources), 'reference-data': failReference } }).compile(); expect(hoboAdvances.status).toBe('success'); expect(hoboAdvances.version).not.toBe(refAdvances.version); expect(hoboAdvances.sourceResults['reference-data']?.status).toBe('failed'); expect(hoboAdvances.sourceResults['reference-data']?.reused).toBe(true); } finally { await rm(root, { recursive: true, force: true }); } }); test('pins a reader across promotion and batches very large type requests', async () => { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); try { const first = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(first.status).toBe('success'); if (first.status !== 'success' || !first.version) throw new Error('initial build missing version'); const pinned = new SqliteReferenceRepository(directory); const request = Array.from({ length: 1501 }, (_, index) => index === 0 ? 34 : index === 1 ? 35 : index === 2 ? 587 : 900000 + index); expect((await pinned.types(request)).map((type) => type.id)).toEqual([34, 35, 587]); await writeFile(join(sources, 'reference-data.json'), `${await readFile(join(sources, 'reference-data.json'), 'utf8')}\n`); const second = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(second.status).toBe('success'); if (second.status !== 'success' || !second.version) throw new Error('second build missing version'); expect(await pinned.version()).toBe(first.version); expect((await pinned.types([34]))[0]?.name).toBe('Tritanium'); const fresh = new SqliteReferenceRepository(directory); expect(await fresh.version()).toBe(second.version); fresh.close(); pinned.close(); } finally { await rm(root, { recursive: true, force: true }); } }); test('rejects a tampered active source cache without changing ACTIVE', async () => { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); try { const first = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(first.status).toBe('success'); if (first.status !== 'success' || !first.version) throw new Error('initial build missing version'); const manifest = await readReferenceManifest(directory); if (!manifest.active) throw new Error('missing active manifest'); const cachePath = join(directory, manifest.active.sourceInputs['reference-data'].cacheFile); await writeFile(cachePath, '{"format":1,"source":"reference-data","fingerprint":"tampered"}'); const failed = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(failed.status).toBe('failed'); expect(failed.activeVersion).toBe(first.version); expect(failed.sourceResults['reference-data']?.error).toContain('cache'); const repository = new SqliteReferenceRepository(directory); expect(await repository.version()).toBe(first.version); repository.close(); } finally { await rm(root, { recursive: true, force: true }); } }); test('rejects tampered source-cache provenance metadata during reuse', async () => { const fields: Array<[string, unknown]> = [ ['canonicalSha256', 'tampered-canonical'], ['archiveByteLength', 1], ['checkedAt', '2099-01-01T00:00:00.000Z'], ['url', 'file:///tampered'], ['byteLength', 1], ['sourceRevision', 999], ['sourceBuild', 'tampered-build'], ['stale', true], ['profile', 'production'], ['importerVersion', 'tampered-importer'], ]; for (const [field, value] of fields) { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); try { const first = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(first.status).toBe('success'); if (first.status !== 'success' || !first.version) throw new Error('initial fixture build did not publish a version'); const manifest = await readReferenceManifest(directory); if (!manifest.active) throw new Error('missing active fixture manifest'); const cachePath = join(directory, manifest.active.sourceInputs['reference-data'].cacheFile); const envelope = JSON.parse(await readFile(cachePath, 'utf8')) as Record; envelope[field] = value; await writeFile(cachePath, JSON.stringify(envelope)); const failed = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(failed.status, field).toBe('failed'); expect(failed.activeVersion, field).toBe(first.version); expect(failed.error, field).toMatch(/cache|metadata|checksum/); } finally { await rm(root, { recursive: true, force: true }); } } }); test('rejects tampered fallback cache metadata and preserves ACTIVE', async () => { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); try { const initial = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(initial.status).toBe('success'); if (initial.status !== 'success' || !initial.version) throw new Error('initial fixture build did not publish a version'); const manifest = await readReferenceManifest(directory); if (!manifest.active) throw new Error('missing active fixture manifest'); const cachePath = join(directory, manifest.active.sourceInputs.hoboleaks.cacheFile); const envelope = JSON.parse(await readFile(cachePath, 'utf8')) as Record; envelope.sourceBuild = 'tampered-build'; await writeFile(cachePath, JSON.stringify(envelope)); await writeFile(join(sources, 'reference-data.json'), `${await readFile(join(sources, 'reference-data.json'), 'utf8')}\n`); const failedHoboleaks: ReferenceSourceAdapter = { source: 'hoboleaks', async check() { throw new Error('Hoboleaks unavailable'); }, async load() { throw new Error('not reached'); } }; const failed = await new ReferenceCompiler({ directory, profile: 'fixture', sources: { ...fixtureSources(sources), hoboleaks: failedHoboleaks } }).compile(); expect(failed.status).toBe('failed'); expect(failed.activeVersion).toBe(initial.version); expect(failed.sourceResults.hoboleaks?.reused).not.toBe(true); expect((await readReferenceManifest(directory)).active?.version).toBe(initial.version); } finally { await rm(root, { recursive: true, force: true }); } }); test('failed validation does not advance source success provenance', async () => { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); try { const first = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(first.status).toBe('success'); if (first.status !== 'success' || !first.version) throw new Error('initial build missing version'); const before = await readReferenceManifest(directory); const beforeSuccess = before.sources['reference-data']?.lastSuccessAt; await writeFile(join(sources, 'reference-data.json'), `${await readFile(join(sources, 'reference-data.json'), 'utf8')}\n`); const failed = await new ReferenceCompiler({ directory, profile: 'fixture', thresholds: { systemsMinimum: 2 }, sources: fixtureSources(sources) }).compile(); expect(failed.status).toBe('failed'); expect(failed.activeVersion).toBe(first.version); const after = await readReferenceManifest(directory); expect(after.sources['reference-data']?.lastSuccessAt).toBe(beforeSuccess); expect(after.active?.version).toBe(first.version); } finally { await rm(root, { recursive: true, force: true }); } }); test('rollback pins old source caches for a later fallback update', async () => { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); try { const first = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(first.status).toBe('success'); if (first.status !== 'success' || !first.version) throw new Error('initial build missing version'); const firstManifest = await readReferenceManifest(directory); await writeFile(join(sources, 'reference-data.json'), `${await readFile(join(sources, 'reference-data.json'), 'utf8')}\n`); const second = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(second.status).toBe('success'); await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).rollback(); const failHoboleaks: ReferenceSourceAdapter = { source: 'hoboleaks', async check() { throw new Error('Hoboleaks unavailable'); }, async load() { throw new Error('not reached'); } }; await writeFile(join(sources, 'reference-data.json'), `${await readFile(join(sources, 'reference-data.json'), 'utf8')}\n`); const updated = await new ReferenceCompiler({ directory, profile: 'fixture', sources: { ...fixtureSources(sources), hoboleaks: failHoboleaks } }).compile(); expect(updated.status).toBe('success'); const after = await readReferenceManifest(directory); expect(after.active?.sourceInputs.hoboleaks.fingerprint).toBe(firstManifest.active?.sourceInputs.hoboleaks.fingerprint); expect(after.active?.version).not.toBe(first.version); } finally { await rm(root, { recursive: true, force: true }); } }); test('anchors reused cache metadata and no-ops after rollback-forward', async () => { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); const checkedReference = (checkedAt: string): Record => { const base = fixtureSources(sources); return { ...base, 'reference-data': { source: 'reference-data', async check() { return { ...(await base['reference-data'].check()), checkedAt }; }, async load(check) { return base['reference-data'].load(check); }, }, }; }; try { const first = await new ReferenceCompiler({ directory, profile: 'fixture', sources: checkedReference('2026-09-19T00:00:01.000Z') }).compile(); expect(first.status).toBe('success'); if (first.status !== 'success' || !first.version) throw new Error('initial fixture build did not publish a version'); await writeFile(join(sources, 'reference-data.json'), `${await readFile(join(sources, 'reference-data.json'), 'utf8')}\n`); const second = await new ReferenceCompiler({ directory, profile: 'fixture', sources: checkedReference('2026-09-19T00:00:02.000Z') }).compile(); expect(second.status).toBe('success'); if (second.status !== 'success' || !second.version) throw new Error('second fixture build did not publish a version'); await new ReferenceCompiler({ directory, profile: 'fixture', sources: checkedReference('2026-09-19T00:00:03.000Z') }).rollback(); const forward = await new ReferenceCompiler({ directory, profile: 'fixture', sources: checkedReference('2026-09-19T00:00:04.000Z') }).compile(); expect(forward.status).toBe('success'); if (forward.status !== 'success' || !forward.version) throw new Error('rollback-forward build did not publish a version'); const manifest = await readReferenceManifest(directory); expect(manifest.active?.sourceInputs['reference-data'].checkedAt).toBe('2026-09-19T00:00:02.000Z'); const db = new Database(join(directory, 'databases', `${forward.version}.sqlite`), { readonly: true }); expect(db.query('SELECT checked_at,source_build FROM source_provenance WHERE source = ?').get('reference-data')).toEqual({ checked_at: '2026-09-19T00:00:02.000Z', source_build: null }); db.close(); const repeat = await new ReferenceCompiler({ directory, profile: 'fixture', sources: checkedReference('2026-09-19T00:00:05.000Z') }).compile(); expect(repeat.status).toBe('noop'); expect(repeat.activeVersion).toBe(forward.version); } finally { await rm(root, { recursive: true, force: true }); } }); test('promotion retains PREVIOUS and rollback restores it', async () => { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); try { const first = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(first.status).toBe('success'); if (first.status !== 'success' || !first.version) throw new Error('initial fixture build did not publish a version'); const firstVersion = first.version; await writeFile(join(sources, 'reference-data.json'), `${await readFile(join(sources, 'reference-data.json'), 'utf8')}\n`); const second = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(second.status).toBe('success'); if (second.status !== 'success' || !second.version) throw new Error('second fixture build did not publish a version'); const secondVersion = second.version; expect(secondVersion).not.toBe(firstVersion); const rollback = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).rollback(); expect(rollback.activeVersion).toBe(firstVersion); expect(rollback.previousVersion).toBe(secondVersion); const repository = new SqliteReferenceRepository(directory); expect(await repository.version()).toBe(firstVersion); repository.close(); const rollForward = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(rollForward.status).toBe('success'); expect(rollForward.version).toBe(secondVersion); const forwardReader = new SqliteReferenceRepository(directory); expect(await forwardReader.version()).toBe(secondVersion); forwardReader.close(); } finally { await rm(root, { recursive: true, force: true }); } }); test('recovers a validated orphan database after manifest publication crash', async () => { const { root, sources } = await fixtureDirectory(); const directory = join(root, 'reference'); try { const first = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(first.status).toBe('success'); if (first.status !== 'success' || !first.version) throw new Error('initial fixture build did not publish a version'); await rm(join(directory, 'manifest.json')); const recovered = await new ReferenceCompiler({ directory, profile: 'fixture', sources: fixtureSources(sources) }).compile(); expect(recovered.status).toBe('success'); expect(recovered.version).toBe(first.version); const reader = new SqliteReferenceRepository(directory); expect(await reader.version()).toBe(first.version); reader.close(); } finally { await rm(root, { recursive: true, force: true }); } }); });