internal/control/buildlistpage_test.go

8dcfa45a8ac03a5ff9c36828274d05acadcf846c
gitbay/internal/control/buildlistpage_test.go history · blame · raw

139 lines · 4229 bytes

  1package control
  2
  3import (
  4	"bytes"
  5	"encoding/json"
  6	"os"
  7	"path/filepath"
  8	"strings"
  9	"testing"
 10
 11	"gitbay.org/gitbay/internal/protocol"
 12	"gitbay.org/gitbay/internal/store"
 13)
 14
 15// A run row led with a sha and said nothing about what the commit was
 16// (#241). build list carries the subject now, for every surface at once.
 17func TestBuildListCarriesCommitSubjects(t *testing.T) {
 18	st, repo, uid := newQueueTestRepo(t)
 19	git := gitRunner(t)
 20	root := t.TempDir()
 21
 22	src := filepath.Join(root, "src")
 23	os.MkdirAll(src, 0o755)
 24	git(root, "init", "-q", "-b", "main", "src")
 25	os.WriteFile(filepath.Join(src, "a"), []byte("one\n"), 0o644)
 26	git(src, "add", ".")
 27	git(src, "commit", "-qm", "runner: cap a build's container")
 28	sha := strings.TrimSpace(git(src, "rev-parse", "HEAD"))
 29
 30	dir := RepoDir(root, repo.OwnerName, repo.Name)
 31	os.MkdirAll(filepath.Dir(dir), 0o755)
 32	git(root, "clone", "-q", "--bare", src, dir)
 33
 34	if _, err := st.CreateBuild(repo.ID, "unit", sha, "main", `["true"]`, "", "", true); err != nil {
 35		t.Fatal(err)
 36	}
 37	// A build whose commit is gone, as a force-push leaves behind.
 38	if _, err := st.CreateBuild(repo.ID, "lint", strings.Repeat("1", 40), "main", `["true"]`, "", "", true); err != nil {
 39		t.Fatal(err)
 40	}
 41
 42	c, errOut := pruneCtx(st, root, store.User{ID: uid})
 43	c.JSON = true
 44	if code := Dispatch(c, []string{"build", "list", repo.Path()}); code != protocol.ExitOK {
 45		t.Fatalf("build list: exit %d: %s", code, errOut.String())
 46	}
 47	var got []BuildOut
 48	decodeData(t, c.Stdout.(*bytes.Buffer).Bytes(), &got)
 49	if len(got) != 2 {
 50		t.Fatalf("builds: %+v", got)
 51	}
 52	for _, b := range got {
 53		switch b.Job {
 54		case "unit":
 55			if b.Subject != "runner: cap a build's container" {
 56				t.Errorf("unit subject = %q", b.Subject)
 57			}
 58		case "lint":
 59			if b.Subject != "" {
 60				t.Errorf("a build whose commit is gone claims a subject: %q", b.Subject)
 61			}
 62		}
 63	}
 64}
 65
 66// The builds list was capped at 50 with no way past it, so filtering to a
 67// sparse status reached further back and appeared to raise the total
 68// (#244). --limit and --cursor page it like every other list command.
 69func TestBuildListPages(t *testing.T) {
 70	st, repo, uid := newQueueTestRepo(t)
 71	for i := 0; i < 5; i++ {
 72		if _, err := st.CreateBuild(repo.ID, "unit", "aaa", "main", `["true"]`, "", "", true); err != nil {
 73			t.Fatal(err)
 74		}
 75	}
 76	c, errOut := pruneCtx(st, t.TempDir(), store.User{ID: uid})
 77	c.JSON = true
 78
 79	page := func(args ...string) (nums []int64, next string) {
 80		t.Helper()
 81		out := c.Stdout.(*bytes.Buffer)
 82		out.Reset()
 83		errOut.Reset()
 84		argv := append([]string{"build", "list", repo.Path()}, args...)
 85		if code := Dispatch(c, argv); code != protocol.ExitOK {
 86			t.Fatalf("build list %v: exit %d: %s", args, code, errOut.String())
 87		}
 88		var got struct {
 89			Items []BuildOut `json:"items"`
 90			Next  string     `json:"next"`
 91		}
 92		decodeData(t, out.Bytes(), &got)
 93		for _, b := range got.Items {
 94			nums = append(nums, b.Number)
 95		}
 96		return nums, got.Next
 97	}
 98
 99	nums, next := page("--limit", "2")
100	if len(nums) != 2 || nums[0] != 5 || nums[1] != 4 {
101		t.Fatalf("first page: %v", nums)
102	}
103	if next == "" {
104		t.Fatal("first page offers no cursor with three builds left")
105	}
106	nums, next = page("--limit", "2", "--cursor", next)
107	if len(nums) != 2 || nums[0] != 3 || nums[1] != 2 {
108		t.Fatalf("second page: %v", nums)
109	}
110	nums, next = page("--limit", "2", "--cursor", next)
111	if len(nums) != 1 || nums[0] != 1 {
112		t.Fatalf("last page: %v", nums)
113	}
114	if next != "" {
115		t.Errorf("last page offers a cursor: %q", next)
116	}
117
118	// A cursor minted by another command is not a build cursor.
119	out := c.Stdout.(*bytes.Buffer)
120	out.Reset()
121	errOut.Reset()
122	if code := Dispatch(c, []string{"build", "list", repo.Path(), "--cursor", encodeCursor("issue", "3")}); code != protocol.ExitUsage {
123		t.Fatalf("foreign cursor: exit %d, want %d", code, protocol.ExitUsage)
124	}
125}
126
127// decodeData unwraps the protocol envelope the JSON emitters write.
128func decodeData(t *testing.T, b []byte, into any) {
129	t.Helper()
130	var env struct {
131		Data json.RawMessage `json:"data"`
132	}
133	if err := json.Unmarshal(b, &env); err != nil {
134		t.Fatalf("envelope: %v\n%s", err, b)
135	}
136	if err := json.Unmarshal(env.Data, into); err != nil {
137		t.Fatalf("data: %v\n%s", err, env.Data)
138	}
139}