mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-01 18:26:38 +00:00
262 lines
8.3 KiB
TypeScript
262 lines
8.3 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import path from "path"
|
|
import { ApplyPatchTool } from "../../src/tool/apply_patch"
|
|
import { Instance } from "../../src/project/instance"
|
|
import { tmpdir } from "../fixture/fixture"
|
|
import { PermissionNext } from "../../src/permission/next"
|
|
import * as fs from "fs/promises"
|
|
|
|
const ctx = {
|
|
sessionID: "test",
|
|
messageID: "",
|
|
callID: "",
|
|
agent: "build",
|
|
abort: AbortSignal.any([]),
|
|
metadata: () => {},
|
|
ask: async () => {},
|
|
}
|
|
|
|
// const patchTool = await PatchTool.init()
|
|
|
|
// describe("tool.patch", () => {
|
|
// test("should validate required parameters", async () => {
|
|
// await Instance.provide({
|
|
// directory: "/tmp",
|
|
// fn: async () => {
|
|
// expect(patchTool.execute({ patchText: "" }, ctx)).rejects.toThrow("patchText is required")
|
|
// },
|
|
// })
|
|
// })
|
|
|
|
// test("should validate patch format", async () => {
|
|
// await Instance.provide({
|
|
// directory: "/tmp",
|
|
// fn: async () => {
|
|
// expect(patchTool.execute({ patchText: "invalid patch" }, ctx)).rejects.toThrow("Failed to parse patch")
|
|
// },
|
|
// })
|
|
// })
|
|
|
|
// test("should handle empty patch", async () => {
|
|
// await Instance.provide({
|
|
// directory: "/tmp",
|
|
// fn: async () => {
|
|
// const emptyPatch = `*** Begin Patch
|
|
// *** End Patch`
|
|
|
|
// expect(patchTool.execute({ patchText: emptyPatch }, ctx)).rejects.toThrow("No file changes found in patch")
|
|
// },
|
|
// })
|
|
// })
|
|
|
|
// test.skip("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, 1000))
|
|
// const pending = await PermissionNext.list()
|
|
// expect(pending.find((p) => p.sessionID === ctx.sessionID)).toBeDefined()
|
|
// },
|
|
// })
|
|
// })
|
|
|
|
// test("should handle simple add file operation", async () => {
|
|
// await using fixture = await tmpdir()
|
|
|
|
// await Instance.provide({
|
|
// directory: fixture.path,
|
|
// fn: async () => {
|
|
// const patchText = `*** Begin Patch
|
|
// *** Add File: test-file.txt
|
|
// +Hello World
|
|
// +This is a test file
|
|
// *** End Patch`
|
|
|
|
// const result = await patchTool.execute({ patchText }, ctx)
|
|
|
|
// expect(result.title).toContain("files changed")
|
|
// expect(result.metadata.diff).toBeDefined()
|
|
// expect(result.output).toContain("Patch applied successfully")
|
|
|
|
// // Verify file was created
|
|
// const filePath = path.join(fixture.path, "test-file.txt")
|
|
// const content = await fs.readFile(filePath, "utf-8")
|
|
// expect(content).toBe("Hello World\nThis is a test file")
|
|
// },
|
|
// })
|
|
// })
|
|
|
|
// test("should handle file with context update", async () => {
|
|
// await using fixture = await tmpdir()
|
|
|
|
// await Instance.provide({
|
|
// directory: fixture.path,
|
|
// fn: async () => {
|
|
// const patchText = `*** Begin Patch
|
|
// *** Add File: config.js
|
|
// +const API_KEY = "test-key"
|
|
// +const DEBUG = false
|
|
// +const VERSION = "1.0"
|
|
// *** End Patch`
|
|
|
|
// const result = await patchTool.execute({ patchText }, ctx)
|
|
|
|
// expect(result.title).toContain("files changed")
|
|
// expect(result.metadata.diff).toBeDefined()
|
|
// expect(result.output).toContain("Patch applied successfully")
|
|
|
|
// // Verify file was created with correct content
|
|
// const filePath = path.join(fixture.path, "config.js")
|
|
// const content = await fs.readFile(filePath, "utf-8")
|
|
// expect(content).toBe('const API_KEY = "test-key"\nconst DEBUG = false\nconst VERSION = "1.0"')
|
|
// },
|
|
// })
|
|
// })
|
|
|
|
// test("should handle multiple file operations", async () => {
|
|
// await using fixture = await tmpdir()
|
|
|
|
// await Instance.provide({
|
|
// directory: fixture.path,
|
|
// fn: async () => {
|
|
// const patchText = `*** Begin Patch
|
|
// *** Add File: file1.txt
|
|
// +Content of file 1
|
|
// *** Add File: file2.txt
|
|
// +Content of file 2
|
|
// *** Add File: file3.txt
|
|
// +Content of file 3
|
|
// *** End Patch`
|
|
|
|
// const result = await patchTool.execute({ patchText }, ctx)
|
|
|
|
// expect(result.title).toContain("3 files changed")
|
|
// expect(result.metadata.diff).toBeDefined()
|
|
// expect(result.output).toContain("Patch applied successfully")
|
|
|
|
// // Verify all files were created
|
|
// for (let i = 1; i <= 3; i++) {
|
|
// const filePath = path.join(fixture.path, `file${i}.txt`)
|
|
// const content = await fs.readFile(filePath, "utf-8")
|
|
// expect(content).toBe(`Content of file ${i}`)
|
|
// }
|
|
// },
|
|
// })
|
|
// })
|
|
|
|
// test("should create parent directories when adding nested files", async () => {
|
|
// await using fixture = await tmpdir()
|
|
|
|
// await Instance.provide({
|
|
// directory: fixture.path,
|
|
// fn: async () => {
|
|
// const patchText = `*** Begin Patch
|
|
// *** Add File: deep/nested/file.txt
|
|
// +Deep nested content
|
|
// *** End Patch`
|
|
|
|
// const result = await patchTool.execute({ patchText }, ctx)
|
|
|
|
// expect(result.title).toContain("files changed")
|
|
// expect(result.output).toContain("Patch applied successfully")
|
|
|
|
// // Verify nested file was created
|
|
// const nestedPath = path.join(fixture.path, "deep", "nested", "file.txt")
|
|
// const exists = await fs
|
|
// .access(nestedPath)
|
|
// .then(() => true)
|
|
// .catch(() => false)
|
|
// expect(exists).toBe(true)
|
|
|
|
// const content = await fs.readFile(nestedPath, "utf-8")
|
|
// expect(content).toBe("Deep nested content")
|
|
// },
|
|
// })
|
|
// })
|
|
|
|
// test("should generate proper unified diff in metadata", async () => {
|
|
// await using fixture = await tmpdir()
|
|
|
|
// await Instance.provide({
|
|
// directory: fixture.path,
|
|
// fn: async () => {
|
|
// // First create a file with simple content
|
|
// const patchText1 = `*** Begin Patch
|
|
// *** Add File: test.txt
|
|
// +line 1
|
|
// +line 2
|
|
// +line 3
|
|
// *** End Patch`
|
|
|
|
// await patchTool.execute({ patchText: patchText1 }, ctx)
|
|
|
|
// // Now create an update patch
|
|
// const patchText2 = `*** Begin Patch
|
|
// *** Update File: test.txt
|
|
// @@
|
|
// line 1
|
|
// -line 2
|
|
// +line 2 updated
|
|
// line 3
|
|
// *** End Patch`
|
|
|
|
// const result = await patchTool.execute({ patchText: patchText2 }, ctx)
|
|
|
|
// expect(result.metadata.diff).toBeDefined()
|
|
// expect(result.metadata.diff).toContain("@@")
|
|
// expect(result.metadata.diff).toContain("-line 2")
|
|
// expect(result.metadata.diff).toContain("+line 2 updated")
|
|
// },
|
|
// })
|
|
// })
|
|
|
|
// test("should handle complex patch with multiple operations", async () => {
|
|
// await using fixture = await tmpdir()
|
|
|
|
// await Instance.provide({
|
|
// directory: fixture.path,
|
|
// fn: async () => {
|
|
// const patchText = `*** Begin Patch
|
|
// *** Add File: new.txt
|
|
// +This is a new file
|
|
// +with multiple lines
|
|
// *** Add File: existing.txt
|
|
// +old content
|
|
// +new line
|
|
// +more content
|
|
// *** Add File: config.json
|
|
// +{
|
|
// + "version": "1.0",
|
|
// + "debug": true
|
|
// +}
|
|
// *** End Patch`
|
|
|
|
// const result = await patchTool.execute({ patchText }, ctx)
|
|
|
|
// expect(result.title).toContain("3 files changed")
|
|
// expect(result.metadata.diff).toBeDefined()
|
|
// expect(result.output).toContain("Patch applied successfully")
|
|
|
|
// // Verify all files were created
|
|
// const newPath = path.join(fixture.path, "new.txt")
|
|
// const newContent = await fs.readFile(newPath, "utf-8")
|
|
// expect(newContent).toBe("This is a new file\nwith multiple lines")
|
|
|
|
// const existingPath = path.join(fixture.path, "existing.txt")
|
|
// const existingContent = await fs.readFile(existingPath, "utf-8")
|
|
// expect(existingContent).toBe("old content\nnew line\nmore content")
|
|
|
|
// const configPath = path.join(fixture.path, "config.json")
|
|
// const configContent = await fs.readFile(configPath, "utf-8")
|
|
// expect(configContent).toBe('{\n "version": "1.0",\n "debug": true\n}')
|
|
// },
|
|
// })
|
|
// })
|
|
// })
|