feat(device-plane): add typed B2 service ping transport

This commit is contained in:
Codex
2026-08-12 22:57:09 +03:00
parent bdb85cb4f7
commit 4b73a15765
24 changed files with 1322 additions and 88 deletions
@@ -81,7 +81,7 @@ test("telemetry ingress persists HEADER2 before acknowledging packages", async (
assert.equal(runtime.status().totalDiscoveries, 1);
assert.equal(runtime.status().totalMessagesAccepted, 1);
assert.equal(runtime.status().totalPackagesAcknowledged, 1);
assert.equal(runtime.status().commandTransport, "disabled");
assert.equal(runtime.status().commandTransport, "typed-service-ping-v1");
assert.equal(runtime.status().publicIngress, "telemetry-ingest");
const response = await fetch(
@@ -91,7 +91,52 @@ test("telemetry ingress persists HEADER2 before acknowledging packages", async (
assert.equal(body.framing, "verified-read-only");
assert.equal(body.tcpListener, "telemetry-ingest");
assert.equal(body.publicIngress, "telemetry-ingest");
assert.equal(body.commandTransport, "disabled");
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();
@@ -319,6 +364,11 @@ function connectAndCollect(port) {
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"));
}
});
});
}
@@ -339,6 +389,7 @@ function gatewayAdapterOptions() {
adapterRegistry: DEVICE_ADAPTER_CATALOG.registry,
protocolProfileRef: DEVICE_ADAPTER_CATALOG.defaultProfileRef,
edgeRef: "edge:test-001",
onCommandStatus: async () => undefined,
};
}
@@ -352,3 +403,11 @@ function acceptanceFor(message, replayed = false) {
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));
}
}