t15-time-district.mjs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import assert from 'node:assert/strict'
  2. import { readFileSync } from 'node:fs'
  3. import { districtQuery, districtLabel, districtFilterLabel, industryLabel } from '../../../src/modules/cockpit/domain/filterOptions.js'
  4. import { buildQuery } from '../../../src/modules/cockpit/services/domainRequest.js'
  5. import { toMonitoringOverview, monitoringOverviewPanel, toMonitoringDistribution, monitoringDistributionPanel, monitoringPointLedgerPanel, toMonitoringSituation, operationOverviewPanel } from '../../../src/modules/cockpit/domain/monitoringPanels.js'
  6. import { toManagementInformation, managementPanel, toBaseArchives } from '../../../src/modules/cockpit/domain/infrastructurePanels.js'
  7. import { archiveRows } from '../../../src/modules/cockpit/domain/equipmentDetailView.js'
  8. import { registerCockpitMocks } from '../../../src/modules/cockpit/mocks/contractMocks.js'
  9. import { fetchMapSnapshot } from '../../../src/modules/cockpit/services/mapApi.js'
  10. import { fetchMonitoringDistribution } from '../../../src/modules/cockpit/services/monitoringApi.js'
  11. import { fetchBaseArchives, fetchManagementInformation } from '../../../src/modules/cockpit/services/infrastructureApi.js'
  12. import { fetchOverviewRegions } from '../../../src/modules/cockpit/services/overviewApi.js'
  13. let passed = 0
  14. async function check(name, run) {
  15. await run()
  16. passed += 1
  17. console.log(`PASS ${name}`)
  18. }
  19. const section = { state: 'success' }
  20. await check('district codes and unknown values', () => {
  21. assert.equal(buildQuery({ district: districtQuery('south') }), '?district=0')
  22. assert.equal(districtQuery('north'), 1)
  23. assert.equal(districtQuery('taichang'), 2)
  24. assert.equal(districtQuery('unassigned'), 'UNASSIGNED')
  25. assert.equal(districtQuery('all'), '')
  26. assert.equal(districtQuery('garbage'), '')
  27. assert.equal(districtLabel(0), '城南')
  28. for (const value of [null, undefined, '', '0', 8]) assert.equal(districtLabel(value), '未分区')
  29. assert.equal(industryLabel('MANHOLE'), '窨井')
  30. })
  31. await check('null district is not enough to infer selection', () => {
  32. assert.equal(districtFilterLabel({ district: null, districtFilter: 'NONE' }), '全域')
  33. assert.equal(districtFilterLabel({ district: null, districtFilter: 'UNASSIGNED' }), '未分区')
  34. assert.equal(districtFilterLabel({ district: 0, districtFilter: 'ASSIGNED' }), '城南')
  35. })
  36. await check('data time never falls back to sample or response time', () => {
  37. const item = (raw) => monitoringOverviewPanel(toMonitoringOverview(raw), section).blocks[0].items.find((row) => row.key === 'createdAt')
  38. assert.equal(item({ createdAt: 'record', sampledAt: 'sample', updatedAt: 'response' }).value, 'record')
  39. assert.equal(item({ createdAt: null, sampledAt: 'sample', updatedAt: 'response' }).value, '—')
  40. })
  41. await check('ledger and abnormal equipment expose object time', () => {
  42. const distribution = toMonitoringDistribution({ createdAt: 'batch', monitoringPoints: [{ pointCode: 'p', district: 0, createdAt: 'point' }], mapFilter: { district: null, districtFilter: 'UNASSIGNED' } })
  43. assert.equal(monitoringPointLedgerPanel(distribution, section).blocks[0].rows[0].dataTime, 'point')
  44. assert.equal(monitoringDistributionPanel(distribution, section).blocks.find((block) => block.kind === 'rows').rows.find((row) => row.key === 'district').value, '未分区')
  45. const situation = toMonitoringSituation({ abnormalEquipment: [{ deviceCode: 'd', createdAt: null, district: 2 }] })
  46. assert.equal(operationOverviewPanel(situation, section).blocks.find((block) => block.kind === 'list').rows[0].dataTime, '—')
  47. })
  48. await check('management uses separate facility and equipment counts only', () => {
  49. const view = managementPanel(toManagementInformation({ totals: { facilityCount: 1, equipmentCount: 0 }, districtIndustry: [{ district: 0, industry: 'MANHOLE', facilityCount: 1, equipmentCount: 0 }], organizations: [{ organizationName: 'FORBIDDEN' }] }), section)
  50. assert.match(JSON.stringify(view), /城南 · 窨井/)
  51. assert.doesNotMatch(JSON.stringify(view), /FORBIDDEN|管理单位|权属|责任区域/)
  52. assert.match(JSON.stringify(view), /设备 0 台/)
  53. assert.equal(toBaseArchives({ districtDistribution: [{ district: 0, count: 2 }] }).regions[0].code, 0)
  54. })
  55. await check('archive retains static ownership and maintenance rows', () => {
  56. const rows = archiveRows({ ownershipUnit: 'owner', maintainer: 'maintainer', maintainerPhone: 'phone' })
  57. for (const [key, value] of [['ownershipUnit', 'owner'], ['maintainer', 'maintainer'], ['maintainerPhone', 'phone']]) assert.equal(rows.find((row) => row.key === key).value, value)
  58. })
  59. const transports = {}
  60. registerCockpitMocks({ handlers: { registerMockTransport: (path, handler) => { transports[path] = handler } } })
  61. const get = (path, query = '') => transports[path]({ path: path + query })
  62. await check('mock distribution filters assigned and unassigned districts', async () => {
  63. for (const [query, code, mode] of [['0', 0, 'ASSIGNED'], ['UNASSIGNED', null, 'UNASSIGNED']]) {
  64. const payload = await get('/monitoring/distribution', `?district=${query}`)
  65. assert.ok(payload.monitoringPoints.length > 0)
  66. assert.ok(payload.monitoringPoints.every((row) => row.district === code))
  67. assert.equal(payload.mapFilter.district, code)
  68. assert.equal(payload.mapFilter.districtFilter, mode)
  69. }
  70. assert.equal((await get('/monitoring/distribution')).mapFilter.districtFilter, 'NONE')
  71. })
  72. await check('mock management filtered totals equal row sums', async () => {
  73. const payload = await get('/infrastructure/management-information', '?district=0')
  74. assert.ok(payload.districtIndustry.length > 0)
  75. assert.ok(payload.districtIndustry.every((row) => row.district === 0))
  76. assert.equal(payload.totals.facilityCount, payload.districtIndustry.reduce((sum, row) => sum + row.facilityCount, 0))
  77. assert.equal(payload.organizations, undefined)
  78. })
  79. await check('archive mock aggregates use the same filtered population', async () => {
  80. for (const query of ['', '?district=0', '?district=UNASSIGNED', '?industry=MANHOLE', '?district=0&industry=GAS']) {
  81. const payload = await get('/infrastructure/base-archives', query)
  82. for (const key of ['industryDistribution', 'districtDistribution', 'ageDistribution', 'facilityTypeDistribution']) {
  83. assert.equal(payload[key].reduce((sum, row) => sum + row.count, 0), payload.totals.facilityCount)
  84. }
  85. }
  86. })
  87. await check('real transport request params use district, never region', async () => {
  88. const config = { dataSource: 'real', api: { baseUrl: 'http://test.invalid' } }
  89. for (const fetcher of [fetchMapSnapshot, fetchMonitoringDistribution, fetchBaseArchives, fetchManagementInformation, fetchOverviewRegions]) {
  90. for (const [scope, expected] of [['south', '0'], ['unassigned', 'UNASSIGNED'], ['all', null]]) {
  91. let seen
  92. await fetcher({ scope, config, fetchImpl: async (url) => {
  93. seen = new URL(url)
  94. return { ok: true, status: 200, json: async () => ({ monitoringPoints: [], unassociatedEquipment: [], pagination: { total: 0 }, total: 0, totals: { facilityCount: 0, equipmentCount: 0 }, districtIndustry: [] }) }
  95. } })
  96. assert.equal(seen.searchParams.get('district'), expected)
  97. assert.equal(seen.searchParams.has('region'), false)
  98. assert.equal(seen.searchParams.has('createdAt'), false)
  99. }
  100. }
  101. })
  102. await check('template exposes separate data and sample time assertions', () => {
  103. const panel = readFileSync(new URL('../../../src/modules/cockpit/components/PanelBlocks.vue', import.meta.url), 'utf8')
  104. const detail = readFileSync(new URL('../../../src/modules/cockpit/components/EquipmentDetailSections.vue', import.meta.url), 'utf8')
  105. assert.match(panel, /data-data-time>数据时间/)
  106. assert.match(detail, /data-sampled-at>采样时间/)
  107. })
  108. console.log(`T15: ${passed}/${passed} checks passed. Browser acceptance not covered.`)