import assert from "node:assert/strict"; import { connect } from "node:net"; import test from "node:test"; import { DEVICE_ADAPTER_CATALOG, } from "../../../packages/device-adapter-catalog/src/index.mjs"; import { createDeviceGatewayRuntime } from "../src/runtime.mjs"; const specificationHeader = Buffer.from( "FF23E9EF782DE7120300", "hex", ); const specificationPackage = Buffer.from( "5B01010000FBDEC251EC5D", "hex", ); test("baseline health exposes no public ingress and no command transport", async () => { const runtime = createDeviceGatewayRuntime({ healthPort: 0, listenEnabled: false, }); const addresses = await runtime.start(); try { const response = await fetch( `http://127.0.0.1:${addresses.healthAddress.port}/healthz`, ); assert.equal(response.status, 200); const body = await response.json(); assert.equal(body.publicIngress, "disabled"); assert.equal(body.commandTransport, "disabled"); assert.equal(body.tcpListener, "disabled"); assert.equal(addresses.tcpAddress, null); } finally { await runtime.stop(); } }); test("telemetry ingress persists HEADER2 before acknowledging packages", async () => { const captured = []; const runtime = createDeviceGatewayRuntime({ healthPort: 0, tcpHost: "0.0.0.0", tcpPort: 0, listenEnabled: true, publicIngressEnabled: true, coreChannelAuthenticated: true, now: () => new Date(0x52db95de * 1000), ...gatewayAdapterOptions(), onDiscovery: async (value) => { captured.push(value); return { lifecycleState: "quarantine" }; }, onMessage: async (message) => acceptanceFor(message), }); const addresses = await runtime.start(); const client = await connectAndCollect(addresses.tcpAddress.port); try { client.socket.write(specificationHeader.subarray(0, 4)); await new Promise((resolve) => setImmediate(resolve)); assert.equal(client.bytes().length, 0); client.socket.write(specificationHeader.subarray(4)); await client.waitForBytes(9); assert.equal( client.bytes().subarray(0, 9).toString("hex").toUpperCase(), "7B0400A0DE95DB527D", ); assert.equal(captured.length, 1); assert.equal(captured[0].identifier.value, "865209039777769"); assert.equal(captured[0].evidence.framingStatus, "verified"); assert.equal(captured[0].commandTransport, undefined); client.socket.write(specificationPackage); await client.waitForBytes(13); assert.equal( client.bytes().subarray(9).toString("hex").toUpperCase(), "7B00017D", ); assert.equal(runtime.status().totalDiscoveries, 1); assert.equal(runtime.status().totalMessagesAccepted, 1); assert.equal(runtime.status().totalPackagesAcknowledged, 1); assert.equal(runtime.status().commandTransport, "typed-service-ping-v1"); assert.equal(runtime.status().publicIngress, "telemetry-ingest"); const response = await fetch( `http://127.0.0.1:${addresses.healthAddress.port}/healthz`, ); const body = await response.json(); assert.equal(body.framing, "verified-read-only"); assert.equal(body.tcpListener, "telemetry-ingest"); assert.equal(body.publicIngress, "telemetry-ingest"); assert.equal(body.commandTransport, "typed-service-ping-v1"); } finally { client.socket.destroy(); await runtime.stop(); } }); test("dispatches a typed service ping on the next telemetry package and records SERV OK", async () => { const statuses = []; const runtime = createDeviceGatewayRuntime({ healthPort: 0, tcpPort: 0, listenEnabled: true, ...gatewayAdapterOptions(), onDiscovery: async () => ({ lifecycleState: "claimed" }), onMessage: async (message) => ({ ...acceptanceFor(message), commandOffer: { commandRef: "command:11111111-1111-4111-8111-111111111111", commandType: "service.ping", accessCode: "123456", transportMessageRef: "edge-command:22222222-2222-4222-8222-222222222222", }, }), onCommandStatus: async (status) => statuses.push(status), }); const addresses = await runtime.start(); const client = await connectAndCollect(addresses.tcpAddress.port); try { client.socket.write(Buffer.concat([specificationHeader, specificationPackage])); await client.waitForBytes(28); assert.equal( client.bytes().subarray(13).toString("ascii"), "123456*SERV*1.1", ); client.socket.write(Buffer.from("SERV OK", "ascii")); await waitFor(() => statuses.length === 1); assert.deepEqual(statuses[0], { commandRef: "command:11111111-1111-4111-8111-111111111111", transportMessageRef: "edge-command:22222222-2222-4222-8222-222222222222", lifecycleState: "acknowledged", resultCode: "serv_ok", observedAt: statuses[0].observedAt, sessionRef: statuses[0].sessionRef, adapterProfileRef: "arusnavi.b2.internal.v1", }); } finally { client.socket.destroy(); await runtime.stop(); } }); test("does not acknowledge malformed or unverified initial bytes", async () => { const captured = []; const runtime = createDeviceGatewayRuntime({ healthPort: 0, tcpPort: 0, listenEnabled: true, ...gatewayAdapterOptions(), onDiscovery: async (value) => { captured.push(value); return { lifecycleState: "quarantine" }; }, onMessage: async (message) => acceptanceFor(message), }); const addresses = await runtime.start(); try { const received = await sendAndCollect( addresses.tcpAddress.port, Buffer.from("not-a-b2-header", "utf8"), ); assert.equal(received.length, 0); assert.equal(captured.length, 0); assert.equal(runtime.status().totalRejected, 1); } finally { await runtime.stop(); } }); test("does not acknowledge a header when Core rejects discovery", async () => { const runtime = createDeviceGatewayRuntime({ healthPort: 0, tcpPort: 0, listenEnabled: true, ...gatewayAdapterOptions(), onDiscovery: async () => { throw new Error("core_unavailable"); }, onMessage: async (message) => acceptanceFor(message), }); const addresses = await runtime.start(); try { const received = await sendAndCollect( addresses.tcpAddress.port, specificationHeader, ); assert.equal(received.length, 0); assert.equal(runtime.status().totalDiscoveries, 0); assert.equal(runtime.status().totalRejected, 1); } finally { await runtime.stop(); } }); test("does not acknowledge a package when durable Core acceptance fails", async () => { const runtime = createDeviceGatewayRuntime({ healthPort: 0, tcpPort: 0, listenEnabled: true, now: () => new Date(0x52db95de * 1000), ...gatewayAdapterOptions(), onDiscovery: async () => ({ lifecycleState: "quarantine" }), onMessage: async () => { throw new Error("core_commit_failed"); }, }); const addresses = await runtime.start(); try { const received = await sendAndCollect( addresses.tcpAddress.port, Buffer.concat([specificationHeader, specificationPackage]), ); assert.equal( received.toString("hex").toUpperCase(), "7B0400A0DE95DB527D", ); assert.equal(runtime.status().totalMessagesAccepted, 0); assert.equal(runtime.status().totalPackagesAcknowledged, 0); assert.equal(runtime.status().totalRejected, 1); } finally { await runtime.stop(); } }); test("acknowledges an idempotent Core replay as accepted delivery", async () => { const runtime = createDeviceGatewayRuntime({ healthPort: 0, tcpPort: 0, listenEnabled: true, now: () => new Date(0x52db95de * 1000), ...gatewayAdapterOptions(), onDiscovery: async () => ({ lifecycleState: "quarantine" }), onMessage: async (message) => acceptanceFor(message, true), }); const addresses = await runtime.start(); try { const received = await sendAndCollect( addresses.tcpAddress.port, Buffer.concat([specificationHeader, specificationPackage]), ); assert.equal( received.toString("hex").toUpperCase(), "7B0400A0DE95DB527D7B00017D", ); assert.equal(runtime.status().totalMessagesAccepted, 1); assert.equal(runtime.status().totalPackagesAcknowledged, 1); } finally { await runtime.stop(); } }); test("closes an oversized tracker buffer and releases its aggregate budget", async () => { const runtime = createDeviceGatewayRuntime({ healthPort: 0, tcpPort: 0, listenEnabled: true, maxBufferedBytes: 1024, maxAggregateBufferedBytes: 1024, ...gatewayAdapterOptions(), onDiscovery: async () => ({ lifecycleState: "quarantine" }), onMessage: async (message) => acceptanceFor(message), }); const addresses = await runtime.start(); try { const received = await sendAndCollect( addresses.tcpAddress.port, Buffer.alloc(1025, 0x01), ); assert.equal(received.length, 0); assert.equal(runtime.status().totalRejected, 1); assert.equal(runtime.status().totalBufferedBytes, 0); assert.equal(runtime.status().activeSessions, 0); } finally { await runtime.stop(); } }); test("public ingress requires both discovery and durable message acceptance sinks", () => { assert.throws( () => createDeviceGatewayRuntime({ listenEnabled: true, publicIngressEnabled: true, coreChannelAuthenticated: true, tcpHost: "0.0.0.0", }), /device_gateway_core_acceptance_sink_required/, ); }); test("public ingress cannot start on the legacy bearer HTTP Core client", () => { assert.throws( () => createDeviceGatewayRuntime({ listenEnabled: true, publicIngressEnabled: true, tcpHost: "0.0.0.0", ...gatewayAdapterOptions(), onDiscovery: async () => ({ lifecycleState: "quarantine" }), onMessage: async (message) => acceptanceFor(message), }), /device_gateway_authenticated_core_channel_required/, ); }); test("baseline rejects non-loopback binding", () => { assert.throws( () => createDeviceGatewayRuntime({ listenEnabled: true, tcpHost: "0.0.0.0", ...gatewayAdapterOptions(), onDiscovery: async () => ({ lifecycleState: "quarantine" }), onMessage: async (message) => acceptanceFor(message), }), /device_gateway_baseline_loopback_only/, ); }); test("container health may bind all interfaces while TCP stays disabled", async () => { const runtime = createDeviceGatewayRuntime({ healthHost: "0.0.0.0", healthPort: 0, listenEnabled: false, }); const addresses = await runtime.start(); try { assert.equal(addresses.healthAddress.address, "0.0.0.0"); assert.equal(addresses.tcpAddress, null); assert.equal(runtime.status().publicIngress, "disabled"); } finally { await runtime.stop(); } }); function connectAndCollect(port) { return new Promise((resolve, reject) => { const chunks = []; let byteLength = 0; const waiters = []; const socket = connect({ host: "127.0.0.1", port }, () => { resolve({ socket, bytes: () => Buffer.concat(chunks, byteLength), waitForBytes: (minimum) => { if (byteLength >= minimum) return Promise.resolve(); return new Promise((waitResolve, waitReject) => { waiters.push({ minimum, waitResolve, waitReject }); }); }, }); }); socket.on("data", (chunk) => { chunks.push(chunk); byteLength += chunk.length; for (let index = waiters.length - 1; index >= 0; index -= 1) { if (byteLength >= waiters[index].minimum) { waiters[index].waitResolve(); waiters.splice(index, 1); } } }); socket.on("error", (error) => { for (const waiter of waiters.splice(0)) waiter.waitReject(error); reject(error); }); socket.on("close", () => { for (const waiter of waiters.splice(0)) { waiter.waitReject(new Error("device_gateway_test_socket_closed")); } }); }); } function sendAndCollect(port, payload) { return new Promise((resolve, reject) => { const chunks = []; const socket = connect({ host: "127.0.0.1", port }, () => { socket.end(payload); }); socket.on("data", (chunk) => chunks.push(chunk)); socket.on("close", () => resolve(Buffer.concat(chunks))); socket.on("error", reject); }); } function gatewayAdapterOptions() { return { adapterRegistry: DEVICE_ADAPTER_CATALOG.registry, protocolProfileRef: DEVICE_ADAPTER_CATALOG.defaultProfileRef, edgeRef: "edge:test-001", onCommandStatus: async () => undefined, }; } function acceptanceFor(message, replayed = false) { return { schemaVersion: "nodedc.device-adapter-acceptance.v1", acceptanceRef: "acceptance:test-001", idempotencyKey: message.idempotencyKey, status: "accepted", replayed, acceptedAt: "2026-08-11T12:00:00.000Z", }; } async function waitFor(predicate, timeoutMs = 1_000) { const deadline = Date.now() + timeoutMs; while (!predicate()) { if (Date.now() >= deadline) throw new Error("test_wait_timeout"); await new Promise((resolve) => setTimeout(resolve, 5)); } }