t48-real-envelope.mjs 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. import assert from 'node:assert/strict'
  2. import { requestCockpitJson } from '../../../src/modules/cockpit/services/cockpitApi.js'
  3. import { fetchMapSnapshot } from '../../../src/modules/cockpit/services/mapApi.js'
  4. import { createMapSummaryLoader } from '../../../src/modules/cockpit/domain/mapSummary.js'
  5. const config = { dataSource: 'real', api: { baseUrl: 'http://acceptance.invalid/api/cockpit/v1' } }
  6. const response = (body, status = 200) => ({ ok: status >= 200 && status < 300, status, json: async () => body })
  7. const rawPage = (page) => ({ snapshotAt: '2026-10-01T10:00:00+08:00', page, pageSize: 1, total: 2, complete: page === 2,
  8. monitoringPoints: [{ pointCode: `P-${page}`, pointName: `Point ${page}`, pointStatus: 'NORMAL', deviceCodes: [],
  9. longitude: 110.4, latitude: 28.44, coordinateValidation: { renderable: false, state: 'UNVERIFIED', reason: 'UNVERIFIED_COORDINATE' } }],
  10. unassociatedEquipment: [],
  11. })
  12. let failed = 0
  13. async function check(name, action) {
  14. try { await action(); console.log(`PASS ${name}`) }
  15. catch (error) { failed += 1; console.error(`FAIL ${name}: ${String(error.message).split('\n')[0]}`) }
  16. }
  17. await check('real AjaxResult unwraps before map adaptation and page2 keeps snapshot token', async () => {
  18. const urls = []
  19. const fetchImpl = async (url) => { urls.push(url); const page = Number(new URL(url).searchParams.get('page')); return response({ code: 200, msg: 'success', data: rawPage(page) }) }
  20. const loader = createMapSummaryLoader({ pageSize: 1, fetchPage: async (query) => {
  21. const section = await fetchMapSnapshot({ ...query, config, fetchImpl })
  22. return { ok: section.state === 'success', data: section.data, reason: section.reason }
  23. } })
  24. await loader.loadNext()
  25. await loader.loadNext()
  26. assert.equal(new URL(urls[1]).searchParams.get('snapshotAt'), rawPage(1).snapshotAt)
  27. assert.equal(loader.getState().objects.length, 2)
  28. assert.equal(loader.getState().phase, 'complete')
  29. assert.ok(loader.getState().objects.every((object) => object.coordinate.renderable === false), 'never promote unverified devices to renderable')
  30. })
  31. await check('HTTP200 business failure is not a successful section', async () => {
  32. const result = await requestCockpitJson('/map-snapshot', { config, fetchImpl: async () => response({ code: 500, msg: 'failure', data: null }) })
  33. assert.equal(result.ok, false)
  34. assert.equal(result.reason, 'unavailable')
  35. })
  36. await check('HTTP200 expired snapshot keeps recoverable category', async () => {
  37. const result = await requestCockpitJson('/map-snapshot', { config, fetchImpl: async () => response({ code: 500, msg: 'failure', data: { category: 'SNAPSHOT_EXPIRED' } }) })
  38. assert.equal(result.ok, false)
  39. assert.equal(result.reason, 'snapshot-expired')
  40. })
  41. await check('successful envelope missing data is invalid instead of fake success', async () => {
  42. const result = await requestCockpitJson('/map-snapshot', { config, fetchImpl: async () => response({ code: 200, msg: 'success' }) })
  43. assert.equal(result.ok, false)
  44. assert.equal(result.reason, 'bad-payload')
  45. })
  46. await check('plain compatibility payload remains unchanged', async () => {
  47. const payload = { value: 0 }
  48. const result = await requestCockpitJson('/foundation', { config, fetchImpl: async () => response(payload) })
  49. assert.equal(result.data, payload)
  50. })
  51. console.log(`T48 real envelope: ${failed ? 'FAILED' : 'passed'}`)
  52. process.exitCode = failed ? 1 : 0