mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-03-30 13:54:01 +00:00
233 lines
7.1 KiB
TypeScript
233 lines
7.1 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import path from "path"
|
|
import { BashTool } from "../../src/tool/bash"
|
|
import { Instance } from "../../src/project/instance"
|
|
import { tmpdir } from "../fixture/fixture"
|
|
import type { PermissionNext } from "../../src/permission/next"
|
|
|
|
const ctx = {
|
|
sessionID: "test",
|
|
messageID: "",
|
|
callID: "",
|
|
agent: "build",
|
|
abort: AbortSignal.any([]),
|
|
metadata: () => {},
|
|
ask: async () => {},
|
|
}
|
|
|
|
const projectRoot = path.join(__dirname, "../..")
|
|
|
|
describe("tool.bash", () => {
|
|
test("basic", async () => {
|
|
await Instance.provide({
|
|
directory: projectRoot,
|
|
fn: async () => {
|
|
const bash = await BashTool.init()
|
|
const result = await bash.execute(
|
|
{
|
|
command: "echo 'test'",
|
|
description: "Echo test message",
|
|
},
|
|
ctx,
|
|
)
|
|
expect(result.metadata.exit).toBe(0)
|
|
expect(result.metadata.output).toContain("test")
|
|
},
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("tool.bash permissions", () => {
|
|
test("asks for bash permission with correct pattern", async () => {
|
|
await using tmp = await tmpdir({ git: true })
|
|
await Instance.provide({
|
|
directory: tmp.path,
|
|
fn: async () => {
|
|
const bash = await BashTool.init()
|
|
const requests: Array<Omit<PermissionNext.Request, "id" | "sessionID" | "tool">> = []
|
|
const testCtx = {
|
|
...ctx,
|
|
ask: async (req: Omit<PermissionNext.Request, "id" | "sessionID" | "tool">) => {
|
|
requests.push(req)
|
|
},
|
|
}
|
|
await bash.execute(
|
|
{
|
|
command: "echo hello",
|
|
description: "Echo hello",
|
|
},
|
|
testCtx,
|
|
)
|
|
expect(requests.length).toBe(1)
|
|
expect(requests[0].permission).toBe("bash")
|
|
expect(requests[0].patterns).toContain("echo hello")
|
|
},
|
|
})
|
|
})
|
|
|
|
test("asks for bash permission with multiple commands", async () => {
|
|
await using tmp = await tmpdir({ git: true })
|
|
await Instance.provide({
|
|
directory: tmp.path,
|
|
fn: async () => {
|
|
const bash = await BashTool.init()
|
|
const requests: Array<Omit<PermissionNext.Request, "id" | "sessionID" | "tool">> = []
|
|
const testCtx = {
|
|
...ctx,
|
|
ask: async (req: Omit<PermissionNext.Request, "id" | "sessionID" | "tool">) => {
|
|
requests.push(req)
|
|
},
|
|
}
|
|
await bash.execute(
|
|
{
|
|
command: "echo foo && echo bar",
|
|
description: "Echo twice",
|
|
},
|
|
testCtx,
|
|
)
|
|
expect(requests.length).toBe(1)
|
|
expect(requests[0].permission).toBe("bash")
|
|
expect(requests[0].patterns).toContain("echo foo")
|
|
expect(requests[0].patterns).toContain("echo bar")
|
|
},
|
|
})
|
|
})
|
|
|
|
test("asks for external_directory permission when cd to parent", async () => {
|
|
await using tmp = await tmpdir({ git: true })
|
|
await Instance.provide({
|
|
directory: tmp.path,
|
|
fn: async () => {
|
|
const bash = await BashTool.init()
|
|
const requests: Array<Omit<PermissionNext.Request, "id" | "sessionID" | "tool">> = []
|
|
const testCtx = {
|
|
...ctx,
|
|
ask: async (req: Omit<PermissionNext.Request, "id" | "sessionID" | "tool">) => {
|
|
requests.push(req)
|
|
},
|
|
}
|
|
await bash.execute(
|
|
{
|
|
command: "cd ../",
|
|
description: "Change to parent directory",
|
|
},
|
|
testCtx,
|
|
)
|
|
const extDirReq = requests.find((r) => r.permission === "external_directory")
|
|
expect(extDirReq).toBeDefined()
|
|
},
|
|
})
|
|
})
|
|
|
|
test("asks for external_directory permission when workdir is outside project", async () => {
|
|
await using tmp = await tmpdir({ git: true })
|
|
await Instance.provide({
|
|
directory: tmp.path,
|
|
fn: async () => {
|
|
const bash = await BashTool.init()
|
|
const requests: Array<Omit<PermissionNext.Request, "id" | "sessionID" | "tool">> = []
|
|
const testCtx = {
|
|
...ctx,
|
|
ask: async (req: Omit<PermissionNext.Request, "id" | "sessionID" | "tool">) => {
|
|
requests.push(req)
|
|
},
|
|
}
|
|
await bash.execute(
|
|
{
|
|
command: "ls",
|
|
workdir: "/tmp",
|
|
description: "List /tmp",
|
|
},
|
|
testCtx,
|
|
)
|
|
const extDirReq = requests.find((r) => r.permission === "external_directory")
|
|
expect(extDirReq).toBeDefined()
|
|
expect(extDirReq!.patterns).toContain("/tmp")
|
|
},
|
|
})
|
|
})
|
|
|
|
test("does not ask for external_directory permission when rm inside project", async () => {
|
|
await using tmp = await tmpdir({ git: true })
|
|
await Instance.provide({
|
|
directory: tmp.path,
|
|
fn: async () => {
|
|
const bash = await BashTool.init()
|
|
const requests: Array<Omit<PermissionNext.Request, "id" | "sessionID" | "tool">> = []
|
|
const testCtx = {
|
|
...ctx,
|
|
ask: async (req: Omit<PermissionNext.Request, "id" | "sessionID" | "tool">) => {
|
|
requests.push(req)
|
|
},
|
|
}
|
|
|
|
await Bun.write(path.join(tmp.path, "tmpfile"), "x")
|
|
|
|
await bash.execute(
|
|
{
|
|
command: "rm tmpfile",
|
|
description: "Remove tmpfile",
|
|
},
|
|
testCtx,
|
|
)
|
|
|
|
const extDirReq = requests.find((r) => r.permission === "external_directory")
|
|
expect(extDirReq).toBeUndefined()
|
|
},
|
|
})
|
|
})
|
|
|
|
test("includes always patterns for auto-approval", async () => {
|
|
await using tmp = await tmpdir({ git: true })
|
|
await Instance.provide({
|
|
directory: tmp.path,
|
|
fn: async () => {
|
|
const bash = await BashTool.init()
|
|
const requests: Array<Omit<PermissionNext.Request, "id" | "sessionID" | "tool">> = []
|
|
const testCtx = {
|
|
...ctx,
|
|
ask: async (req: Omit<PermissionNext.Request, "id" | "sessionID" | "tool">) => {
|
|
requests.push(req)
|
|
},
|
|
}
|
|
await bash.execute(
|
|
{
|
|
command: "git log --oneline -5",
|
|
description: "Git log",
|
|
},
|
|
testCtx,
|
|
)
|
|
expect(requests.length).toBe(1)
|
|
expect(requests[0].always.length).toBeGreaterThan(0)
|
|
expect(requests[0].always.some((p) => p.endsWith("*"))).toBe(true)
|
|
},
|
|
})
|
|
})
|
|
|
|
test("does not ask for bash permission when command is cd only", async () => {
|
|
await using tmp = await tmpdir({ git: true })
|
|
await Instance.provide({
|
|
directory: tmp.path,
|
|
fn: async () => {
|
|
const bash = await BashTool.init()
|
|
const requests: Array<Omit<PermissionNext.Request, "id" | "sessionID" | "tool">> = []
|
|
const testCtx = {
|
|
...ctx,
|
|
ask: async (req: Omit<PermissionNext.Request, "id" | "sessionID" | "tool">) => {
|
|
requests.push(req)
|
|
},
|
|
}
|
|
await bash.execute(
|
|
{
|
|
command: "cd .",
|
|
description: "Stay in current directory",
|
|
},
|
|
testCtx,
|
|
)
|
|
const bashReq = requests.find((r) => r.permission === "bash")
|
|
expect(bashReq).toBeUndefined()
|
|
},
|
|
})
|
|
})
|
|
})
|