internal/httpd/buildpages_test.go
206 lines · 7061 bytes
1package httpd
2
3import (
4 "gitbay.org/gitbay/internal/control"
5 "strings"
6 "testing"
7
8 "gitbay.org/gitbay/internal/store"
9 "gitbay.org/gitbay/internal/web"
10)
11
12func testRepoPage() repoPage {
13 var p repoPage
14 p.Repo = store.Repo{OwnerName: "krz", Name: "gitbay", DefaultBranch: "main"}
15 p.Host = "gitbay.org"
16 return p
17}
18
19// The build pages read their data from the build commands' JSON now, not
20// from store.Build. A renamed field would be a blank cell rather than a
21// compile error, so render both pages and look for the values.
22func TestBuildsPageRendersCommandOutput(t *testing.T) {
23 var sb strings.Builder
24 builds := []control.BuildOut{{
25 Number: 60, Job: "build", Status: "success",
26 SHA: "ff6271a9d4570cd46f169091637a9d2e40ad5c2b",
27 Ref: "cli-coverage", CreatedAt: "2026-08-28T04:42:54Z",
28 }}
29 jobs := []control.JobOut{{Name: "build"}, {Name: "nightly", Schedule: "0 3 * * *"}}
30 filter := buildFilter{}
31 err := web.Render(&sb, "builds.html", struct {
32 repoPage
33 Builds []control.BuildOut
34 Jobs []control.JobOut
35 Runs []buildRun
36 Filter buildFilter
37 Facets []facetGroup
38 Refs []string
39 Older string
40 CanWrite bool
41 Notice string
42 }{
43 testRepoPage(), builds, jobs, groupRuns(builds), filter, nil,
44 distinctRefs(builds, filter.Ref), "", true, "",
45 })
46 if err != nil {
47 t.Fatalf("render: %v", err)
48 }
49 out := sb.String()
50 for _, want := range []string{
51 "build", "success", "cli-coverage", "ff6271a9d4",
52 `value="build"`, `value="nightly"`, "schedule 0 3 * * *",
53 } {
54 if !strings.Contains(out, want) {
55 t.Errorf("builds.html missing %q", want)
56 }
57 }
58}
59
60func TestBuildPageRendersCommandOutput(t *testing.T) {
61 var sb strings.Builder
62 err := web.Render(&sb, "build.html", struct {
63 repoPage
64 Build control.BuildOut
65 Log string
66 CanWrite bool
67 Notice string
68 }{
69 testRepoPage(),
70 control.BuildOut{
71 Number: 60, Job: "build", Status: "success",
72 SHA: "ff6271a9d4570cd46f169091637a9d2e40ad5c2b", Ref: "cli-coverage",
73 CreatedAt: "2026-08-28T04:42:54Z", FinishedAt: "2026-08-28T04:43:06Z",
74 },
75 "step 1 ok",
76 true, "",
77 })
78 if err != nil {
79 t.Fatalf("render: %v", err)
80 }
81 out := sb.String()
82 for _, want := range []string{
83 "Build 60", "success", "build on cli-coverage", "ff6271a9d4",
84 "2026-08-28 04:42", "2026-08-28 04:43", "step 1 ok",
85 } {
86 if !strings.Contains(out, want) {
87 t.Errorf("build.html missing %q", want)
88 }
89 }
90 // A finished build offers no cancel control, even to a writer.
91 if strings.Contains(out, "/cancel") {
92 t.Error("build.html offers cancel on a finished build")
93 }
94}
95
96// The count under the heading said "15 runs" while listing 23 builds
97// (#240). It names both numbers now, so neither is mistaken for the other.
98func TestBuildsPageCountsBuildsAndRuns(t *testing.T) {
99 builds := []control.BuildOut{
100 {Number: 4, Job: "instances", Status: "success", SHA: "aaa", Ref: "main", CreatedAt: "2026-09-20T06:00:00Z"},
101 {Number: 3, Job: "instances", Status: "success", SHA: "aaa", Ref: "main", CreatedAt: "2026-09-19T06:00:00Z"},
102 {Number: 2, Job: "lint", Status: "success", SHA: "aaa", Ref: "main", CreatedAt: "2026-09-12T11:20:03Z"},
103 {Number: 1, Job: "unit", Status: "success", SHA: "aaa", Ref: "main", CreatedAt: "2026-09-12T11:20:03Z"},
104 }
105 var sb strings.Builder
106 filter := buildFilter{}
107 err := web.Render(&sb, "builds.html", struct {
108 repoPage
109 Builds []control.BuildOut
110 Jobs []control.JobOut
111 Runs []buildRun
112 Filter buildFilter
113 Facets []facetGroup
114 Refs []string
115 Older string
116 CanWrite bool
117 Notice string
118 }{
119 testRepoPage(), builds, nil, groupRuns(builds), filter, nil,
120 distinctRefs(builds, filter.Ref), "", true, "",
121 })
122 if err != nil {
123 t.Fatalf("render: %v", err)
124 }
125 if !strings.Contains(sb.String(), "4 builds in 3 runs") {
126 t.Errorf("builds.html count line: %q", sb.String())
127 }
128}
129
130// A run row led with a ten-character sha and nothing said what the commit
131// was (#241). The subject leads now, the sha follows as metadata, and a
132// build whose commit is gone falls back to the sha alone.
133func TestBuildsPageLeadsWithTheSubject(t *testing.T) {
134 builds := []control.BuildOut{
135 {Number: 2, Job: "unit", Status: "success", SHA: "ff6271a9d4570cd46f169091637a9d2e40ad5c2b",
136 Ref: "main", CreatedAt: "2026-09-20T06:00:00Z", Subject: "runner: cap a build's container"},
137 {Number: 1, Job: "unit", Status: "failure", SHA: "aa11bb22cc33dd44ee55ff6677889900aabbccdd",
138 Ref: "main", CreatedAt: "2026-09-19T06:00:00Z"},
139 }
140 out := renderBuilds(t, builds, buildFilter{}, "?cursor=abc")
141 if !strings.Contains(out, "runner: cap a build's container") {
142 t.Errorf("builds.html does not lead with the subject:\n%s", out)
143 }
144 if !strings.Contains(out, "<code>ff6271a9d4</code>") {
145 t.Errorf("builds.html drops the sha from the metadata line:\n%s", out)
146 }
147 // The commit is gone, so the sha is all there is to name the row by.
148 if !strings.Contains(out, ">aa11bb22cc</a>") {
149 t.Errorf("a subjectless run does not fall back to its sha:\n%s", out)
150 }
151}
152
153// Filtering appeared to raise the run count because the list was capped
154// at 50 with the window unstated (#244). The page is paged now: the count
155// says it counts this page, and the link to the next one carries every
156// filter.
157func TestBuildsPagePagesAndSaysSo(t *testing.T) {
158 builds := []control.BuildOut{{Number: 1, Job: "unit", Status: "failure", SHA: "aaa", Ref: "main", CreatedAt: "2026-09-19T06:00:00Z"}}
159 out := renderBuilds(t, builds, buildFilter{Status: "failure"}, "?cursor=abc&status=failure")
160 if !strings.Contains(out, "1 build in 1 run on this page") {
161 t.Errorf("count line does not name the page:\n%s", out)
162 }
163 if !strings.Contains(out, `href="?cursor=abc&status=failure"`) {
164 t.Errorf("pager link missing or drops the filter:\n%s", out)
165 }
166 // With everything on one page the count is the whole count.
167 if out := renderBuilds(t, builds, buildFilter{}, ""); strings.Contains(out, "on this page") {
168 t.Errorf("an unpaged listing still hedges the count:\n%s", out)
169 }
170}
171
172func TestOlderBuildsCarriesFilters(t *testing.T) {
173 if got := olderBuilds(buildFilter{Ref: "main"}, ""); got != "" {
174 t.Errorf("no next cursor should mean no link, got %q", got)
175 }
176 got := olderBuilds(buildFilter{Ref: "feature/x", Status: "failure", Job: "unit"}, "c1")
177 for _, want := range []string{"cursor=c1", "ref=feature%2Fx", "status=failure", "job=unit"} {
178 if !strings.Contains(got, want) {
179 t.Errorf("olderBuilds = %q, missing %q", got, want)
180 }
181 }
182}
183
184func renderBuilds(t *testing.T, builds []control.BuildOut, filter buildFilter, older string) string {
185 t.Helper()
186 var sb strings.Builder
187 err := web.Render(&sb, "builds.html", struct {
188 repoPage
189 Builds []control.BuildOut
190 Jobs []control.JobOut
191 Runs []buildRun
192 Filter buildFilter
193 Facets []facetGroup
194 Refs []string
195 Older string
196 CanWrite bool
197 Notice string
198 }{
199 testRepoPage(), builds, nil, groupRuns(builds), filter, nil,
200 distinctRefs(builds, filter.Ref), older, true, "",
201 })
202 if err != nil {
203 t.Fatalf("render: %v", err)
204 }
205 return sb.String()
206}