test: clear device pairing broad matchers

This commit is contained in:
Peter Steinberger
2026-05-10 11:49:17 +01:00
parent 4cf4c97d34
commit 46dcf4ef78

View File

@@ -82,6 +82,38 @@ function requireValue<T>(value: T | null | undefined, message: string): T {
return value;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function requireRecord(value: unknown, message: string): Record<string, unknown> {
if (!isRecord(value)) {
throw new Error(message);
}
return value;
}
function expectRecordFields(
value: unknown,
message: string,
expected: Record<string, unknown>,
): Record<string, unknown> {
const record = requireRecord(value, message);
for (const [key, expectedValue] of Object.entries(expected)) {
expect(record[key], `${message}.${key}`).toEqual(expectedValue);
}
return record;
}
function expectArrayIncludesAll(value: unknown, expected: readonly unknown[], message: string) {
expect(Array.isArray(value), `${message} must be an array`).toBe(true);
for (const expectedValue of expected) {
expect(value as unknown[], `${message} must include ${String(expectedValue)}`).toContain(
expectedValue,
);
}
}
function requireRotatedEntry(result: RotateDeviceTokenResult) {
expect(result.ok).toBe(true);
if (!result.ok) {
@@ -183,18 +215,20 @@ describe("device pairing tokens", () => {
baseDir,
);
expect(approved).toEqual(
expect.objectContaining({
status: "approved",
device: expect.objectContaining({ deviceId: "device-array-state" }),
}),
);
const approvedRecord = expectRecordFields(approved, "approved result", {
status: "approved",
});
expectRecordFields(approvedRecord.device, "approved device", {
deviceId: "device-array-state",
});
expect(Array.isArray(JSON.parse(await readFile(paths.pendingPath, "utf8")))).toBe(false);
expect(JSON.parse(await readFile(paths.pairedPath, "utf8"))).toEqual(
expect.objectContaining({
"device-array-state": expect.objectContaining({ deviceId: "device-array-state" }),
}),
const pairedByDeviceId = requireRecord(
JSON.parse(await readFile(paths.pairedPath, "utf8")),
"paired devices",
);
expectRecordFields(pairedByDeviceId["device-array-state"], "paired device", {
deviceId: "device-array-state",
});
});
test("re-requesting with identical params preserves the original ts to prevent queue-jumping", async () => {
@@ -263,9 +297,11 @@ describe("device pairing tokens", () => {
expect(second.created).toBe(true);
expect(second.request.requestId).not.toBe(first.request.requestId);
expect(second.request.role).toBe("operator");
expect(second.request.roles).toEqual(expect.arrayContaining(["node", "operator"]));
expect(second.request.scopes).toEqual(
expect.arrayContaining(["operator.read", "operator.write"]),
expectArrayIncludesAll(second.request.roles, ["node", "operator"], "request roles");
expectArrayIncludesAll(
second.request.scopes,
["operator.read", "operator.write"],
"request scopes",
);
const list = await listDevicePairing(baseDir);
@@ -278,8 +314,8 @@ describe("device pairing tokens", () => {
baseDir,
);
const paired = await getPairedDevice("device-1", baseDir);
expect(paired?.roles).toEqual(expect.arrayContaining(["node", "operator"]));
expect(paired?.scopes).toEqual(expect.arrayContaining(["operator.read", "operator.write"]));
expectArrayIncludesAll(paired?.roles, ["node", "operator"], "paired roles");
expectArrayIncludesAll(paired?.scopes, ["operator.read", "operator.write"], "paired scopes");
});
test("approves mixed node and operator requests with admin caller scopes", async () => {
@@ -294,13 +330,12 @@ describe("device pairing tokens", () => {
baseDir,
);
await expect(
approveDevicePairing(
request.request.requestId,
{ callerScopes: ["operator.admin", "operator.pairing"] },
baseDir,
),
).resolves.toMatchObject({
const approved = await approveDevicePairing(
request.request.requestId,
{ callerScopes: ["operator.admin", "operator.pairing"] },
baseDir,
);
expectRecordFields(approved, "approved result", {
status: "approved",
requestId: request.request.requestId,
});
@@ -347,13 +382,12 @@ describe("device pairing tokens", () => {
baseDir,
);
await expect(
approveDevicePairing(
upgrade.request.requestId,
{ callerScopes: ["operator.read", "operator.write"] },
baseDir,
),
).resolves.toMatchObject({ status: "approved" });
const approved = await approveDevicePairing(
upgrade.request.requestId,
{ callerScopes: ["operator.read", "operator.write"] },
baseDir,
);
expectRecordFields(approved, "approved result", { status: "approved" });
const paired = await getPairedDevice("device-1", baseDir);
expect(paired?.approvedScopes).toEqual(["operator.read", "operator.write"]);
@@ -375,13 +409,12 @@ describe("device pairing tokens", () => {
baseDir,
);
await expect(
approveDevicePairing(
upgrade.request.requestId,
{ callerScopes: ["operator.read", "operator.talk.secrets", "operator.write"] },
baseDir,
),
).resolves.toMatchObject({ status: "approved" });
const approved = await approveDevicePairing(
upgrade.request.requestId,
{ callerScopes: ["operator.read", "operator.talk.secrets", "operator.write"] },
baseDir,
);
expectRecordFields(approved, "approved result", { status: "approved" });
const paired = await getPairedDevice("device-1", baseDir);
expect(paired?.approvedScopes).toEqual([
@@ -405,7 +438,8 @@ describe("device pairing tokens", () => {
baseDir,
);
await expect(approveDevicePairing(request.request.requestId, baseDir)).resolves.toMatchObject({
const approved = await approveDevicePairing(request.request.requestId, baseDir);
expectRecordFields(approved, "approved result", {
status: "approved",
requestId: request.request.requestId,
});
@@ -482,7 +516,8 @@ describe("device pairing tokens", () => {
},
baseDir,
);
await expect(approveDevicePairing(initial.request.requestId, baseDir)).resolves.toMatchObject({
const approvedInitial = await approveDevicePairing(initial.request.requestId, baseDir);
expectRecordFields(approvedInitial, "initial approved result", {
status: "approved",
requestId: initial.request.requestId,
});
@@ -496,9 +531,12 @@ describe("device pairing tokens", () => {
},
baseDir,
);
await expect(
approveDevicePairing(repair.request.requestId, { callerScopes: ["operator.read"] }, baseDir),
).resolves.toMatchObject({
const approvedRepair = await approveDevicePairing(
repair.request.requestId,
{ callerScopes: ["operator.read"] },
baseDir,
);
expectRecordFields(approvedRepair, "repair approved result", {
status: "approved",
requestId: repair.request.requestId,
});
@@ -616,20 +654,17 @@ describe("device pairing tokens", () => {
scope: "operator.admin",
});
await expect(
approveDevicePairing(
request.request.requestId,
{
callerScopes: ["operator.admin"],
},
baseDir,
),
).resolves.toEqual(
expect.objectContaining({
status: "approved",
requestId: request.request.requestId,
}),
const approved = await approveDevicePairing(
request.request.requestId,
{
callerScopes: ["operator.admin"],
},
baseDir,
);
expectRecordFields(approved, "approved result", {
status: "approved",
requestId: request.request.requestId,
});
});
test("metadata refresh cannot mutate approved role and scope fields", async () => {
@@ -657,7 +692,7 @@ describe("device pairing tokens", () => {
expect(paired?.roles).toEqual(["node"]);
expect(paired?.scopes).toStrictEqual([]);
expect(paired?.approvedScopes).toStrictEqual([]);
expect(paired?.tokens?.node).toMatchObject({ token: expect.any(String) });
expect(typeof paired?.tokens?.node?.token).toBe("string");
expect(paired?.tokens?.operator).toBeUndefined();
});
@@ -680,12 +715,10 @@ describe("device pairing tokens", () => {
);
const paired = await getPairedDevice("node-1", baseDir);
expect(paired).toEqual(
expect.objectContaining({
lastSeenAtMs: 4321,
lastSeenReason: "bg_app_refresh",
}),
);
expectRecordFields(paired, "paired device", {
lastSeenAtMs: 4321,
lastSeenReason: "bg_app_refresh",
});
});
test("generates base64url device tokens with 256-bit entropy output length", async () => {
@@ -866,13 +899,14 @@ describe("device pairing tokens", () => {
callerScopes: ["operator.admin"],
baseDir,
});
expect(revoked).toEqual({
ok: true,
entry: expect.objectContaining({
role: "operator",
revokedAtMs: expect.any(Number),
}),
expect(revoked.ok).toBe(true);
if (!revoked.ok) {
throw new Error(`expected revoked token entry, got ${revoked.reason}`);
}
expectRecordFields(revoked.entry, "revoked entry", {
role: "operator",
});
expect(revoked.entry.revokedAtMs).toBeTypeOf("number");
const after = await getPairedDevice("device-1", baseDir);
expect(after?.tokens?.operator?.revokedAtMs).toBeTypeOf("number");
@@ -907,14 +941,13 @@ describe("device pairing tokens", () => {
expect(paired?.approvedScopes).toStrictEqual([]);
const seededToken = requireToken(paired?.tokens?.node?.token);
await expect(
ensureDeviceToken({
deviceId: "node-1",
role: "node",
scopes: [],
baseDir,
}),
).resolves.toEqual(expect.objectContaining({ token: seededToken, scopes: [] }));
const ensured = await ensureDeviceToken({
deviceId: "node-1",
role: "node",
scopes: [],
baseDir,
});
expectRecordFields(ensured, "ensured token", { token: seededToken, scopes: [] });
await expect(
verifyDeviceToken({
@@ -966,22 +999,25 @@ describe("device pairing tokens", () => {
baseDir,
);
await expect(
approveBootstrapDevicePairing(
request.request.requestId,
PAIRING_SETUP_BOOTSTRAP_PROFILE,
baseDir,
),
).resolves.toEqual(expect.objectContaining({ status: "approved" }));
const approved = await approveBootstrapDevicePairing(
request.request.requestId,
PAIRING_SETUP_BOOTSTRAP_PROFILE,
baseDir,
);
expectRecordFields(approved, "approved result", { status: "approved" });
const paired = await getPairedDevice("bootstrap-device-1", baseDir);
expect(paired?.roles).toEqual(expect.arrayContaining(["node", "operator"]));
expect(paired?.approvedScopes).toEqual(
expect.arrayContaining(PAIRING_SETUP_BOOTSTRAP_PROFILE.scopes),
expectArrayIncludesAll(paired?.roles, ["node", "operator"], "paired roles");
expectArrayIncludesAll(
paired?.approvedScopes,
PAIRING_SETUP_BOOTSTRAP_PROFILE.scopes,
"paired approved scopes",
);
expect(paired?.tokens?.node?.scopes).toStrictEqual([]);
expect(paired?.tokens?.operator?.scopes).toEqual(
expect.arrayContaining(PAIRING_SETUP_BOOTSTRAP_PROFILE.scopes),
expectArrayIncludesAll(
paired?.tokens?.operator?.scopes,
PAIRING_SETUP_BOOTSTRAP_PROFILE.scopes,
"operator token scopes",
);
});
@@ -999,16 +1035,15 @@ describe("device pairing tokens", () => {
baseDir,
);
await expect(
approveBootstrapDevicePairing(
request.request.requestId,
{
roles: ["node", "operator"],
scopes: ["node.exec", "operator.pairing", "operator.read", "operator.write"],
},
baseDir,
),
).resolves.toEqual(expect.objectContaining({ status: "approved" }));
const approved = await approveBootstrapDevicePairing(
request.request.requestId,
{
roles: ["node", "operator"],
scopes: ["node.exec", "operator.pairing", "operator.read", "operator.write"],
},
baseDir,
);
expectRecordFields(approved, "approved result", { status: "approved" });
const paired = await getPairedDevice("bootstrap-device-operator-scope", baseDir);
expect(paired?.tokens?.operator?.scopes).toEqual(["operator.read", "operator.write"]);
@@ -1029,24 +1064,23 @@ describe("device pairing tokens", () => {
baseDir,
);
await expect(
approveBootstrapDevicePairing(
request.request.requestId,
{
roles: ["node", "operator"],
scopes: [
"node.exec",
"operator.admin",
"operator.approvals",
"operator.pairing",
"operator.read",
"operator.talk.secrets",
"operator.write",
],
},
baseDir,
),
).resolves.toEqual(expect.objectContaining({ status: "approved" }));
const approved = await approveBootstrapDevicePairing(
request.request.requestId,
{
roles: ["node", "operator"],
scopes: [
"node.exec",
"operator.admin",
"operator.approvals",
"operator.pairing",
"operator.read",
"operator.talk.secrets",
"operator.write",
],
},
baseDir,
);
expectRecordFields(approved, "approved result", { status: "approved" });
const paired = await getPairedDevice("bootstrap-device-bounded-baseline", baseDir);
expect(paired?.approvedScopes).toEqual([
@@ -1107,13 +1141,12 @@ describe("device pairing tokens", () => {
},
baseDir,
);
await expect(
approveBootstrapDevicePairing(
repair.request.requestId,
PAIRING_SETUP_BOOTSTRAP_PROFILE,
baseDir,
),
).resolves.toEqual(expect.objectContaining({ status: "approved" }));
const approved = await approveBootstrapDevicePairing(
repair.request.requestId,
PAIRING_SETUP_BOOTSTRAP_PROFILE,
baseDir,
);
expectRecordFields(approved, "approved result", { status: "approved" });
const paired = await getPairedDevice("bootstrap-device-legacy-baseline", baseDir);
expect(paired?.approvedScopes).toEqual(PAIRING_SETUP_BOOTSTRAP_PROFILE.scopes);