const test=require('node:test'),assert=require('node:assert/strict'),m=require('./web/device-model.js'); const sites=[{id:'floor',name:'一楼'},{id:'bed',name:'卧室',parentId:'floor'},{id:'office',name:'办公室'}]; const devices=[{id:'lamp',name:'卧室台灯',deviceType:'灯',siteId:'bed',managerName:'乙',ownerName:'甲',tags:['照明','常用']},{id:'desk',name:'办公灯',deviceType:'灯',siteId:'office',managerName:'甲',ownerName:'乙',tags:['照明']},{id:'phone',name:'手机',deviceType:'手机',siteId:'bed',managerName:'乙',ownerName:'乙',tags:[]}]; test('type then area and area then type reuse the same device identities',()=>{ const a=m.groups(devices,['type','space'],sites),b=m.groups(devices,['space','type'],sites); assert.deepEqual(a.find(g=>g.items.some(x=>x.id==='lamp')).labels,['灯','一楼','卧室']); assert.deepEqual(b.find(g=>g.items.some(x=>x.id==='lamp')).labels,['一楼','卧室','灯']); assert.equal(a.flatMap(g=>g.items).length,3);assert.equal(b.flatMap(g=>g.items).length,3); }); test('responsible person differs from owner and supports multitag grouping',()=>{ assert.equal(m.groups(devices,['manager'],sites).find(g=>g.labels[0]==='乙').items.length,2); assert.equal(m.groups(devices,['owner'],sites).find(g=>g.labels[0]==='乙').items.length,2); assert.equal(m.groups(devices,['tag'],sites).find(g=>g.labels[0]==='照明').items.length,2); assert.equal(m.groups(devices,['tag'],sites).find(g=>g.labels[0]==='未标记').items[0].id,'phone'); }); test('spaces have no fixed depth and traversal handles corrupt cycles safely',()=>{ const deep=Array.from({length:2500},(_,i)=>({id:String(i),name:'Area '+i,parentId:i?String(i-1):''})); assert.equal(m.path(deep,'2499').length,2500);assert.equal(m.descendants(deep,'0').size,2500); assert.equal(m.path([{id:'a',parentId:'b'},{id:'b',parentId:'a'}],'a').length,2); assert.equal(m.descendants([{id:'a',parentId:'b'},{id:'b',parentId:'a'}],'a').size,2); }); test('identical area names remain separate groups by stable identifiers',()=>{ const areas=[{id:'a',name:'卧室'},{id:'b',name:'卧室'}]; assert.equal(m.groups([{siteId:'a',id:1},{siteId:'b',id:2}],['space'],areas).length,2); }); test('camera recorder and general device records appear in one catalog without mutation',()=>{ const state={assets:[{id:'a',name:'Camera'}],recorders:[{id:'b',name:'Recorder'}],devices:[{id:'c',deviceType:'冰箱'}]},before=JSON.stringify(state); assert.deepEqual(m.catalog(state).map(d=>d.deviceType),['摄像机','录像机','冰箱']);assert.equal(JSON.stringify(state),before); assert.equal(m.groups(devices,[],sites).length,1); });