99a21b95ba
- Added markdown `Row` chunk support by introducing `ChunkKind::Row`, mapping it to tag `"row"`, and emitting row chunks for table rows. - Updated Go chunk parsing to support method signatures (`func (recv) Name`), prefer method parsing in signatures, and adjust root rules to classify package/import chunks correctly. - Changed chunk-edit behavior to add read-only `read:true`, replace `write:null` deletes with explicit `delete`, and require one explicit action per entry. - Added safer chunk-edit execution with region matching, replacement reindentation, table-row append handling, and stricter warnings/errors for boundary and invalid operations.
89 lines
3.4 KiB
TypeScript
89 lines
3.4 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { dropIncompleteLastEdit, EDIT_MODE_STRATEGIES } from "@oh-my-pi/pi-coding-agent/edit";
|
|
|
|
describe("dropIncompleteLastEdit", () => {
|
|
test("keeps all entries when partialJson is undefined", () => {
|
|
const edits = [{ path: "a" }, { path: "b" }];
|
|
expect(dropIncompleteLastEdit(edits, undefined, "edits")).toEqual(edits);
|
|
});
|
|
|
|
test("keeps all entries when the trailing object is closed", () => {
|
|
const edits = [{ path: "a" }, { path: "b" }];
|
|
const partial = '{"edits":[{"path":"a"},{"path":"b"}]}';
|
|
expect(dropIncompleteLastEdit(edits, partial, "edits")).toEqual(edits);
|
|
});
|
|
|
|
test("drops the last entry when its closing } has not arrived", () => {
|
|
const edits = [{ path: "a" }, { path: "b" }];
|
|
const partial = '{"edits":[{"path":"a"},{"path":"b"';
|
|
expect(dropIncompleteLastEdit(edits, partial, "edits")).toEqual([{ path: "a" }]);
|
|
});
|
|
|
|
test("drops the last entry when a new {} has opened after the last close", () => {
|
|
const edits = [{ path: "a" }, { path: "b" }];
|
|
const partial = '{"edits":[{"path":"a"},{"pat';
|
|
expect(dropIncompleteLastEdit(edits, partial, "edits")).toEqual([{ path: "a" }]);
|
|
});
|
|
|
|
test("leaves empty edits alone", () => {
|
|
expect(dropIncompleteLastEdit([], '{"edits":[', "edits")).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("chunk extractCompleteEdits", () => {
|
|
const strategy = EDIT_MODE_STRATEGIES.chunk;
|
|
|
|
test("passes through a single complete entry", () => {
|
|
const args = {
|
|
edits: [{ path: "a.ts", write: "foo" }],
|
|
__partialJson: '{"edits":[{"path":"a.ts","write":"foo"}]}',
|
|
};
|
|
const out = strategy.extractCompleteEdits(args, args.__partialJson) as typeof args;
|
|
expect(out.edits).toHaveLength(1);
|
|
});
|
|
|
|
test("drops trailing entry when partial JSON has open-brace after last close", () => {
|
|
const args = {
|
|
edits: [{ path: "a.ts", write: "foo" }, { path: "b.ts" }],
|
|
__partialJson: '{"edits":[{"path":"a.ts","write":"foo"},{"path":"b.ts"',
|
|
};
|
|
const out = strategy.extractCompleteEdits(args, args.__partialJson) as typeof args;
|
|
expect(out.edits).toHaveLength(1);
|
|
expect(out.edits[0].path).toBe("a.ts");
|
|
});
|
|
|
|
test("drops trailing entry when partial JSON ends in ':nu' (write: null guard)", () => {
|
|
const args = {
|
|
edits: [
|
|
{ path: "a.ts", write: "foo" },
|
|
{ path: "b.ts", write: null },
|
|
],
|
|
// simulates partial-json coercing the in-flight `nu` to `null`
|
|
__partialJson: '{"edits":[{"path":"a.ts","write":"foo"},{"path":"b.ts","write":nu',
|
|
};
|
|
const out = strategy.extractCompleteEdits(args, args.__partialJson) as typeof args;
|
|
// Last entry should be dropped because its `}` hasn't arrived yet, so
|
|
// incomplete null-write errors are suppressed while streaming.
|
|
expect(out.edits).toHaveLength(1);
|
|
expect(out.edits[0].path).toBe("a.ts");
|
|
});
|
|
});
|
|
|
|
describe("apply_patch extractCompleteEdits", () => {
|
|
const strategy = EDIT_MODE_STRATEGIES.apply_patch;
|
|
|
|
test("returns args unchanged (payload is plain text)", () => {
|
|
const args = { input: "*** Begin Patch\n*** Update File: a.ts\n@@\n-x\n+y\n*** End Patch\n" };
|
|
expect(strategy.extractCompleteEdits(args, undefined)).toEqual(args);
|
|
});
|
|
});
|
|
|
|
describe("vim extractCompleteEdits", () => {
|
|
const strategy = EDIT_MODE_STRATEGIES.vim;
|
|
|
|
test("returns args unchanged (vim stream handled elsewhere)", () => {
|
|
const args = { file: "a.ts", steps: [] };
|
|
expect(strategy.extractCompleteEdits(args, undefined)).toEqual(args);
|
|
});
|
|
});
|