internal/httpd/mrpage_test.go
133 lines · 4528 bytes
1package httpd
2
3import (
4 "html/template"
5 "strings"
6 "testing"
7 "time"
8
9 "gitbay.org/gitbay/internal/gitutil"
10 "gitbay.org/gitbay/internal/store"
11 "gitbay.org/gitbay/internal/web"
12)
13
14// mrPageData mirrors the anonymous struct the mr handler renders with.
15type mrPageData struct {
16 repoPage
17 MR store.MR
18 View string
19 BodyHTML template.HTML
20 Checks []store.Check
21 Combined string
22 Comments []renderedComment
23 Reviews []reviewRow
24 DiffFiles []diffFile
25 Stat diffStat
26 Commits []struct{}
27 Branches []gitutil.Ref
28 CanEdit bool
29 CanWrite bool
30 Unresolved int
31 Revisions []store.MRHead
32 Notice string
33 DetachedThreads []diffThread
34}
35
36func renderMR(t *testing.T, m store.MR, reviews []store.MRReview, checks []store.Check) string {
37 rows := make([]reviewRow, 0, len(reviews))
38 for _, r := range reviews {
39 // The page test renders reviews that count; whether a given
40 // reviewer's does is decided by access, which e2e covers.
41 rows = append(rows, reviewRow{MRReview: r, Counts: true})
42 }
43 t.Helper()
44 var sb strings.Builder
45 if err := web.Render(&sb, "mr.html", mrPageData{
46 repoPage: testRepoPage(), MR: m, View: "conversation",
47 Reviews: rows, Checks: checks, Combined: "",
48 }); err != nil {
49 t.Fatalf("render: %v", err)
50 }
51 return sb.String()
52}
53
54func testMR(state string) store.MR {
55 return store.MR{Number: 42, Title: "org native rendering", Author: "cmc", State: state,
56 SourcePath: "krz/hutch", SourceRef: "org-native-rendering", TargetRef: "main",
57 HeadSHA: "ff6271a9d4570cd46f169091637a9d2e40ad5c2b"}
58}
59
60// The header states what happened to the MR. "wants to merge" is only true
61// while it is still open.
62func TestMRHeaderByState(t *testing.T) {
63 open := renderMR(t, testMR("open"), nil, nil)
64 if !strings.Contains(open, "wants to merge") {
65 t.Errorf("open MR does not say wants to merge:\n%s", open)
66 }
67
68 merged := testMR("merged")
69 merged.MergedAt, merged.MergedBy = "2026-08-27T14:03:11.000Z", "cmc"
70 out := renderMR(t, merged, nil, nil)
71 for _, want := range []string{"merged", "krz/hutch:org-native-rendering", "2026-08-27 14:03"} {
72 if !strings.Contains(out, want) {
73 t.Errorf("merged header missing %q:\n%s", want, out)
74 }
75 }
76 if strings.Contains(out, "wants to merge") {
77 t.Errorf("merged MR still wants to merge:\n%s", out)
78 }
79
80 closed := testMR("closed")
81 closed.ClosedAt, closed.ClosedBy = "2026-08-27T14:03:11.000Z", "cmc"
82 out = renderMR(t, closed, nil, nil)
83 if !strings.Contains(out, "without merging") || strings.Contains(out, "wants to merge") {
84 t.Errorf("closed header:\n%s", out)
85 }
86
87 // Imports and pre-0029 merges carry no stamp; the wording drops the
88 // claim rather than inventing a time.
89 out = renderMR(t, testMR("merged"), nil, nil)
90 if strings.Contains(out, "wants to merge") || strings.Contains(out, " on 20") {
91 t.Errorf("unstamped merged header:\n%s", out)
92 }
93}
94
95// Approvals and checks carry their times in the aside, so reading the MR
96// does not mean opening the build.
97func TestMRAsideTimestamps(t *testing.T) {
98 out := renderMR(t, testMR("open"),
99 []store.MRReview{{Reviewer: "cmc", Verdict: "approve", CreatedAt: "2026-08-27T14:03:11.000Z"}},
100 []store.Check{
101 {CommitStatus: store.CommitStatus{Context: "ci/test", State: "success",
102 UpdatedAt: "2026-08-27T14:05:00.000Z"}, Duration: 72 * time.Second, Build: 60},
103 {CommitStatus: store.CommitStatus{Context: "external/lint", State: "success",
104 UpdatedAt: "2026-08-27T14:06:00.000Z"}},
105 })
106 for _, want := range []string{"2026-08-27 14:03", "2026-08-27 14:05", "1m12s", "2026-08-27 14:06"} {
107 if !strings.Contains(out, want) {
108 t.Errorf("aside missing %q:\n%s", want, out)
109 }
110 }
111}
112
113// A job a path filter excluded has no build behind its status: the page
114// must show it as skipped rather than linking to a build that never ran
115// (#172).
116func TestMRChecksRenderSkippedWithoutBuildLink(t *testing.T) {
117 out := renderMR(t, testMR("open"), nil, []store.Check{
118 {CommitStatus: store.CommitStatus{Context: "ci/unit", State: "skipped",
119 Description: "every changed file matched paths-ignore", UpdatedAt: "2026-08-27T14:05:00.000Z"}},
120 })
121 row := ""
122 for _, line := range strings.Split(out, "\n") {
123 if strings.Contains(line, "ci/unit") {
124 row = line
125 }
126 }
127 if row == "" || !strings.Contains(row, ">skipped<") {
128 t.Fatalf("skipped check not rendered:\n%s", out)
129 }
130 if strings.Contains(row, "<a href") {
131 t.Errorf("skipped check with no build linked anyway: %s", row)
132 }
133}