Files
Endoleap/tests/headless/import-file-retention.test.mjs
T

19 lines
3.1 KiB
JavaScript

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {createImportJob,validateImportJob} from '../../src/lib/canvas-runtime/imports.ts';
import {markImportFiles,importFiles,prepareImportFileRetention} from '../../src/lib/canvas-runtime/import-file-retention.ts';
const job={...createImportJob({id:'job',projectId:'P',sourceFingerprint:'source',parserVersion:1,source:{kind:'native-ndjson',fileId:'source',byteLength:10,targetCanvasId:'P'}}),status:'cancelled'},runtime={imports:new Map([['job',job]]),resources:new Map()},coverage=()=>({complete:true,pins:new Map()});
test('source retention marks are immutable metadata with monotonic job generations and per-file clearing',()=>{
const next=markImportFiles(runtime,new Map([['source',{version:1,unreferencedAt:100}]]));assert.notEqual(next,runtime);assert.equal(job.source.retirement,undefined);assert.equal(next.imports.get('job').generation,1);assert.ok(Object.isFrozen(next.imports.get('job').source.retirement));validateImportJob(next.imports.get('job'));assert.equal(markImportFiles(next,new Map([['source',{version:1,unreferencedAt:100}]])),next);
const pinned=prepareImportFileRetention(next,next,()=>({complete:true,pins:new Map([['source',new Set(['export'])]])}),new Set());assert.equal(pinned.imports.get('job').source.retirement,undefined);assert.equal(pinned.imports.get('job').generation,2);assert.equal(next.imports.get('job').source.retirement.unreferencedAt,100);
const incomplete=prepareImportFileRetention(next,next,()=>({complete:false,pins:new Map()}),new Set());assert.equal(incomplete.imports.get('job').source.retirement,undefined);
});
test('source ownership, archive direction and completed purge metadata cannot be silently rewritten',()=>{
const archived={...job,source:{...job.source,fileId:'next',previousFiles:[{fileId:'source',byteLength:10}]}};assert.equal(importFiles(new Map([['job',archived]])).size,2);assert.equal(prepareImportFileRetention(runtime,{...runtime,imports:new Map([['job',archived]])},coverage,new Set()).imports.get('job'),archived);
assert.throws(()=>prepareImportFileRetention(runtime,{...runtime,imports:new Map()},coverage,new Set()),/import_file_identity_conflict/);assert.throws(()=>prepareImportFileRetention(runtime,{...runtime,resources:new Map([['source',{}]])},coverage,new Set()),/import_file_identity_conflict/);
const retired=markImportFiles(runtime,new Map([['source',{version:1,unreferencedAt:100,purgedAt:200}]]));assert.throws(()=>prepareImportFileRetention(retired,runtime,coverage,new Set()),/import_source_retired/);
assert.throws(()=>importFiles(new Map([['job',job],['other',{...job,id:'other'}]])),/import_file_identity_conflict/);
const resumed={...job,status:'running',source:{...job.source,retirement:{version:1,unreferencedAt:100,purgedAt:200}}};assert.throws(()=>validateImportJob(resumed),/invalid_import_file_retirement/);
for(const retirement of [{version:2,unreferencedAt:1},{version:1,unreferencedAt:-1},{version:1,unreferencedAt:2,purgedAt:1}])assert.throws(()=>validateImportJob({...job,source:{...job.source,retirement}}),/invalid_import_file_retirement/);
});