

























@@ -8,8 +8,11 @@ installBaseProgramMocks();
88let registerNodesCli: typeof import("./nodes-cli.js").registerNodesCli;
991010type GatewayCallRequest = {
11+clientName?: string;
1112method?: string;
13+mode?: string;
1214params?: unknown;
15+scopes?: unknown;
1316};
14171518function formatRuntimeLogCallArg(value: unknown): string {
@@ -434,19 +437,103 @@ describe("cli program (nodes basics)", () => {
434437435438expectGatewayRequest("node.list", {});
436439expectGatewayRequest("node.describe", { nodeId: "ios-node" });
440+const describeRequest = gatewayRequests().find(
441+(candidate) => candidate.method === "node.describe",
442+);
443+expect(describeRequest?.clientName).toBe("cli");
444+expect(describeRequest?.mode).toBe("cli");
437445438446const out = getRuntimeOutput();
439447expect(out).toContain("Commands");
440448expect(out).toContain("canvas.eval");
441449});
442450443-it("runs nodes approve and calls node.pair.approve", async () => {
444-callGateway.mockResolvedValue({
445-requestId: "r1",
446-node: { nodeId: "n1", token: "t1" },
451+it("runs nodes approve with the pending request approval scopes", async () => {
452+callGateway.mockImplementation(async (...args: unknown[]) => {
453+const opts = (args[0] ?? {}) as { method?: string };
454+if (opts.method === "node.pair.list") {
455+return {
456+pending: [
457+{
458+requestId: "r1",
459+nodeId: "n1",
460+ts: Date.now(),
461+requiredApproveScopes: ["operator.pairing", "operator.admin"],
462+},
463+],
464+paired: [],
465+};
466+}
467+if (opts.method === "node.pair.approve") {
468+return {
469+requestId: "r1",
470+node: { nodeId: "n1", token: "t1" },
471+};
472+}
473+return { ok: true };
447474});
475+448476await runProgram(["nodes", "approve", "r1"]);
477+expectGatewayRequest("node.pair.list", {});
449478expectGatewayRequest("node.pair.approve", { requestId: "r1" });
479+const listRequest = gatewayRequests().find(
480+(candidate) => candidate.method === "node.pair.list",
481+);
482+const approveRequest = gatewayRequests().find(
483+(candidate) => candidate.method === "node.pair.approve",
484+);
485+expect(listRequest?.clientName).toBe("gateway-client");
486+expect(listRequest?.mode).toBe("backend");
487+expect(approveRequest?.scopes).toEqual(["operator.pairing", "operator.admin"]);
488+expect(approveRequest?.clientName).toBe("gateway-client");
489+expect(approveRequest?.mode).toBe("backend");
490+});
491+492+it("falls back to command-derived nodes approve scopes", async () => {
493+callGateway.mockImplementation(async (...args: unknown[]) => {
494+const opts = (args[0] ?? {}) as { method?: string };
495+if (opts.method === "node.pair.list") {
496+return {
497+pending: [
498+{
499+requestId: "r1",
500+nodeId: "n1",
501+ts: Date.now(),
502+commands: ["system.run"],
503+},
504+],
505+paired: [],
506+};
507+}
508+if (opts.method === "node.pair.approve") {
509+return {
510+requestId: "r1",
511+node: { nodeId: "n1", token: "t1" },
512+};
513+}
514+return { ok: true };
515+});
516+517+await runProgram(["nodes", "approve", "r1"]);
518+519+const approveRequest = gatewayRequests().find(
520+(candidate) => candidate.method === "node.pair.approve",
521+);
522+expect(approveRequest?.scopes).toEqual(["operator.pairing", "operator.admin"]);
523+});
524+525+it("rejects unsupported node approval backend methods at runtime", async () => {
526+const { callNodePairApprovalGatewayCliRuntime } = await import("./nodes-cli/rpc.runtime.js");
527+528+await expect(
529+callNodePairApprovalGatewayCliRuntime(
530+"node.invoke" as never,
531+{ json: true },
532+{},
533+{ scopes: ["operator.admin"] },
534+),
535+).rejects.toThrow("unsupported node pair approval gateway method: node.invoke");
536+expect(callGateway).not.toHaveBeenCalled();
450537});
451538452539it("runs nodes remove and calls node.pair.remove", async () => {
@@ -500,5 +587,8 @@ describe("cli program (nodes basics)", () => {
500587timeoutMs: 15000,
501588idempotencyKey: "idem-test",
502589});
590+const invokeRequest = gatewayRequests().find((candidate) => candidate.method === "node.invoke");
591+expect(invokeRequest?.clientName).toBe("cli");
592+expect(invokeRequest?.mode).toBe("cli");
503593});
504594});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。