feat(device-core): add registry management commands
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
assertPlatformCatalogAuthority,
|
||||
DEVICE_INFRASTRUCTURE_COMMAND_KINDS,
|
||||
normalizeInfrastructureManagementCommand,
|
||||
} from "../src/infrastructure-management.mjs";
|
||||
import {
|
||||
ALL_DEVICE_MANAGEMENT_COMMAND_KINDS,
|
||||
normalizeDeviceManagementCommand,
|
||||
} from "../src/management-command.mjs";
|
||||
import { normalizeManagementActor } from "../src/project-management.mjs";
|
||||
|
||||
const projectRef = "project:11111111-1111-4111-8111-111111111111";
|
||||
const packageRef = "adapter-package:22222222-2222-4222-8222-222222222222";
|
||||
const versionRef = "adapter-version:33333333-3333-4333-8333-333333333333";
|
||||
const edgeRef = "edge:44444444-4444-4444-8444-444444444444";
|
||||
const routeRef = "route:55555555-5555-4555-8555-555555555555";
|
||||
const digest = `sha256:${"a".repeat(64)}`;
|
||||
const identifierDigest = `hmac-sha256:${"b".repeat(64)}`;
|
||||
|
||||
test("aggregates project and infrastructure commands without a session mutation", () => {
|
||||
for (const kind of DEVICE_INFRASTRUCTURE_COMMAND_KINDS) {
|
||||
assert.equal(ALL_DEVICE_MANAGEMENT_COMMAND_KINDS.includes(kind), true);
|
||||
}
|
||||
assert.equal(ALL_DEVICE_MANAGEMENT_COMMAND_KINDS.includes("project.ensure"), true);
|
||||
assert.equal(ALL_DEVICE_MANAGEMENT_COMMAND_KINDS.includes("session.ensure"), false);
|
||||
assert.throws(
|
||||
() => normalizeDeviceManagementCommand("session.ensure", {}),
|
||||
/device_management_command_kind_invalid/,
|
||||
);
|
||||
});
|
||||
|
||||
test("normalizes immutable adapter version metadata and sorted capabilities", () => {
|
||||
const command = normalizeInfrastructureManagementCommand(
|
||||
"adapter_version.register",
|
||||
{
|
||||
adapterPackageRef: packageRef,
|
||||
version: "1.2.3",
|
||||
runtimePackageRef: "artifact:device-adapters/generic-1.2.3",
|
||||
contentDigest: digest,
|
||||
contractVersion: "nodedc.device-adapter.v1",
|
||||
capabilities: ["telemetry.observe", "command.typed", "telemetry.observe"],
|
||||
lifecycleState: "active",
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(command.adapterPackageId, packageRef.slice("adapter-package:".length));
|
||||
assert.deepEqual(command.capabilities, ["command.typed", "telemetry.observe"]);
|
||||
assert.equal(command.contentDigest, digest);
|
||||
});
|
||||
|
||||
test("normalizes a generic model profile as artifact metadata, not executable payload", () => {
|
||||
const command = normalizeInfrastructureManagementCommand(
|
||||
"model_profile.register",
|
||||
{
|
||||
adapterVersionRef: versionRef,
|
||||
profileRef: "vendor.model.protocol.v1",
|
||||
schemaVersion: "nodedc.device-model-profile.v1",
|
||||
vendor: "Example Vendor",
|
||||
model: "Model One",
|
||||
deviceType: "tracker",
|
||||
protocol: "GENERIC_TCP",
|
||||
schemaArtifactRef: "artifact:model-profiles/vendor-model-v1",
|
||||
profileDigest: digest,
|
||||
capabilities: ["telemetry.observe"],
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(command.adapterVersionId, versionRef.slice("adapter-version:".length));
|
||||
assert.equal(command.protocol, "GENERIC_TCP");
|
||||
assert.equal(command.lifecycleState, "draft");
|
||||
assert.equal("profile" in command, false);
|
||||
assert.equal("source" in command, false);
|
||||
});
|
||||
|
||||
test("route and enrollment commands resolve only scoped references", () => {
|
||||
const route = normalizeInfrastructureManagementCommand("route.ensure", {
|
||||
projectRef,
|
||||
routeKey: "primary-ingress",
|
||||
displayName: "Primary ingress",
|
||||
edgeRef,
|
||||
modelProfileRef: "vendor.model.protocol.v1",
|
||||
listenerRef: "listener:generic-tcp-primary",
|
||||
protocol: "GENERIC_TCP",
|
||||
direction: "bidirectional",
|
||||
});
|
||||
const enrollment = normalizeInfrastructureManagementCommand(
|
||||
"enrollment_intent.ensure",
|
||||
{
|
||||
projectRef,
|
||||
enrollmentKey: "pilot-device",
|
||||
routeRef,
|
||||
modelProfileRef: "vendor.model.protocol.v1",
|
||||
displayName: "Pilot device",
|
||||
identifierKind: "serial",
|
||||
identifierDigest,
|
||||
identifierMasked: "********0001",
|
||||
expiresAt: "2026-09-01T00:00:00.000Z",
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(route.projectId, projectRef.slice("project:".length));
|
||||
assert.equal(route.edgeId, edgeRef.slice("edge:".length));
|
||||
assert.equal(enrollment.routeId, routeRef.slice("route:".length));
|
||||
assert.equal(enrollment.identifierDigest, identifierDigest);
|
||||
});
|
||||
|
||||
test("enrollment contract rejects raw identifiers and credential-shaped fields", () => {
|
||||
const base = {
|
||||
projectRef,
|
||||
enrollmentKey: "pilot-device",
|
||||
routeRef,
|
||||
modelProfileRef: "vendor.model.protocol.v1",
|
||||
displayName: "Pilot device",
|
||||
identifierKind: "imei",
|
||||
identifierDigest,
|
||||
identifierMasked: "***********0001",
|
||||
};
|
||||
|
||||
assert.throws(
|
||||
() => normalizeInfrastructureManagementCommand(
|
||||
"enrollment_intent.ensure",
|
||||
{ ...base, identifierMasked: "000000000000001" },
|
||||
),
|
||||
/safe_projection_contains_unmasked_imei/,
|
||||
);
|
||||
assert.throws(
|
||||
() => normalizeInfrastructureManagementCommand(
|
||||
"enrollment_intent.ensure",
|
||||
{ ...base, credential: "forbidden" },
|
||||
),
|
||||
/device_management_command_field_unexpected:credential/,
|
||||
);
|
||||
});
|
||||
|
||||
test("shared catalog and Edge authority requires the Hub owner ceiling", () => {
|
||||
assert.doesNotThrow(() => assertPlatformCatalogAuthority(actor("owner")));
|
||||
assert.throws(
|
||||
() => assertPlatformCatalogAuthority(actor("admin")),
|
||||
/device_platform_catalog_access_denied/,
|
||||
);
|
||||
});
|
||||
|
||||
function actor(hubRole) {
|
||||
return normalizeManagementActor({
|
||||
userRef: "user:platform-admin",
|
||||
hubRole,
|
||||
groupRefs: [],
|
||||
ownerScopes: [],
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user