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 · 2h ago 4b8c9e1d
history blame raw 4.7 KiB
1package index
2
3// A contributor count has to be the same story the metadata branch tells. These
4// tests hold the four rules apart: a merged change credits its author, the issues
5// it closed credit both the fixer and the reporter, a stale approval is not a
6// review, and an unlanded promise to close an issue counts for nothing.
7
8import (
9 "strings"
10 "testing"
11 "time"
12
13 "rodent.software/vole/core/git"
14 "rodent.software/vole/metadata"
15)
16
17func TestContributorSummariesCountsTheFourKindsOfWork(t *testing.T) {
18 t.Parallel()
19 s, m, g := incrementalRepo(t)
20 now := time.Date(2026, 11, 1, 12, 0, 0, 0, time.UTC)
21
22 newIssue := func(title, author string) *metadata.Issue {
23 t.Helper()
24 iss, err := metadata.NewIssue(title, "body", author, nil, nil, now)
25 if err != nil {
26 t.Fatal(err)
27 }
28 if err := metadata.CreateIssue(g, iss, author); err != nil {
29 t.Fatal(err)
30 }
31 now = now.Add(time.Minute)
32 return iss
33 }
34 reportedByBob := newIssue("bob's report", "bob")
35 reportedByAlice := newIssue("alice's own report", "alice")
36 reportedByDave := newIssue("dave's report", "dave")
37 // Closing an issue by hand is not a landing, so it proves no fix.
38 if _, err := metadata.SetIssueState(g, reportedByDave.ID, "closed", "dave"); err != nil {
39 t.Fatal(err)
40 }
41
42 head := requestHead
43 moved := strings.Repeat("b", 40)
44
45 // Alice fixes both reports. The request closes the issue Dave opened in a
46 // different request, which never lands.
47 req, err := metadata.NewRequest("fix two reports", "why", "alice", "main", head, now)
48 if err != nil {
49 t.Fatal(err)
50 }
51 req.Closes = []string{reportedByBob.ID, reportedByAlice.ID}
52 if err := metadata.CreateRequest(g, req, "alice"); err != nil {
53 t.Fatal(err)
54 }
55 // Dave reviewed the first head. Alice pushes a new one afterwards, so his
56 // approval no longer names what merges.
57 if _, err := metadata.ApproveRequest(g, req.ID, "dave", "main", head, now, "dave"); err != nil {
58 t.Fatal(err)
59 }
60 if _, err := metadata.SetRequestHead(g, req.ID, moved, "", "alice"); err != nil {
61 t.Fatal(err)
62 }
63 if _, err := metadata.ApproveRequest(g, req.ID, "carol", "main", moved, now, "carol"); err != nil {
64 t.Fatal(err)
65 }
66 if _, _, err := metadata.MarkMerged(g, req.ID, head, moved, moved, "carol", now); err != nil {
67 t.Fatal(err)
68 }
69
70 // Dave promises to close his own report, but the request stays open.
71 open, err := metadata.NewRequest("dave's fix", "why", "dave", "main", head, now)
72 if err != nil {
73 t.Fatal(err)
74 }
75 open.Closes = []string{reportedByDave.ID}
76 if err := metadata.CreateRequest(g, open, "dave"); err != nil {
77 t.Fatal(err)
78 }
79
80 if err := s.Ensure(m); err != nil {
81 t.Fatal(err)
82 }
83 sums, err := s.ContributorSummaries(m.ID)
84 if err != nil {
85 t.Fatal(err)
86 }
87
88 alice := sums["alice"]
89 if alice.Merged != 1 || alice.Fixed != 2 || alice.Reported != 1 || alice.Reviews != 0 {
90 t.Fatalf("alice = %+v, want 1 merged, 2 fixed, 1 reported", alice)
91 }
92 if alice.Total() != 4 {
93 t.Fatalf("alice total = %d, want 4", alice.Total())
94 }
95 if bob := sums["bob"]; bob.Reported != 1 || bob.Merged != 0 {
96 t.Fatalf("bob = %+v, want only the one report that was fixed", bob)
97 }
98 if carol := sums["carol"]; carol.Reviews != 1 {
99 t.Fatalf("carol = %+v, want one review that gated the landing", carol)
100 }
101 // Dave's review was of an old head, his report was closed by hand, and his
102 // change never landed. There is no row to show.
103 if dave, ok := sums["dave"]; ok {
104 t.Fatalf("dave = %+v, want no counted work", dave)
105 }
106}
107
108func TestContributorSummariesIsOneRepository(t *testing.T) {
109 t.Parallel()
110 s, m, g := incrementalRepo(t)
111 other, og := newSecondRepo(t, s, "other")
112 now := time.Date(2026, 11, 1, 12, 0, 0, 0, time.UTC)
113
114 landed := func(repo *git.Repo, author string) {
115 t.Helper()
116 req, err := metadata.NewRequest("landed in "+author, "why", author, "main", requestHead, now)
117 if err != nil {
118 t.Fatal(err)
119 }
120 if err := metadata.CreateRequest(repo, req, author); err != nil {
121 t.Fatal(err)
122 }
123 if _, _, err := metadata.MarkMerged(repo, req.ID, requestHead, requestHead, requestHead, author, now); err != nil {
124 t.Fatal(err)
125 }
126 }
127 landed(g, "alice")
128 landed(og, "bob")
129
130 if err := s.Ensure(m); err != nil {
131 t.Fatal(err)
132 }
133 if err := s.Ensure(other); err != nil {
134 t.Fatal(err)
135 }
136
137 demo, err := s.ContributorSummaries(m.ID)
138 if err != nil {
139 t.Fatal(err)
140 }
141 if demo["alice"].Merged != 1 {
142 t.Fatalf("demo = %+v, want alice's one merged change", demo)
143 }
144 if _, ok := demo["bob"]; ok {
145 t.Fatalf("demo inherited the other repository's work: %+v", demo)
146 }
147
148 otherSums, err := s.ContributorSummaries(other.ID)
149 if err != nil {
150 t.Fatal(err)
151 }
152 if otherSums["bob"].Merged != 1 {
153 t.Fatalf("other = %+v, want bob's one merged change", otherSums)
154 }
155 if _, ok := otherSums["alice"]; ok {
156 t.Fatalf("the other repository inherited demo's work: %+v", otherSums)
157 }
158}
159