mirror of
https://github.com/anomalyco/opencode.git
synced 2026-04-29 01:07:04 +00:00
185 lines
4.4 KiB
TypeScript
185 lines
4.4 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { applyHashlineEdits, hashlineID, hashlineLine, hashlineRef, parseHashlineRef } from "../../src/tool/hashline"
|
|
|
|
function swapID(ref: string) {
|
|
const [line, id] = ref.split("#")
|
|
const next = id[0] === "Z" ? `P${id[1]}` : `Z${id[1]}`
|
|
return `${line}#${next}`
|
|
}
|
|
|
|
describe("tool.hashline", () => {
|
|
test("hash computation is stable and 2-char alphabet encoded", () => {
|
|
const a = hashlineID(1, " const x = 1")
|
|
const b = hashlineID(1, "constx=1")
|
|
const c = hashlineID(99, "constx=1")
|
|
expect(a).toBe(b)
|
|
expect(a).toBe(c)
|
|
expect(a).toMatch(/^[ZPMQVRWSNKTXJBYH]{2}$/)
|
|
})
|
|
|
|
test("autocorrect strips copied hashline prefixes when enabled", () => {
|
|
const old = Bun.env.OPENCODE_HL_AUTOCORRECT
|
|
Bun.env.OPENCODE_HL_AUTOCORRECT = "1"
|
|
try {
|
|
const result = applyHashlineEdits({
|
|
lines: ["a"],
|
|
trailing: false,
|
|
edits: [
|
|
{
|
|
type: "set_line",
|
|
line: hashlineRef(1, "a"),
|
|
text: hashlineLine(1, "a"),
|
|
},
|
|
],
|
|
})
|
|
expect(result.lines).toEqual(["a"])
|
|
} finally {
|
|
if (old === undefined) delete Bun.env.OPENCODE_HL_AUTOCORRECT
|
|
else Bun.env.OPENCODE_HL_AUTOCORRECT = old
|
|
}
|
|
})
|
|
|
|
test("parses strict LINE#ID references with tolerant extraction", () => {
|
|
const ref = parseHashlineRef(">>> 12#ZP:const value = 1", "line")
|
|
expect(ref.line).toBe(12)
|
|
expect(ref.id).toBe("ZP")
|
|
expect(ref.raw).toBe("12#ZP")
|
|
|
|
expect(() => parseHashlineRef("12#ab", "line")).toThrow("LINE#ID")
|
|
})
|
|
|
|
test("aggregates mismatch errors with >>> context and retry refs", () => {
|
|
const lines = ["alpha", "beta", "gamma"]
|
|
const wrong = swapID(hashlineRef(2, lines[1]))
|
|
|
|
expect(() =>
|
|
applyHashlineEdits({
|
|
lines,
|
|
trailing: false,
|
|
edits: [
|
|
{
|
|
type: "set_line",
|
|
line: wrong,
|
|
text: "BETA",
|
|
},
|
|
],
|
|
}),
|
|
).toThrow("changed since last read")
|
|
|
|
expect(() =>
|
|
applyHashlineEdits({
|
|
lines,
|
|
trailing: false,
|
|
edits: [
|
|
{
|
|
type: "set_line",
|
|
line: wrong,
|
|
text: "BETA",
|
|
},
|
|
],
|
|
}),
|
|
).toThrow(">>> retry with")
|
|
})
|
|
|
|
test("applies batched line edits bottom-up for stable results", () => {
|
|
const lines = ["a", "b", "c", "d"]
|
|
const one = hashlineRef(1, lines[0])
|
|
const two = hashlineRef(2, lines[1])
|
|
const three = hashlineRef(3, lines[2])
|
|
const four = hashlineRef(4, lines[3])
|
|
|
|
const result = applyHashlineEdits({
|
|
lines,
|
|
trailing: false,
|
|
edits: [
|
|
{
|
|
type: "replace_lines",
|
|
start_line: two,
|
|
end_line: three,
|
|
text: ["B", "C"],
|
|
},
|
|
{
|
|
type: "insert_after",
|
|
line: one,
|
|
text: "A1",
|
|
},
|
|
{
|
|
type: "set_line",
|
|
line: four,
|
|
text: "D",
|
|
},
|
|
],
|
|
})
|
|
|
|
expect(result.lines).toEqual(["a", "A1", "B", "C", "D"])
|
|
})
|
|
|
|
test("orders append and prepend deterministically on empty files", () => {
|
|
const result = applyHashlineEdits({
|
|
lines: [],
|
|
trailing: false,
|
|
edits: [
|
|
{
|
|
type: "append",
|
|
text: "end",
|
|
},
|
|
{
|
|
type: "prepend",
|
|
text: "start",
|
|
},
|
|
],
|
|
})
|
|
|
|
expect(result.lines).toEqual(["start", "end"])
|
|
})
|
|
|
|
test("validates ranges, between constraints, and non-empty insert text", () => {
|
|
const lines = ["a", "b", "c"]
|
|
const one = hashlineRef(1, lines[0])
|
|
const two = hashlineRef(2, lines[1])
|
|
|
|
expect(() =>
|
|
applyHashlineEdits({
|
|
lines,
|
|
trailing: false,
|
|
edits: [
|
|
{
|
|
type: "replace_lines",
|
|
start_line: two,
|
|
end_line: one,
|
|
text: "x",
|
|
},
|
|
],
|
|
}),
|
|
).toThrow("start_line")
|
|
|
|
expect(() =>
|
|
applyHashlineEdits({
|
|
lines,
|
|
trailing: false,
|
|
edits: [
|
|
{
|
|
type: "insert_between",
|
|
after_line: two,
|
|
before_line: one,
|
|
text: "x",
|
|
},
|
|
],
|
|
}),
|
|
).toThrow("insert_between.after_line")
|
|
|
|
expect(() =>
|
|
applyHashlineEdits({
|
|
lines,
|
|
trailing: false,
|
|
edits: [
|
|
{
|
|
type: "append",
|
|
text: "",
|
|
},
|
|
],
|
|
}),
|
|
).toThrow("append.text")
|
|
})
|
|
})
|