fix tests

This commit is contained in:
Dax Raad
2025-11-06 11:42:46 -05:00
parent 065f656fb0
commit 67f3c934fe
2 changed files with 53 additions and 10 deletions

View File

@@ -75,12 +75,23 @@ export namespace Permission {
async (state) => { async (state) => {
for (const pending of Object.values(state.pending)) { for (const pending of Object.values(state.pending)) {
for (const item of Object.values(pending)) { for (const item of Object.values(pending)) {
item.reject(new RejectedError(item.info.sessionID, item.info.id, item.info.callID, item.info.metadata)) item.reject(
new RejectedError(
item.info.sessionID,
item.info.id,
item.info.callID,
item.info.metadata,
),
)
} }
} }
}, },
) )
export function pending() {
return state().pending
}
export async function ask(input: { export async function ask(input: {
type: Info["type"] type: Info["type"]
title: Info["title"] title: Info["title"]
@@ -139,7 +150,11 @@ export namespace Permission {
export const Response = z.enum(["once", "always", "reject"]) export const Response = z.enum(["once", "always", "reject"])
export type Response = z.infer<typeof Response> export type Response = z.infer<typeof Response>
export function respond(input: { sessionID: Info["sessionID"]; permissionID: Info["id"]; response: Response }) { export function respond(input: {
sessionID: Info["sessionID"]
permissionID: Info["id"]
response: Response
}) {
log.info("response", input) log.info("response", input)
const { pending, approved } = state() const { pending, approved } = state()
const match = pending[input.sessionID]?.[input.permissionID] const match = pending[input.sessionID]?.[input.permissionID]
@@ -151,7 +166,14 @@ export namespace Permission {
response: input.response, response: input.response,
}) })
if (input.response === "reject") { if (input.response === "reject") {
match.reject(new RejectedError(input.sessionID, input.permissionID, match.info.callID, match.info.metadata)) match.reject(
new RejectedError(
input.sessionID,
input.permissionID,
match.info.callID,
match.info.metadata,
),
)
return return
} }
match.resolve() match.resolve()
@@ -166,7 +188,11 @@ export namespace Permission {
for (const item of Object.values(items)) { for (const item of Object.values(items)) {
const itemKeys = toKeys(item.info.pattern, item.info.type) const itemKeys = toKeys(item.info.pattern, item.info.type)
if (covered(itemKeys, approved[input.sessionID])) { if (covered(itemKeys, approved[input.sessionID])) {
respond({ sessionID: item.info.sessionID, permissionID: item.info.id, response: input.response }) respond({
sessionID: item.info.sessionID,
permissionID: item.info.id,
response: input.response,
})
} }
} }
} }
@@ -179,7 +205,9 @@ export namespace Permission {
public readonly toolCallID?: string, public readonly toolCallID?: string,
public readonly metadata?: Record<string, any>, public readonly metadata?: Record<string, any>,
) { ) {
super(`The user rejected permission to use this specific tool call. You may try again with different parameters.`) super(
`The user rejected permission to use this specific tool call. You may try again with different parameters.`,
)
} }
} }
} }

View File

@@ -3,6 +3,7 @@ import path from "path"
import { PatchTool } from "../../src/tool/patch" import { PatchTool } from "../../src/tool/patch"
import { Instance } from "../../src/project/instance" import { Instance } from "../../src/project/instance"
import { tmpdir } from "../fixture/fixture" import { tmpdir } from "../fixture/fixture"
import { Permission } from "../../src/permission"
import * as fs from "fs/promises" import * as fs from "fs/promises"
const ctx = { const ctx = {
@@ -21,9 +22,7 @@ describe("tool.patch", () => {
await Instance.provide({ await Instance.provide({
directory: "/tmp", directory: "/tmp",
fn: async () => { fn: async () => {
await expect(patchTool.execute({ patchText: "" }, ctx)).rejects.toThrow( expect(patchTool.execute({ patchText: "" }, ctx)).rejects.toThrow("patchText is required")
"patchText is required",
)
}, },
}) })
}) })
@@ -32,7 +31,7 @@ describe("tool.patch", () => {
await Instance.provide({ await Instance.provide({
directory: "/tmp", directory: "/tmp",
fn: async () => { fn: async () => {
await expect(patchTool.execute({ patchText: "invalid patch" }, ctx)).rejects.toThrow( expect(patchTool.execute({ patchText: "invalid patch" }, ctx)).rejects.toThrow(
"Failed to parse patch", "Failed to parse patch",
) )
}, },
@@ -46,13 +45,29 @@ describe("tool.patch", () => {
const emptyPatch = `*** Begin Patch const emptyPatch = `*** Begin Patch
*** End Patch` *** End Patch`
await expect(patchTool.execute({ patchText: emptyPatch }, ctx)).rejects.toThrow( expect(patchTool.execute({ patchText: emptyPatch }, ctx)).rejects.toThrow(
"No file changes found in patch", "No file changes found in patch",
) )
}, },
}) })
}) })
test("should ask permission for files outside working directory", async () => {
await Instance.provide({
directory: "/tmp",
fn: async () => {
const maliciousPatch = `*** Begin Patch
*** Add File: /etc/passwd
+malicious content
*** End Patch`
patchTool.execute({ patchText: maliciousPatch }, ctx)
// TODO: this sucks
await new Promise((resolve) => setTimeout(resolve, 100))
expect(Permission.pending()[ctx.sessionID]).toBeDefined()
},
})
})
test("should handle simple add file operation", async () => { test("should handle simple add file operation", async () => {
await using fixture = await tmpdir() await using fixture = await tmpdir()