import {test} from 'node:test'; import assert from 'node:assert/strict'; import {validJson,freeze} from '../../src/lib/canvas-core/values.ts'; test('transitively immutable JSON can reuse its proof without enumerating the same object again',()=>{ const value=freeze({data:{text:'safe'},meta:{number:1,flag:true,empty:null}});assert.equal(validJson(value),true);const values=Object.values;try{Object.values=object=>{if(object===value)throw Error('immutable root enumerated twice');return values(object);};assert.equal(validJson(value),true);}finally{Object.values=values;} }); test('a cached subtree still observes the validation depth limit at each new position',()=>{ const leaf=freeze({value:1});assert.equal(validJson(leaf),true);const nest=count=>{let value=leaf;while(count--)value=Object.freeze({child:value});return value;};assert.equal(validJson(nest(63)),true);assert.equal(validJson(nest(64)),false);assert.equal(validJson(leaf,64),false);assert.equal(validJson(Object.freeze({}),64),true); }); test('shallow freeze and getters do not hide mutable values or bypass subsequent validation',()=>{ const child={value:1},parent=Object.freeze({child});assert.equal(validJson(parent),true);child.value=undefined;assert.equal(validJson(parent),false);child.value=2;assert.equal(validJson(parent),true); let field=1,calls=0;const getter=Object.freeze({get value(){calls++;return field;}});assert.equal(validJson(getter),true);field=Infinity;assert.equal(validJson(getter),false);assert.equal(calls,2); }); test('sparse/inherited array entries and overridden every methods keep their observable behavior',()=>{ const prototype=Object.create(Array.prototype);prototype[0]=1;const sparse=new Array(1);Object.setPrototypeOf(sparse,prototype);Object.freeze(sparse);assert.equal(validJson(sparse),true);prototype[0]=undefined;assert.equal(validJson(sparse),false); let accepted=true,calls=0;const custom=[1];Object.defineProperty(custom,'every',{value(){calls++;return accepted;}});Object.freeze(custom);assert.equal(validJson(custom),true);accepted=false;assert.equal(validJson(custom),false);assert.equal(calls,2); let native=true,methodReads=0;const getterArray=[1];Object.defineProperty(getterArray,'every',{get(){methodReads++;return native?Array.prototype.every:()=>false;}});Object.freeze(getterArray);const parent=Object.freeze({getterArray});assert.equal(validJson(parent),true);native=false;assert.equal(validJson(parent),false);assert.equal(methodReads,2); const hole=Object.freeze(new Array(1));assert.equal(validJson(hole),true);assert.equal(validJson(hole,64),true); }); test('ordinary invalid JSON, cycles and unsupported prototypes remain invalid',()=>{ const cycle={};cycle.self=cycle;for(const value of [undefined,NaN,Infinity,()=>{},Symbol('x'),1n,{value:undefined},[undefined],new Date(),Object.create(null),cycle])assert.equal(validJson(value),false);assert.equal(validJson({text:'中文',number:0,empty:[]}),true); }); test('proxy reflection failures and mutable inherited array methods cannot produce an immutable proof',()=>{ const target={value:1},proxy=new Proxy(target,{isExtensible(){throw Error('optional inspection denied');}});assert.equal(validJson(proxy),true);target.value=undefined;assert.equal(validJson(proxy),false); const arrayTarget=Object.freeze([1]);let accepts=true;const arrayProxy=new Proxy(arrayTarget,{get(target,key,receiver){if(key==='every')return accepts?Array.prototype.every:()=>false;return Reflect.get(target,key,receiver);}}),parent=Object.freeze({arrayProxy});assert.equal(validJson(parent),true);accepts=false;assert.equal(validJson(parent),false); const other=new Proxy([1],{getPrototypeOf(){throw Error('no shape proof');}});assert.equal(validJson(other),true); });