oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

1package docs_test
2
3import (
4 "net/http"
5 "strings"
6 "testing"
7
8 "github.com/carlosframework/docs/internal/docs"
9)
10
11// The difference between "sharing works" and "sharing is usable". Slice 2
12// left a grantee able to open a shared document at its direct URL and with
13// no way to find it again: List was owner-only, so the document Alice
14// shared was reachable only by somebody who had kept the link.
15//
16// A person who has to keep a URL in a chat scrollback to reach a document
17// does not have the document.
18func TestASharedDocumentAppearsInTheGranteesList(t *testing.T) {
19 app, _ := docs.NewForTest(t)
20 alice := signInCookies(t, app, "alice@example.com")
21 sqid := createDoc(t, app, alice, "Minutes")
22
23 // Before the grant, Bob's list does not mention it. Without this the
24 // test would pass on a list that shows everybody everything.
25 bob := signInCookies(t, app, "bob@example.com")
26 if rec := get(t, app, bob, "/"); strings.Contains(rec.Body.String(), "Minutes") {
27 t.Fatal("Bob's list named a document nobody had shared with him")
28 }
29
30 if rec := post(t, app, alice, "/d/"+sqid+"/share", "address=bob@example.com&role=reader"); rec.Code != http.StatusSeeOther {
31 t.Fatalf("POST share: status %d, want 303", rec.Code)
32 }
33
34 rec := get(t, app, bob, "/")
35 if rec.Code != http.StatusOK {
36 t.Fatalf("GET / as Bob: status %d, want 200", rec.Code)
37 }
38 if !strings.Contains(rec.Body.String(), "Minutes") {
39 t.Error("Bob's list does not name the document shared with him")
40 }
41 if !strings.Contains(rec.Body.String(), "/d/"+sqid) {
42 t.Error("Bob's list names the document but does not link to it")
43 }
44
45 // Carol was granted nothing and sees nothing: the list widened by
46 // exactly one grant, not into a list of everything.
47 carol := signInCookies(t, app, "carol@example.com")
48 if rec := get(t, app, carol, "/"); strings.Contains(rec.Body.String(), "Minutes") {
49 t.Error("Carol's list named a document she holds no grant on")
50 }
51}
52
53// A shared row says it is shared, and the reader's own rows do not.
54//
55// The list stopped being "your documents" the day sharing landed. A row
56// that looks like yours and is not is how somebody opens a document
57// expecting to edit it — and with a reader grant they now get a page with
58// no editor, which is correct and needs explaining before the click, not
59// after it. (Branch review, finding 9.)
60func TestASharedRowSaysSo(t *testing.T) {
61 app, _ := docs.NewForTest(t)
62 alice := signInCookies(t, app, "alice@example.com")
63 sqid := createDoc(t, app, alice, "Alices")
64 bob := signInCookies(t, app, "bob@example.com")
65 createDoc(t, app, bob, "Bobs")
66 post(t, app, alice, "/d/"+sqid+"/share", "address=bob@example.com&role=reader")
67
68 // The catalog KEY, not the English. NewForTest's app never wires
69 // rastrillo.Options.BaseCatalog (only cmd/docs/main.go does), so every
70 // catalog string renders as its own key here. Asserting the prose
71 // would be asserting a string that never appears — and, worse, a
72 // prose fragment can accidentally match a key: a nearby test checked
73 // for "published" and passed on the key "docs.share.public.published"
74 // rather than on anything being published.
75 const marker = "docs.list.shared"
76
77 body := get(t, app, bob, "/").Body.String()
78 if !strings.Contains(body, marker) {
79 t.Error("the shared row carries no marker")
80 }
81 // Exactly one row is marked: Bob's own must not be. A list where
82 // every row is badged says nothing.
83 if n := strings.Count(body, marker); n != 1 {
84 t.Errorf("%d rows marked shared, want exactly 1", n)
85 }
86
87 // And Alice, who owns hers, sees no marker at all.
88 if strings.Contains(get(t, app, alice, "/").Body.String(), marker) {
89 t.Error("the owner's own document is marked as shared with her")
90 }
91}
92
93// Revoking takes it back out of the list, not just out of the document. A
94// revoke that left the row on the grantee's list would leave them clicking
95// into a 404 and reading it as a bug.
96func TestRevokingRemovesItFromTheGranteesList(t *testing.T) {
97 app, _ := docs.NewForTest(t)
98 alice := signInCookies(t, app, "alice@example.com")
99 sqid := createDoc(t, app, alice, "Minutes")
100 bob := signInCookies(t, app, "bob@example.com")
101
102 post(t, app, alice, "/d/"+sqid+"/share", "address=bob@example.com&role=reader")
103 if rec := get(t, app, bob, "/"); !strings.Contains(rec.Body.String(), "Minutes") {
104 t.Fatal("precondition: the shared document is not on Bob's list")
105 }
106
107 if rec := post(t, app, alice, "/d/"+sqid+"/share/revoke", "address=bob@example.com"); rec.Code != http.StatusSeeOther {
108 t.Fatalf("POST revoke: status %d, want 303", rec.Code)
109 }
110 if rec := get(t, app, bob, "/"); strings.Contains(rec.Body.String(), "Minutes") {
111 t.Error("a revoked document is still on Bob's list")
112 }
113}
114
115// Search is the list's other door. Slice 2's Search ran over List, so
116// widening only the list handler would have produced an app where a shared
117// document is on the list but cannot be searched for — the kind of split
118// that reads as a broken search rather than as a scope decision.
119func TestAGranteeCanSearchForASharedDocument(t *testing.T) {
120 app, _ := docs.NewForTest(t)
121 alice := signInCookies(t, app, "alice@example.com")
122 sqid := createDoc(t, app, alice, "Quarterly plan")
123 bob := signInCookies(t, app, "bob@example.com")
124
125 post(t, app, alice, "/d/"+sqid+"/share", "address=bob@example.com&role=reader")
126
127 rec := get(t, app, bob, "/?q=quarterly")
128 if rec.Code != http.StatusOK {
129 t.Fatalf("GET /?q=quarterly as Bob: status %d, want 200", rec.Code)
130 }
131 if !strings.Contains(rec.Body.String(), "Quarterly plan") {
132 t.Error("Bob cannot search for the document shared with him")
133 }
134
135 carol := signInCookies(t, app, "carol@example.com")
136 if rec := get(t, app, carol, "/?q=quarterly"); strings.Contains(rec.Body.String(), "Quarterly plan") {
137 t.Error("Carol's search found a document she holds no grant on")
138 }
139}
140