feat: establish standalone Device Core repository
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
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 {
|
||||
createControlCoreApp,
|
||||
} from "../../device-control-core/src/app.mjs";
|
||||
import { createCoreGatewayClient } from "../src/core-client.mjs";
|
||||
import { createDeviceGatewayRuntime } from "../src/runtime.mjs";
|
||||
|
||||
const gatewayToken = "test-only-gateway-token-with-32-bytes";
|
||||
const identifierPepper = "test-only-identifier-pepper-with-32-bytes";
|
||||
const specificationHeader = Buffer.from(
|
||||
"FF23E9EF782DE7120300",
|
||||
"hex",
|
||||
);
|
||||
const specificationPackage = Buffer.from(
|
||||
"5B01010000FBDEC251EC5D",
|
||||
"hex",
|
||||
);
|
||||
|
||||
test("B2 HEADER2 becomes a persisted masked quarantine discovery before ACK", async () => {
|
||||
let stored;
|
||||
let storedMessage;
|
||||
const core = createControlCoreApp({
|
||||
discoveryIngestEnabled: true,
|
||||
gatewayToken,
|
||||
identifierPepper,
|
||||
repository: {
|
||||
health: async () => "ready",
|
||||
upsertQuarantineDiscovery: async (value) => {
|
||||
stored = value;
|
||||
return {
|
||||
created: true,
|
||||
value: {
|
||||
...value.safeView,
|
||||
discoveryRef: "discovery:integration-001",
|
||||
},
|
||||
};
|
||||
},
|
||||
acceptAdapterMessage: async (value) => {
|
||||
storedMessage = value;
|
||||
return {
|
||||
acceptance: {
|
||||
schemaVersion: "nodedc.device-adapter-acceptance.v1",
|
||||
acceptanceRef: "acceptance:integration-001",
|
||||
idempotencyKey: value.safeView.idempotencyKey,
|
||||
status: "accepted",
|
||||
replayed: false,
|
||||
acceptedAt: "2026-08-11T12:00:00.000Z",
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
await listen(core);
|
||||
const coreAddress = core.address();
|
||||
const observe = createCoreGatewayClient({
|
||||
coreUrl: `http://127.0.0.1:${coreAddress.port}`,
|
||||
gatewayToken,
|
||||
});
|
||||
const gateway = createDeviceGatewayRuntime({
|
||||
healthPort: 0,
|
||||
tcpHost: "0.0.0.0",
|
||||
tcpPort: 0,
|
||||
listenEnabled: true,
|
||||
publicIngressEnabled: true,
|
||||
coreChannelAuthenticated: true,
|
||||
adapterRegistry: DEVICE_ADAPTER_CATALOG.registry,
|
||||
protocolProfileRef: DEVICE_ADAPTER_CATALOG.defaultProfileRef,
|
||||
edgeRef: "edge:integration-001",
|
||||
now: () => new Date(0x52db95de * 1000),
|
||||
onDiscovery: observe.observeDiscovery,
|
||||
onMessage: observe.acceptMessage,
|
||||
onCommandStatus: async () => undefined,
|
||||
});
|
||||
const addresses = await gateway.start();
|
||||
try {
|
||||
const response = await exchange(
|
||||
addresses.tcpAddress.port,
|
||||
Buffer.concat([specificationHeader, specificationPackage]),
|
||||
13,
|
||||
);
|
||||
assert.equal(
|
||||
response.toString("hex").toUpperCase(),
|
||||
"7B0400A0DE95DB527D7B00017D",
|
||||
);
|
||||
assert.match(stored.identifierDigest, /^hmac-sha256:[a-f0-9]{64}$/);
|
||||
assert.equal(stored.safeView.lifecycleState, "quarantine");
|
||||
assert.equal(stored.safeView.identifier.masked, "***********7769");
|
||||
assert.equal(stored.safeView.commandTransport, "disabled");
|
||||
assert.equal(
|
||||
JSON.stringify(stored).includes("865209039777769"),
|
||||
false,
|
||||
);
|
||||
assert.equal(storedMessage.safeView.messageType, "telemetry.package");
|
||||
assert.equal(storedMessage.safeView.identifier.masked, "***********7769");
|
||||
assert.equal(
|
||||
JSON.stringify(storedMessage).includes("865209039777769"),
|
||||
false,
|
||||
);
|
||||
} finally {
|
||||
await gateway.stop();
|
||||
await close(core);
|
||||
}
|
||||
});
|
||||
|
||||
function listen(server) {
|
||||
return new Promise((resolve, reject) => {
|
||||
server.once("error", reject);
|
||||
server.listen(0, "127.0.0.1", resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function close(server) {
|
||||
return new Promise((resolve, reject) => {
|
||||
server.close((error) => (error ? reject(error) : resolve()));
|
||||
server.closeAllConnections?.();
|
||||
});
|
||||
}
|
||||
|
||||
function exchange(port, payload, expectedBytes) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks = [];
|
||||
let byteLength = 0;
|
||||
const socket = connect({ host: "127.0.0.1", port }, () => {
|
||||
socket.write(payload);
|
||||
});
|
||||
socket.on("data", (chunk) => {
|
||||
chunks.push(chunk);
|
||||
byteLength += chunk.length;
|
||||
if (byteLength >= expectedBytes) {
|
||||
socket.destroy();
|
||||
resolve(Buffer.concat(chunks, byteLength));
|
||||
}
|
||||
});
|
||||
socket.on("error", reject);
|
||||
socket.on("close", () => {
|
||||
if (byteLength < expectedBytes) {
|
||||
reject(new Error(
|
||||
`device_gateway_test_socket_closed_early:${byteLength}/${expectedBytes}`,
|
||||
));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user