Monorepo for Tangled tangled.org
5

Configure Feed

Select the types of activity you want to include in your feed.

1package repo 2 3import ( 4 "strings" 5 "testing" 6 "time" 7 8 "github.com/bluekeyes/go-gitdiff/gitdiff" 9 "github.com/go-git/go-git/v5/plumbing" 10 "github.com/go-git/go-git/v5/plumbing/object" 11 "tangled.org/core/types" 12) 13 14// parseDiff is a test helper that parses a unified diff string into gitdiff.TextFragment slices. 15func parseDiff(t *testing.T, src string) []*gitdiff.File { 16 t.Helper() 17 files, _, err := gitdiff.Parse(strings.NewReader(src)) 18 if err != nil { 19 t.Fatalf("gitdiff.Parse: %v", err) 20 } 21 return files 22} 23 24// niceDiffFromParsed builds a NiceDiff from parsed gitdiff.File entries, mirroring 25// what the knotserver does in git.Diff(). 26func niceDiffFromParsed(files []*gitdiff.File, commit types.Commit, stat types.DiffStat) *types.NiceDiff { 27 nd := &types.NiceDiff{Commit: commit, Stat: stat} 28 for _, f := range files { 29 d := types.Diff{ 30 IsBinary: f.IsBinary, 31 IsNew: f.IsNew, 32 IsDelete: f.IsDelete, 33 IsCopy: f.IsCopy, 34 IsRename: f.IsRename, 35 } 36 d.Name.Old = f.OldName 37 d.Name.New = f.NewName 38 for _, tf := range f.TextFragments { 39 d.TextFragments = append(d.TextFragments, *tf) 40 } 41 nd.Diff = append(nd.Diff, d) 42 } 43 return nd 44} 45 46func TestRenderUnifiedDiff_nil(t *testing.T) { 47 if got := renderUnifiedDiff(nil); got != "" { 48 t.Errorf("expected empty string for nil NiceDiff, got %q", got) 49 } 50} 51 52func TestRenderUnifiedDiff_modified(t *testing.T) { 53 const src = `diff --git a/foo.go b/foo.go 54--- a/foo.go 55+++ b/foo.go 56@@ -1,3 +1,3 @@ 57 package main 58-// old comment 59+// new comment 60 func main() {} 61` 62 files := parseDiff(t, src) 63 nd := niceDiffFromParsed(files, types.Commit{}, types.DiffStat{}) 64 got := renderUnifiedDiff(nd) 65 66 checks := []string{ 67 "diff --git a/foo.go b/foo.go\n", 68 "--- a/foo.go\n", 69 "+++ b/foo.go\n", 70 "@@ -1,3 +1,3 @@", 71 "-// old comment\n", 72 "+// new comment\n", 73 } 74 for _, want := range checks { 75 if !strings.Contains(got, want) { 76 t.Errorf("renderUnifiedDiff output missing %q\ngot:\n%s", want, got) 77 } 78 } 79} 80 81func TestRenderUnifiedDiff_newFile(t *testing.T) { 82 const src = `diff --git a/new.go b/new.go 83new file mode 100644 84--- /dev/null 85+++ b/new.go 86@@ -0,0 +1,2 @@ 87+package main 88+func main() {} 89` 90 files := parseDiff(t, src) 91 nd := niceDiffFromParsed(files, types.Commit{}, types.DiffStat{}) 92 got := renderUnifiedDiff(nd) 93 94 checks := []string{ 95 "diff --git a/new.go b/new.go\n", 96 "new file mode 100644\n", 97 "--- /dev/null\n", 98 "+++ b/new.go\n", 99 "+package main\n", 100 } 101 for _, want := range checks { 102 if !strings.Contains(got, want) { 103 t.Errorf("renderUnifiedDiff output missing %q\ngot:\n%s", want, got) 104 } 105 } 106} 107 108func TestRenderUnifiedDiff_deletedFile(t *testing.T) { 109 const src = `diff --git a/old.go b/old.go 110deleted file mode 100644 111--- a/old.go 112+++ /dev/null 113@@ -1,2 +0,0 @@ 114-package main 115-func main() {} 116` 117 files := parseDiff(t, src) 118 nd := niceDiffFromParsed(files, types.Commit{}, types.DiffStat{}) 119 got := renderUnifiedDiff(nd) 120 121 checks := []string{ 122 "diff --git a/old.go b/old.go\n", 123 "deleted file mode 100644\n", 124 "--- a/old.go\n", 125 "+++ /dev/null\n", 126 "-package main\n", 127 } 128 for _, want := range checks { 129 if !strings.Contains(got, want) { 130 t.Errorf("renderUnifiedDiff output missing %q\ngot:\n%s", want, got) 131 } 132 } 133} 134 135func TestRenderUnifiedDiff_renamedFile(t *testing.T) { 136 const src = `diff --git a/old.go b/renamed.go 137rename from old.go 138rename to renamed.go 139--- a/old.go 140+++ b/renamed.go 141@@ -1,2 +1,2 @@ 142 package main 143-func old() {} 144+func renamed() {} 145` 146 files := parseDiff(t, src) 147 nd := niceDiffFromParsed(files, types.Commit{}, types.DiffStat{}) 148 got := renderUnifiedDiff(nd) 149 150 checks := []string{ 151 "diff --git a/old.go b/renamed.go\n", 152 "rename from old.go\n", 153 "rename to renamed.go\n", 154 "--- a/old.go\n", 155 "+++ b/renamed.go\n", 156 } 157 for _, want := range checks { 158 if !strings.Contains(got, want) { 159 t.Errorf("renderUnifiedDiff output missing %q\ngot:\n%s", want, got) 160 } 161 } 162} 163 164func TestRenderUnifiedDiff_multipleFiles(t *testing.T) { 165 const src = `diff --git a/a.go b/a.go 166--- a/a.go 167+++ b/a.go 168@@ -1,1 +1,1 @@ 169-old a 170+new a 171diff --git a/b.go b/b.go 172--- a/b.go 173+++ b/b.go 174@@ -1,1 +1,1 @@ 175-old b 176+new b 177` 178 files := parseDiff(t, src) 179 nd := niceDiffFromParsed(files, types.Commit{}, types.DiffStat{}) 180 got := renderUnifiedDiff(nd) 181 182 for _, want := range []string{"diff --git a/a.go b/a.go", "diff --git a/b.go b/b.go"} { 183 if !strings.Contains(got, want) { 184 t.Errorf("missing %q in output:\n%s", want, got) 185 } 186 } 187} 188 189func TestRenderFormatPatch_nil(t *testing.T) { 190 if got := renderFormatPatch(nil); got != "" { 191 t.Errorf("expected empty string for nil NiceDiff, got %q", got) 192 } 193} 194 195func TestRenderFormatPatch_headers(t *testing.T) { 196 when := time.Date(2024, 3, 15, 10, 30, 0, 0, time.UTC) 197 hash := plumbing.NewHash("abc1234567890000000000000000000000000000") 198 199 nd := &types.NiceDiff{ 200 Commit: types.Commit{ 201 Hash: hash, 202 Message: "Fix the bug\n\nThis patch resolves the long-standing issue.\n", 203 Author: object.Signature{ 204 Name: "Alice Dev", 205 Email: "alice@example.com", 206 When: when, 207 }, 208 }, 209 Stat: types.DiffStat{FilesChanged: 1, Insertions: 2, Deletions: 1}, 210 } 211 212 got := renderFormatPatch(nd) 213 214 checks := []string{ 215 "From abc1234567890000000000000000000000000000 Mon Sep 17 00:00:00 2001\n", 216 "From: Alice Dev <alice@example.com>\n", 217 "Date: Fri, 15 Mar 2024 10:30:00 +0000\n", 218 "Subject: [PATCH] Fix the bug\n", 219 "This patch resolves the long-standing issue.\n", 220 "---\n", 221 " 1 file(s) changed, 2 insertion(s)(+), 1 deletion(s)(-)\n", 222 "\n--\ntangled.sh\n", 223 } 224 for _, want := range checks { 225 if !strings.Contains(got, want) { 226 t.Errorf("renderFormatPatch output missing %q\ngot:\n%s", want, got) 227 } 228 } 229} 230 231func TestRenderFormatPatch_subjectOnly(t *testing.T) { 232 // Single-line message (no body) should not emit a blank body section. 233 nd := &types.NiceDiff{ 234 Commit: types.Commit{ 235 Message: "Single line commit", 236 Author: object.Signature{When: time.Now()}, 237 }, 238 } 239 got := renderFormatPatch(nd) 240 241 if !strings.Contains(got, "Subject: [PATCH] Single line commit\n") { 242 t.Errorf("missing subject in output:\n%s", got) 243 } 244 // Body should not appear between Subject and "---" 245 parts := strings.SplitN(got, "---\n", 2) 246 if len(parts) < 2 { 247 t.Fatalf("expected '---' separator in output:\n%s", got) 248 } 249 beforeSep := parts[0] 250 // Only the blank line between headers and body should be there, no extra content. 251 afterSubject := strings.SplitN(beforeSep, "Subject: [PATCH] Single line commit\n", 2) 252 if len(afterSubject) == 2 && strings.TrimSpace(afterSubject[1]) != "" { 253 t.Errorf("unexpected body content before '---': %q", afterSubject[1]) 254 } 255} 256 257func TestRenderFormatPatch_containsDiff(t *testing.T) { 258 const src = `diff --git a/foo.go b/foo.go 259--- a/foo.go 260+++ b/foo.go 261@@ -1,2 +1,2 @@ 262 package main 263-// old 264+// new 265` 266 files := parseDiff(t, src) 267 nd := niceDiffFromParsed(files, types.Commit{ 268 Author: object.Signature{When: time.Now()}, 269 }, types.DiffStat{FilesChanged: 1, Insertions: 1, Deletions: 1}) 270 271 got := renderFormatPatch(nd) 272 273 checks := []string{ 274 "diff --git a/foo.go b/foo.go\n", 275 "-// old\n", 276 "+// new\n", 277 " foo.go |", 278 } 279 for _, want := range checks { 280 if !strings.Contains(got, want) { 281 t.Errorf("renderFormatPatch output missing %q\ngot:\n%s", want, got) 282 } 283 } 284}