mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-08 02:43:22 +00:00
test(core): cover config precedence
This commit is contained in:
parent
d35ca49c31
commit
cd64a17e37
2 changed files with 106 additions and 3 deletions
|
|
@ -12,6 +12,7 @@ import { LayerNode } from "@opencode-ai/util/effect/layer-node"
|
|||
import { FSUtil } from "@opencode-ai/util/fs-util"
|
||||
import { Global } from "@opencode-ai/util/global"
|
||||
import { Permission } from "@opencode-ai/core/permission"
|
||||
import { AgentPlugin } from "@opencode-ai/core/plugin/agent"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { ConfigMigrateV1 } from "@opencode-ai/core/v1/config/migrate"
|
||||
import { advance, drain } from "../lib/clock"
|
||||
|
|
@ -59,6 +60,96 @@ describe("ConfigAgentPlugin.Plugin", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("applies remote permission defaults before explicit global and build rules", () =>
|
||||
Effect.gen(function* () {
|
||||
const agents = yield* Agent.Service
|
||||
const global = yield* Global.Service
|
||||
yield* AgentPlugin.Plugin.effect(host({ agent: agentHost(agents) }))
|
||||
|
||||
const entries = [
|
||||
new Document({
|
||||
type: "document",
|
||||
info: decode(
|
||||
ConfigMigrateV1.migrate({
|
||||
permission: {
|
||||
bash: "ask",
|
||||
edit: "ask",
|
||||
webfetch: "ask",
|
||||
read: {
|
||||
"*": "allow",
|
||||
"*.env": "deny",
|
||||
"*.env.*": "deny",
|
||||
"*.env.example": "allow",
|
||||
"*.dev.vars": "deny",
|
||||
"~/.local/share/opencode/mcp-auth.json": "deny",
|
||||
"$HOME/.local/share/opencode/mcp-auth.json": "deny",
|
||||
},
|
||||
external_directory: {
|
||||
"*": "ask",
|
||||
"~/.local/share/opencode/*": "deny",
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
}),
|
||||
new Document({
|
||||
type: "document",
|
||||
info: decode({
|
||||
permissions: [{ action: "*", resource: "*", effect: "allow" }],
|
||||
agents: {
|
||||
build: {
|
||||
permissions: [
|
||||
{ action: "external_directory", resource: "*", effect: "allow" },
|
||||
{
|
||||
action: "external_directory",
|
||||
resource: "~/.local/share/opencode/*",
|
||||
effect: "deny",
|
||||
},
|
||||
{ action: "read", resource: "*.env", effect: "deny" },
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
}),
|
||||
]
|
||||
|
||||
yield* ConfigAgentPlugin.Plugin.effect(host({ agent: agentHost(agents) })).pipe(
|
||||
Effect.provide(Config.testLayer(entries)),
|
||||
)
|
||||
|
||||
const build = yield* agents.get(Agent.defaultID)
|
||||
if (!build) throw new Error("expected configured build agent")
|
||||
const opencodeData = path.join(global.home, ".local", "share", "opencode", "*")
|
||||
const mcpAuth = path.join(global.home, ".local", "share", "opencode", "mcp-auth.json")
|
||||
expect(build.permissions).toEqual([
|
||||
...defaultPermissions(global),
|
||||
{ action: "question", resource: "*", effect: "allow" },
|
||||
{ action: "shell", resource: "*", effect: "ask" },
|
||||
{ action: "edit", resource: "*", effect: "ask" },
|
||||
{ action: "webfetch", resource: "*", effect: "ask" },
|
||||
{ action: "read", resource: "*", effect: "allow" },
|
||||
{ action: "read", resource: "*.env", effect: "deny" },
|
||||
{ action: "read", resource: "*.env.*", effect: "deny" },
|
||||
{ action: "read", resource: "*.env.example", effect: "allow" },
|
||||
{ action: "read", resource: "*.dev.vars", effect: "deny" },
|
||||
{ action: "read", resource: mcpAuth, effect: "deny" },
|
||||
{ action: "read", resource: mcpAuth, effect: "deny" },
|
||||
{ action: "external_directory", resource: "*", effect: "ask" },
|
||||
{ action: "external_directory", resource: opencodeData, effect: "deny" },
|
||||
{ action: "*", resource: "*", effect: "allow" },
|
||||
{ action: "external_directory", resource: "*", effect: "allow" },
|
||||
{ action: "external_directory", resource: opencodeData, effect: "deny" },
|
||||
{ action: "read", resource: "*.env", effect: "deny" },
|
||||
])
|
||||
expect(Permission.evaluate("shell", "bun test", build.permissions).effect).toBe("allow")
|
||||
expect(Permission.evaluate("edit", "src/index.ts", build.permissions).effect).toBe("allow")
|
||||
expect(Permission.evaluate("webfetch", "https://example.com", build.permissions).effect).toBe("allow")
|
||||
expect(Permission.evaluate("read", ".env", build.permissions).effect).toBe("deny")
|
||||
expect(Permission.evaluate("external_directory", opencodeData, build.permissions).effect).toBe("deny")
|
||||
expect(Permission.evaluate("external_directory", "/outside/*", build.permissions).effect).toBe("allow")
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("applies all global permissions before agent-specific permissions", () =>
|
||||
Effect.gen(function* () {
|
||||
const agents = yield* Agent.Service
|
||||
|
|
|
|||
|
|
@ -307,7 +307,7 @@ describe("Config", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.live("loads authenticated wellknown config below project config", () =>
|
||||
it.live("loads authenticated wellknown config before user configuration", () =>
|
||||
Effect.acquireUseRelease(
|
||||
Effect.promise(() => tmpdir()),
|
||||
(tmp) =>
|
||||
|
|
@ -370,7 +370,13 @@ describe("Config", () => {
|
|||
return yield* Effect.gen(function* () {
|
||||
const config = yield* Config.Service
|
||||
const bus = yield* Bus.Service
|
||||
expect(Config.latest(yield* config.entries(), "shell")).toBe("project")
|
||||
const initial = yield* config.entries()
|
||||
expect(Config.latest(initial, "shell")).toBe("project")
|
||||
expect(
|
||||
initial.flatMap((entry) =>
|
||||
entry.type === "document" && entry.info.shell ? [entry.info.shell] : [],
|
||||
),
|
||||
).toEqual(["secret", "global", "project"])
|
||||
const updated = yield* bus
|
||||
.subscribe(Event.Updated)
|
||||
.pipe(Stream.take(1), Stream.runCollect, Effect.forkScoped)
|
||||
|
|
@ -378,7 +384,13 @@ describe("Config", () => {
|
|||
key = "next"
|
||||
yield* bus.publish(Integration.Event.ConnectionUpdated, { integrationID })
|
||||
expect(yield* Fiber.join(updated)).toHaveLength(1)
|
||||
expect(Config.latest(yield* config.entries(), "shell")).toBe("project")
|
||||
const refreshed = yield* config.entries()
|
||||
expect(Config.latest(refreshed, "shell")).toBe("project")
|
||||
expect(
|
||||
refreshed.flatMap((entry) =>
|
||||
entry.type === "document" && entry.info.shell ? [entry.info.shell] : [],
|
||||
),
|
||||
).toEqual(["next", "global", "project"])
|
||||
}).pipe(
|
||||
Effect.provide(testLayer(project, global, project, undefined, undefined, credentialNode, wellknownNode)),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue