Ctrl K
ComunidadGuías…

test/claude-permission-controls.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
var test = require("node:test");
var assert = require("node:assert/strict");
var kit = require("../lib/yoke/adapters/claude").contractTestKit;

function workerFixture(send) {
  var receive;
  return {
    worker: {
      process: { killed: false, exitCode: null },
      exitPromise: Promise.resolve(),
      permissionModeTimeoutMs: 5,
      onMessage: function(handler) { receive = handler; },
      send: send || function() { return true; },
    },
    receive: function(message) { receive(message); },
  };
}

test("PreToolUse policy defers undecided calls and maps mandatory decisions", async function () {
  var options = { hooks: { PreToolUse: [{ hooks: [async function() { return { continue: true }; }] }] } };
  var seenOptions = null;
  kit.attachPreToolUsePolicy(options, function(toolName, input, hookOptions) {
    seenOptions = hookOptions;
    if (toolName === "Read") return { behavior: "allow", updatedInput: input };
    if (toolName === "Blocked") return { behavior: "deny", message: "Mandatory policy" };
    return null;
  });
  assert.equal(options.hooks.PreToolUse.length, 2);
  var hook = options.hooks.PreToolUse[1].hooks[0];
  assert.deepEqual(await hook({ tool_name: "Other", tool_input: {} }), { continue: true });
  var allowed = await hook({ tool_name: "Read", tool_input: { path: "a" }, tool_use_id: "t1" });
  assert.equal(allowed.hookSpecificOutput.permissionDecision, "allow");
  assert.deepEqual(allowed.hookSpecificOutput.updatedInput, { path: "a" });
  var denied = await hook({ tool_name: "Blocked", tool_input: {} });
  assert.equal(denied.hookSpecificOutput.permissionDecision, "deny");
  assert.equal(denied.hookSpecificOutput.permissionDecisionReason, "Mandatory policy");
  var signal = {};
  await hook({ tool_name: "Other", tool_input: {}, tool_use_id: "input-id" }, "argument-id", { signal: signal });
  assert.equal(seenOptions.toolUseID, "argument-id");
  assert.equal(seenOptions.signal, signal);
  assert.deepEqual(await hook(), { continue: true });
});

test("worker permission setter resolves only after its correlated acknowledgement", async function () {
  var sent;
  var fixture = workerFixture(function(message) { sent = message; return true; });
  var handle = kit.createWorkerQueryHandle(fixture.worker);
  var pending = handle.setPermissionMode("auto");
  assert.equal(sent.mode, "auto");
  fixture.receive({ type: "permission_mode_result", requestId: sent.requestId });
  await pending;
});

test("worker permission setter rejects correlated errors and failed sends", async function () {
  var sent;
  var fixture = workerFixture(function(message) { sent = message; return true; });
  var handle = kit.createWorkerQueryHandle(fixture.worker);
  var pending = handle.setPermissionMode("auto");
  fixture.receive({ type: "permission_mode_result", requestId: sent.requestId, error: "SDK refused" });
  await assert.rejects(pending, /SDK refused/);

  var failed = workerFixture(function() { return false; });
  await assert.rejects(kit.createWorkerQueryHandle(failed.worker).setPermissionMode("auto"), /unavailable/);

  var thrown = workerFixture(function() { throw new Error("IPC send failed"); });
  await assert.rejects(kit.createWorkerQueryHandle(thrown.worker).setPermissionMode("auto"), /IPC send failed/);
});

test("worker permission setter rejects query completion, worker exit, close, and timeout", async function () {
  var doneFixture = workerFixture();
  var doneHandle = kit.createWorkerQueryHandle(doneFixture.worker);
  var donePending = doneHandle.setPermissionMode("auto");
  doneFixture.receive({ type: "query_done" });
  await assert.rejects(donePending, /ended/);

  var exitFixture = workerFixture();
  var exitHandle = kit.createWorkerQueryHandle(exitFixture.worker);
  var exitPending = exitHandle.setPermissionMode("auto");
  exitFixture.receive({ type: "query_error", error: "Worker exited with code 1", exitCode: 1 });
  await assert.rejects(exitPending, /exited with code 1/);

  var closeFixture = workerFixture();
  var closeHandle = kit.createWorkerQueryHandle(closeFixture.worker);
  var closePending = closeHandle.setPermissionMode("auto");
  closeHandle.close();
  await assert.rejects(closePending, /closed/);

  var timeoutFixture = workerFixture();
  await assert.rejects(kit.createWorkerQueryHandle(timeoutFixture.worker).setPermissionMode("auto"), /Timed out/);
});

test("worker MCP policy setter has correlated success and errors", async function () {
  var sent;
  var fixture = workerFixture(function(message) { sent = message; return true; });
  var handle = kit.createWorkerQueryHandle(fixture.worker);
  var pending = handle.setMcpPermissionModeOverride("github", "auto");
  fixture.receive({ type: "mcp_permission_mode_result", requestId: sent.requestId, result: { applied: true } });
  assert.deepEqual(await pending, { applied: true });

  var errorPending = handle.setMcpPermissionModeOverride("github", "default");
  fixture.receive({ type: "mcp_permission_mode_result", requestId: sent.requestId, error: "not applicable" });
  await assert.rejects(errorPending, /not applicable/);
});

test("worker bridge converts synchronous PreToolUse policy errors into correlated denial", async function () {
  var sent = [];
  var fixture = workerFixture(function(message) { sent.push(message); return true; });
  kit.createWorkerQueryHandle(fixture.worker, null, null, null, null, null, null, function() {
    throw new Error("policy failed");
  });
  fixture.receive({ type: "pre_tool_policy_request", requestId: "policy-1", toolName: "Write", input: {} });
  await new Promise(function(resolve) { setImmediate(resolve); });
  assert.equal(sent[0].type, "pre_tool_policy_response");
  assert.equal(sent[0].requestId, "policy-1");
  assert.equal(sent[0].error, "policy failed");
});