import assert from 'node:assert/strict' import { planPipeModels } from '../../../src/modules/cockpit/domain/pipeModels.js' import { toPipeNetwork } from '../../../src/modules/cockpit/domain/pipeNetwork.js' const pipe=(code,x1,x2,diameter,industry='GAS')=>({code,industry,diameter,positions:[[110+x1*.0001,28.44],[110+x2*.0001,28.44]]}) const rows=[pipe('left',0,1,300),pipe('gap1',1,2,null),pipe('gap2',2,3,null),pipe('right',3,4,600)] const plan=planPipeModels({pipes:rows,components:[]}) const a=plan.instances.find(row=>row.id==='pipe:gap1') const b=plan.instances.find(row=>row.id==='pipe:gap2') assert.ok(a&&b,'double-sided anchored gap produces models') assert.ok(Math.abs(a.diameterM-.375)<1e-8) assert.ok(Math.abs(b.diameterM-.525)<1e-8) assert.equal(a.diameterSource,'interpolated') assert.deepEqual(a.diameterAnchors.map(row=>row.code),['left','right']) assert.equal(rows[1].diameter,null,'do not overwrite raw business diameter') assert.deepEqual(plan.diameterSummary,{reported:2,interpolated:2,missing:0}) console.log('PASS two-sided linear fill, traceable anchors, immutable raw diameter') const middle=(pipes,code)=>planPipeModels({pipes,components:[]}).instances.find(row=>row.id==='pipe:'+code&&row.diameterSource!=='visual-default') const weighted=[pipe('l',0,1,200),pipe('a',1,2,null),pipe('b',2,5,null),pipe('r',5,6,600)] assert.ok(Math.abs(middle(weighted,'a').diameterM-.25)<1e-8) assert.ok(Math.abs(middle(weighted,'b').diameterM-.45)<1e-8) const reversed=weighted.slice().reverse().map(row=>({...row,positions:row.positions.slice().reverse()})) assert.ok(Math.abs(middle(reversed,'a').diameterM-.25)<1e-8,'independent of input and path direction') assert.equal(middle([pipe('l',0,1,400),pipe('a',1,2,null),pipe('r',2,3,400)],'a').diameterM,.4) assert.equal(middle([pipe('l',0,1,400),pipe('a',1,2,null)],'a'),undefined,'one-sided does not fill') assert.equal(middle([pipe('a',0,1,null),pipe('b',1,2,null)],'a'),undefined,'no source means no invented diameter') assert.equal(middle([pipe('l',0,1,400),pipe('a',1,2,null),pipe('r',2,3,600,'WATER_SUPPLY')],'a'),undefined,'do not cross industries') assert.equal(middle([...rows,pipe('fork',2,8,800)],'gap1'),undefined,'stop before branch node') const almost=pipe('near',2,3,600);almost.positions[0][0]+=1e-10 assert.equal(middle([pipe('l',0,1,300),pipe('gap',1,2,null),almost],'gap'),undefined,'do not snap nearly coincident endpoints') assert.equal(middle([pipe('l',0,1,300),pipe('bad',1,2,0),pipe('r',2,3,600)],'bad').diameterSource,'interpolated') console.log('PASS length weighting, reversed paths, equal anchors, no one-sided/branch/industry/nearby guessing') const payload={features:rows.map((row,i)=>({geometry:{type:'LineString',coordinates:row.positions},properties:{code:row.code,industry:'GAS',diameter:i===0?'300.0':row.diameter,coordinateValidation:{renderable:true}}}))} const adapted=toPipeNetwork(payload) assert.equal(adapted.pipes[1].diameter,null) assert.equal(adapted.pipes[0].diameter,300) assert.deepEqual(planPipeModels(adapted).diameterSummary,{reported:2,interpolated:2,missing:0}) delete payload.features[0].properties.diameter assert.equal(toPipeNetwork(payload).pipes[0].diameter,null) console.log('PASS API raw diameter parsing preserves null/absent and feeds interpolation') const loop={...pipe('loop',1,1,null),positions:[[110.0001,28.44],[110.0008,28.44],[110.0001,28.44]]} assert.equal(middle([pipe('l',0,1,300),pipe('gap',1,2,null),pipe('r',2,3,600),loop],'gap'),undefined,'closed branch still counts toward junction degree') console.log('PASS closed side loop prevents ambiguous fill') for(const value of [true,false,[],[300],{},' ','no-diameter']) { payload.features[0].properties.diameter=value assert.equal(toPipeNetwork(payload).pipes[0].diameter,null,'reject non numeric diameter '+JSON.stringify(value)) } console.log('PASS nonnumeric JSON cannot become reported anchors') assert.ok(Math.abs(middle(weighted.slice().reverse(),'a').diameterM-.25)<1e-8) assert.ok(Math.abs(middle(weighted.map(row=>({...row,positions:row.positions.slice().reverse()})),'a').diameterM-.25)<1e-8) const closed=[pipe('only-anchor',0,1,300),pipe('ca',1,2,null),pipe('cb',2,0,null)] assert.equal(middle(closed,'ca'),undefined,'same reported pipe reached from both sides is not two anchors') assert.equal(middle(closed,'cb'),undefined) const ring=[pipe('ra',0,1,300),pipe('rg1',1,2,null),pipe('rb',2,3,600),pipe('rg2',3,0,null)] assert.equal(middle(ring,'rg1').diameterM,.45) assert.equal(middle(ring,'rg2').diameterM,.45) const multi={...pipe('poly',1,3,null),positions:[[110.0001,28.44],[110.0002,28.44],[110.0003,28.44]]} assert.ok(Math.abs(middle([pipe('l',0,1,200),multi,pipe('b',3,4,null),pipe('r',4,5,500)],'poly').diameterM-.3)<1e-8) assert.equal(middle([pipe('l',0,1,-300),pipe('gap',1,2,null),pipe('r',2,3,600)],'gap'),undefined) assert.equal(middle([pipe('l',0,1,300,'UNKNOWN'),pipe('gap',1,2,null,'UNKNOWN'),pipe('r',2,3,600,'UNKNOWN')],'gap'),undefined) console.log('PASS independent reversals, one/two-anchor cycles, polyline lengths, negative and unknown-industry anchors')