@@ -0,0 +1,406 @@
8+func TestLooksLikeDiff(t *testing.T) {
17+ name: "simple unified diff",
18+ content: `diff --git a/main.go b/main.go
28++ fmt.Println("hello")
35+ content: "This is just some plain text with no diff markers.",
39+ name: "empty string",
44+ name: "markdown with headers",
51+More content with **bold** text.
56+ name: "diff with mixed content",
57+ content: `diff --git a/file.txt b/file.txt
67+ name: "only plus/minus without hunk or headers",
68+ content: `Hello world
70+This is not really a diff
71+Just some text with a few symbols
73+More regular content here
79+ name: "GitHub PR diff format",
80+ content: `diff --git a/src/app.ts b/src/app.ts
81+index abc1234..def5678 100644
84+@@ -10,6 +10,8 @@ function handleRequest() {
85+ const data = getData();
88+ return process(data);
94+ name: "non-git unified patch with hunk and headers",
95+ content: `--- a/old.c
100++int main(int argc, char **argv) {
107+ name: "file headers without hunk markers",
108+ content: `--- a/somefile.txt
110+Just some content here
111+No hunk markers at all
116+ name: "hunk markers without file headers",
117+ content: `@@ -1,3 +1,4 @@
125+ name: "markdown list with plus signs",
126+ content: `- Item one
135+ for _, tt := range tests {
136+ t.Run(tt.name, func(t *testing.T) {
138+ got := looksLikeDiff(tt.content)
140+ t.Errorf("looksLikeDiff() = %v, want %v", got, tt.want)
146+func TestParseUnifiedDiff(t *testing.T) {
152+ want []parsedDiffFile
155+ name: "simple diff with additions and removals",
156+ input: `diff --git a/main.go b/main.go
166++ fmt.Println("hello")
169+ want: []parsedDiffFile{
172+ before: "package main\n\nfunc main() {\n println(\"hello\")\n}",
173+ after: "package main\n\nimport \"fmt\"\n\nfunc main() {\n fmt.Println(\"hello\")\n}",
178+ name: "new file creation",
179+ input: `diff --git a/newfile.go b/newfile.go
188+ want: []parsedDiffFile{
192+ after: "package main\n\nfunc main() {}",
197+ name: "file deletion",
198+ input: `diff --git a/oldfile.go b/oldfile.go
199+deleted file mode 100644
207+ want: []parsedDiffFile{
210+ before: "package main\n\nfunc main() {}",
216+ name: "non-diff content",
217+ input: "Just some regular text",
221+ name: "diff with timestamp in header",
222+ input: `diff --git a/config.yml b/config.yml
223+--- a/config.yml 2024-01-15 10:30:00
224++++ b/config.yml 2024-01-15 10:31:00
231+ want: []parsedDiffFile{
234+ before: "name: myapp\nversion: 1.0",
235+ after: "name: myapp\nversion: 1.1\ndebug: true",
240+ name: "multi-file diff",
241+ input: `diff --git a/one.txt b/one.txt
249+diff --git a/two.txt b/two.txt
257+ want: []parsedDiffFile{
260+ before: "line one\nline two\nline three",
261+ after: "line one\nline two updated\nline three",
265+ before: "alpha\ngamma",
266+ after: "alpha\nbeta\ngamma",
271+ name: "non-git unified patch",
277++int main(int argc, char **argv) {
281+ want: []parsedDiffFile{
284+ before: "#include <stdio.h>\nint main() {\n return 0;\n}",
285+ after: "#include <stdio.h>\nint main(int argc, char **argv) {\n return 0;\n}",
290+ name: "non-git new file from /dev/null",
291+ input: `--- /dev/null
297+ want: []parsedDiffFile{
299+ path: "newfile.txt",
301+ after: "hello\nworld",
306+ name: "non-git new file with only +++ header",
307+ input: `+++ brand_new.go
313+ want: []parsedDiffFile{
315+ path: "brand_new.go",
317+ after: "package main\n\nfunc main() {}",
322+ name: "multi-hunk single file",
323+ input: `diff --git a/big.go b/big.go
333++ fmt.Println("done")
336+ want: []parsedDiffFile{
339+ before: "package main\n\nfunc init() {\n println(\"done\")\n}",
340+ after: "package main\nimport \"os\"\n\nfunc init() {\n fmt.Println(\"done\")\n}",
345+ name: "hunk content starting with header-like prefixes",
346+ input: `diff --git a/file.txt b/file.txt
354+ want: []parsedDiffFile{
357+ before: "--- tricky\nkeep",
358+ after: "+++ newer\nkeep",
364+ for _, tt := range tests {
365+ t.Run(tt.name, func(t *testing.T) {
367+ got := parseUnifiedDiff(tt.input)
368+ if len(got) != len(tt.want) {
369+ t.Errorf("parseUnifiedDiff() returned %d files, want %d", len(got), len(tt.want))
372+ for i, w := range tt.want {
373+ if got[i].path != w.path {
374+ t.Errorf("parseUnifiedDiff()[%d].path = %q, want %q", i, got[i].path, w.path)
376+ if got[i].before != w.before {
377+ t.Errorf("parseUnifiedDiff()[%d].before = %q, want %q", i, got[i].before, w.before)
379+ if got[i].after != w.after {
380+ t.Errorf("parseUnifiedDiff()[%d].after = %q, want %q", i, got[i].after, w.after)
387+func TestLooksLikeDiffVersusMarkdown(t *testing.T) {
390+ // A unified diff should be detected as a diff, not markdown,
391+ // even though it contains "-" which could match markdown patterns.
392+ diffContent := strings.Join([]string{
393+ "diff --git a/README.md b/README.md",
403+ if !looksLikeDiff(diffContent) {
404+ t.Error("looksLikeDiff() should detect unified diff")