test: migrate permission task config tests (#27343)

This commit is contained in:
Kit Langton
2026-05-13 10:56:26 -04:00
committed by GitHub
parent 268d758130
commit 0b112e5bcf

View File

@@ -1,16 +1,12 @@
import { afterEach, describe, test, expect } from "bun:test" import { describe, test, expect } from "bun:test"
import { Effect } from "effect"
import { Permission } from "../src/permission" import { Permission } from "../src/permission"
import { Config } from "@/config/config" import { Config } from "@/config/config"
import { Instance } from "../src/project/instance" import { testEffect } from "./lib/effect"
import { WithInstance } from "../src/project/with-instance"
import { disposeAllInstances, tmpdir } from "./fixture/fixture"
import { AppRuntime } from "../src/effect/app-runtime"
const load = () => AppRuntime.runPromise(Config.Service.use((svc) => svc.get())) const it = testEffect(Config.defaultLayer)
afterEach(async () => { const load = Config.Service.use((svc) => svc.get())
await disposeAllInstances()
})
describe("Permission.evaluate for permission.task", () => { describe("Permission.evaluate for permission.task", () => {
const createRuleset = (rules: Record<string, "allow" | "deny" | "ask">): Permission.Ruleset => const createRuleset = (rules: Record<string, "allow" | "deny" | "ask">): Permission.Ruleset =>
@@ -147,8 +143,18 @@ describe("Permission.disabled for task tool", () => {
// Integration tests that load permissions from real config files // Integration tests that load permissions from real config files
describe("permission.task with real config files", () => { describe("permission.task with real config files", () => {
test("loads task permissions from opencode.json config", async () => { it.instance(
await using tmp = await tmpdir({ "loads task permissions from opencode.json config",
() =>
Effect.gen(function* () {
const config = yield* load
const ruleset = Permission.fromConfig(config.permission ?? {})
// general and orchestrator-fast should be allowed, code-reviewer denied
expect(Permission.evaluate("task", "general", ruleset).action).toBe("allow")
expect(Permission.evaluate("task", "orchestrator-fast", ruleset).action).toBe("allow")
expect(Permission.evaluate("task", "code-reviewer", ruleset).action).toBe("deny")
}),
{
git: true, git: true,
config: { config: {
permission: { permission: {
@@ -158,22 +164,21 @@ describe("permission.task with real config files", () => {
}, },
}, },
}, },
}) },
await WithInstance.provide({ )
directory: tmp.path,
fn: async () => {
const config = await load()
const ruleset = Permission.fromConfig(config.permission ?? {})
// general and orchestrator-fast should be allowed, code-reviewer denied
expect(Permission.evaluate("task", "general", ruleset).action).toBe("allow")
expect(Permission.evaluate("task", "orchestrator-fast", ruleset).action).toBe("allow")
expect(Permission.evaluate("task", "code-reviewer", ruleset).action).toBe("deny")
},
})
})
test("loads task permissions with wildcard patterns from config", async () => { it.instance(
await using tmp = await tmpdir({ "loads task permissions with wildcard patterns from config",
() =>
Effect.gen(function* () {
const config = yield* load
const ruleset = Permission.fromConfig(config.permission ?? {})
// general and code-reviewer should be ask, orchestrator-* denied
expect(Permission.evaluate("task", "general", ruleset).action).toBe("ask")
expect(Permission.evaluate("task", "code-reviewer", ruleset).action).toBe("ask")
expect(Permission.evaluate("task", "orchestrator-fast", ruleset).action).toBe("deny")
}),
{
git: true, git: true,
config: { config: {
permission: { permission: {
@@ -183,22 +188,21 @@ describe("permission.task with real config files", () => {
}, },
}, },
}, },
}) },
await WithInstance.provide({ )
directory: tmp.path,
fn: async () => {
const config = await load()
const ruleset = Permission.fromConfig(config.permission ?? {})
// general and code-reviewer should be ask, orchestrator-* denied
expect(Permission.evaluate("task", "general", ruleset).action).toBe("ask")
expect(Permission.evaluate("task", "code-reviewer", ruleset).action).toBe("ask")
expect(Permission.evaluate("task", "orchestrator-fast", ruleset).action).toBe("deny")
},
})
})
test("evaluate respects task permission from config", async () => { it.instance(
await using tmp = await tmpdir({ "evaluate respects task permission from config",
() =>
Effect.gen(function* () {
const config = yield* load
const ruleset = Permission.fromConfig(config.permission ?? {})
expect(Permission.evaluate("task", "general", ruleset).action).toBe("allow")
expect(Permission.evaluate("task", "code-reviewer", ruleset).action).toBe("deny")
// Unspecified agents default to "ask"
expect(Permission.evaluate("task", "unknown-agent", ruleset).action).toBe("ask")
}),
{
git: true, git: true,
config: { config: {
permission: { permission: {
@@ -208,38 +212,14 @@ describe("permission.task with real config files", () => {
}, },
}, },
}, },
}) },
await WithInstance.provide({ )
directory: tmp.path,
fn: async () => {
const config = await load()
const ruleset = Permission.fromConfig(config.permission ?? {})
expect(Permission.evaluate("task", "general", ruleset).action).toBe("allow")
expect(Permission.evaluate("task", "code-reviewer", ruleset).action).toBe("deny")
// Unspecified agents default to "ask"
expect(Permission.evaluate("task", "unknown-agent", ruleset).action).toBe("ask")
},
})
})
test("mixed permission config with task and other tools", async () => { it.instance(
await using tmp = await tmpdir({ "mixed permission config with task and other tools",
git: true, () =>
config: { Effect.gen(function* () {
permission: { const config = yield* load
bash: "allow",
edit: "ask",
task: {
"*": "deny",
general: "allow",
},
},
},
})
await WithInstance.provide({
directory: tmp.path,
fn: async () => {
const config = await load()
const ruleset = Permission.fromConfig(config.permission ?? {}) const ruleset = Permission.fromConfig(config.permission ?? {})
// Verify task permissions // Verify task permissions
@@ -257,27 +237,27 @@ describe("permission.task with real config files", () => {
// task is NOT disabled because disabled() uses findLast, and the last rule // task is NOT disabled because disabled() uses findLast, and the last rule
// matching "task" permission is {pattern: "general", action: "allow"}, not pattern: "*" // matching "task" permission is {pattern: "general", action: "allow"}, not pattern: "*"
expect(disabled.has("task")).toBe(false) expect(disabled.has("task")).toBe(false)
}, }),
}) {
})
test("task tool disabled when global deny comes last in config", async () => {
await using tmp = await tmpdir({
git: true, git: true,
config: { config: {
permission: { permission: {
bash: "allow",
edit: "ask",
task: { task: {
general: "allow",
"code-reviewer": "allow",
"*": "deny", "*": "deny",
general: "allow",
}, },
}, },
}, },
}) },
await WithInstance.provide({ )
directory: tmp.path,
fn: async () => { it.instance(
const config = await load() "task tool disabled when global deny comes last in config",
() =>
Effect.gen(function* () {
const config = yield* load
const ruleset = Permission.fromConfig(config.permission ?? {}) const ruleset = Permission.fromConfig(config.permission ?? {})
// Last matching rule wins - "*" deny is last, so all agents are denied // Last matching rule wins - "*" deny is last, so all agents are denied
@@ -289,26 +269,26 @@ describe("permission.task with real config files", () => {
// and sees pattern: "*" with action: "deny", so task is disabled // and sees pattern: "*" with action: "deny", so task is disabled
const disabled = Permission.disabled(["task"], ruleset) const disabled = Permission.disabled(["task"], ruleset)
expect(disabled.has("task")).toBe(true) expect(disabled.has("task")).toBe(true)
}, }),
}) {
})
test("task tool NOT disabled when specific allow comes last in config", async () => {
await using tmp = await tmpdir({
git: true, git: true,
config: { config: {
permission: { permission: {
task: { task: {
"*": "deny",
general: "allow", general: "allow",
"code-reviewer": "allow",
"*": "deny",
}, },
}, },
}, },
}) },
await WithInstance.provide({ )
directory: tmp.path,
fn: async () => { it.instance(
const config = await load() "task tool NOT disabled when specific allow comes last in config",
() =>
Effect.gen(function* () {
const config = yield* load
const ruleset = Permission.fromConfig(config.permission ?? {}) const ruleset = Permission.fromConfig(config.permission ?? {})
// Evaluate uses findLast - "general" allow comes after "*" deny // Evaluate uses findLast - "general" allow comes after "*" deny
@@ -321,7 +301,17 @@ describe("permission.task with real config files", () => {
// So the task tool is NOT disabled (even though most subagents are denied) // So the task tool is NOT disabled (even though most subagents are denied)
const disabled = Permission.disabled(["task"], ruleset) const disabled = Permission.disabled(["task"], ruleset)
expect(disabled.has("task")).toBe(false) expect(disabled.has("task")).toBe(false)
}),
{
git: true,
config: {
permission: {
task: {
"*": "deny",
general: "allow",
},
},
}, },
}) },
}) )
}) })