e2e/savedqueries_test.go

e6cd75b5f28bacf51620bb531320c30fd4e66bfd
gitbay/e2e/savedqueries_test.go history · blame · raw

156 lines · 5858 bytes

1 symbol in this file
  1package e2e
  2
  3import (
  4	"encoding/json"
  5	"os"
  6	"path/filepath"
  7	"strings"
  8	"testing"
  9)
 10
 11// Saved queries over stock ssh: a query spans the repositories the
 12// caller reads, never another user's private one, pages with the
 13// cursor, and a pinned one reaches the dashboard and its web page (#292).
 14func TestSavedQueries(t *testing.T) {
 15	t.Parallel()
 16	inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
 17	aliceKey := inst.newKey(t, "alice")
 18	bobKey := inst.newKey(t, "bob")
 19	inst.admin(t, "admin", "user", "create", "alice",
 20		"--key", aliceKey+".pub", "--email", "alice@example.test", "--verified")
 21	inst.admin(t, "admin", "user", "create", "bob", "--key", bobKey+".pub")
 22	ssh := func(key string, want int, args ...string) string {
 23		t.Helper()
 24		out, errOut, code := inst.ssh(t, key, "", args...)
 25		if code != want {
 26			t.Fatalf("%v: exit %d, want %d\n%s%s", args, code, want, out, errOut)
 27		}
 28		return out + errOut
 29	}
 30
 31	ssh(aliceKey, 0, "repo", "create", "alice/app")
 32	ssh(aliceKey, 0, "repo", "create", "alice/lib")
 33	ssh(bobKey, 0, "repo", "create", "bob/secret", "--private")
 34	work := t.TempDir()
 35	env := inst.gitEnv(aliceKey)
 36	mustGit(t, work, env, "clone", inst.sshURL("alice/app"), "w")
 37	dir := filepath.Join(work, "w")
 38	os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a\n"), 0o644)
 39	mustGit(t, dir, env, "checkout", "-q", "-b", "main")
 40	mustGit(t, dir, env, "add", ".")
 41	mustGit(t, dir, env, "commit", "-q", "-m", "base")
 42	mustGit(t, dir, env, "push", "-q", "origin", "main")
 43	mustGit(t, dir, env, "checkout", "-q", "-b", "feat")
 44	os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a\nb\n"), 0o644)
 45	mustGit(t, dir, env, "commit", "-q", "-am", "feat")
 46	mustGit(t, dir, env, "push", "-q", "origin", "feat")
 47	ssh(aliceKey, 0, "mr", "create", "alice/app", "--source", "feat", "--target", "main", "--title", "feature")
 48	ssh(aliceKey, 0, "issue", "create", "alice/app", "--title", "app-bug", "--label", "bug")
 49	ssh(aliceKey, 0, "issue", "create", "alice/lib", "--title", "lib-bug", "--label", "bug")
 50	ssh(bobKey, 0, "issue", "create", "bob/secret", "--title", "hidden-bug", "--label", "bug")
 51
 52	if out := ssh(aliceKey, 2, "query", "save", "bad", "is:open", "bogus:x"); !strings.Contains(out, "bogus:x") {
 53		t.Errorf("a bad term is not named: %s", out)
 54	}
 55	ssh(aliceKey, 0, "query", "save", "bugs", "'repo:alice/*'", "label:bug", "is:open")
 56	ssh(aliceKey, 1, "query", "save", "bugs", "is:open")
 57	ssh(aliceKey, 0, "query", "save", "all", "is:open")
 58	ssh(bobKey, 0, "query", "save", "all", "is:open")
 59
 60	type page struct {
 61		Data struct {
 62			Items []struct {
 63				Kind  string `json:"kind"`
 64				Repo  string `json:"repo"`
 65				Title string `json:"title"`
 66			} `json:"items"`
 67			Next string `json:"next"`
 68		} `json:"data"`
 69	}
 70	run := func(key string, args ...string) page {
 71		t.Helper()
 72		var p page
 73		if err := json.Unmarshal([]byte(ssh(key, 0, append(args, "--json")...)), &p); err != nil {
 74			t.Fatal(err)
 75		}
 76		return p
 77	}
 78	titles := func(p page) string {
 79		var out []string
 80		for _, it := range p.Data.Items {
 81			out = append(out, it.Repo+":"+it.Title)
 82		}
 83		return strings.Join(out, " ")
 84	}
 85
 86	if got := titles(run(aliceKey, "issue", "list", "--query", "bugs")); got != "alice/lib:lib-bug alice/app:app-bug" {
 87		t.Errorf("issue list --query bugs = %s", got)
 88	}
 89	if got := titles(run(aliceKey, "mr", "list", "--query", "all")); got != "alice/app:feature" {
 90		t.Errorf("mr list --query all = %s", got)
 91	}
 92	if got := titles(run(bobKey, "query", "run", "all")); !strings.Contains(got, "hidden-bug") {
 93		t.Errorf("bob's own private issue is missing from his query: %s", got)
 94	}
 95
 96	// alice pages through everything she reads, one row at a time.
 97	var seen []string
 98	cursor := ""
 99	for i := 0; i < 6; i++ {
100		args := []string{"query", "run", "all", "--limit", "1"}
101		if cursor != "" {
102			args = append(args, "--cursor", cursor)
103		}
104		p := run(aliceKey, args...)
105		seen = append(seen, titles(p))
106		if cursor = p.Data.Next; cursor == "" {
107			break
108		}
109	}
110	if got := strings.Join(seen, " "); got != "alice/lib:lib-bug alice/app:app-bug alice/app:feature" {
111		t.Errorf("paged run = %s", got)
112	}
113
114	ssh(aliceKey, 0, "query", "pin", "all")
115	var dash struct {
116		Data struct {
117			Queries []struct {
118				Name  string `json:"name"`
119				Count int    `json:"count"`
120			} `json:"queries"`
121		} `json:"data"`
122	}
123	json.Unmarshal([]byte(ssh(aliceKey, 0, "dashboard", "--json")), &dash)
124	if len(dash.Data.Queries) != 1 || dash.Data.Queries[0].Name != "all" || dash.Data.Queries[0].Count != 3 {
125		t.Errorf("dashboard queries = %+v; want all with 3, bob's private issue uncounted", dash.Data.Queries)
126	}
127
128	out := ssh(aliceKey, 0, "web", "login", "--json")
129	var login struct {
130		Data struct {
131			URL string `json:"url"`
132		} `json:"data"`
133	}
134	json.Unmarshal([]byte(out), &login)
135	browser := newBrowser(t)
136	if status, _ := browserGet(t, browser, inst.base()+login.Data.URL[strings.Index(login.Data.URL, "/login"):]); status != 200 {
137		t.Fatalf("login: %d", status)
138	}
139	_, body := browserGet(t, browser, inst.base()+"/")
140	if !strings.Contains(body, `<a href="/alice/-/queries/all">all</a> <span class="count">3</span>`) {
141		t.Errorf("dashboard lacks the pinned query:\n%s", body)
142	}
143	status, body := browserGet(t, browser, inst.base()+"/alice/-/queries/bugs")
144	if status != 200 || !strings.Contains(body, "lib-bug") || strings.Contains(body, "hidden-bug") {
145		t.Errorf("/alice/-/queries/bugs: %d\n%s", status, body)
146	}
147	if status, _ := browserGet(t, browser, inst.base()+"/alice/-/queries/nosuch"); status != 404 {
148		t.Errorf("/alice/-/queries/nosuch: %d, want 404", status)
149	}
150	if status, _ := browserGet(t, browser, inst.base()+"/bob/-/queries/all"); status != 404 {
151		t.Errorf("alice reached bob's query page: %d", status)
152	}
153	if _, body := inst.get(t, "/alice/-/queries/bugs"); strings.Contains(body, "lib-bug") {
154		t.Error("an anonymous visitor reached a saved query page")
155	}
156}