Files
NODEDC_PLATFORM/device-plane/services/device-control-core/test/lifecycle-management.test.mjs
T

112 lines
3.5 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import {
DEVICE_LIFECYCLE_COMMAND_KINDS,
normalizeLifecycleManagementCommand,
} from "../src/lifecycle-management.mjs";
import {
ALL_DEVICE_MANAGEMENT_COMMAND_KINDS,
normalizeDeviceManagementCommand,
} from "../src/management-command.mjs";
const projectRef = "project:11111111-1111-4111-8111-111111111111";
const targetProjectRef = "project:22222222-2222-4222-8222-222222222222";
const discoveryRef = "discovery:33333333-3333-4333-8333-333333333333";
const enrollmentIntentRef =
"enrollment-intent:44444444-4444-4444-8444-444444444444";
const deviceRef = "device:55555555-5555-4555-8555-555555555555";
test("lifecycle commands join the same strict management command surface", () => {
for (const kind of DEVICE_LIFECYCLE_COMMAND_KINDS) {
assert.equal(ALL_DEVICE_MANAGEMENT_COMMAND_KINDS.includes(kind), true);
}
assert.equal(
normalizeDeviceManagementCommand("device.claim", claimInput()).projectId,
projectRef.slice("project:".length),
);
});
test("claim accepts only opaque evidence references and presentation fields", () => {
const command = normalizeLifecycleManagementCommand(
"device.claim",
claimInput(),
);
assert.equal(
command.enrollmentIntentId,
enrollmentIntentRef.slice("enrollment-intent:".length),
);
assert.equal(command.discoveryId, discoveryRef.slice("discovery:".length));
assert.equal(command.deviceKey, "pilot-device");
assert.equal("identifier" in command, false);
assert.equal("credentialRef" in command, false);
});
test("claim rejects raw identity and credential-shaped input", () => {
assert.throws(
() => normalizeLifecycleManagementCommand("device.claim", {
...claimInput(),
identifier: "000000000000001",
}),
/device_management_command_field_unexpected:identifier/,
);
assert.throws(
() => normalizeLifecycleManagementCommand("device.claim", {
...claimInput(),
credentialRef: "secret:test",
}),
/device_management_command_field_unexpected:credentialRef/,
);
});
test("transfer binds both project boundaries and rejects a no-op", () => {
const command = normalizeLifecycleManagementCommand("device.transfer", {
deviceRef,
sourceProjectRef: projectRef,
targetProjectRef,
targetDeviceKey: "transferred-device",
});
assert.equal(command.deviceId, deviceRef.slice("device:".length));
assert.notEqual(command.sourceProjectId, command.targetProjectId);
assert.throws(
() => normalizeLifecycleManagementCommand("device.transfer", {
deviceRef,
sourceProjectRef: projectRef,
targetProjectRef: projectRef,
targetDeviceKey: "same-project",
}),
/device_transfer_target_same_as_source/,
);
});
test("reject and expire require bounded machine-readable resolution codes", () => {
for (const kind of ["discovery.reject", "discovery.expire"]) {
const command = normalizeLifecycleManagementCommand(kind, {
projectRef,
discoveryRef,
resolutionCode: "operator.identity_mismatch",
});
assert.equal(command.resolutionCode, "operator.identity_mismatch");
}
assert.throws(
() => normalizeLifecycleManagementCommand("discovery.reject", {
projectRef,
discoveryRef,
resolutionCode: "free form reason is forbidden",
}),
/device_discovery_resolution_code_invalid/,
);
});
function claimInput() {
return {
projectRef,
enrollmentIntentRef,
discoveryRef,
deviceKey: "pilot-device",
displayName: "Pilot device",
};
}