| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- import assert from 'node:assert/strict'
- import { readFile } from 'node:fs/promises'
- import { register } from 'node:module'
- register('./t32-adapter-io-loader.mjs',import.meta.url)
- const { createCesiumPipeModels }=await import('../../../src/modules/cockpit/services/cesiumPipeModels.js')
- globalThis.t32Models=[]
- globalThis.t32TerrainFails=false
- globalThis.window={location:{href:'http://localhost:8002/cockpit/',origin:'http://localhost:8002'}}
- const originalFetch=globalThis.fetch
- let missing=false
- const requested=[]
- globalThis.fetch=async(input,options)=>{
- const url=String(input)
- if(url.startsWith('blob:')) return originalFetch(input,options)
- requested.push(url)
- if(missing) return new Response('missing',{status:404})
- const name=url.split('/').at(-1)
- const json=await readFile(new URL('../../../public/cockpit/models/pipes/'+name,import.meta.url),'utf8')
- return new Response(json,{status:200})
- }
- const source={entities:{values:[]}}
- const added=[],removed=[]
- const viewer={terrainProvider:{},isDestroyed:()=>false,scene:{requestRender(){},primitives:{
- add(model){added.push(model)},remove(model){removed.push(model);model.destroy()},
- }}}
- const states=[]
- let adapter=createCesiumPipeModels(viewer,{source,onChange:state=>states.push(state),base:'/'})
- const pipe=(code,lon)=>({code,industry:'GAS',diameter:400,positions:[[lon,28.44],[lon+.00004,28.44]]})
- const network={pipes:[pipe('east',110.391),pipe('west',110.411)],components:[]}
- source.entities.values=network.pipes.map(row=>({id:'pipe:'+row.code,show:true}))
- await adapter.sync(network)
- assert.equal(added.length,2)
- assert.equal(states.at(-1).pending,2)
- assert.ok(source.entities.values.every(e=>e.show),'load promise does not remove fallback')
- for(const model of added) model.readyEvent.raiseEvent()
- assert.ok(source.entities.values.every(e=>!e.show),'ready switches complete pipes to models')
- assert.equal(requested.filter(url=>url.endsWith('spline.gltf')).length,1)
- assert.equal(added[0].gltf.buffers[0].uri,added[1].gltf.buffers[0].uri)
- assert.equal(added[0].gltf.buffers[0].uri,'http://localhost:8002/cockpit/models/pipes/spline.bin')
- await adapter.sync({...network,pipes:network.pipes.map(row=>({...row,name:'changed metadata'}))})
- assert.equal(added.length,2,'metadata-only change does not rebuild GPU batches')
- await adapter.sync({...network,pipes:[{...network.pipes[0],diameter:600},network.pipes[1]]})
- assert.equal(added.length,3,'only changed tile recreated')
- assert.equal(removed.length,1)
- assert.equal(source.entities.values[0].show,true)
- assert.equal(source.entities.values[1].show,false)
- added[2].readyEvent.raiseEvent()
- added[2].errorEvent.raiseEvent(new Error('late buffer failure'))
- assert.equal(source.entities.values[0].show,true)
- assert.equal(states.at(-1).failed,1)
- adapter.destroy()
- assert.ok(added.every(model=>model.destroyed))
- console.log('PASS real adapter readiness, shared URL, stable metadata, selective update, late error and cleanup')
- const guide = { id:'pipe:guide', show:true, polyline:{} }
- source.entities.values=[guide]
- adapter=createCesiumPipeModels(viewer,{source,base:'/'})
- const guideNetwork={pipes:[pipe('guide',110.451)],components:[]}
- await adapter.sync(guideNetwork)
- added.at(-1).readyEvent.raiseEvent()
- assert.equal(guide.polyline.distanceDisplayCondition.near,600)
- guide.polyline={}
- await adapter.sync({pipes:[{...guideNetwork.pipes[0],name:'metadata-only'}],components:[]})
- assert.equal(guide.polyline.distanceDisplayCondition?.near,600,'replaced polyline must retain near-model/far-guide rule')
- adapter.destroy()
- console.log('PASS replacement guide graphics receive distance restriction again')
- const terrainStates=[]
- globalThis.t32TerrainFails=true
- source.entities.values=[{id:'pipe:terrain',show:true}]
- adapter=createCesiumPipeModels(viewer,{source,onChange:state=>terrainStates.push(state),base:'/'})
- await adapter.sync({pipes:[pipe('terrain',110.431)],components:[]})
- assert.equal(terrainStates.at(-1).terrainFailed,true)
- assert.equal(terrainStates.at(-1).fallback,1)
- assert.equal(source.entities.values[0].show,true)
- adapter.destroy()
- console.log('PASS terrain failure preserves fallback and publishes reason')
- globalThis.t32TerrainFails=false
- missing=true
- const assetStates=[]
- source.entities.values=[{id:'component:404',show:true}]
- adapter=createCesiumPipeModels(viewer,{source,onChange:state=>assetStates.push(state),base:'missing-base/'})
- await adapter.sync({pipes:[],components:[{code:'404',kind:'valve',industry:'GAS',position:[110.431,28.44]}]})
- assert.equal(assetStates.at(-1).failed,1)
- assert.equal(assetStates.at(-1).fallback,1)
- assert.equal(source.entities.values[0].show,true)
- adapter.destroy()
- globalThis.fetch=originalFetch
- console.log('PASS template 404 reaches real adapter failure path and keeps symbol')
|