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