import assert from 'node:assert/strict'; import {before,after,test} from 'node:test'; import {readFileSync,readdirSync} from 'node:fs'; import {createServer} from 'vite'; let server,api,resolveContribution,wirelessContributions,revealEnrollment; before(async()=>{ server=await createServer({appType:'custom',logLevel:'silent',server:{middlewareMode:true}}); api=await server.ssrLoadModule('@xgrids-k1/frontend/sensors/enrollment.ts'); ({revealEnrollment}=await server.ssrLoadModule('@xgrids-k1/frontend/sensors/revealEnrollment.ts')); ({sensorContribution:resolveContribution}=await server.ssrLoadModule('../../packages/sensor-ui/src/extensions.ts')); ({wirelessContributions}=await server.ssrLoadModule('../../packages/sensor-ui/src/extensions.ts')); }); after(async()=>{await server?.close();}); const initial={node_id:'node-one',available:true,fresh:true,runtime_id:'runtime-one',snapshot_revision:1,runtime_started_at:'2026-09-06T00:00:00Z',selected_device_id:'synthetic-ble'}; function fixture(){ let request,reads=0,posts=0,clock=Date.now(); const accepted=[]; const value=(status='running',revision=2)=>({...initial,snapshot_revision:revision,connected:status==='succeeded',connection_attempt:{schema_version:'missioncore.xgrids-k1-connection-attempt/v1',attempt_id:request.operation_id,connection_mode:'bridge',status,phase:'network_applied',control_state:status==='succeeded'?'ready':'unknown',safe_next_action:status==='succeeded'?'start-acquisition':'wait-for-current-attempt'}}); const transport={ submit:async input=>{posts++;request=input;return {operation_id:request.operation_id,state:'complete',result:value()};}, operation:async id=>({operation_id:id,state:'complete',result:value()}), state:async()=>{reads++;return value('succeeded',3);}, }; return {transport,value,accepted,parameters:{device_id:'synthetic-ble',ssid:'synthetic-network',password:crypto.randomUUID()}, observer:{onState:s=>accepted.push(s),now:()=>clock,pause:async()=>{clock+=100000;}}, counts:()=>({reads,posts}),request:()=>request}; } test('network ACK waits for the exact owned control bootstrap with one POST',async()=>{ const f=fixture();const result=await api.enroll(f.transport,initial,'connect',f.parameters,f.observer); assert.equal(result.connected,true);assert.deepEqual(f.counts(),{reads:1,posts:1}); assert.equal(f.accepted[0].connection_attempt.status,'running');assert.equal(f.accepted.at(-1).connection_attempt.status,'succeeded'); assert.equal('password' in f.parameters,false); }); test('lost POST response reads the same operation ID and never submits again',async()=>{ const f=fixture();const submit=f.transport.submit; f.transport.submit=async input=>{await submit(input);throw new Error('response lost');}; let ids=[];const operation=f.transport.operation; f.transport.operation=async id=>{ids.push(id);return operation(id);}; const result=await api.enroll(f.transport,initial,'connect',f.parameters,f.observer); assert.equal(result.connected,true);assert.equal(f.counts().posts,1);assert.deepEqual(ids,[f.request().operation_id]); assert.equal('password' in f.parameters,false); }); test('late snapshot cannot restore readiness or overwrite newer attempt evidence',()=>{ const current={...initial,snapshot_revision:8,connected:false}; assert.equal(api.mergeEnrollmentState(current,{...initial,snapshot_revision:7,connected:true}),current); const newer={...current,runtime_id:'runtime-two',runtime_started_at:'2026-09-06T00:01:00Z'}; assert.equal(api.mergeEnrollmentState(newer,{...initial,snapshot_revision:900,connected:true}),newer); assert.equal(api.mergeEnrollmentState(current,newer).runtime_id,'runtime-two'); assert.equal(api.mergeEnrollmentState(current,{available:false}).fresh,false); assert.equal(api.mergeEnrollmentState(current,{...initial,node_id:'another-board'}),current); }); test('a response for another operation is never accepted',async()=>{ const f=fixture();const submit=f.transport.submit; f.transport.submit=async input=>({...await submit(input),operation_id:'another-operation'}); await assert.rejects(api.enroll(f.transport,initial,'connect',f.parameters,f.observer),/другое действие/); assert.equal(f.counts().reads,0);assert.equal(f.counts().posts,1); }); test('foreign historical success cannot settle this attempt',async()=>{ const f=fixture();const submit=f.transport.submit; f.transport.submit=async input=>({...await submit(input),result:{...initial,connected:true,connection_attempt:{...f.value('succeeded').connection_attempt,attempt_id:'foreign'}}}); f.transport.state=async()=>({...initial,connected:true,connection_attempt:{...f.value('succeeded').connection_attempt,attempt_id:'foreign'}}); await assert.rejects(api.enroll(f.transport,initial,'connect',f.parameters,f.observer),/пока не подтверждён/); assert.equal(f.counts().posts,1); }); test('runtime restart interrupts observation without another command',async()=>{ const f=fixture();f.transport.state=async()=>({...initial,runtime_id:'runtime-two',runtime_started_at:'2026-09-06T00:01:00Z'}); await assert.rejects(api.enroll(f.transport,initial,'connect',f.parameters,f.observer),/Сеанс K1 изменился/); assert.equal(f.counts().posts,1);assert.equal(f.accepted.at(-1).runtime_id,'runtime-two'); }); test('closing the observer stops reads without cancelling/replaying a device command',async()=>{ const f=fixture();const controller=new AbortController(); f.observer.signal=controller.signal;f.observer.pause=async()=>controller.abort(); await assert.rejects(api.enroll(f.transport,initial,'connect',f.parameters,f.observer),/Наблюдение закрыто/); assert.deepEqual(f.counts(),{reads:0,posts:1}); }); test('station refusal is the same message in onboard and LAB presentations',()=>{ const state={...initial,connection_attempt:{schema_version:'missioncore.xgrids-k1-connection-attempt/v1',attempt_id:'test',status:'failed',phase:'network_outcome_unknown',public_error_code:'k1-wifi-network-not-found'}}; assert.match(api.enrollmentNotice(state),/Проверьте название сети и пароль/); assert.doesNotMatch(api.enrollmentNotice(state),/Bluetooth.*ошиб/); }); test('Bluetooth failure before dispatch asks for discovery without blaming Wi-Fi',()=>{ const state={...initial,connection_attempt:{schema_version:'missioncore.xgrids-k1-connection-attempt/v1',attempt_id:'test',status:'failed',stage:'connect-failed',phase:'network_not_applied',public_error_code:'BleakError',side_effect_status:'none'}}; assert.equal(api.enrollmentBluetoothFailure(state),true); assert.match(api.enrollmentNotice(state),/Bluetooth.*не были отправлены/s); assert.doesNotMatch(api.enrollmentNotice(state),/Проверьте.*пароль/); const unknown={...state,connection_attempt:{...state.connection_attempt,phase:'network_outcome_unknown',side_effect_status:'unknown'}}; assert.equal(api.enrollmentBluetoothFailure(unknown),false); assert.doesNotMatch(api.enrollmentNotice(unknown),/не были отправлены/); }); test('applied and restored network states admit read-only verification without credentials',async()=>{ const f=fixture(); const applied={...initial,allowed_actions:['observe-configured-device-network'],connection_attempt:{schema_version:'missioncore.xgrids-k1-connection-attempt/v1',attempt_id:'previous',status:'failed',stage:'control-bootstrap-failed',phase:'network_applied',side_effect_status:'applied',public_error_code:'TypeError'}}; assert.equal(api.enrollmentAllowed(applied,'verify'),true); assert.match(api.enrollmentNotice(applied),/Проверить состояние K1/); assert.equal(api.enrollmentAllowed({...applied,connection_attempt:null},'verify'),true); await api.enroll(f.transport,applied,'verify',{device_id:'synthetic-ble'},f.observer); assert.equal(f.request().action,'verify'); assert.deepEqual(f.request().parameters,{device_id:'synthetic-ble'}); assert.equal(f.counts().posts,1); }); test('new workflow content scrolls only the containing viewport without stealing focus',()=>{ const original={document:globalThis.document,window:globalThis.window,getComputedStyle:globalThis.getComputedStyle}; const calls=[];let reduced=false; const body={}; const scroller={parentElement:body,scrollHeight:1600,clientHeight:500,scrollTop:100, getBoundingClientRect:()=>({top:100,bottom:600}),scrollTo:value=>calls.push(value)}; globalThis.document={body,documentElement:{}}; globalThis.window={matchMedia:()=>({matches:reduced})}; globalThis.getComputedStyle=element=>({overflowY:element===scroller?'auto':'visible'}); const target=(top,height)=>({parentElement:scroller,getBoundingClientRect:()=>({top,bottom:top+height,height}),focus:()=>assert.fail('focus must remain with the operator')}); try{ revealEnrollment(target(120,250));assert.equal(calls.length,0); revealEnrollment(target(620,100));assert.deepEqual(calls.pop(),{top:220,behavior:'smooth'}); reduced=true; revealEnrollment(target(620,800));assert.deepEqual(calls.pop(),{top:620,behavior:'instant'}); revealEnrollment({...target(700,100),parentElement:body});assert.equal(calls.length,0); }finally{Object.assign(globalThis,original);} }); test('sensor host can resolve zero or unrelated integrations and rejects ambiguity',()=>{ const alpha={kind:'alpha',Detail:()=>null},beta={kind:'beta',Detail:()=>null}; assert.equal(resolveContribution([],{kind:'alpha'}),undefined); assert.equal(resolveContribution([alpha,beta],{kind:'beta'}),beta); assert.equal(resolveContribution([alpha,{...alpha}],{kind:'alpha'}),undefined); for(const name of readdirSync(new URL('../../../packages/sensor-ui/src/',import.meta.url))){ if(!/\.tsx?$/.test(name))continue; assert.doesNotMatch(readFileSync(new URL('../../../packages/sensor-ui/src/'+name,import.meta.url),'utf8'),/xgrids|lixel|\bk1\b|K1Detail/,'vendor dependency in '+name); } }); test('wireless device choices come from installed contributions without a single-vendor slot',()=>{ const wired={kind:'wired',Detail:()=>null}; const alpha={kind:'alpha',wirelessEnrollment:{label:'Device alpha',View:()=>null}}; const beta={kind:'beta',wirelessEnrollment:{label:'Device beta',View:()=>null}}; assert.deepEqual(wirelessContributions([]),[]); assert.deepEqual(wirelessContributions([wired,alpha,beta]),[alpha,beta]); assert.deepEqual(wirelessContributions([alpha,{...alpha},beta]),[beta]); }); test('onboard actions use current server policy and never infer authority from readiness',()=>{ const state={...initial,connected:true,allowed_actions:['verify-control-read-only']}; assert.equal(api.enrollmentAllowed(state,'connect'),false); assert.equal(api.enrollmentAllowed(state,'scan'),false); assert.equal(api.enrollmentAllowed(state,'verify'),true); assert.equal(api.enrollmentAllowed({...state,fresh:false},'verify'),false); }); test('delayed operation result cannot clear an observed board outage',()=>{ const current={...initial,fresh:false,available:false}; const result=api.mergeEnrollmentState(current,{...initial,fresh:undefined,snapshot_revision:2}); assert.equal(result.fresh,false); assert.equal(api.mergeEnrollmentState(result,{...initial,fresh:true,snapshot_revision:3}).fresh,true); }); test('failed or unknown Bluetooth search is never presented as an empty successful scan',async()=>{ for(const state of ['failed','rejected','unknown']){ let posts=0; const transport={submit:async command=>{posts++;return { operation_id:command.operation_id,state:state==='unknown'?'unknown':'complete', error:'unconfirmed device command', result:{...initial,candidates:[],command_result:{operation_id:command.operation_id,action:'scan',status:state}}, };}}; await assert.rejects(api.enroll(transport,initial,'scan'),/Не удалось выполнить поиск Bluetooth на БК/); assert.equal(posts,1); } assert.equal(api.enrollmentNotice({...initial,command_result:{action:'scan',status:'failed'}}),''); }); test('a confirmed scan with zero candidates remains a valid empty result',async()=>{ const transport={submit:async command=>({operation_id:command.operation_id,state:'complete', result:{...initial,candidates:[],command_result:{operation_id:command.operation_id,action:'scan',status:'succeeded'}}, })}; assert.deepEqual((await api.enroll(transport,initial,'scan')).candidates,[]); }); test('finishing enrollment requires a verified current device session and survives no stale fence',()=>{ const connected={...initial,connected:true,discovery_generation:3,mode_revision:2, device_session:{device_session_id:'session-one'},command_result:{status:'succeeded'}}; assert.equal(api.enrollmentProof(initial,'synthetic-ble'),null); const proof=api.enrollmentProof(connected,'synthetic-ble'); assert.ok(proof); assert.equal(api.enrollmentProofCurrent(proof,connected,'synthetic-ble'),true); for(const patch of [{fresh:false},{available:false},{connected:false},{runtime_id:'other-runtime'}, {discovery_generation:4},{mode_revision:3},{selected_device_id:'other-device'}, {device_session:{device_session_id:'other-session'}},{command_result:{status:'rejected'}}]){ assert.equal(api.enrollmentProofCurrent(proof,{...connected,...patch},'synthetic-ble'),false); } assert.equal(api.enrollmentProofCurrent(null,connected,'synthetic-ble'),false); assert.equal(api.enrollmentProofCurrent(proof,connected,'other-device'),false); }); test('lost BLE candidate tells the operator to rescan without blaming Ethernet or Wi-Fi credentials',()=>{ const notice=api.enrollmentNotice({...initial,connection_attempt:{ schema_version:'missioncore.xgrids-k1-connection-attempt/v1',attempt_id:'test',status:'failed', public_error_code:'network-provision-candidate-not-fresh', }}); assert.match(notice,/Найдите K1 ещё раз/); assert.match(notice,/Настройки Wi-Fi не были отправлены/); assert.doesNotMatch(notice,/пароль|Ethernet/); }); test('pending enrollment is presented by its action button without a second notice',()=>{ for(const phase of ['connecting','network_applied']){ assert.equal(api.enrollmentNotice({...initial,connection_attempt:{ schema_version:'missioncore.xgrids-k1-connection-attempt/v1',attempt_id:'test',status:'running',phase, }}),''); } }); test('discovery errors preserve their cause and never imply a Wi-Fi refusal',()=>{ for(const [code,text,requiresScan] of [ ['ble-discovery-busy',/поиск уже выполняется/i,false], ['ble-adapter-unavailable',/Bluetooth недоступен/,false], ['ble-discovery-failed',/Служба Bluetooth/,false], ['ble-selected-device-unavailable',/Выбранный K1 не найден/,true], ]){ const state={...initial,connection_attempt:{schema_version:'missioncore.xgrids-k1-connection-attempt/v1', attempt_id:'test',status:'failed',stage:'resolution-failed',phase:'network_not_applied', public_error_code:code,side_effect_status:'none'}}; assert.match(api.enrollmentNotice(state),text); assert.match(api.enrollmentNotice(state),/Настройки Wi-Fi не были отправлены/); assert.doesNotMatch(api.enrollmentNotice(state),/Проверьте.*пароль/); assert.equal(api.enrollmentBluetoothFailure(state),requiresScan); state.connection_attempt.side_effect_status='unknown'; state.connection_attempt.phase='network_outcome_unknown'; assert.doesNotMatch(api.enrollmentNotice(state),/не были отправлены/); } }); test('legacy resolution failure invalidates the form before a second stale submission',()=>{ const state={...initial,connection_attempt:{schema_version:'missioncore.xgrids-k1-connection-attempt/v1', attempt_id:'test',status:'failed',stage:'resolution-failed',public_error_code:'BleakDBusError',side_effect_status:'none'}}; assert.equal(api.enrollmentBluetoothFailure(state),true); assert.match(api.enrollmentNotice(state),/Найдите K1 ещё раз/); });