import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import test from 'node:test'; import { createDeviceInventoryRoute } from '../../dist/server/http/routes/deviceInventoryRoute.js'; const deviceId = 'dev_0123456789abcdef'; function response() { return { writeHead(status, headers) { this.status = status; this.headers = headers; }, end(payload) { this.payload = JSON.parse(payload); }, }; } function createHarness({ inventory = {}, body = {} } = {}) { const calls = []; let bodyReads = 0; const deviceInventory = inventory === null ? null : { snapshot: () => { calls.push(['snapshot']); return inventory.snapshot ?? { revision: 1, devices: [] }; }, refresh: async () => { calls.push(['refresh']); return inventory.refresh ?? { revision: 2, devices: [] }; }, update: (...args) => { calls.push(['update', ...args]); return inventory.update ?? { revision: 3 }; }, resetTraffic: async (...args) => { calls.push(['resetTraffic', ...args]); return inventory.resetTraffic ?? { revision: 4 }; }, setPolicy: async (...args) => { calls.push(['setPolicy', ...args]); return inventory.setPolicy ?? { revision: 5 }; }, }; const route = createDeviceInventoryRoute({ deviceInventory, readBody: async () => { bodyReads += 1; return body; }, }); return { route, calls, bodyReads: () => bodyReads }; } test('device route forwards list and refresh query paths as raw JSON responses', async () => { const harness = createHarness({ inventory: { snapshot: { revision: 11, devices: [{ id: deviceId }] }, refresh: { revision: 12, devices: [] }, }, }); const listResponse = response(); assert.equal(await harness.route.handle({ method: 'GET', url: '/api/devices?source=ui', }, listResponse), true); assert.equal(listResponse.status, 200); assert.equal(listResponse.headers['content-type'], 'application/json; charset=utf-8'); assert.deepEqual(listResponse.payload, { revision: 11, devices: [{ id: deviceId }] }); const refreshResponse = response(); assert.equal(await harness.route.handle({ method: 'POST', url: '/api/devices/refresh?source=ui', }, refreshResponse), true); assert.deepEqual(refreshResponse.payload, { revision: 12, devices: [] }); assert.deepEqual(harness.calls, [['snapshot'], ['refresh']]); }); test('device route forwards metadata patch and policy arguments without coercion', async () => { const patch = { expectedRevision: 7, alias: 'Desk', pinned: false, deprioritized: true, extra: 0 }; const metadata = createHarness({ body: patch }); const metadataResponse = response(); assert.equal(await metadata.route.handle({ method: 'PUT', url: `/api/devices/${deviceId}?source=ui`, }, metadataResponse), true); assert.deepEqual(metadata.calls, [[ 'update', deviceId, { alias: 'Desk', pinned: false, deprioritized: true, extra: 0 }, 7, ]]); assert.deepEqual(metadataResponse.payload, { revision: 3 }); const policy = createHarness({ body: { mode: 42, expectedRevision: '8', ignored: true } }); const policyResponse = response(); assert.equal(await policy.route.handle({ method: 'PUT', url: `/api/devices/${deviceId}/policy?source=ui`, }, policyResponse), true); assert.deepEqual(policy.calls, [['setPolicy', deviceId, 42, '8']]); assert.deepEqual(policyResponse.payload, { revision: 5 }); const reset = createHarness({ body: { expectedRevision: '9', ignored: true } }); const resetResponse = response(); assert.equal(await reset.route.handle({ method: 'DELETE', url: '/api/devices/traffic?source=ui', }, resetResponse), true); assert.deepEqual(reset.calls, [['resetTraffic', '9']]); assert.deepEqual(resetResponse.payload, { revision: 4 }); }); test('device route preserves endpoint gating and strict lowercase IDs', async () => { for (const [method, url] of [ ['POST', '/api/devices'], ['GET', '/api/devices/refresh'], ['GET', '/api/devices/traffic'], ['GET', `/api/devices/${deviceId}`], ['POST', `/api/devices/${deviceId}/policy`], ]) { const harness = createHarness(); await assert.rejects( harness.route.handle({ method, url }, response()), (error) => error.code === 'ENDPOINT_NOT_FOUND', ); assert.deepEqual(harness.calls, []); assert.equal(harness.bodyReads(), 0); } for (const url of [ '/api/other', '/api/devices/dev_0123456789ABCDEF', '/api/devices/dev_short', ]) { const harness = createHarness(); assert.equal(await harness.route.handle({ method: 'PUT', url }, response()), false); assert.deepEqual(harness.calls, []); assert.equal(harness.bodyReads(), 0); } for (const [method, url] of [ ['GET', '/api/devices'], ['POST', '/api/devices/refresh'], ['DELETE', '/api/devices/traffic'], ['PUT', `/api/devices/${deviceId}`], ['PUT', `/api/devices/${deviceId}/policy`], ]) { const client = createHarness({ inventory: null }); await assert.rejects( client.route.handle({ method, url }, response()), (error) => error.code === 'ENDPOINT_NOT_FOUND', ); assert.equal(client.bodyReads(), 0); } }); test('device route propagates synchronous and asynchronous service errors unchanged', async () => { const syncError = new Error('snapshot failed'); const syncRoute = createDeviceInventoryRoute({ deviceInventory: { snapshot: () => { throw syncError; }, refresh: async () => ({}), update: () => ({}), resetTraffic: async () => ({}), setPolicy: async () => ({}), }, readBody: async () => ({}), }); await assert.rejects( syncRoute.handle({ method: 'GET', url: '/api/devices' }, response()), (error) => error === syncError, ); const asyncError = new Error('refresh failed'); const asyncRoute = createDeviceInventoryRoute({ deviceInventory: { snapshot: () => ({}), refresh: async () => { throw asyncError; }, update: () => ({}), resetTraffic: async () => ({}), setPolicy: async () => ({}), }, readBody: async () => ({}), }); await assert.rejects( asyncRoute.handle({ method: 'POST', url: '/api/devices/refresh' }, response()), (error) => error === asyncError, ); }); test('device route is the only HTTP owner while lifecycle stays in composition', () => { const index = readFileSync(new URL('../../src/server/index.ts', import.meta.url), 'utf8'); const route = readFileSync( new URL('../../src/server/http/routes/deviceInventoryRoute.ts', import.meta.url), 'utf8', ); assert.match(index, /createDeviceInventoryRoute\(\{/); assert.match(index, /deviceInventoryRoute\.handle\(req, res\)/); assert.doesNotMatch(index, /\/api\/devices/); assert.doesNotMatch(index, /deviceInventory\.(?:snapshot|update|setPolicy)\(/); assert.match(index, /deviceInventory\.reconcilePolicies\(\)/); assert.match(index, /deviceInventory\.refresh\(\)/); assert.match(route, /DEVICE_PATH/); assert.match(route, /DEVICE_POLICY_PATH/); });