import {test} from 'node:test'; import assert from 'node:assert/strict'; import {readFile,mkdtemp,mkdir,writeFile,rm} from 'node:fs/promises'; import {tmpdir} from 'node:os'; import path from 'node:path'; import {managedMedia,readRepositoryMedia} from '../scripts/managed-media.mjs'; test('media registry uses immutable objects and consistent identities',()=>{ const byKey=new Map(); assert.equal(Object.keys(managedMedia).length,603); for(const [name,asset] of Object.entries(managedMedia)){ assert.ok(!name.includes('..')&&!name.startsWith('/')); assert.match(asset.sha256,/^[a-f0-9]{64}$/); assert.equal(asset.key,`repository-media/sha256/${asset.sha256}.png`); assert.match(asset.id,/^[a-f0-9-]{36}$/); assert.ok(asset.bytes>0&&asset.width>0&&asset.height>0); if(byKey.has(asset.key))assert.deepEqual(byKey.get(asset.key),asset); byKey.set(asset.key,asset); } assert.equal(byKey.size,572); }); test('external cache preserves all bytes and detects corruption',async()=>{ if(!process.env.MEDIA_SOURCE_ROOT)return; for(const name of Object.keys(managedMedia))assert.equal((await readRepositoryMedia(name)).length,managedMedia[name].bytes); await assert.rejects(()=>readRepositoryMedia('../unregistered.png'),/Unregistered/); const previous=process.env.MEDIA_SOURCE_ROOT;const temporary=await mkdtemp(path.join(tmpdir(),'media-corruption-')); try{ const name=Object.keys(managedMedia)[0];const file=path.join(temporary,name);await mkdir(path.dirname(file),{recursive:true});await writeFile(file,'corrupt'); process.env.MEDIA_SOURCE_ROOT=temporary;await assert.rejects(()=>readRepositoryMedia(name),/checksum mismatch/); }finally{process.env.MEDIA_SOURCE_ROOT=previous;await rm(temporary,{recursive:true,force:true});} }); test('every public raster path has an S3 mapping and runtime importer has no directory reads',async()=>{ const publicImages=Object.keys(managedMedia).filter(name=>name.startsWith('apps/waterfall-wiki/public/')); assert.equal(publicImages.length,5); const importer=await readFile(new URL('../apps/waterfall-wiki/scripts/import-character-packs.mjs',import.meta.url),'utf8'); assert.ok(importer.includes('/api/media/'));assert.ok(!importer.includes('readdir')); for(const pack of JSON.parse(await readFile(new URL('../apps/waterfall-wiki/seed/characters/packs.json',import.meta.url)))){ assert.equal(Object.keys(managedMedia).filter(name=>name.startsWith(`apps/waterfall-wiki/seed/characters/${pack.directory}/`)).length,pack.count); } });