Files
oh-my-pi/packages/coding-agent/test/core/chunk-tree.test.ts
T
can1357 6bf09bedb0 refactor(chunk): restructured language classifiers to declarative rule tables
- Migrated language classifiers from imperative methods to declarative semantic rule tables with ClassifierTables and StructuralOverrides.
- Extracted node shape analysis into dedicated shape module with field priority constants and helper functions for AST traversal.
- Added schema module with language-aware node metadata and thread-local language context management.
- Centralized environment variable parsing across codebase using $flag(), $envpos(), and isBunTestRuntime() utilities.
- Added PI_CHUNK_AUTOINDENT configuration to control indentation normalization in chunk read/edit operations.
- Enhanced system prompt with instruction priority, output contract, tool persistence, and completeness guidelines.
2026-04-10 13:33:00 +02:00

1190 lines
39 KiB
TypeScript

import { afterEach, describe, expect, test } from "bun:test";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import { ChunkState } from "@oh-my-pi/pi-natives";
import { applyChunkEdits, formatChunkedRead, parseChunkReadPath } from "../../src/edit/modes/chunk";
// ═══════════════════════════════════════════════════════════════════════════
// parseChunkReadPath
// ═══════════════════════════════════════════════════════════════════════════
describe("parseChunkReadPath", () => {
test("plain file path returns filePath only", () => {
expect(parseChunkReadPath("file.ts")).toEqual({ filePath: "file.ts" });
});
test("path with chunk selector returns both", () => {
expect(parseChunkReadPath("file.ts:class_Foo")).toEqual({
filePath: "file.ts",
selector: "class_Foo",
});
});
test("path with raw line selector", () => {
expect(parseChunkReadPath("file.ts:L42")).toEqual({
filePath: "file.ts",
selector: "L42",
});
});
test("path with chunk checksum suffix normalizes selector", () => {
expect(parseChunkReadPath("file.ts:class_Foo.fn_bar#ZZPM")).toEqual({
filePath: "file.ts",
selector: "class_Foo.fn_bar#ZZPM",
});
});
test("trailing colon with no selector yields undefined selector", () => {
const result = parseChunkReadPath("file.ts:");
expect(result.filePath).toBe("file.ts");
expect(result.selector).toBeUndefined();
});
});
// ═══════════════════════════════════════════════════════════════════════════
// applyChunkEdits
// ═══════════════════════════════════════════════════════════════════════════
const testSource = `class Worker {
\tconstructor(name: string) {
\t\tthis.name = name;
\t}
\trun(): void {
\t\tconsole.log(this.name);
\t}
}
`;
let tmpDir: string | undefined;
afterEach(async () => {
if (tmpDir) {
await fs.rm(tmpDir, { recursive: true, force: true });
tmpDir = undefined;
}
});
function edit(operations: Parameters<typeof applyChunkEdits>[0]["operations"], source = testSource) {
return applyEdit({
source,
language: "typescript",
operations,
});
}
function applyEdit(params: {
source: string;
language: string;
operations: Parameters<typeof applyChunkEdits>[0]["operations"];
filePath?: string;
}) {
return applyChunkEdits({
source: params.source,
language: params.language,
cwd: "/tmp",
filePath: params.filePath ?? `/tmp/source.${params.language}`,
operations: params.operations,
});
}
function getChecksum(source: string, chunkPath: string, language = "typescript"): string {
const chunk = ChunkState.parse(source, language).chunk(chunkPath);
if (!chunk) {
throw new Error(`Chunk not found in test fixture: ${chunkPath}`);
}
return chunk.checksum;
}
function targetWithChecksum(chunkPath: string, checksum: string, region?: "head" | "body" | "tail"): string {
return `${chunkPath}#${checksum}${region ? `@${region}` : ""}`;
}
function currentTarget(
source: string,
chunkPath: string,
language = "typescript",
region?: "head" | "body" | "tail",
): string {
return targetWithChecksum(chunkPath, getChecksum(source, chunkPath, language), region);
}
function bodyTarget(chunkPath: string): string {
return `${chunkPath}@body`;
}
describe("applyChunkEdits", () => {
test("replace accepts a copied chunk header with checksum suffix", () => {
const originalChecksum = getChecksum(testSource, "class_Worker.fn_run");
const ac = { sel: targetWithChecksum("class_Worker.fn_run", originalChecksum) };
const result = edit([
{
op: "replace",
...ac,
content: `\tstop(): void {\n\t\tconsole.log("stopped");\n\t}`,
},
]);
expect(result.diffSourceAfter).toContain("stop()");
expect(result.diffSourceAfter).not.toContain("run()");
const newChecksum = getChecksum(result.diffSourceAfter, "class_Worker.fn_stop");
expect(newChecksum).not.toBe(originalChecksum);
});
test("replace with wrong checksum throws with mismatch", () => {
expect(() =>
edit([
{
op: "replace",
sel: targetWithChecksum("class_Worker.fn_run", "ZZZZ"),
content: "replacement",
},
]),
).toThrow(/Checksum mismatch/);
});
test("append on a class body inserts after existing members", () => {
const result = edit([
{
op: "append",
sel: bodyTarget("class_Worker"),
content: `\tstatus(): string {\n\t\treturn "active";\n\t}`,
},
]);
const after = result.diffSourceAfter;
expect(after).toContain("status()");
const runPos = after.indexOf("run()");
const statusPos = after.indexOf("status()");
expect(statusPos).toBeGreaterThan(runPos);
expect(after).toContain("class Worker");
});
test("append on an empty container body inserts inside the container", () => {
const result = edit(
[
{
op: "append",
sel: bodyTarget("class_Empty"),
content: "method(): void {}\n",
},
],
"class Empty {}\n",
);
expect(result.diffSourceAfter).toContain("class Empty {");
expect(result.diffSourceAfter).toContain("method(): void {}");
expect(result.diffSourceAfter).toMatch(/class Empty \{[\s\S]*method\(\): void \{\}[\s\S]*\}/);
expect(result.diffSourceAfter).not.toEndWith("}\nmethod(): void {}\n");
});
test("replace with empty content removes the target chunk", () => {
const ac = { sel: currentTarget(testSource, "class_Worker.fn_run") };
const result = edit([{ op: "replace", ...ac, content: "" }]);
expect(result.diffSourceAfter).not.toContain("run()");
expect(result.diffSourceAfter).toContain("constructor");
});
test("replace does not duplicate attached doc comments when replacement includes a new one", () => {
const source = `class Worker {\n\t/** restart note */\n\trestart(): void {\n\t\tboot();\n\t}\n}\n`;
const checksum = getChecksum(source, "class_Worker.fn_restart");
const result = edit(
[
{
op: "replace",
sel: targetWithChecksum("class_Worker.fn_restart", checksum),
content: `\t/** updated restart note */\n\trestart(): void {\n\t\tshutdown();\n\t}`,
},
],
source,
);
expect(result.diffSourceAfter).toContain("\t/** updated restart note */\n\trestart(): void {");
expect(result.diffSourceAfter).not.toContain("/** restart note */");
expect(result.diffSourceAfter.match(/updated restart note/g)).toHaveLength(1);
});
test("sibling chunk crc from before the batch still validates after an unrelated sibling is replaced first", () => {
const ctorCrc = getChecksum(testSource, "class_Worker.constructor");
const runCrc = getChecksum(testSource, "class_Worker.fn_run");
const result = applyChunkEdits({
source: testSource,
language: "typescript",
cwd: "/tmp",
filePath: "/tmp/worker.ts",
operations: [
{
op: "replace",
sel: targetWithChecksum("class_Worker.constructor", ctorCrc),
content: `\tconstructor(name: string) {\n\t\tthis.name = name.trim();\n\t}`,
},
{
op: "replace",
sel: targetWithChecksum("class_Worker.fn_run", runCrc),
content: `\trun(): void {\n\t\tconsole.log(this.name + "!");\n\t}`,
},
],
});
expect(result.parseValid).toBe(true);
expect(result.diffSourceAfter).toContain("name.trim()");
expect(result.diffSourceAfter).toMatch(/this\.name\s*\+\s*"!"/);
});
test("prepend on a class body inserts before existing members", () => {
const result = edit([
{
op: "prepend",
sel: bodyTarget("class_Worker"),
content: `\tid = 0;`,
},
]);
const after = result.diffSourceAfter;
expect(after).toContain("id = 0");
const idPos = after.indexOf("id = 0");
const ctorPos = after.indexOf("constructor");
expect(idPos).toBeGreaterThan(-1);
expect(idPos).toBeLessThan(ctorPos);
});
});
describe("insertion boundaries", () => {
test("keeps prepend separated from the first existing TypeScript class member", () => {
const source = `class Box<T> {\n value(): T {\n return this.current;\n }\n}\n`;
const result = applyEdit({
source,
language: "typescript",
filePath: "/tmp/box.ts",
operations: [
{
op: "prepend",
sel: bodyTarget("class_Box"),
content: ` items(): T[] {\n return [];\n }`,
},
],
});
expect(result.diffSourceAfter).toContain(" }\n\n value(): T {");
expect(result.diffSourceAfter).not.toContain("}\n value(): T {");
});
test("keeps prepend separated from the first existing Rust impl member", () => {
const source = `impl Widget {\n fn old(&self) -> bool {\n true\n }\n}\n`;
const result = applyEdit({
source,
language: "rust",
filePath: "/tmp/widget.rs",
operations: [
{
op: "prepend",
sel: bodyTarget("impl_Widget"),
content: ` fn build(&self) -> bool {\n false\n }`,
},
],
});
expect(result.diffSourceAfter).toContain(" }\n\n fn old(&self) -> bool {");
expect(result.diffSourceAfter).not.toContain("}\n fn old(&self) -> bool {");
});
test("keeps before separated before a Go top-level function", () => {
const source = `package main\n\nfunc format() {}\n`;
const result = applyEdit({
source,
language: "go",
filePath: "/tmp/format.go",
operations: [
{
op: "before",
sel: "fn_format",
content: "func formatLog() {}",
},
],
});
expect(result.diffSourceAfter).toContain("func formatLog() {}\n\nfunc format() {}");
expect(result.diffSourceAfter).not.toContain("func formatLog() {}\nfunc format() {}");
});
test("keeps after separated from the next Go top-level function", () => {
const source = `package main\n\nfunc first() {}\nfunc second() {}\n`;
const result = applyEdit({
source,
language: "go",
filePath: "/tmp/functions.go",
operations: [
{
op: "after",
sel: "fn_first",
content: "func middle() {}",
},
],
});
expect(result.diffSourceAfter).toContain("func first() {}\n\nfunc middle() {}\n\nfunc second() {}");
expect(result.diffSourceAfter).not.toContain("func middle() {}\nfunc second() {}");
});
test("append on a Go receiver type container inserts after the last receiver method", () => {
const source = `package main\n\ntype Server struct {}\n\nfunc (s *Server) Start() {}\nfunc (s *Server) Stop() {}\n`;
const result = applyEdit({
source,
language: "go",
filePath: "/tmp/server.go",
operations: [
{
op: "append",
sel: "type_Server",
content: "func (s *Server) Restart() {}",
},
],
});
expect(result.diffSourceAfter).toContain(
"func (s *Server) Start() {}\nfunc (s *Server) Stop() {}\n\nfunc (s *Server) Restart() {}",
);
expect(result.diffSourceAfter).not.toContain("type Server struct {\nfunc (s *Server) Restart() {}");
});
test("append on Go type_Server container with struct fields still inserts file-scope func at column 0", () => {
const source = `package main
type Server struct {
Addr string
}
`;
const result = applyEdit({
source,
language: "go",
filePath: "/tmp/server.go",
operations: [
{
op: "append",
sel: "type_Server",
content: "func (s *Server) Ping() {}",
},
],
});
expect(result.parseValid).toBe(true);
expect(result.diffSourceAfter).toContain("func (s *Server) Ping() {}");
expect(result.diffSourceAfter).not.toMatch(/Addr string\n[ \t]+func \(s \*Server\) Ping/);
});
test("append on Go type_Server container keeps receiver method body indentation relative to the anchor chunk", () => {
const source = `package main
type Server struct {
Addr string
}
`;
const result = applyEdit({
source,
language: "go",
filePath: "/tmp/server.go",
operations: [
{
op: "append",
sel: "type_Server",
content: "func (s *Server) LogCount() int {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\treturn 0\n}",
},
],
});
expect(result.parseValid).toBe(true);
expect(result.diffSourceAfter).toContain(
"\n\tfunc (s *Server) LogCount() int {\n\t\ts.mu.Lock()\n\t\tdefer s.mu.Unlock()\n\t\treturn 0\n\t}\n",
);
expect(result.diffSourceAfter).not.toContain("\nfunc (s *Server) LogCount() int {");
});
test("keeps append separated from the closing delimiter when adding the last child", () => {
const result = edit([
{
op: "append",
sel: bodyTarget("class_Worker"),
content: '\tstatus(): string {\n\t\treturn "active";\n\t}',
},
]);
expect(result.diffSourceAfter).toContain('\t}\n\n\tstatus(): string {\n\t\treturn "active";\n\t}\n}');
expect(result.diffSourceAfter).not.toContain("\t}\n\tstatus(): string {");
});
test("replace with empty content on last impl method collapses extra whitespace-only lines before the closing brace", () => {
const source = `impl S {
fn a() {
keep();
}
fn b() {
drop();
}
}
`;
const crc = getChecksum(source, "impl_S.fn_b", "rust");
const result = applyEdit({
source,
language: "rust",
filePath: "/tmp/impl.rs",
operations: [{ op: "replace", sel: targetWithChecksum("impl_S.fn_b", crc), content: "" }],
});
expect(result.diffSourceAfter).toBe("impl S {\n fn a() {\n keep();\n }\n\n}\n");
});
});
describe("edit safety invariants", () => {
const runChunkPath = "class_Worker.fn_run";
function buildStaleRunFixture(): { source: string; staleChecksum: string; currentChecksum: string } {
const staleChecksum = getChecksum(testSource, runChunkPath);
const source = edit([
{
op: "replace",
sel: targetWithChecksum(runChunkPath, staleChecksum),
content: '\trun(): void {\n\t\tconsole.log("updated");\n\t}',
},
]).diffSourceAfter;
return {
source,
staleChecksum,
currentChecksum: getChecksum(source, runChunkPath),
};
}
for (const operation of ["replace", "replace_empty"] as const) {
test(`rejects stale checksum for ${operation} with current and provided checksums in the error`, () => {
const { source, staleChecksum } = buildStaleRunFixture();
const invoke = () => {
if (operation === "replace") {
return edit(
[
{
op: "replace",
sel: targetWithChecksum(runChunkPath, staleChecksum),
content: '\trun(): void {\n\t\tconsole.log("again");\n\t}',
},
],
source,
);
}
return edit([{ op: "replace", sel: targetWithChecksum(runChunkPath, staleChecksum), content: "" }], source);
};
expect(invoke).toThrow(new RegExp(`got "${staleChecksum}"`));
});
}
test("auto-accepts stale CRC for a second same-path replace in one batch", () => {
const checksum = getChecksum(testSource, runChunkPath);
const result = edit([
{
op: "replace",
sel: targetWithChecksum(runChunkPath, checksum),
content: '\trun(): void {\n\t\tconsole.log("first");\n\t}',
},
{
op: "replace",
sel: targetWithChecksum(runChunkPath, checksum),
content: '\trun(): void {\n\t\tconsole.log("second");\n\t}',
},
]);
expect(result.diffSourceAfter).toContain('console.log("second")');
expect(result.diffSourceAfter).not.toContain('console.log("first")');
});
test("applies two same-path replaces in one batch when the second checksum matches the post-first state", () => {
const checksum = getChecksum(testSource, runChunkPath);
const firstContent = '\trun(): void {\n\t\tconsole.log("first");\n\t}';
const afterFirst = edit([
{ op: "replace", sel: targetWithChecksum(runChunkPath, checksum), content: firstContent },
]).diffSourceAfter;
const checksum2 = getChecksum(afterFirst, runChunkPath);
const result = edit([
{ op: "replace", sel: targetWithChecksum(runChunkPath, checksum), content: firstContent },
{
op: "replace",
sel: targetWithChecksum(runChunkPath, checksum2),
content: '\trun(): void {\n\t\tconsole.log("second");\n\t}',
},
]);
expect(result.diffSourceAfter).toContain('console.log("second")');
expect(result.diffSourceAfter).not.toContain('console.log("first")');
});
test("auto-accepts stale CRC when a second whole-chunk replace refines the same method", () => {
const checksum = getChecksum(testSource, runChunkPath);
const result = edit([
{
op: "replace",
sel: targetWithChecksum(runChunkPath, checksum),
content: '\trun(task = "default"): void {\n\t\tconsole.log(this.name);\n\t}',
},
{
op: "replace",
sel: targetWithChecksum(runChunkPath, checksum),
content: '\trun(task = "default"): void {\n\t\tconsole.log(task);\n\t}',
},
]);
expect(result.diffSourceAfter).toContain('run(task = "default")');
expect(result.diffSourceAfter).toContain("console.log(task)");
});
test("second whole-chunk replace uses post-first checksum when refining signature and body", () => {
const checksum = getChecksum(testSource, runChunkPath);
const afterFirst = edit([
{
op: "replace",
sel: targetWithChecksum(runChunkPath, checksum),
content: "\trun(): void {\n\t\tconsole.log(task);\n\t}",
},
]).diffSourceAfter;
const checksum2 = getChecksum(afterFirst, runChunkPath);
const result = edit([
{
op: "replace",
sel: targetWithChecksum(runChunkPath, checksum),
content: "\trun(): void {\n\t\tconsole.log(task);\n\t}",
},
{
op: "replace",
sel: targetWithChecksum(runChunkPath, checksum2),
content: '\trun(task = "default"): void {\n\t\tconsole.log(task);\n\t}',
},
]);
expect(result.diffSourceAfter).toContain('run(task = "default"): void {');
expect(result.diffSourceAfter).toContain("\t\tconsole.log(task)");
expect(result.diffSourceAfter).not.toContain("console.log(this.name)");
});
test("reports batch rollback when a later operation is invalid", () => {
expect(() =>
edit([
{
op: "append",
sel: bodyTarget("class_Worker"),
content: '\tstatus(): string {\n\t\treturn "active";\n\t}',
},
{
op: "replace",
sel: targetWithChecksum("class_Worker.fn_run", "ZZZZ"),
content: "",
},
]),
).toThrow(/Edit operation 2\/2 failed.*Checksum mismatch/s);
});
test("keeps untouched sibling checksums stable after a nearby edit", () => {
const before = getChecksum(testSource, "class_Worker.constructor");
const after = edit([
{
op: "replace",
sel: currentTarget(testSource, runChunkPath),
content: '\trun(): void {\n\t\tconsole.log("nearby");\n\t}',
},
]).diffSourceAfter;
expect(getChecksum(after, "class_Worker.constructor")).toBe(before);
});
test("preserves a chunk checksum after unrelated file changes outside the chunk", () => {
const checksum = getChecksum(testSource, runChunkPath);
const sourceWithExternalTail = `${testSource}// externally appended line\n`;
const result = edit(
[
{
op: "replace",
sel: targetWithChecksum(runChunkPath, checksum),
content: '\trun(): void {\n\t\tconsole.log("updated");\n\t}',
},
],
sourceWithExternalTail,
);
expect(result.diffSourceAfter).toContain("// externally appended line");
expect(result.diffSourceAfter).toContain('console.log("updated")');
});
});
// ═══════════════════════════════════════════════════════════════════════════
// Content prefix stripping
// ═══════════════════════════════════════════════════════════════════════════
describe("content prefix stripping", () => {
test("line-number prefixes are stripped from replacement content", () => {
const ac = { sel: currentTarget(testSource, "class_Worker.fn_run") };
const result = edit([
{
op: "replace",
...ac,
content: ` 120 │ \texec(): void {\n 121 │ \t\tconsole.log("exec");\n 122 │ \t}`,
},
]);
expect(result.diffSourceAfter).not.toMatch(/\d+\s*[|│]/);
expect(result.diffSourceAfter).toContain("exec()");
});
test("hashline prefixes are stripped from replacement content", () => {
const ac = { sel: currentTarget(testSource, "class_Worker.fn_run") };
const result = edit([
{
op: "replace",
...ac,
content: '1#ZP:exec(): void {\n2#PM:\tconsole.log("exec");\n3#QV:}',
},
]);
expect(result.diffSourceAfter).not.toContain("#ZP:");
expect(result.diffSourceAfter).not.toMatch(/\b\d+#[A-Z]{2}:/);
expect(result.diffSourceAfter).toContain("exec(): void");
});
});
describe("chunk path resolution errors", () => {
test("unknown leaf under an existing parent lists direct children as a tree", () => {
let message = "";
try {
applyEdit({
source: testSource,
language: "typescript",
filePath: "/tmp/worker.ts",
operations: [
{
op: "before",
sel: "class_Worker.fn_ghost",
content: "\tghost(): void {}",
},
],
});
} catch (err) {
message = (err as Error).message;
}
expect(message).toContain('Direct children of "class_Worker":');
expect(message).toMatch(/├── \.constructor#[A-Z]{4}\s+L\d+-L\d+/);
expect(message).toMatch(/└── \.fn_run#[A-Z]{4}\s+L\d+-L\d+/);
});
});
describe("formatChunkedRead", () => {
test("root read uses recursive chunk headers without legacy labels", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
const filePath = path.join(tmpDir, "worker.ts");
await Bun.write(filePath, `${testSource}\nconst READY = true;\n`);
const result = await formatChunkedRead({
filePath,
readPath: filePath,
cwd: tmpDir,
language: "typescript",
});
expect(result.text).toContain("worker.ts·");
expect(result.text).toContain("class_Worker#");
expect(result.text).toContain("class_Worker.fn_run#");
expect(result.text).not.toContain("ck:");
expect(result.text).not.toContain("[branch");
});
test("root read header reports the full file line count", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
const filePath = path.join(tmpDir, "with-tail.ts");
const body = `${testSource}\n${Array.from({ length: 40 }, () => "// trailing\n").join("")}`;
await Bun.write(filePath, body);
const totalLines = body.split("\n").length;
const result = await formatChunkedRead({
filePath,
readPath: filePath,
cwd: tmpDir,
language: "typescript",
});
expect(result.text).toMatch(new RegExp(`with-tail\\.ts·${totalLines}L`));
});
test("leaf read shows absolute file lines and canonical tab indentation", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
const filePath = path.join(tmpDir, "worker.ts");
await Bun.write(filePath, testSource);
const result = await formatChunkedRead({
filePath,
readPath: `${filePath}:class_Worker.fn_run`,
cwd: tmpDir,
language: "typescript",
});
expect(result.text).toContain("worker.ts:class_Worker.fn_run·");
expect(result.text).toContain("class_Worker.fn_run#");
expect(result.text).toContain("6| \trun(): void {");
expect(result.text).toContain("7| \t\tconsole.log(this.name);");
expect(result.text).toContain("console.log(this.name);");
});
test("large child leaves stay expanded under the default preview threshold", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
const filePath = path.join(tmpDir, "service.ts");
const longBody = Array.from({ length: 40 }, (_, index) => ` step(${index});`).join("\n");
await Bun.write(filePath, `class Service {\n handle(): void {\n${longBody}\n done();\n }\n}\n`);
const result = await formatChunkedRead({
filePath,
readPath: `${filePath}:class_Service.fn_handle`,
cwd: tmpDir,
language: "typescript",
});
expect(result.text).not.toContain("to expand ⋮");
expect(result.text).toContain("service.ts:class_Service.fn_handle·");
expect(result.text).toContain("3| \t\tstep(0);");
expect(result.text).toContain("27| \t\tstep(24);");
expect(result.text).toContain("done();");
});
});
describe("leaf insert indentation", () => {
test("before on a nested method uses the method's indent level", () => {
const result = edit([
{
op: "before",
sel: "class_Worker.fn_run",
content: "validate(): boolean {\n\treturn true;\n}",
},
]);
const after = result.diffSourceAfter;
expect(after).toContain("\tvalidate(): boolean {");
expect(after).toContain("\trun(): void {");
const validatePos = after.indexOf("validate()");
const runPos = after.indexOf("run()");
expect(validatePos).toBeLessThan(runPos);
});
test("after on a nested method uses the method's indent level", () => {
const result = edit([
{
op: "after",
sel: "class_Worker.fn_run",
content: 'stop(): void {\n\tconsole.log("stopped");\n}',
},
]);
const after = result.diffSourceAfter;
expect(after).toContain("\tstop(): void {");
const runPos = after.indexOf("run()");
const stopPos = after.indexOf("stop()");
expect(stopPos).toBeGreaterThan(runPos);
});
});
describe("replace last child formatting", () => {
test("replacing last method does not merge with closing brace", () => {
const ac = { sel: currentTarget(testSource, "class_Worker.fn_run") };
const result = edit([
{
op: "replace",
...ac,
content: '\texec(): void {\n\t\tconsole.log("exec");\n\t}',
},
]);
const after = result.diffSourceAfter;
expect(after).not.toContain("}}");
expect(after).toContain("\texec(): void {");
expect(after).toMatch(/\t\}\n\}/);
});
test("replace dedents uniformly over-indented content so the chunk column is not applied twice", () => {
const ac = { sel: currentTarget(testSource, "class_Worker.fn_run") };
const result = edit([
{
op: "replace",
...ac,
content: '\t\texec(): void {\n\t\t\tconsole.log("exec");\n\t\t}',
},
]);
const after = result.diffSourceAfter;
expect(after).toContain("\texec(): void {");
expect(after).toContain('\t\tconsole.log("exec");');
expect(after).not.toContain("\t\texec(): void");
});
});
describe("addressable member rendering", () => {
test("renders trivial Rust enum variants as addressable children", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
const filePath = path.join(tmpDir, "log.rs");
await Bun.write(filePath, `pub enum LogLevel {\n Debug,\n Info,\n Warn,\n Error,\n}\n`);
const result = await formatChunkedRead({
filePath,
readPath: filePath,
cwd: tmpDir,
language: "rust",
});
expect(result.text).toContain("enum_LogLevel#");
expect(result.text).toContain("enum_LogLevel.variant_Debug#");
expect(result.text).toContain("enum_LogLevel.variant_Error#");
});
test("renders a single-method Go interface inline on the parent chunk", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
const filePath = path.join(tmpDir, "handler.go");
await Bun.write(
filePath,
`package main\n\ntype Handler interface {\n Handle(method, path string) Result\n}\n`,
);
const result = await formatChunkedRead({
filePath,
readPath: filePath,
cwd: tmpDir,
language: "go",
});
expect(result.text).toContain("type_Handler#");
expect(result.text).toContain("3| type Handler interface {");
expect(result.text).toContain("4| \tHandle(method, path string) Result");
});
test("renders Go receiver methods as top-level siblings", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
const filePath = path.join(tmpDir, "server.go");
await Bun.write(
filePath,
`package main\n\ntype Server struct {\n Addr string\n}\n\nfunc (s *Server) Start() {}\nfunc (s Server) Stop() {}\n`,
);
const result = await formatChunkedRead({
filePath,
readPath: filePath,
cwd: tmpDir,
language: "go",
});
expect(result.text).toContain("type_Server#");
expect(result.text).toContain("type_Server.field_Addr#");
expect(result.text).toContain("fn_Start#");
expect(result.text).toContain("fn_Stop#");
expect(result.text).not.toContain("type_Server.fn_Start#");
expect(result.text).not.toContain("type_Server.fn_Stop#");
});
test("line range filter shows top-level receiver methods even when the range skips the type header", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
const filePath = path.join(tmpDir, "server.go");
await Bun.write(
filePath,
`package main\n\ntype Server struct {\n Addr string\n}\n\nfunc (s *Server) Start() {}\nfunc (s Server) Stop() {}\n`,
);
const result = await formatChunkedRead({
filePath,
readPath: `${filePath}:L7-L8`,
cwd: tmpDir,
language: "go",
});
expect(result.text).toContain("L7-L8");
expect(result.text).toContain("fn_Start#");
expect(result.text).toContain("fn_Stop#");
expect(result.text).not.toContain("type_Server.fn_Start#");
});
test("renders trivial TypeScript enum variants as addressable children", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
const filePath = path.join(tmpDir, "status.ts");
await Bun.write(filePath, `enum Status {\n Idle = "idle",\n Busy = "busy",\n}\n`);
const result = await formatChunkedRead({
filePath,
readPath: filePath,
cwd: tmpDir,
language: "typescript",
});
expect(result.text).toContain("enum_Status#");
expect(result.text).toContain("enum_Status.variant_Idle#");
expect(result.text).toContain("enum_Status.variant_Busy#");
});
test("keeps non-trivial containers expanded", () => {
const classState = ChunkState.parse(
`class Tiny {\n foo() {\n return 1;\n }\n\n bar() {\n return 2;\n }\n}\n`,
"typescript",
);
const classChildren = classState.children("class_Tiny").map((chunk: { path: string }) => chunk.path);
expect(classChildren).toContain("class_Tiny.fn_foo");
expect(classChildren).toContain("class_Tiny.fn_bar");
const enumSource = Array.from({ length: 35 }, (_, i) => ` V${i} = ${i},`).join("\n");
const enumState = ChunkState.parse(`enum Big {\n${enumSource}\n}\n`, "typescript");
const enumChunk = enumState.chunk("enum_Big");
expect(enumChunk?.leaf).toBe(false);
});
});
describe("Go type chunk headers", () => {
test("reports Go type chunk line counts from the type body only", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-core-"));
const filePath = path.join(tmpDir, "server.go");
await Bun.write(
filePath,
`package main\n\ntype Server struct {\n Addr string\n}\n\nfunc (s *Server) Start() {}\nfunc (s Server) Stop() {}\n`,
);
const result = await formatChunkedRead({
filePath,
readPath: `${filePath}:type_Server`,
cwd: tmpDir,
language: "go",
});
expect(result.text).toContain("server.go:type_Server·3L");
expect(result.text).not.toContain("fn_Start#");
expect(result.text).not.toContain("fn_Stop#");
});
});
describe("addressable member editing", () => {
const enumSource = `enum Status {\n Idle = "idle",\n Busy = "busy",\n}\n`;
test("replace accepts full-source edits on the parent enum container", () => {
const ac = { sel: currentTarget(enumSource, "enum_Status") };
const result = edit(
[
{
op: "replace",
...ac,
content: `enum Status {\n Idle = "idle",\n Done = "done",\n}\n`,
},
],
enumSource,
);
expect(result.diffSourceAfter).toContain('Done = "done"');
expect(result.diffSourceAfter).not.toContain('Busy = "busy"');
});
test("after inserts beside an individually addressable enum variant without extra blank lines", () => {
const result = edit(
[
{
op: "after",
sel: "enum_Status.variant_Idle",
content: 'Paused = "paused",',
},
],
enumSource,
);
expect(result.diffSourceAfter).toContain(' Idle = "idle",\n Paused = "paused",\n Busy = "busy",');
});
test("replace with empty content removes an individually addressable enum variant", () => {
const busy = { sel: currentTarget(enumSource, "enum_Status.variant_Busy") };
const result = edit([{ op: "replace", ...busy, content: "" }], enumSource);
expect(result.diffSourceAfter).toContain('Idle = "idle"');
expect(result.diffSourceAfter).not.toContain('Busy = "busy"');
});
});
describe("Go receiver render ownership", () => {
test("before inserts beside a top-level Go receiver method", () => {
const source = `package main\n\ntype Server struct {\n Addr string\n}\n\nfunc (s *Server) Start() {}\nfunc (s Server) Stop() {}\n`;
const result = applyEdit({
source,
language: "go",
filePath: "/tmp/server.go",
operations: [
{
op: "before",
sel: "fn_Start",
content: "func DefaultServer() *Server {\n return &Server{}\n}",
},
],
});
expect(result.responseText).toContain("func DefaultServer() *Server");
expect(result.responseText).toContain("fn_DefaultServer#");
expect(result.responseText).toContain("fn_Start#");
expect(result.responseText).not.toContain("type_Server.fn_Start#");
});
});
describe("blank-line cleanup", () => {
const commentedSource = `class Worker {
stop(): void {
cleanup();
}
// restart note
restart(): void {
boot();
}
}
`;
test("replace with empty content collapses triple newlines at the edit seam", () => {
const checksum = getChecksum(commentedSource, "class_Worker.fn_restart");
const result = edit(
[
{
op: "replace",
sel: targetWithChecksum("class_Worker.fn_restart", checksum),
content: "",
},
],
commentedSource,
);
expect(result.diffSourceAfter).toContain("\t}\n\n}");
expect(result.diffSourceAfter).not.toContain("\t}\n\n\n}");
});
});
// ═══════════════════════════════════════════════════════════════════════════
// splice
// ═══════════════════════════════════════════════════════════════════════════
describe("prepend warnings", () => {
test("warns when comment-only body prepend may merge into the next chunk", () => {
const source = `package main\n\nimport "fmt"\n`;
const result = applyChunkEdits({
source,
language: "go",
cwd: "/",
filePath: "main.go",
operations: [{ op: "prepend", sel: "@body", content: "// AUTO-GENERATED\n" }],
});
expect(result.warnings.some(w => w.includes("Comment-only @body.prepend"))).toBe(true);
});
});
describe("chunk selector auto-resolution", () => {
test("warns on suffix auto-resolution", () => {
const result = edit([
{
op: "replace",
sel: targetWithChecksum("fn_run", getChecksum(testSource, "class_Worker.fn_run")),
content: "run(): void {\n\tconsole.log(this.name);\n}",
},
]);
expect(result.warnings.join("\n")).toMatch(/Auto-resolved chunk selector "fn_run" to "class_Worker\.fn_run#/);
});
test("warns on prefix auto-resolution", () => {
const result = edit([
{
op: "replace",
sel: targetWithChecksum("run", getChecksum(testSource, "class_Worker.fn_run")),
content: "run(): void {\n\tconsole.log(this.name);\n}",
},
]);
expect(result.warnings.join("\n")).toMatch(/Auto-resolved chunk selector "run" to "class_Worker\.fn_run#/);
});
test("errors on ambiguous suffix matches", () => {
const source = `class Foo {\n\trun(): void {}\n}\nclass Bar {\n\trun(): void {}\n}\n`;
expect(() =>
applyEdit({
source,
language: "typescript",
operations: [
{
op: "replace",
sel: targetWithChecksum("fn_run", getChecksum(source, "class_Foo.fn_run")),
content: "",
},
],
}),
).toThrow(/Ambiguous chunk selector "fn_run" matches 2 chunks/);
});
});
describe("prepend preamble guard", () => {
test("errors when comment-only body prepend targets root with preamble", () => {
// JS source with a leading comment block that becomes preamble
const source = `/**\n * License header\n */\nconst x = 1;\n`;
const state = ChunkState.parse(source, "javascript");
const hasPreamble = state.chunks().some((chunk: { path: string }) => chunk.path === "preamble");
if (!hasPreamble) {
// Skip if parser doesn't produce preamble for this source
return;
}
expect(() =>
applyChunkEdits({
source,
language: "javascript",
cwd: "/",
filePath: "index.js",
operations: [{ op: "prepend", sel: "@body", content: "// AUTO-GENERATED\n" }],
}),
).toThrow(/Comment-only @body.prepend on root is not allowed when the file has a preamble/);
});
});
// ═══════════════════════════════════════════════════════════════════════════
// move
// ═══════════════════════════════════════════════════════════════════════════
describe("tlaplus chunk rendering", () => {
const tlaplusSource = `---- MODULE Spec ----\nVARIABLE x\n\nInit == x = 0\n\n(* --algorithm Demo\nvariables x = 0;\nbegin\n Inc:\n x := x + 1;\nend algorithm; *)\n\\* BEGIN TRANSLATION\nVARIABLES pc\nNext == pc' = pc\n\\* END TRANSLATION\n====\n`;
test("formatChunkedRead hides translated content behind a synthetic translation chunk", async () => {
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "chunk-tree-tlaplus-read-"));
const filePath = path.join(tmpDir, "Spec.tla");
await Bun.write(filePath, tlaplusSource);
const result = await formatChunkedRead({
filePath,
readPath: filePath,
cwd: tmpDir,
language: "tlaplus",
});
expect(result.text).toContain("mod_Spec.translation_12#");
expect(result.text).toContain("\\* [translation hidden]");
expect(result.text).not.toContain("Next == pc' = pc");
});
test("applyChunkEdits keeps translated content hidden in responseText", () => {
const state = ChunkState.parse(tlaplusSource, "tlaplus");
const initChunk = state
.chunks()
.find((chunk: { path: string; checksum: string }) => chunk.path.endsWith("operator_Init"));
if (!initChunk) throw new Error("Expected operator_Init chunk in tlaplus fixture");
const result = applyChunkEdits({
source: tlaplusSource,
language: "tlaplus",
cwd: "/tmp",
filePath: "/tmp/Spec.tla",
operations: [
{
op: "replace",
sel: targetWithChecksum(initChunk.path, initChunk.checksum),
content: "Start == x = 0",
},
],
});
expect(result.diffSourceAfter).toContain("Start == x = 0");
// The scoped response tree includes the touched chunk and adjacent siblings,
// but not distant ones like translation_12. The translation content must
// still be hidden from any chunk that IS visible.
expect(result.responseText).toContain("mod_Spec.operator_Start#");
expect(result.responseText).not.toContain("Next == pc' = pc");
});
});