Files

600 lines
18 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import {
DEVICE_ADAPTER_MESSAGE_SCHEMA,
DEVICE_DISCOVERY_SIGNAL_SCHEMA,
} from "../../../packages/device-protocol-contract/src/index.mjs";
import { createControlCoreApp } from "../src/app.mjs";
const gatewayToken = "test-only-gateway-token-with-32-bytes";
const identifierPepper = "test-only-identifier-pepper-with-32-bytes";
const managementToken = "test-only-management-token-with-32-bytes";
const fakeImei = "000000000000001";
test("health reports database readiness and disabled command transport", async () => {
const runtime = await startTestServer({
repository: {
health: async () => "ready",
},
});
try {
const response = await fetch(`${runtime.baseUrl}/healthz`);
assert.equal(response.status, 200);
assert.deepEqual(await response.json(), {
ok: true,
service: "nodedc-device-control-core",
database: "ready",
discoveryIngest: "disabled",
managementApi: "disabled",
edgeChannels: {
enabled: false,
configured: 0,
accepted: 0,
degraded: 0,
},
commandTransport: "disabled",
});
} finally {
await runtime.close();
}
});
test("management API is closed by default", async () => {
const runtime = await startTestServer({
repository: {
health: async () => "ready",
},
});
try {
const response = await fetch(
`${runtime.baseUrl}/internal/v1/management/owner-scopes:ensure`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: "{}",
},
);
assert.equal(response.status, 404);
assert.equal((await response.json()).error, "device_management_api_disabled");
} finally {
await runtime.close();
}
});
test("management API cannot start without its repository boundary and strong token", () => {
assert.throws(
() => createControlCoreApp({
managementApiEnabled: true,
managementToken,
repository: { health: async () => "ready" },
}),
/device_management_repository_required/,
);
assert.throws(
() => createControlCoreApp({
managementApiEnabled: true,
managementToken: "short",
repository: {
health: async () => "ready",
executeManagementCommand: async () => ({}),
},
}),
/device_management_token_invalid/,
);
assert.throws(
() => createControlCoreApp({
managementApiEnabled: true,
managementToken,
repository: {
health: async () => "ready",
executeManagementCommand: async () => ({}),
},
}),
/device_identifier_pepper_invalid/,
);
});
test("management API requires service auth and an idempotency key", async () => {
const runtime = await startTestServer({
managementApiEnabled: true,
managementToken,
repository: {
health: async () => "ready",
executeManagementCommand: async () => {
throw new Error("must_not_execute");
},
},
});
try {
const unauthorized = await fetch(
`${runtime.baseUrl}/internal/v1/management/owner-scopes:ensure`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: "{}",
},
);
assert.equal(unauthorized.status, 401);
const missingIdempotency = await fetch(
`${runtime.baseUrl}/internal/v1/management/owner-scopes:ensure`,
{
method: "POST",
headers: managementHeaders({ includeIdempotency: false }),
body: JSON.stringify(ownerScopeCommand()),
},
);
assert.equal(missingIdempotency.status, 400);
assert.equal(
(await missingIdempotency.json()).error,
"device_management_header_required",
);
} finally {
await runtime.close();
}
});
test("management API forwards only normalized actor and command data", async () => {
let executed;
const runtime = await startTestServer({
managementApiEnabled: true,
managementToken,
repository: {
health: async () => "ready",
executeManagementCommand: async (value) => {
executed = value;
return {
replayed: false,
result: {
created: true,
ownerScope: {
ownerScopeRef: "owner-scope:11111111-1111-4111-8111-111111111111",
scopeKind: "company",
ownerRef: "client:example",
displayName: "Example Company",
lifecycleState: "active",
},
},
};
},
},
});
try {
const response = await fetch(
`${runtime.baseUrl}/internal/v1/management/owner-scopes:ensure`,
{
method: "POST",
headers: managementHeaders(),
body: JSON.stringify(ownerScopeCommand()),
},
);
assert.equal(response.status, 200);
assert.equal(response.headers.get("idempotency-key"), "phase2-test-0001");
assert.equal(response.headers.get("idempotency-replayed"), "false");
assert.match(executed.requestDigest, /^sha256:[a-f0-9]{64}$/);
assert.equal(executed.commandKind, "owner_scope.ensure");
assert.deepEqual(executed.command, ownerScopeCommand());
assert.deepEqual(executed.actor, {
userRef: "user:engineer",
hubRole: "admin",
groupRefs: ["group:engineers", "group:operators"],
ownerScopes: [{ scopeKind: "company", ownerRef: "client:example" }],
});
assert.equal((await response.json()).result.created, true);
} finally {
await runtime.close();
}
});
test("management API rejects unexpected fields before repository execution", async () => {
let executions = 0;
const runtime = await startTestServer({
managementApiEnabled: true,
managementToken,
repository: {
health: async () => "ready",
executeManagementCommand: async () => {
executions += 1;
return { replayed: false, result: {} };
},
},
});
try {
const response = await fetch(
`${runtime.baseUrl}/internal/v1/management/owner-scopes:ensure`,
{
method: "POST",
headers: managementHeaders(),
body: JSON.stringify({
...ownerScopeCommand(),
credential: "must-never-cross-boundary",
}),
},
);
assert.equal(response.status, 400);
assert.match(
(await response.json()).error,
/device_management_command_field_unexpected:credential/,
);
assert.equal(executions, 0);
} finally {
await runtime.close();
}
});
test("management API exposes a repository idempotency conflict without retrying", async () => {
let executions = 0;
const runtime = await startTestServer({
managementApiEnabled: true,
managementToken,
repository: {
health: async () => "ready",
executeManagementCommand: async () => {
executions += 1;
const error = new Error("device_idempotency_key_conflict");
error.statusCode = 409;
throw error;
},
},
});
try {
const response = await fetch(
`${runtime.baseUrl}/internal/v1/management/owner-scopes:ensure`,
{
method: "POST",
headers: managementHeaders(),
body: JSON.stringify(ownerScopeCommand()),
},
);
assert.equal(response.status, 409);
assert.equal((await response.json()).error, "device_idempotency_key_conflict");
assert.equal(executions, 1);
} finally {
await runtime.close();
}
});
test("typed service ping accepts a transient access code and never echoes it", async () => {
const projectRef = "project:11111111-1111-4111-8111-111111111111";
const deviceRef = "device:22222222-2222-4222-8222-222222222222";
let planned;
const runtime = await startTestServer({
managementApiEnabled: true,
managementToken,
repository: {
health: async () => "ready",
executeManagementCommand: async () => ({ replayed: false, result: {} }),
},
typedCommandRuntime: {
status: () => ({ commandTransport: "typed-service-ping-v1" }),
planServicePing: async (value) => {
planned = value;
return {
replayed: false,
command: {
commandRef: "command:33333333-3333-4333-8333-333333333333",
deviceRef,
commandType: "service.ping",
lifecycleState: "queued",
},
};
},
},
});
try {
const response = await fetch(
`${runtime.baseUrl}/internal/v1/commands:service-ping`,
{
method: "POST",
headers: managementHeaders(),
body: JSON.stringify({
projectRef,
deviceRef,
accessCode: "654321",
expiresInSeconds: 300,
}),
},
);
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.result.lifecycleState, "queued");
assert.equal(JSON.stringify(body).includes("654321"), false);
assert.equal(planned.input.accessCode, "654321");
assert.equal(planned.idempotencyKey, "phase2-test-0001");
} finally {
await runtime.close();
}
});
test("project query is service-authenticated and forwards only the trusted actor", async () => {
let queriedActor;
const runtime = await startTestServer({
managementApiEnabled: true,
managementToken,
repository: {
health: async () => "ready",
executeManagementCommand: async () => ({ replayed: false, result: {} }),
listAccessibleProjects: async (actor) => {
queriedActor = actor;
return [{ projectRef: "project:11111111-1111-4111-8111-111111111111" }];
},
},
});
try {
const unauthorized = await fetch(
`${runtime.baseUrl}/internal/v1/query/projects`,
);
assert.equal(unauthorized.status, 401);
const response = await fetch(
`${runtime.baseUrl}/internal/v1/query/projects`,
{ headers: managementHeaders() },
);
assert.equal(response.status, 200);
assert.equal((await response.json()).projects.length, 1);
assert.deepEqual(queriedActor, {
userRef: "user:engineer",
hubRole: "admin",
groupRefs: ["group:engineers", "group:operators"],
ownerScopes: [{ scopeKind: "company", ownerRef: "client:example" }],
});
} finally {
await runtime.close();
}
});
test("project workspace query accepts only a canonical project path", async () => {
const projectId = "11111111-1111-4111-8111-111111111111";
let queried;
const edgeChannels = {
enabled: true,
edges: [{
edgeRegistrationId: "edge:bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
channel: "accepted",
lastErrorCode: null,
}],
};
const runtime = await startTestServer({
managementApiEnabled: true,
managementToken,
edgeChannelStatusProvider: () => edgeChannels,
repository: {
health: async () => "ready",
executeManagementCommand: async () => ({ replayed: false, result: {} }),
getProjectWorkspace: async (actor, id, options) => {
queried = { actor, id, options };
return { project: { projectRef: `project:${id}` }, devices: [] };
},
},
});
try {
const response = await fetch(
`${runtime.baseUrl}/internal/v1/query/projects/${projectId}/workspace`,
{ headers: managementHeaders() },
);
assert.equal(response.status, 200);
assert.equal((await response.json()).workspace.devices.length, 0);
assert.equal(queried.id, projectId);
assert.equal(queried.options.edgeChannelStatus, edgeChannels);
const invalid = await fetch(
`${runtime.baseUrl}/internal/v1/query/projects/not-a-project/workspace`,
{ headers: managementHeaders() },
);
assert.equal(invalid.status, 404);
} finally {
await runtime.close();
}
});
test("discovery ingest is closed by default", async () => {
const runtime = await startTestServer({
repository: {
health: async () => "ready",
},
});
try {
const response = await fetch(
`${runtime.baseUrl}/internal/v1/device-discoveries:observe`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: "{}",
},
);
assert.equal(response.status, 404);
assert.equal(
(await response.json()).error,
"device_discovery_ingest_disabled",
);
} finally {
await runtime.close();
}
});
test("authenticated ingest stores only digest and returns a masked view", async () => {
let stored;
const runtime = await startTestServer({
discoveryIngestEnabled: true,
gatewayToken,
identifierPepper,
repository: {
health: async () => "ready",
upsertQuarantineDiscovery: async (value) => {
stored = value;
return {
created: true,
value: {
...value.safeView,
discoveryRef: "discovery:test-001",
},
};
},
acceptAdapterMessage: async () => {
throw new Error("must_not_accept_message");
},
},
});
try {
const unauthorized = await fetch(
`${runtime.baseUrl}/internal/v1/device-discoveries:observe`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(fakeSignal()),
},
);
assert.equal(unauthorized.status, 401);
const response = await fetch(
`${runtime.baseUrl}/internal/v1/device-discoveries:observe`,
{
method: "POST",
headers: {
Authorization: `Bearer ${gatewayToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify(fakeSignal()),
},
);
assert.equal(response.status, 201);
const body = await response.json();
const serialized = JSON.stringify(body);
assert.equal(serialized.includes(fakeImei), false);
assert.equal(body.discovery.identifier.masked, "***********0001");
assert.match(stored.identifierDigest, /^hmac-sha256:[a-f0-9]{64}$/);
assert.equal(stored.sessionRef, "session:test-001");
assert.equal(stored.routeRef, null);
assert.equal(JSON.stringify(stored).includes(fakeImei), false);
} finally {
await runtime.close();
}
});
test("gateway message endpoint returns acceptance only after repository commit", async () => {
let stored;
const runtime = await startTestServer({
discoveryIngestEnabled: true,
gatewayToken,
identifierPepper,
repository: {
health: async () => "ready",
upsertQuarantineDiscovery: async () => {
throw new Error("must_not_observe_discovery");
},
acceptAdapterMessage: async (value) => {
stored = value;
return {
acceptance: {
schemaVersion: "nodedc.device-adapter-acceptance.v1",
acceptanceRef: "acceptance:test-001",
idempotencyKey: value.safeView.idempotencyKey,
status: "accepted",
replayed: false,
acceptedAt: "2026-08-11T12:00:00.000Z",
},
claimedDeviceRef: "device:11111111-1111-4111-8111-111111111111",
};
},
},
});
try {
const response = await fetch(
`${runtime.baseUrl}/internal/v1/gateway/messages:accept`,
{
method: "POST",
headers: {
Authorization: `Bearer ${gatewayToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify(fakeAdapterMessage()),
},
);
assert.equal(response.status, 201);
const body = await response.json();
assert.equal(body.acceptance.status, "accepted");
assert.match(stored.identifierDigest, /^hmac-sha256:[a-f0-9]{64}$/);
assert.match(stored.requestDigest, /^sha256:[a-f0-9]{64}$/);
assert.equal(stored.safeView.identifier.masked, "***********0001");
assert.equal(JSON.stringify(stored).includes(fakeImei), false);
} finally {
await runtime.close();
}
});
function fakeSignal() {
return {
schemaVersion: DEVICE_DISCOVERY_SIGNAL_SCHEMA,
sessionRef: "session:test-001",
modelProfileRef: "arusnavi.b2.internal.v1",
protocol: "INTERNAL",
observedAt: "2026-07-25T00:00:00.000Z",
identifier: { kind: "imei", value: fakeImei },
evidence: {
transport: "tcp",
bytesObserved: 128,
framingStatus: "verified",
specificationRef: "arusnavi.internal.framing.test-v1",
},
};
}
function fakeAdapterMessage() {
return {
schemaVersion: DEVICE_ADAPTER_MESSAGE_SCHEMA,
edgeRef: "edge:test-001",
adapterRef: "arusnavi-b2",
protocolProfileRef: "arusnavi.b2.internal.v1",
protocol: "INTERNAL",
sessionRef: "session:test-001",
messageRef: "package:1:test",
messageType: "telemetry.package",
sequence: 1,
observedAt: "2026-08-11T12:00:00.000Z",
idempotencyKey: `sha256:${"a".repeat(64)}`,
identifier: { kind: "imei", value: fakeImei },
payloadSchemaRef: "arusnavi.internal.package-metadata.v1",
payload: {
packageNumber: 1,
packetCount: 1,
packageDigest: `sha256:${"b".repeat(64)}`,
},
};
}
function managementHeaders({ includeIdempotency = true } = {}) {
return {
Authorization: `Bearer ${managementToken}`,
"Content-Type": "application/json",
...(includeIdempotency ? { "Idempotency-Key": "phase2-test-0001" } : {}),
"X-NODEDC-User-Ref": "user:engineer",
"X-NODEDC-Hub-Role": "admin",
"X-NODEDC-Group-Refs": "group:operators,group:engineers",
"X-NODEDC-Owner-Scopes": "company=client:example",
};
}
function ownerScopeCommand() {
return {
scopeKind: "company",
ownerRef: "client:example",
displayName: "Example Company",
};
}
async function startTestServer(options) {
const server = createControlCoreApp({ identifierPepper, ...options });
await new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", resolve);
});
const address = server.address();
return {
baseUrl: `http://127.0.0.1:${address.port}`,
close: () => new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
}),
};
}