import assert from 'node:assert/strict' // This replaces only HTTP transport. The real detail API, adapter and row view still run. const scenario = process.env.T56_LIVE_SCENARIO || 'normal' const value = scenario === 'zero' ? 0 : scenario === 'missing' ? null : 17.3 const time = scenario === 'invalid-time' ? 'not-a-timestamp' : '2026-10-23T17:04:29' const source = { id: 'captured-node-2', deviceId: 21119740, nodeId: 2, hum: value, tem: 22.2, createTime: time } if (scenario === 'wrong-node') source.nodeId = 3 const metrics = [ { metricCode: 'ENV_COD', metricName: 'COD', value, unit: scenario === 'wrong-unit' ? 'MPa' : 'mg/L', sampledAt: time === 'not-a-timestamp' ? time : `${time}+08:00`, timeSource: 'CREATED_AT', freshness: 'STALE' }, { metricCode: 'ENV_NODE_2_TEMPERATURE', metricName: 'COD节点温度', value: 22.2, unit: '℃', sampledAt: time === 'not-a-timestamp' ? time : `${time}+08:00`, timeSource: 'CREATED_AT', freshness: 'STALE' }, ] // phantom-temperature:源记录在,但后端漏发该节点的温度指标行。 if (scenario === 'phantom-temperature') metrics.pop() const reply = (data) => new Response(JSON.stringify({ code: 200, data }), { headers: { 'Content-Type': 'application/json' } }) globalThis.fetch = async (input) => { const url = new URL(input) assert.equal(url.hostname, 'fixture.invalid', 'The offline guard must never contact a live host') if (url.pathname === '/api/env/visualization/latest') { assert.equal(url.searchParams.get('deviceId'), '21119740') // no-source:源侧无记录;其余场景仅节点 2 有捕获记录。 return reply(url.searchParams.get('nodeId') === '2' && scenario !== 'no-source' ? source : null) } assert.equal(url.pathname, '/api/cockpit/v1/equipments/21119740/detail-aggregate') return reply({ deviceCode: '21119740', aggregateState: 'READY', archive: { state: 'SUCCESS', data: { deviceCode: '21119740', deviceName: '环境设备', deviceType: '15', deviceTypeName: '仁科环境监测器' } }, metrics: { state: 'STALE', data: { deviceCode: '21119740', state: 'STALE', stateReason: 'SAMPLE_OUTDATED', metrics } }, }) }