verify-after-deploy.mjs 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. /**
  2. * 票 71 部署后真实复验(2026-10-23)。
  3. *
  4. * 对照 verify/artifacts/t71/expected-after-deploy.md 的部署前预演表,逐项判 PASS/FAIL:
  5. * 1. map-snapshot 里「预警未解决 + 未结束工单、无报警」的两台设备应已翻为 WORK_ORDER;
  6. * 2. 只有预警、没有工单的设备仍 WARNING;
  7. * 3. 报警设备 21119740 的点位仍 ALARM(票 52 分支不受改序影响);
  8. * 4. 未关联设备业务轴计数:预警 3→1、工单 3→5;
  9. * 5. Q3 留档:monitoring/situation 的 metricStatus 不跟随(两台仍是 WARNING)。
  10. *
  11. * 用法:node verify-after-deploy.mjs
  12. */
  13. import fs from 'node:fs'
  14. const BASE = 'http://192.168.110.96:8301/api/cockpit/v1'
  15. const OUT = 'after-deploy'
  16. fs.mkdirSync(OUT, { recursive: true })
  17. const get = async (path, file) => {
  18. const res = await fetch(BASE + path)
  19. const text = await res.text()
  20. fs.writeFileSync(`${OUT}/${file}`, text)
  21. return JSON.parse(text)
  22. }
  23. const snap = (await get('/map-snapshot?page=1&pageSize=1000', 'map-snapshot.json')).data
  24. const situation = (await get('/monitoring/situation', 'situation.json')).data
  25. const devices = new Map(snap.unassociatedEquipment.map((d) => [d.deviceCode, d.deviceStatus]))
  26. const sortKeys = (o) => Object.fromEntries(Object.entries(o).sort(([a], [b]) => a.localeCompare(b)))
  27. const counts = {}
  28. for (const status of devices.values()) counts[status] = (counts[status] || 0) + 1
  29. const checks = []
  30. const check = (name, actual, expected) => {
  31. const ok = JSON.stringify(actual) === JSON.stringify(expected)
  32. checks.push({ name, ok, actual, expected })
  33. console.log(`${ok ? 'PASS' : 'FAIL'} | ${name} | 实测 ${JSON.stringify(actual)} | 预期 ${JSON.stringify(expected)}`)
  34. }
  35. console.log('snapshotAt:', snap.snapshotAt)
  36. check('41010500001320000002 应翻 WORK_ORDER', devices.get('41010500001320000002'), 'WORK_ORDER')
  37. check('TEST-CBYL5432168 应翻 WORK_ORDER', devices.get('TEST-CBYL5432168'), 'WORK_ORDER')
  38. check('TEST-TCLLJ002215 无工单应仍 WARNING', devices.get('TEST-TCLLJ002215'), 'WARNING')
  39. check('860347065467627 有工单无预警应仍 WORK_ORDER', devices.get('860347065467627'), 'WORK_ORDER')
  40. check('41010500001320000001 应仍 NORMAL', devices.get('41010500001320000001'), 'NORMAL')
  41. check('8652052334 应仍 OFFLINE', devices.get('8652052334'), 'OFFLINE')
  42. const alarmPoint = snap.monitoringPoints.find((p) => (p.deviceCodes || []).includes('21119740'))
  43. check('21119740 所在点位应仍 ALARM', alarmPoint && alarmPoint.pointStatus, 'ALARM')
  44. check('未关联设备业务轴计数', sortKeys(counts), sortKeys({ NORMAL: 1, WARNING: 1, WORK_ORDER: 5, OFFLINE: 2 }))
  45. // Q3:situation 按事实判,不跟随业务序 —— 该端点的设备状态只在异常设备清单里(status 字段)
  46. const abnormal = situation.abnormalEquipment || []
  47. const statusInSituation = (code) => {
  48. const row = abnormal.find((d) => d.deviceCode === code)
  49. return row ? row.status : 'NOT_LISTED'
  50. }
  51. // 「不跟随」的判据是**不出现 WORK_ORDER**(situation 没有这个桶,按事实落 WARNING/ALARM/OFFLINE),
  52. // 不钉死具体是 WARNING:设备在线态是独立事实,会随现网漂移(CBYL 今日即被判 OFFLINE)。
  53. const checkNotFollowing = (name, code) => {
  54. const actual = statusInSituation(code)
  55. const ok = actual !== 'WORK_ORDER'
  56. checks.push({ name, ok, actual, expected: '≠ WORK_ORDER' })
  57. console.log(`${ok ? 'PASS' : 'FAIL'} | ${name} | 实测 ${actual} | 预期 ≠ WORK_ORDER`)
  58. }
  59. checkNotFollowing('Q3: 41010500001320000002 的 situation 状态不跟随业务序', '41010500001320000002')
  60. checkNotFollowing('Q3: TEST-CBYL5432168 的 situation 状态不跟随业务序', 'TEST-CBYL5432168')
  61. const failed = checks.filter((c) => !c.ok)
  62. console.log(`\n复验 ${checks.length - failed.length}/${checks.length} 通过`)
  63. process.exit(failed.length === 0 ? 0 : 1)