21 lines
2.3 KiB
JavaScript
21 lines
2.3 KiB
JavaScript
import {test} from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {createHash,randomBytes} from 'node:crypto';
|
|
import {createSha256} from '../../src/lib/canvas-runtime/sha256.ts';
|
|
import {importSourceDigest} from '../../src/lib/canvas-runtime/import-integrity.ts';
|
|
const expected=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
test('incremental SHA-256 matches known vectors, every padding boundary and varied chunk splits',()=>{
|
|
for(const bytes of [Buffer.alloc(0),Buffer.from('abc'),Buffer.from('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'),Buffer.alloc(1000000,97),...Array.from({length:260},(_,n)=>randomBytes(n))]){
|
|
for(const chunk of [1,7,63,64,65,8192]){const hash=createSha256();for(let offset=0;offset<bytes.length;offset+=chunk)hash.update(bytes.subarray(offset,offset+chunk));assert.equal(hash.hex(),expected(bytes),`${bytes.length}/${chunk}`);assert.equal(hash.hex(),expected(bytes));assert.throws(()=>hash.update(new Uint8Array()),/digest_finalized/);}
|
|
}
|
|
});
|
|
test('source digest reads bounded slices, yields and never materializes the entire Blob',async()=>{
|
|
const data=randomBytes(1024*1024+13),reads=[];let yields=0;
|
|
class SlicedBlob extends Blob{arrayBuffer(){throw Error('whole Blob read');}slice(start,end){reads.push(end-start);return super.slice(start,end);}}
|
|
assert.equal(await importSourceDigest(new SlicedBlob([data]),undefined,{chunkBytes:65536,yieldTask:async()=>{yields++;}}),expected(data));assert.equal(reads.length,17);assert.ok(reads.every(size=>size<=65536));assert.equal(yields,16);
|
|
});
|
|
test('source digest cancellation fences a late chunk and a scheduler yield',async()=>{
|
|
let release,entered;const gate=new Promise(resolve=>release=resolve),ready=new Promise(resolve=>entered=resolve),controller=new AbortController();class Chunk extends Blob{async arrayBuffer(){entered();await gate;return super.arrayBuffer();}}class Source extends Blob{slice(...args){return new Chunk([super.slice(...args)]);}}
|
|
const pending=importSourceDigest(new Source(['abc']),controller.signal);await ready;controller.abort();release();await assert.rejects(pending,/import_aborted/);const second=new AbortController();await assert.rejects(importSourceDigest(new Blob(['abcdef']),second.signal,{chunkBytes:2,yieldTask:async()=>second.abort()}),/import_aborted/);
|
|
});
|