import fs from 'node:fs'; import assert from 'node:assert/strict'; import {parseGLB,encodeGLB} from '../locenice/js/model-io.js'; import {solarPosition} from '../locenice/js/solar.js'; const root=new URL('../locenice/',import.meta.url); const bytes=new Uint8Array(fs.readFileSync(new URL('models/dum.glb',root))); const {gltf,binary}=parseGLB(bytes); const catalog=JSON.parse(fs.readFileSync(new URL('data/catalog.json',root),'utf8')); let count=0;function check(name,test){assert.ok(test,name);count++;console.log('PASS',name)} check('GLB roundtrip',parseGLB(encodeGLB(gltf,binary)).gltf.nodes.length===gltf.nodes.length); check('source model has 9 configurable rectangular windows',gltf.extras.allstavStandards.windows.length===9); check('source model has 9 rooms',gltf.extras.interiorStudy.rooms.length===9); check('default entry',catalog.defaults.entryModel==='three'&&catalog.defaults.entryColor==='7016'); check('default gutters',catalog.defaults.gutter==='015'); check('default roof',catalog.defaults.roof==='darkgray'); check('default sills',catalog.defaults.extSill==='anthracite'); check('blinds off at startup',catalog.defaults.blinds===false); check('stable IDs unique',new Set(gltf.nodes.map(n=>n.extras.loceniceId)).size===gltf.nodes.length); const excluded=gltf.extras.allstavStandards.triangleExcludedNode; check('triangle has no blinds',!gltf.nodes.some(n=>n.extras?.stdBlind&&n.extras.windowNode===excluded)); for(const [role,indices]of Object.entries(gltf.extras.allstavStandards.materials))check('material role '+role,indices.length>0&&indices.every(i=>gltf.materials[i])); for(const file of ['assets.json','references.json']){ const assets=JSON.parse(fs.readFileSync(new URL('data/'+file,root),'utf8')); for(const [id,value]of Object.entries(assets)){const uri=typeof value==='string'?value:value.src;check('asset '+id,fs.existsSync(new URL(uri,root)));} } check('June noon sun',solarPosition(Date.UTC(2026,5,21,11),48.8242,14.5275).elevation>60); console.log('All checks passed:',count);