internal/httpd/buildpages_test.go

v1.34.0
gitbay/internal/httpd/buildpages_test.go history · blame · raw

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&amp;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}