mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-03 10:10:22 +00:00
test(infra): reuse suite temp root tracker in device pairing tests
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
||||
import { PAIRING_SETUP_BOOTSTRAP_PROFILE } from "../shared/device-bootstrap-profile.js";
|
||||
import { createSuiteTempRootTracker } from "../test-helpers/temp-dir.js";
|
||||
import { issueDeviceBootstrapToken, verifyDeviceBootstrapToken } from "./device-bootstrap.js";
|
||||
import {
|
||||
approveBootstrapDevicePairing,
|
||||
@@ -50,7 +49,7 @@ async function setupPairedNodeDevice(baseDir: string) {
|
||||
}
|
||||
|
||||
async function setupOperatorToken(scopes: string[]) {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, scopes);
|
||||
const paired = await getPairedDevice("device-1", baseDir);
|
||||
const token = requireToken(paired?.tokens?.operator?.token);
|
||||
@@ -124,9 +123,23 @@ async function clearPairedOperatorApprovalBaseline(baseDir: string) {
|
||||
});
|
||||
}
|
||||
|
||||
const suiteRootTracker = createSuiteTempRootTracker({ prefix: "openclaw-device-pairing-" });
|
||||
|
||||
async function makeDevicePairingDir(): Promise<string> {
|
||||
return await suiteRootTracker.make("case");
|
||||
}
|
||||
|
||||
describe("device pairing tokens", () => {
|
||||
beforeAll(async () => {
|
||||
await suiteRootTracker.setup();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await suiteRootTracker.cleanup();
|
||||
});
|
||||
|
||||
test("reuses existing pending requests for the same device", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const first = await requestDevicePairing(
|
||||
{
|
||||
deviceId: "device-1",
|
||||
@@ -148,7 +161,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("supersedes pending requests when requested roles/scopes change", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const first = await requestDevicePairing(
|
||||
{
|
||||
deviceId: "device-1",
|
||||
@@ -191,7 +204,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("approves mixed node and operator requests with admin caller scopes", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const request = await requestDevicePairing(
|
||||
{
|
||||
deviceId: "device-1",
|
||||
@@ -242,7 +255,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("preserves requested non-operator scopes on newly minted role tokens", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const request = await requestDevicePairing(
|
||||
{
|
||||
deviceId: "device-1",
|
||||
@@ -272,7 +285,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("preserves existing non-operator scopes during operator-only mixed-role repairs", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const initial = await requestDevicePairing(
|
||||
{
|
||||
deviceId: "device-1",
|
||||
@@ -318,7 +331,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("keeps superseded requests interactive when an existing pending request is interactive", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const first = await requestDevicePairing(
|
||||
{
|
||||
deviceId: "device-1",
|
||||
@@ -348,7 +361,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("rejects bootstrap token replay before pending scope escalation can be approved", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const issued = await issueDeviceBootstrapToken({
|
||||
baseDir,
|
||||
roles: ["operator"],
|
||||
@@ -399,7 +412,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("fails closed for operator approvals when caller scopes are omitted", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const request = await requestDevicePairing(
|
||||
{
|
||||
deviceId: "device-1",
|
||||
@@ -432,7 +445,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("generates base64url device tokens with 256-bit entropy output length", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.admin"]);
|
||||
|
||||
const paired = await getPairedDevice("device-1", baseDir);
|
||||
@@ -442,7 +455,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("allows down-scoping from admin and preserves approved scope baseline", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.admin"]);
|
||||
|
||||
const downscoped = await rotateDeviceToken({
|
||||
@@ -468,7 +481,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("preserves existing token scopes when approving a repair without requested scopes", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.admin"]);
|
||||
|
||||
const repair = await requestDevicePairing(
|
||||
@@ -496,7 +509,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("rejects scope escalation when rotating a token and leaves state unchanged", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.read"]);
|
||||
const before = await getPairedDevice("device-1", baseDir);
|
||||
|
||||
@@ -516,7 +529,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("rejects scope escalation when ensuring a token and leaves state unchanged", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.read"]);
|
||||
const before = await getPairedDevice("device-1", baseDir);
|
||||
|
||||
@@ -536,7 +549,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("preserves explicit empty scope baselines for node device tokens", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedNodeDevice(baseDir);
|
||||
|
||||
const paired = await getPairedDevice("node-1", baseDir);
|
||||
@@ -565,7 +578,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("normalizes legacy node token scopes back to [] on re-approval", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedNodeDevice(baseDir);
|
||||
|
||||
await mutatePairedDevice(baseDir, "node-1", (device) => {
|
||||
@@ -594,7 +607,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("bootstrap pairing seeds node and operator device tokens explicitly", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const request = await requestDevicePairing(
|
||||
{
|
||||
deviceId: "bootstrap-device-1",
|
||||
@@ -627,7 +640,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("bootstrap pairing keeps operator token scopes operator-only", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const request = await requestDevicePairing(
|
||||
{
|
||||
deviceId: "bootstrap-device-operator-scope",
|
||||
@@ -720,7 +733,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("accepts custom operator scopes under an operator.admin approval baseline", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.admin"]);
|
||||
|
||||
const rotated = await rotateDeviceToken({
|
||||
@@ -742,7 +755,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("fails closed when the paired device approval baseline is missing during ensure", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.admin"]);
|
||||
await clearPairedOperatorApprovalBaseline(baseDir);
|
||||
|
||||
@@ -757,7 +770,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("fails closed when the paired device approval baseline is missing during rotation", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.admin"]);
|
||||
await clearPairedOperatorApprovalBaseline(baseDir);
|
||||
|
||||
@@ -786,7 +799,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("derives effective roles from active tokens instead of sticky historical roles", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
const request = await requestDevicePairing(
|
||||
{
|
||||
deviceId: "device-1",
|
||||
@@ -863,7 +876,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("rejects rotating a token for a role that was never approved", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.pairing"]);
|
||||
|
||||
await expect(
|
||||
@@ -880,7 +893,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("removes paired devices by device id", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.read"]);
|
||||
|
||||
const removed = await removePairedDevice("device-1", baseDir);
|
||||
@@ -891,7 +904,7 @@ describe("device pairing tokens", () => {
|
||||
});
|
||||
|
||||
test("clears paired device state by device id", async () => {
|
||||
const baseDir = await mkdtemp(join(tmpdir(), "openclaw-device-pairing-"));
|
||||
const baseDir = await makeDevicePairingDir();
|
||||
await setupPairedOperatorDevice(baseDir, ["operator.read"]);
|
||||
|
||||
await expect(clearDevicePairing("device-1", baseDir)).resolves.toBe(true);
|
||||
|
||||
Reference in New Issue
Block a user