tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

local-modules.test.mjs (4261B)


      1 import test from 'node:test';
      2 import assert from 'node:assert/strict';
      3 import {makeBundle,sizedBundle} from './bundle-fixture.mjs';
      4 import {decodeBundle,sha256} from '../dist/bundle.js';
      5 import {encodeModule,moduleSnapshot} from '../dist/module-bundle.js';
      6 import {validateLocalModule,localModuleInputs} from '../dist/local-modules.js';
      7 import {moduleIdentity} from '../dist/module-identity.js';
      8 import {moduleDependencies} from '../dist/module-stage.js';
      9 
     10 test('local modules validate complete interfaces and distinct manifest/bundle identities',async()=>{
     11  const bytes=makeBundle([[],[0],[0,1]],[['left',2],['right',1]]);
     12  const local=await validateLocalModule(bytes);
     13  assert.equal(local.hash,await sha256(bytes));assert.equal(local.manifest,await moduleIdentity(decodeBundle(bytes)));
     14  assert.notEqual(local.hash,local.manifest);assert.deepEqual(local.names,['left','right']);
     15  bytes[0]=0;assert.equal(local.bytes[0],65,'owned immutable byte snapshot');
     16  await assert.rejects(()=>validateLocalModule(bytes),/magic/);
     17  await assert.rejects(()=>validateLocalModule(makeBundle([[]],[['not a name',0]])),/interface/);
     18  await assert.rejects(()=>validateLocalModule(makeBundle([[]],[['x',0]],{evaluation:'other'})),/semantics/);
     19  await assert.rejects(()=>validateLocalModule(new Uint8Array(16777217)),/16 MiB/);
     20 });
     21 test('local-first resolution is offline and never falls back after corrupt local input',async()=>{
     22  const entry=await validateLocalModule(makeBundle([[],[0]],[['value',1]]));
     23  const stage={native:true,body:'',imports:[[entry.manifest,'L']],exports:[]};
     24  const fetch=globalThis.fetch;globalThis.fetch=()=>{throw Error('Unexpected network access');};
     25  try{
     26   const modules=await moduleDependencies(new URL('https://invalid.test/'),stage,[entry]);
     27   assert.deepEqual([...modules.keys()],[entry.manifest]);
     28   await assert.rejects(()=>moduleDependencies(new URL('https://invalid.test/'),stage,[{...entry,bytes:makeBundle([[]],[['value',0]])}]),/manifest mismatch/);
     29   await assert.rejects(()=>moduleDependencies(new URL('https://invalid.test/'),stage,[{...entry,bytes:new Uint8Array([0])}]),/Truncated/);
     30  }finally{globalThis.fetch=fetch;}
     31 });
     32 test('local collection transfer bounds, duplicates and copies',async()=>{
     33  const entry=await validateLocalModule(makeBundle([[]]));
     34  const inputs=localModuleInputs([entry]);entry.bytes[0]=0;assert.equal(inputs[0].bytes[0],65);
     35  assert.throws(()=>localModuleInputs([entry,entry]),/Duplicate/);
     36  assert.throws(()=>localModuleInputs(Array(33).fill(entry)),/count/);
     37  assert.throws(()=>localModuleInputs([{manifest:'x',bytes:entry.bytes}]),/hash/);
     38  assert.throws(()=>localModuleInputs([{...entry,bytes:[]}]),/byte/);
     39  const large=sizedBundle(16777216);
     40  assert.throws(()=>localModuleInputs(Array.from({length:5},(_,i)=>({manifest:String(i).repeat(64),bytes:large}))),/64 MiB/);
     41 });
     42 test('portable module encoding canonicalizes duplicates without unfolding shared branches',async()=>{
     43  const nodes=[{kind:'leaf'},{kind:'leaf'},{kind:'stem',child:1},{kind:'stem',child:0}];
     44  for(let i=4;i<44;i++)nodes.push({kind:'fork',left:i-1,right:i-1});
     45  const bundle={nodes,exports:[{name:'deep',root:43},{name:'alias',root:43},{name:'small',root:2}],roots:[43],metadata:{}};
     46  const decoded=decodeBundle(encodeModule(bundle));
     47  assert.equal(decoded.nodes.length,42);assert.equal(decoded.exports[0].root,decoded.exports[1].root);
     48  assert.equal(decoded.exports[2].root,1);
     49  assert.ok(encodeModule(bundle).length<2000);
     50  assert.deepEqual(encodeModule(decoded),encodeModule(bundle));
     51  assert.throws(()=>encodeModule({...bundle,exports:[{name:'duplicate',root:0},{name:'duplicate',root:1}]}),/duplicate/);
     52 });
     53 test('module snapshots select public roots, drop synthetic list spine and refuse partial snapshots',()=>{
     54  const snapshot={root:5,aliases:[],truncated:false,nodes:[
     55   {id:0,kind:'leaf',children:[]},{id:1,kind:'stem',children:[0]},
     56   {id:3,kind:'fork',children:[1,0]},{id:5,kind:'fork',children:[1,3]}]};
     57  const bundle=decodeBundle(moduleSnapshot(['one','two'],snapshot));
     58  assert.equal(bundle.nodes.length,2);assert.deepEqual(bundle.exports.map(e=>e.root),[1,1]);
     59  assert.throws(()=>moduleSnapshot(['one'],snapshot),/tail/);
     60  assert.throws(()=>moduleSnapshot(['one','two'],{...snapshot,truncated:true}),/partial/);
     61 });