import { describe, expect, it } from "vite-plus/test"; import { buildTurnDiffTree, partitionTurnDiffFiles, summarizeTurnDiffStats } from "./turnDiffTree"; describe("summarizeTurnDiffStats", () => { it("sums only files with numeric additions/deletions", () => { const stat = summarizeTurnDiffStats([ { path: "README.md", kind: "modified", additions: 3, deletions: 1 }, { path: "docs/notes.md", kind: "modified", additions: 0, deletions: 0 }, { path: "src/index.ts", kind: "modified", additions: 5, deletions: 2 }, ]); expect(stat).toEqual({ additions: 8, deletions: 3 }); });}); describe("buildTurnDiffTree", () => { it("builds nested directory nodes with aggregated stats", () => { const tree = buildTurnDiffTree([ { path: "src/index.ts", kind: "modified", additions: 2, deletions: 1 }, { path: "src/components/Button.tsx", kind: "modified", additions: 4, deletions: 2 }, { path: "README.md", kind: "modified", additions: 1, deletions: 0 }, ]); expect(tree).toEqual([ { kind: "directory", name: "src", path: "src", stat: { additions: 6, deletions: 3 }, children: [ { kind: "directory", name: "components", path: "src/components", stat: { additions: 4, deletions: 2 }, children: [ { kind: "file", name: "Button.tsx", path: "src/components/Button.tsx", stat: { additions: 4, deletions: 2 }, }, ], }, { kind: "file", name: "index.ts", path: "src/index.ts", stat: { additions: 2, deletions: 1 }, }, ], }, { kind: "file", name: "README.md", path: "README.md", stat: { additions: 1, deletions: 0 }, }, ]); }); it("keeps zero-valued file stats and includes only their numeric contribution", () => { const tree = buildTurnDiffTree([ { path: "docs/notes.md", kind: "modified", additions: 0, deletions: 0 }, { path: "docs/todo.md", kind: "modified", additions: 1, deletions: 1 }, ]); expect(tree).toEqual([ { kind: "directory", name: "docs", path: "docs", stat: { additions: 1, deletions: 1 }, children: [ { kind: "file", name: "notes.md", path: "docs/notes.md", stat: { additions: 0, deletions: 0 }, }, { kind: "file", name: "todo.md", path: "docs/todo.md", stat: { additions: 1, deletions: 1 }, }, ], }, ]); }); it("normalizes file paths with windows separators", () => { const tree = buildTurnDiffTree([ { path: "apps\\web\\src\\index.ts", kind: "modified", additions: 2, deletions: 1 }, ]); expect(tree).toEqual([ { kind: "directory", name: "apps/web/src", path: "apps/web/src", stat: { additions: 2, deletions: 1 }, children: [ { kind: "file", name: "index.ts", path: "apps/web/src/index.ts", stat: { additions: 2, deletions: 1 }, }, ], }, ]); }); it("compacts only single-directory chains and stops at branch points", () => { const tree = buildTurnDiffTree([ { path: "apps/server/src/index.ts", kind: "modified", additions: 2, deletions: 1 }, { path: "apps/server/main.ts", kind: "modified", additions: 4, deletions: 0 }, ]); expect(tree).toEqual([ { kind: "directory", name: "apps/server", path: "apps/server", stat: { additions: 6, deletions: 1 }, children: [ { kind: "directory", name: "src", path: "apps/server/src", stat: { additions: 2, deletions: 1 }, children: [ { kind: "file", name: "index.ts", path: "apps/server/src/index.ts", stat: { additions: 2, deletions: 1 }, }, ], }, { kind: "file", name: "main.ts", path: "apps/server/main.ts", stat: { additions: 4, deletions: 0 }, }, ], }, ]); }); it("preserves leading/trailing whitespace in path segments", () => { const tree = buildTurnDiffTree([ { path: "a/file.ts", kind: "modified", additions: 1, deletions: 0 }, { path: " a/file.ts", kind: "modified", additions: 2, deletions: 0 }, ]); expect(tree).toHaveLength(2); const directoryNodes = tree.filter( (node): node is Extract<(typeof tree)[number], { kind: "directory" }> => node.kind === "directory", ); expect(directoryNodes.map((node) => node.name).toSorted()).toEqual([" a", "a"]); expect(directoryNodes.map((node) => node.path).toSorted()).toEqual([" a", "a"]); });}); describe("partitionTurnDiffFiles", () => { it("splits files by origin, defaulting missing origin to agent", () => { const agentEdit = { path: "edited.ts", kind: "modified", additions: 1, deletions: 0 } as const; const legacy = { path: "legacy.ts", kind: "modified", additions: 2, deletions: 0 } as const; const pulled = { path: "pulled.ts", kind: "modified", additions: 3, deletions: 1, origin: "git", } as const; const explicitAgent = { path: "explicit.ts", kind: "modified", additions: 4, deletions: 0, origin: "agent", } as const; const { agentFiles, gitFiles } = partitionTurnDiffFiles([ agentEdit, legacy, pulled, explicitAgent, ]); expect(agentFiles.map((file) => file.path)).toEqual(["edited.ts", "legacy.ts", "explicit.ts"]); expect(gitFiles.map((file) => file.path)).toEqual(["pulled.ts"]); });});
import { describe, expect, it } from "vite-plus/test";
import { buildTurnDiffTree, partitionTurnDiffFiles, summarizeTurnDiffStats } from "./turnDiffTree";
describe("summarizeTurnDiffStats", () => {
it("sums only files with numeric additions/deletions", () => {
const stat = summarizeTurnDiffStats([
{ path: "README.md", kind: "modified", additions: 3, deletions: 1 },
{ path: "docs/notes.md", kind: "modified", additions: 0, deletions: 0 },
{ path: "src/index.ts", kind: "modified", additions: 5, deletions: 2 },
]);
expect(stat).toEqual({ additions: 8, deletions: 3 });
});
});
describe("buildTurnDiffTree", () => {
it("builds nested directory nodes with aggregated stats", () => {
const tree = buildTurnDiffTree([
{ path: "src/index.ts", kind: "modified", additions: 2, deletions: 1 },
{ path: "src/components/Button.tsx", kind: "modified", additions: 4, deletions: 2 },
{ path: "README.md", kind: "modified", additions: 1, deletions: 0 },
]);
expect(tree).toEqual([
{
kind: "directory",
name: "src",
path: "src",
stat: { additions: 6, deletions: 3 },
children: [
{
kind: "directory",
name: "components",
path: "src/components",
stat: { additions: 4, deletions: 2 },
children: [
{
kind: "file",
name: "Button.tsx",
path: "src/components/Button.tsx",
stat: { additions: 4, deletions: 2 },
},
],
},
{
kind: "file",
name: "index.ts",
path: "src/index.ts",
stat: { additions: 2, deletions: 1 },
},
],
},
{
kind: "file",
name: "README.md",
path: "README.md",
stat: { additions: 1, deletions: 0 },
},
]);
});
it("keeps zero-valued file stats and includes only their numeric contribution", () => {
const tree = buildTurnDiffTree([
{ path: "docs/notes.md", kind: "modified", additions: 0, deletions: 0 },
{ path: "docs/todo.md", kind: "modified", additions: 1, deletions: 1 },
]);
expect(tree).toEqual([
{
kind: "directory",
name: "docs",
path: "docs",
stat: { additions: 1, deletions: 1 },
children: [
{
kind: "file",
name: "notes.md",
path: "docs/notes.md",
stat: { additions: 0, deletions: 0 },
},
{
kind: "file",
name: "todo.md",
path: "docs/todo.md",
stat: { additions: 1, deletions: 1 },
},
],
},
]);
});
it("normalizes file paths with windows separators", () => {
const tree = buildTurnDiffTree([
{ path: "apps\\web\\src\\index.ts", kind: "modified", additions: 2, deletions: 1 },
]);
expect(tree).toEqual([
{
kind: "directory",
name: "apps/web/src",
path: "apps/web/src",
stat: { additions: 2, deletions: 1 },
children: [
{
kind: "file",
name: "index.ts",
path: "apps/web/src/index.ts",
stat: { additions: 2, deletions: 1 },
},
],
},
]);
});
it("compacts only single-directory chains and stops at branch points", () => {
const tree = buildTurnDiffTree([
{ path: "apps/server/src/index.ts", kind: "modified", additions: 2, deletions: 1 },
{ path: "apps/server/main.ts", kind: "modified", additions: 4, deletions: 0 },
]);
expect(tree).toEqual([
{
kind: "directory",
name: "apps/server",
path: "apps/server",
stat: { additions: 6, deletions: 1 },
children: [
{
kind: "directory",
name: "src",
path: "apps/server/src",
stat: { additions: 2, deletions: 1 },
children: [
{
kind: "file",
name: "index.ts",
path: "apps/server/src/index.ts",
stat: { additions: 2, deletions: 1 },
},
],
},
{
kind: "file",
name: "main.ts",
path: "apps/server/main.ts",
stat: { additions: 4, deletions: 0 },
},
],
},
]);
});
it("preserves leading/trailing whitespace in path segments", () => {
const tree = buildTurnDiffTree([
{ path: "a/file.ts", kind: "modified", additions: 1, deletions: 0 },
{ path: " a/file.ts", kind: "modified", additions: 2, deletions: 0 },
]);
expect(tree).toHaveLength(2);
const directoryNodes = tree.filter(
(node): node is Extract<(typeof tree)[number], { kind: "directory" }> =>
node.kind === "directory",
);
expect(directoryNodes.map((node) => node.name).toSorted()).toEqual([" a", "a"]);
expect(directoryNodes.map((node) => node.path).toSorted()).toEqual([" a", "a"]);
});
});
describe("partitionTurnDiffFiles", () => {
it("splits files by origin, defaulting missing origin to agent", () => {
const agentEdit = { path: "edited.ts", kind: "modified", additions: 1, deletions: 0 } as const;
const legacy = { path: "legacy.ts", kind: "modified", additions: 2, deletions: 0 } as const;
const pulled = {
path: "pulled.ts",
kind: "modified",
additions: 3,
deletions: 1,
origin: "git",
} as const;
const explicitAgent = {
path: "explicit.ts",
kind: "modified",
additions: 4,
deletions: 0,
origin: "agent",
} as const;
const { agentFiles, gitFiles } = partitionTurnDiffFiles([
agentEdit,
legacy,
pulled,
explicitAgent,
]);
expect(agentFiles.map((file) => file.path)).toEqual(["edited.ts", "legacy.ts", "explicit.ts"]);
expect(gitFiles.map((file) => file.path)).toEqual(["pulled.ts"]);
});
});