internal/httpd/queries_test.go
117 lines · 3567 bytes
1 symbol in this file
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, `"`)], "&", "&")
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}