t56-device-metrics.mjs 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. import assert from 'node:assert/strict'
  2. import { deviceMetricKind, deviceMetricPath, toTypedMetricsPayload } from '../../../src/modules/cockpit/domain/deviceMetricSources.js'
  3. import { fetchEquipmentDetailAggregate } from '../../../src/modules/cockpit/services/detailApi.js'
  4. assert.equal(deviceMetricKind({ name: '测试流量计' }), 'flow')
  5. assert.equal(deviceMetricPath('flow', 'TEST-TCLLJ002215'), '/waterSupply/monitoring/flow/trend/TEST-TCLLJ002215')
  6. const typed = toTypedMetricsPayload({ data: [{ flow: 12.5, unit: 'm³/h', timestamp: '2026-10-19T10:00:00+08:00' }] }, 'D', 'flow')
  7. assert.equal(typed.metrics[0].value, 12.5)
  8. assert.equal(typed.metrics[0].unit, 'm³/h')
  9. const paths = []
  10. const options = {
  11. config: { dataSource: 'real', api: { baseUrl: 'http://test.invalid/api/cockpit/v1' } },
  12. fetchImpl: async (url) => {
  13. paths.push(url)
  14. if (url.endsWith('/detail-aggregate')) return new Response(JSON.stringify({ code: 200, data: {
  15. deviceCode: 'TEST-TCLLJ002215', aggregateState: 'READY',
  16. archive: { state: 'SUCCESS', data: { deviceCode: 'TEST-TCLLJ002215', deviceName: '测试流量计', deviceTypeName: '流量计' } },
  17. metrics: { state: 'EMPTY', reason: 'NO_METRICS', data: null }, warnings: { state: 'SUCCESS', data: [] }, alarms: { state: 'SUCCESS', data: [] },
  18. alarmWarningRelations: { state: 'SUCCESS', data: [] }, workOrders: { state: 'EMPTY', data: null }, timelines: { state: 'EMPTY', data: null }, latestImage: { state: 'EMPTY', data: null },
  19. } }))
  20. if (url.includes('/waterSupply/monitoring/flow/trend/')) return new Response(JSON.stringify({ code: 200, data: [{ flow: 12.5, unit: 'm³/h', timestamp: '2026-10-19T10:00:00+08:00' }] }))
  21. throw new Error(`unexpected ${url}`)
  22. },
  23. }
  24. const result = await fetchEquipmentDetailAggregate('TEST-TCLLJ002215', options)
  25. assert.equal(paths.length, 2)
  26. assert.equal(result.data.sections.metrics.data.metrics[0].value, 12.5)
  27. assert.equal(result.data.sections.metrics.data.metrics[0].unit, 'm³/h')
  28. assert.equal(deviceMetricKind({ type: 'FLOW_METER', name: '设备' }), 'flow')
  29. assert.equal(deviceMetricKind({ type: 'LEVEL_METER', name: '设备' }), '')
  30. const failedPaths = []
  31. const failed = await fetchEquipmentDetailAggregate('D-401', { config: options.config, fetchImpl: async (url) => {
  32. failedPaths.push(url)
  33. if (url.endsWith('/detail-aggregate')) return new Response(JSON.stringify({ code: 200, data: {
  34. deviceCode: 'D-401', aggregateState: 'READY', archive: { state: 'SUCCESS', data: { deviceCode: 'D-401', deviceType: 'FLOW_METER', deviceName: '设备' } },
  35. metrics: { state: 'EMPTY', reason: 'NO_METRICS', data: null }, warnings: { state: 'SUCCESS', data: [] }, alarms: { state: 'SUCCESS', data: [] },
  36. alarmWarningRelations: { state: 'SUCCESS', data: [] }, workOrders: { state: 'EMPTY', data: null }, timelines: { state: 'EMPTY', data: null }, latestImage: { state: 'EMPTY', data: null },
  37. } }))
  38. return new Response(JSON.stringify({ code: 401, msg: 'unauthorized' }), { status: 401 })
  39. } })
  40. assert.equal(failedPaths.length, 2)
  41. assert.equal(failed.data.sections.metrics.state, 'unavailable')
  42. assert.equal(failed.data.sections.warnings.state, 'success')
  43. console.log('PASS 流量计按设备类型切专用指标源,通用EMPTY不覆盖专用读数')