e2e/searchweb_test.go
112 lines · 4585 bytes
1package e2e
2
3import (
4 "net/url"
5 "strings"
6 "testing"
7)
8
9// TestGlobalSearchAndNotificationsWeb covers the two web surfaces #118
10// still lacked: /search across the instance, and the notification inbox.
11func TestGlobalSearchAndNotificationsWeb(t *testing.T) {
12 t.Parallel()
13 inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\n")
14 aliceKey := inst.newKey(t, "alice")
15 bobKey := inst.newKey(t, "bob")
16 inst.admin(t, "admin", "user", "create", "alice", "--key", aliceKey+".pub")
17 inst.admin(t, "admin", "user", "create", "bob", "--key", bobKey+".pub")
18
19 if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/widget"); code != 0 {
20 t.Fatalf("repo create: %s", errOut)
21 }
22 if _, errOut, code := inst.ssh(t, aliceKey, "", "repo", "create", "alice/secret", "--private"); code != 0 {
23 t.Fatalf("private repo create: %s", errOut)
24 }
25 if _, errOut, code := inst.ssh(t, bobKey, "", "issue", "create", "alice/widget",
26 "--title", "'widget leaks memory'", "--body", "'it climbs'"); code != 0 {
27 t.Fatalf("issue create: %s", errOut)
28 }
29 if _, errOut, code := inst.ssh(t, aliceKey, "", "issue", "create", "alice/secret",
30 "--title", "'widget in the private repo'"); code != 0 {
31 t.Fatalf("private issue create: %s", errOut)
32 }
33
34 // Anonymous search reaches the public repository and its issue, and
35 // neither the private repository nor its issue.
36 status, body := inst.get(t, "/search?q=widget")
37 if status != 200 {
38 t.Fatalf("anonymous search: %d", status)
39 }
40 if !strings.Contains(body, `href="/alice/widget"`) ||
41 !strings.Contains(body, "widget leaks memory") ||
42 !strings.Contains(body, `href="/alice/widget/issues/1"`) {
43 t.Fatalf("anonymous search missing public hits:\n%s", body)
44 }
45 if strings.Contains(body, "alice/secret") || strings.Contains(body, "private repo") {
46 t.Fatal("anonymous search leaked a private repository")
47 }
48
49 // Filtering by kind keeps the repository row and drops the issue.
50 _, body = inst.get(t, "/search?q=widget&kind=repo")
51 if !strings.Contains(body, `href="/alice/widget"`) || strings.Contains(body, "widget leaks memory") {
52 t.Fatalf("kind=repo filter:\n%s", body)
53 }
54 _, body = inst.get(t, "/search?q=x")
55 if !strings.Contains(body, "2 to 200 characters") {
56 t.Fatal("short query not refused")
57 }
58
59 // Alice sees her private repository and its issue in the same page.
60 browser := inst.login(t, aliceKey)
61 status, body = browserGet(t, browser, inst.base()+"/search?q=widget")
62 if status != 200 || !strings.Contains(body, "alice/secret") ||
63 !strings.Contains(body, "widget in the private repo") {
64 t.Fatalf("owner search misses private rows:\n%s", body)
65 }
66
67 // The rail carries the unread badge and the notifications page lists
68 // the issue bob opened.
69 status, body = browserGet(t, browser, inst.base()+"/notifications")
70 if status != 200 || !strings.Contains(body, "bob opened issue #1") ||
71 !strings.Contains(body, `href="/alice/widget/issues/1"`) {
72 t.Fatalf("notifications page:\n%s", body)
73 }
74 if !strings.Contains(body, `href="/notifications"`) {
75 t.Fatal("rail has no notifications link")
76 }
77
78 // Marking all read empties the unread list but not the full one.
79 if status, _ := browserPost(t, browser, inst.base()+"/notifications", url.Values{}); status != 200 {
80 t.Fatalf("mark all read: %d", status)
81 }
82 _, body = browserGet(t, browser, inst.base()+"/notifications")
83 if !strings.Contains(body, "nothing unread") {
84 t.Fatalf("unread list after sweep:\n%s", body)
85 }
86 _, body = browserGet(t, browser, inst.base()+"/notifications?all=1")
87 if !strings.Contains(body, "bob opened issue #1") {
88 t.Fatalf("all list after sweep:\n%s", body)
89 }
90
91 // Watching from the repository header is the same state the CLI sets.
92 if status, _ := browserPost(t, browser, inst.base()+"/alice/widget/watch", url.Values{}); status != 200 {
93 t.Fatalf("watch toggle: %d", status)
94 }
95 out, errOut, code := inst.ssh(t, aliceKey, "", "notifications", "list", "--all", "--json")
96 if code != 0 {
97 t.Fatalf("notifications list: %s", errOut)
98 }
99 if !strings.Contains(out, "alice/widget") {
100 t.Fatalf("inbox over ssh: %s", out)
101 }
102 _, body = browserGet(t, browser, inst.base()+"/alice/widget")
103 if !strings.Contains(body, ">Watching</button>") {
104 t.Fatalf("repo header does not show the watch state:\n%s", body)
105 }
106
107 // Anonymous visitors get the search page; the inbox sends them to log
108 // in. http.Get follows the redirect, so the login page is the proof.
109 if status, body := inst.get(t, "/notifications"); status != 200 || !strings.Contains(body, "Sign in") {
110 t.Fatalf("anonymous /notifications: %d\n%s", status, body)
111 }
112}