// Smoke test restricted to the independent embedded handoff identity. No binding, // provider registration, firmware upload, or OTA instruction is performed. const fs=require('node:fs'),crypto=require('node:crypto'),net=require('node:net'),assert=require('node:assert/strict'); const {sign}=require('../docs/embedded-signing-reference.cjs'); function text(v){const b=Buffer.from(v),h=Buffer.alloc(2);h.writeUInt16BE(b.length);return Buffer.concat([h,b]);} function packet(type,body){const h=[type];let n=body.length;do{let b=n%128;n=Math.floor(n/128);if(n)b|=128;h.push(b);}while(n);return Buffer.concat([Buffer.from(h),body]);} (async()=>{ const init=JSON.parse(fs.readFileSync(process.argv[2],'utf8').replace(/^\uFEFF/,''));assert.equal(init.factory_id,'KP01-020000260914');assert.equal(init.origin,'https://82.157.201.114'); async function post(path,body){const raw=JSON.stringify(body);const s=sign(Buffer.from(init.device_key_hex,'hex'),{path,raw,deviceId:init.factory_id,keyVersion:init.key_version,timestamp:Date.now(),nonce:crypto.randomBytes(16).toString('base64url'),requestId:crypto.randomUUID()});const r=await fetch(init.origin+path,{method:'POST',headers:s.headers,body:raw,redirect:'error',signal:AbortSignal.timeout(15000)});return {status:r.status,body:await r.json()};} const state=await post('/api/v1/device/ble-state',{});assert.equal(state.body.data.bound,false); const response=await post('/api/v1/device/mqtt-config',{});assert.equal(response.status,200);const config=response.body.data;assert.equal(config.broker_url,'mqtt://82.157.201.114:1883'); const missing=await post('/api/v1/device/ota/report',{task_id:crypto.randomUUID(),status:'failed',message:'smoke test - nonexistent task'});assert.equal(missing.status,409);assert.equal(missing.body.code,'OTA_NOT_FOUND'); const host=new URL(config.broker_url);const tunnel=process.env.MQTT_TUNNEL_PORT;if(tunnel)assert(/^\d{4,5}$/.test(tunnel)); const socket=net.connect({host:tunnel?'127.0.0.1':host.hostname,port:Number(tunnel||host.port)}); let buffer=Buffer.alloc(0),queue=[],waiter; const receive=()=>new Promise((resolve,reject)=>{if(queue.length){resolve(queue.shift());return;}const timer=setTimeout(()=>{waiter=null;reject(new Error('MQTT response timeout'));},8000);waiter={resolve:v=>{clearTimeout(timer);waiter=null;resolve(v);},reject:e=>{clearTimeout(timer);waiter=null;reject(e);}};}); socket.on('data',b=>{buffer=Buffer.concat([buffer,b]);while(buffer.length>1){let size=0,m=1,i=1,done=false;for(;i{if(waiter)waiter.reject(e);}); try{ await new Promise((resolve,reject)=>{const timer=setTimeout(()=>{socket.destroy();reject(new Error('MQTT TCP timeout'));},10000);socket.once('connect',()=>{clearTimeout(timer);resolve();});socket.once('error',e=>{clearTimeout(timer);reject(e);});}); socket.write(packet(0x10,Buffer.concat([text('MQTT'),Buffer.from([4,0xc2,0,60]),text(config.client_id),text(config.username),text(config.password)]))); let p=await receive();assert.equal(p.type,0x20);assert.equal(p.body[1],0); socket.write(packet(0x82,Buffer.concat([Buffer.from([0,1]),text(config.ota_topic),Buffer.from([1])])));p=await receive();assert.equal(p.type,0x90);assert.equal(p.body[2],1); socket.write(packet(0x32,Buffer.concat([text(config.status_topic),Buffer.from([0,2]),Buffer.from(JSON.stringify({online:true,firmware_version:'handoff-mqtt-simulator',version_code:0,ota_supported:true}))])));p=await receive();assert.equal(p.type,0x40); socket.write(Buffer.from([0xe0,0])); console.log(JSON.stringify({device_id:init.factory_id,config_status:response.status,transport_encrypted:false,mqtt_connected:true,ota_subscription:true,heartbeat_ack:true,ota_report_route:true,real_device_modified:false})); }finally{socket.end();socket.destroy();} })().catch(e=>{console.error('MQTT probe failed:',e.code||e.name,'No credentials printed.');process.exitCode=1;});