Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Code

392 commit(s) on main

Merge change request 01M70WES5ZZDJ6RP226WB11BBZ: Let people rename and remove their own passkeys keenan · 16m ago 4b8c9e1d
history blame raw 6.1 KiB
1package web
2
3import (
4 "strings"
5 "testing"
6)
7
8// The parser reads what git prints. These fixtures are real `git diff` output,
9// because a hand-written approximation is what lets a parser drift from git.
10const patchTwoFiles = `diff --git a/readme.md b/readme.md
11index 1111111..2222222 100644
12--- a/readme.md
13+++ b/readme.md
14@@ -1,3 +1,4 @@
15 # Title
16
17-old line
18+new line
19+another line
20diff --git a/notes.txt b/notes.txt
21deleted file mode 100644
22index 3333333..0000000
23--- a/notes.txt
24+++ /dev/null
25@@ -1,2 +0,0 @@
26-gone
27-gone too
28`
29
30func TestParseDiff(t *testing.T) {
31 t.Parallel()
32 files := parseDiff(patchTwoFiles)
33 if len(files) != 2 {
34 t.Fatalf("parsed %d files, want 2: %+v", len(files), files)
35 }
36 first := files[0]
37 if first.OldPath != "readme.md" || first.NewPath != "readme.md" {
38 t.Fatalf("paths = %q -> %q", first.OldPath, first.NewPath)
39 }
40 if first.Added != 2 || first.Removed != 1 {
41 t.Fatalf("counts = +%d −%d, want +2 −1", first.Added, first.Removed)
42 }
43 if first.Language != "markdown" {
44 t.Fatalf("readme language = %q, want markdown", first.Language)
45 }
46 if len(first.Hunks) != 1 || len(first.Hunks[0].Lines) != 5 {
47 t.Fatalf("hunk shape = %+v", first.Hunks)
48 }
49 lines := first.Hunks[0].Lines
50 if lines[0].Kind != "ctx" || lines[0].Text != "# Title" || lines[0].OldNo != 1 || lines[0].NewNo != 1 {
51 t.Fatalf("first line = %+v", lines[0])
52 }
53 if lines[2].Kind != "del" || lines[2].OldNo != 3 || lines[2].NewNo != 0 {
54 t.Fatalf("deleted line = %+v", lines[2])
55 }
56 if lines[3].Kind != "add" || lines[3].NewNo != 3 || lines[3].Text != "new line" {
57 t.Fatalf("added line = %+v", lines[3])
58 }
59 second := files[1]
60 if second.NewPath != "" || second.Label() != "notes.txt" {
61 t.Fatalf("deletion = %+v, label %q", second, second.Label())
62 }
63 if second.Removed != 2 {
64 t.Fatalf("deletion counts = −%d", second.Removed)
65 }
66 if second.Language != "" {
67 t.Fatalf("plain-text deletion language = %q, want none", second.Language)
68 }
69}
70
71// A deletion has no new path, so its language comes from the old one. A rename
72// uses the new name, which is the file a reviewer sees in the review tree.
73func TestDiffLanguageUsesTheVisiblePath(t *testing.T) {
74 t.Parallel()
75 files := parseDiff(`diff --git a/gone.go b/gone.go
76deleted file mode 100644
77--- a/gone.go
78+++ /dev/null
79@@ -1 +0,0 @@
80-package gone
81diff --git a/old.py b/new.py
82similarity index 90%
83rename from old.py
84rename to new.py
85--- a/old.py
86+++ b/new.py
87@@ -1 +1 @@
88-old
89+new
90`)
91 if len(files) != 2 {
92 t.Fatalf("parsed %d files, want 2: %+v", len(files), files)
93 }
94 if files[0].Language != "go" {
95 t.Fatalf("deletion language = %q, want go", files[0].Language)
96 }
97 if files[1].Language != "python" {
98 t.Fatalf("rename language = %q, want python", files[1].Language)
99 }
100}
101
102func TestParseDiffOfABinaryFile(t *testing.T) {
103 t.Parallel()
104 files := parseDiff(`diff --git a/logo.png b/logo.png
105index 4444444..5555555 100644
106Binary files a/logo.png and b/logo.png differ
107`)
108 if len(files) != 1 || !files[0].Binary {
109 t.Fatalf("binary file not recognised: %+v", files)
110 }
111}
112
113func TestParseDiffOfANewFile(t *testing.T) {
114 t.Parallel()
115 files := parseDiff(`diff --git a/new.txt b/new.txt
116new file mode 100644
117index 0000000..6666666
118--- /dev/null
119+++ b/new.txt
120@@ -0,0 +1,2 @@
121+one
122+two
123`)
124 if len(files) != 1 {
125 t.Fatalf("files = %+v", files)
126 }
127 f := files[0]
128 if f.OldPath != "" || f.NewPath != "new.txt" || f.Added != 2 {
129 t.Fatalf("new file = %+v", f)
130 }
131}
132
133func TestParseDiffOfARename(t *testing.T) {
134 t.Parallel()
135 files := parseDiff(`diff --git a/old.md b/new.md
136similarity index 100%
137rename from old.md
138rename to new.md
139`)
140 if len(files) != 1 {
141 t.Fatalf("files = %+v", files)
142 }
143 // A pure rename has no ---/+++ lines, so both paths are empty and the file is
144 // shown under the name git gave it in the diff header.
145 if files[0].OldPath != "" || files[0].NewPath != "" {
146 t.Fatalf("rename paths = %+v", files[0])
147 }
148}
149
150func TestParseDiffIgnoresPlumbing(t *testing.T) {
151 t.Parallel()
152 files := parseDiff(`diff --git a/app.go b/app.go
153index aaa..bbb 100644
154--- a/app.go
155+++ b/app.go
156@@ -10,3 +10,3 @@ func main() {
157 ctx := context.Background()
158- run(ctx)
159+ run(ctx, cfg)
160`)
161 got := files[0].Hunks[0].Lines
162 for _, l := range got {
163 if strings.HasPrefix(l.Text, "index aaa") {
164 t.Fatalf("plumbing leaked into the rendered lines: %+v", got)
165 }
166 }
167 if got[1].OldNo != 11 || got[2].NewNo != 11 {
168 t.Fatalf("line numbers = %+v", got)
169 }
170}
171
172func TestParseDiffToleratesAPatchThatEndsMidHunk(t *testing.T) {
173 t.Parallel()
174 // A patch can end mid-hunk, whether git was interrupted or a caller handed
175 // the parser an excerpt: it must render what it has, not panic or claim the
176 // missing lines were unchanged.
177 files := parseDiff(`diff --git a/big.txt b/big.txt
178--- a/big.txt
179+++ b/big.txt
180@@ -1,2 +1,2 @@
181- first
182+ second`)
183 if len(files) != 1 {
184 t.Fatalf("files = %+v", files)
185 }
186 if files[0].Added != 1 || files[0].Removed != 1 {
187 t.Fatalf("counts = %+v", files[0])
188 }
189}
190
191func TestDiffSummary(t *testing.T) {
192 t.Parallel()
193 added, removed := diffSummary(parseDiff(patchTwoFiles))
194 if added != 2 || removed != 3 {
195 t.Fatalf("summary = +%d −%d, want +2 −3", added, removed)
196 }
197}
198
199// TestPathspecNamesTheSideThatExists: git refuses an empty pathspec, and a
200// deletion has only an old side. Handing it both sides used to fail the page for
201// any deleted file; handing it none would print the whole commit instead.
202func TestPathspecNamesTheSideThatExists(t *testing.T) {
203 t.Parallel()
204 cases := []struct {
205 name string
206 file diffFile
207 want []string
208 }{
209 {"modified", diffFile{OldPath: "a.go", NewPath: "a.go"}, []string{"a.go"}},
210 {"added", diffFile{NewPath: "a.go"}, []string{"a.go"}},
211 {"deleted", diffFile{OldPath: "a.go"}, []string{"a.go"}},
212 {"renamed", diffFile{OldPath: "old.go", NewPath: "new.go"}, []string{"old.go", "new.go"}},
213 {"unset", diffFile{}, nil},
214 }
215 for _, tc := range cases {
216 t.Run(tc.name, func(t *testing.T) {
217 got := tc.file.pathspec()
218 if len(got) != len(tc.want) {
219 t.Fatalf("pathspec = %q, want %q", got, tc.want)
220 }
221 for i := range got {
222 if got[i] != tc.want[i] {
223 t.Fatalf("pathspec = %q, want %q", got, tc.want)
224 }
225 if got[i] == "" {
226 t.Fatalf("pathspec holds an empty path: %q", got)
227 }
228 }
229 })
230 }
231}
232