internal/httpd/queries_test.go

v1.43.0
gitbay/internal/httpd/queries_test.go history · blame · raw

117 lines · 3567 bytes

  1package httpd
  2
  3import (
  4	"net/http"
  5	"net/http/httptest"
  6	"strings"
  7	"testing"
  8
  9	"gitbay.org/gitbay/internal/config"
 10	"gitbay.org/gitbay/internal/store"
 11)
 12
 13// A pinned query shows on the dashboard and its page lists what it
 14// matches, one page at a time, never another user's private rows.
 15func TestSavedQueryPages(t *testing.T) {
 16	st, err := store.Open(":memory:")
 17	if err != nil {
 18		t.Fatal(err)
 19	}
 20	defer st.Close()
 21	if err := st.MigrateUp(); err != nil {
 22		t.Fatal(err)
 23	}
 24	alice, err := st.CreateUser("alice", false)
 25	if err != nil {
 26		t.Fatal(err)
 27	}
 28	bob, err := st.CreateUser("bob", false)
 29	if err != nil {
 30		t.Fatal(err)
 31	}
 32	pub, err := st.CreateRepo("user", alice, "pub", "public")
 33	if err != nil {
 34		t.Fatal(err)
 35	}
 36	secret, err := st.CreateRepo("user", bob, "secret", "private")
 37	if err != nil {
 38		t.Fatal(err)
 39	}
 40	for i := 0; i < queryPerPage+1; i++ {
 41		if _, err := st.CreateIssue(pub, alice, "public issue", "", "md"); err != nil {
 42			t.Fatal(err)
 43		}
 44	}
 45	if _, err := st.CreateIssue(secret, bob, "bob's secret", "", "md"); err != nil {
 46		t.Fatal(err)
 47	}
 48	if err := st.SaveQuery(alice, "open", "is:open", false); err != nil {
 49		t.Fatal(err)
 50	}
 51	if err := st.PinSavedQuery(alice, "open", true); err != nil {
 52		t.Fatal(err)
 53	}
 54	cfg := config.Default()
 55	cfg.Web.Mode = "accounts"
 56	s := New(cfg, st, nil)
 57	viewer := store.User{ID: alice, Username: "alice"}
 58
 59	get := func(path string) *httptest.ResponseRecorder {
 60		rr := httptest.NewRecorder()
 61		req := httptest.NewRequest("GET", path, nil)
 62		if owner, rest, ok := strings.Cut(strings.TrimPrefix(req.URL.Path, "/"), "/-/queries"); ok {
 63			req.SetPathValue("owner", owner)
 64			req.SetPathValue("name", strings.TrimPrefix(rest, "/"))
 65		}
 66		if req.URL.Path == "/" {
 67			s.dashboard(rr, req, viewer)
 68		} else {
 69			s.queriesPage(rr, req, viewer)
 70		}
 71		return rr
 72	}
 73
 74	dash := get("/").Body.String()
 75	for _, want := range []string{
 76		`<a href="/alice/-/queries/open">open</a> <span class="count">51</span>`,
 77		`<code>is:open</code>`,
 78		`<a href="/alice/-/queries/open">all 51 →</a>`,
 79	} {
 80		if !strings.Contains(dash, want) {
 81			t.Errorf("dashboard lacks %q", want)
 82		}
 83	}
 84
 85	first := get("/alice/-/queries/open")
 86	if first.Code != http.StatusOK {
 87		t.Fatalf("query page: %d %s", first.Code, first.Body.String())
 88	}
 89	body := first.Body.String()
 90	if n := strings.Count(body, `<span class="repo">alice/pub#`); n != queryPerPage {
 91		t.Errorf("first page lists %d rows, want %d", n, queryPerPage)
 92	}
 93	if strings.Contains(body, "secret") || strings.Contains(dash, "secret") {
 94		t.Error("a private repository of another user reached the page")
 95	}
 96	i := strings.Index(body, `<p class="pager"><a href="?cursor=`)
 97	if i < 0 {
 98		t.Fatalf("no next link:\n%s", body)
 99	}
100	href := body[i+len(`<p class="pager"><a href="`):]
101	href = strings.ReplaceAll(href[:strings.Index(href, `"`)], "&amp;", "&")
102	second := get("/alice/-/queries/open" + href).Body.String()
103	if n := strings.Count(second, `<span class="repo">alice/pub#`); n != 1 || !strings.Contains(second, `alice/pub#1<`) {
104		t.Errorf("second page lists %d rows, want the oldest one", n)
105	}
106
107	if rr := get("/alice/-/queries/nosuch"); rr.Code != http.StatusNotFound {
108		t.Errorf("unknown query: %d, want 404", rr.Code)
109	}
110	if rr := get("/bob/-/queries/open"); rr.Code != http.StatusNotFound {
111		t.Errorf("alice under bob's name: %d, want 404", rr.Code)
112	}
113	list := get("/alice/-/queries").Body.String()
114	if !strings.Contains(list, `<a href="/alice/-/queries/open">open</a> <span class="chip">pinned</span>`) {
115		t.Errorf("query list lacks the pinned query:\n%s", list)
116	}
117}