import assert from 'node:assert/strict' import { requestCockpitJson } from '../../../src/modules/cockpit/services/cockpitApi.js' import { fetchMapSnapshot } from '../../../src/modules/cockpit/services/mapApi.js' import { createMapSummaryLoader } from '../../../src/modules/cockpit/domain/mapSummary.js' const config = { dataSource: 'real', api: { baseUrl: 'http://acceptance.invalid/api/cockpit/v1' } } const response = (body, status = 200) => ({ ok: status >= 200 && status < 300, status, json: async () => body }) const rawPage = (page) => ({ snapshotAt: '2026-10-01T10:00:00+08:00', page, pageSize: 1, total: 2, complete: page === 2, monitoringPoints: [{ pointCode: `P-${page}`, pointName: `Point ${page}`, pointStatus: 'NORMAL', deviceCodes: [], longitude: 110.4, latitude: 28.44, coordinateValidation: { renderable: false, state: 'UNVERIFIED', reason: 'UNVERIFIED_COORDINATE' } }], unassociatedEquipment: [], }) let failed = 0 async function check(name, action) { try { await action(); console.log(`PASS ${name}`) } catch (error) { failed += 1; console.error(`FAIL ${name}: ${String(error.message).split('\n')[0]}`) } } await check('real AjaxResult unwraps before map adaptation and page2 keeps snapshot token', async () => { const urls = [] 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) }) } const loader = createMapSummaryLoader({ pageSize: 1, fetchPage: async (query) => { const section = await fetchMapSnapshot({ ...query, config, fetchImpl }) return { ok: section.state === 'success', data: section.data, reason: section.reason } } }) await loader.loadNext() await loader.loadNext() assert.equal(new URL(urls[1]).searchParams.get('snapshotAt'), rawPage(1).snapshotAt) assert.equal(loader.getState().objects.length, 2) assert.equal(loader.getState().phase, 'complete') assert.ok(loader.getState().objects.every((object) => object.coordinate.renderable === false), 'never promote unverified devices to renderable') }) await check('HTTP200 business failure is not a successful section', async () => { const result = await requestCockpitJson('/map-snapshot', { config, fetchImpl: async () => response({ code: 500, msg: 'failure', data: null }) }) assert.equal(result.ok, false) assert.equal(result.reason, 'unavailable') }) await check('HTTP200 expired snapshot keeps recoverable category', async () => { const result = await requestCockpitJson('/map-snapshot', { config, fetchImpl: async () => response({ code: 500, msg: 'failure', data: { category: 'SNAPSHOT_EXPIRED' } }) }) assert.equal(result.ok, false) assert.equal(result.reason, 'snapshot-expired') }) await check('successful envelope missing data is invalid instead of fake success', async () => { const result = await requestCockpitJson('/map-snapshot', { config, fetchImpl: async () => response({ code: 200, msg: 'success' }) }) assert.equal(result.ok, false) assert.equal(result.reason, 'bad-payload') }) await check('plain compatibility payload remains unchanged', async () => { const payload = { value: 0 } const result = await requestCockpitJson('/foundation', { config, fetchImpl: async () => response(payload) }) assert.equal(result.data, payload) }) console.log(`T48 real envelope: ${failed ? 'FAILED' : 'passed'}`) process.exitCode = failed ? 1 : 0