import assert from 'node:assert/strict'; import {test} from 'node:test'; import {loadTypescript} from './support/load-typescript.mjs'; const load=()=>loadTypescript(new URL('../src/cutout/CutoutMask.ts',import.meta.url)); test('cutout bounds retain all components, padded edges, and unchanged soft alpha',async()=>{const {analyzeCutout}=await load();const mask=new Uint8Array(100*80);mask[0]=255;mask[7999]=255;mask[1]=12;const original=mask.slice();const result=analyzeCutout(mask,100,80);assert.deepEqual(result.bounds,{x0:0,y0:0,x1:100,y1:80});assert.deepEqual(mask,original);assert.equal(result.largeComponentCount,2);assert.ok(result.warnings.includes('multiple-foreground'));}); test('invalid masks fail, binary silhouettes pass, tiny silhouettes require review',async()=>{const {analyzeCutout}=await load();for(const a of [new Uint8Array(4),new Uint8Array(4).fill(255),new Uint8Array(3)])assert.throws(()=>analyzeCutout(a,2,2));const a=new Uint8Array(100);a[55]=255;const r=analyzeCutout(a,10,10);assert.ok(r.warnings.includes('tiny-foreground'));assert.ok(r.bounds.x1>r.bounds.x0);}); test('model alpha rejects NaN/constant and returns canonical soft values without thresholding',async()=>{const {normalizeMask}=await load();assert.throws(()=>normalizeMask(Float32Array.of(1,NaN,2,3),2,2,2,2));assert.throws(()=>normalizeMask(new Float32Array(4).fill(.4),2,2,2,2));const result=normalizeMask(Float32Array.of(0,.25,.5,1),2,2,2,2);assert.deepEqual([...result],[0,64,128,255]);}); test('U2Net preprocessing uses one RGB maximum, channel mean/std and CHW',async()=>{const {preprocessU2Net}=await loadTypescript(new URL('../src/cutout/U2NetPreprocess.ts',import.meta.url));const x=preprocessU2Net(Uint8ClampedArray.of(255,128,0,255),1,1);assert.equal(x.length,3);assert.ok(Math.abs(x[0]-(1-.485)/.229)<1e-5);assert.ok(Math.abs(x[1]-(128/255-.456)/.224)<1e-5);assert.ok(Math.abs(x[2]+.406/.225)<1e-5);});