Skip to content

Commit dab9700

Browse files
committed
test: file — File.status() git changed file detection
File.status() powers the TUI changed-file indicator and had zero direct test coverage. New tests cover modified/untracked/deleted file detection, binary diff parsing (dash line counts), and path normalization. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> https://claude.ai/code/session_01P7m7yBWTz8L7Qz17mK9GPP
1 parent f030bf8 commit dab9700

1 file changed

Lines changed: 122 additions & 0 deletions

File tree

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { describe, test, expect } from "bun:test"
2+
import path from "path"
3+
import fs from "fs/promises"
4+
import { $ } from "bun"
5+
import { File } from "../../src/file"
6+
import { Instance } from "../../src/project/instance"
7+
import { tmpdir } from "../fixture/fixture"
8+
9+
describe("File.status()", () => {
10+
test("detects modified files with line counts", async () => {
11+
await using tmp = await tmpdir({ git: true })
12+
const filepath = path.join(tmp.path, "test.txt")
13+
await fs.writeFile(filepath, "line1\nline2\n")
14+
await $`git add test.txt && git commit -m "initial"`.cwd(tmp.path).quiet()
15+
16+
// Modify the file — append two lines
17+
await fs.writeFile(filepath, "line1\nline2\nline3\nline4\n")
18+
19+
await Instance.provide({
20+
directory: tmp.path,
21+
fn: async () => {
22+
const changed = await File.status()
23+
expect(changed.length).toBe(1)
24+
expect(changed[0].path).toBe("test.txt")
25+
expect(changed[0].status).toBe("modified")
26+
expect(changed[0].added).toBe(2)
27+
expect(changed[0].removed).toBe(0)
28+
},
29+
})
30+
})
31+
32+
test("detects untracked (new) files", async () => {
33+
await using tmp = await tmpdir({ git: true })
34+
// No trailing newline so split("\n") gives exactly 2 elements
35+
await fs.writeFile(path.join(tmp.path, "newfile.ts"), "const x = 1\nconst y = 2")
36+
37+
await Instance.provide({
38+
directory: tmp.path,
39+
fn: async () => {
40+
const changed = await File.status()
41+
const added = changed.find((f) => f.path === "newfile.ts")
42+
expect(added).toBeDefined()
43+
expect(added!.status).toBe("added")
44+
expect(added!.added).toBe(2) // "const x = 1\nconst y = 2".split("\n").length === 2
45+
},
46+
})
47+
})
48+
49+
test("detects deleted files", async () => {
50+
await using tmp = await tmpdir({ git: true })
51+
const filepath = path.join(tmp.path, "to-delete.txt")
52+
await fs.writeFile(filepath, "content\n")
53+
await $`git add to-delete.txt && git commit -m "add file"`.cwd(tmp.path).quiet()
54+
55+
// Delete the file
56+
await fs.rm(filepath)
57+
58+
await Instance.provide({
59+
directory: tmp.path,
60+
fn: async () => {
61+
const changed = await File.status()
62+
// Deleted files appear in diff --numstat (as "modified") AND diff --diff-filter=D (as "deleted")
63+
const deleted = changed.find((f) => f.path === "to-delete.txt" && f.status === "deleted")
64+
expect(deleted).toBeDefined()
65+
expect(deleted!.status).toBe("deleted")
66+
},
67+
})
68+
})
69+
70+
test("returns empty array for clean working tree", async () => {
71+
await using tmp = await tmpdir({ git: true })
72+
73+
await Instance.provide({
74+
directory: tmp.path,
75+
fn: async () => {
76+
const changed = await File.status()
77+
expect(changed).toEqual([])
78+
},
79+
})
80+
})
81+
82+
test("handles binary files with dash line counts", async () => {
83+
await using tmp = await tmpdir({ git: true })
84+
const filepath = path.join(tmp.path, "image.png")
85+
// Write binary content with null bytes so git classifies it as binary
86+
await fs.writeFile(filepath, Buffer.from([0x00, 0x89, 0x50, 0x4e, 0x47, 0x00]))
87+
await $`git add image.png && git commit -m "add image"`.cwd(tmp.path).quiet()
88+
89+
// Modify the binary
90+
await fs.writeFile(filepath, Buffer.from([0x00, 0x89, 0x50, 0x4e, 0x47, 0x00, 0xff, 0x00]))
91+
92+
await Instance.provide({
93+
directory: tmp.path,
94+
fn: async () => {
95+
const changed = await File.status()
96+
const binary = changed.find((f) => f.path === "image.png")
97+
expect(binary).toBeDefined()
98+
expect(binary!.status).toBe("modified")
99+
// Binary files report "-" in diff --numstat, which gets parsed as 0
100+
expect(binary!.added).toBe(0)
101+
expect(binary!.removed).toBe(0)
102+
},
103+
})
104+
})
105+
106+
test("normalizes paths to be relative", async () => {
107+
await using tmp = await tmpdir({ git: true })
108+
await fs.mkdir(path.join(tmp.path, "src"), { recursive: true })
109+
await fs.writeFile(path.join(tmp.path, "src", "app.ts"), "const x = 1\n")
110+
111+
await Instance.provide({
112+
directory: tmp.path,
113+
fn: async () => {
114+
const changed = await File.status()
115+
for (const file of changed) {
116+
// All paths should be relative, not absolute
117+
expect(path.isAbsolute(file.path)).toBe(false)
118+
}
119+
},
120+
})
121+
})
122+
})

0 commit comments

Comments
 (0)