wiki_test.gno
10.23 Kb · 288 lines
1package wiki
2
3import (
4 "strings"
5 "testing"
6
7 "gno.land/p/moul/x/wiki/v0"
8 "gno.land/p/nt/testutils/v0"
9 "gno.land/p/nt/uassert/v0"
10)
11
12var (
13 alice = testutils.TestAddress("alice")
14 bob = testutils.TestAddress("bob")
15 // carol edits only in TestCooldown: the cooldown is keyed by address and
16 // by block height, and gno's testing.SkipHeights is relative, so a test
17 // that reused an address another test already edited with would see a
18 // zero-height delta and trip the cooldown before it meant to.
19 carol = testutils.TestAddress("carol")
20)
21
22// TestBasePathMatchesTheRealm pins the constant used to build every link
23// against the realm's own path. A wrong basePath produces a page whose links
24// all 404, and nothing else would catch it.
25func TestBasePathMatchesTheRealm(t *testing.T) {
26 uassert.True(t, strings.HasSuffix(basePath, "/r/moul/x/wiki/v0"), basePath)
27 out := Render("Gno_land")
28 uassert.True(t, strings.Contains(out, "("+basePath+":Tendermint2)"), out)
29}
30
31func TestRenderRoutes(t *testing.T) {
32 cases := []struct {
33 path string
34 want string
35 }{
36 {"", "# Wiki"},
37 {"Gno_land", "# Gno land"},
38 {"gno_land", "# Gno land"}, // titles normalize
39 {"Gno_land/history", "# History of Gno land"}, //
40 {"Gno_land/raw", "# Source of Gno land"}, //
41 {"Gno_land/rev/1", "revision 1"}, //
42 {"Nonexistent_page", "does not exist yet"}, //
43 {"Special:AllPages", "# All pages"}, //
44 {"Special:allpages", "# All pages"}, // case-insensitive
45 {"Special:Categories", "# Categories"}, //
46 {"Special:RecentChanges", "# Recent changes"}, //
47 {"Special:Stats", "# Wiki stats"}, //
48 {"Special:Backlinks?page=Gno", "Pages that link to Gno"},
49 {"Special:Nope", "404"},
50 {"Gno_land/nope", "404"},
51 {"Gno_land/rev/999", "404"},
52 {"Category:Chains", "Pages in this category"},
53 {"Gno_land/talk", "# Discussion: Gno land"},
54 }
55 for _, c := range cases {
56 out := Render(c.path)
57 uassert.True(t, strings.Contains(out, c.want),
58 "Render("+c.path+") should contain "+c.want+", got: "+first80(out))
59 }
60}
61
62func TestRenderIsTotal(t *testing.T) {
63 // Render is reached from a URL, so malformed input must produce a page,
64 // never an abort that makes the realm unreadable.
65 for _, path := range []string{"Gno|land", "Special:Backlinks?page=", "Gno_land/diff?from=x&to=y", "//"} {
66 uassert.NotEmpty(t, Render(path), path)
67 }
68}
69
70func TestEditAndRevert(cur realm, t *testing.T) {
71 testing.SetRealm(testing.NewUserRealm(alice))
72 rev := Edit(cross(cur), "Alice page", "First body.\n", "create")
73 uassert.True(t, rev > 0)
74
75 testing.SetRealm(testing.NewUserRealm(bob))
76 Edit(cross(cur), "Alice page", "Vandalized.\n", "oops")
77 uassert.True(t, strings.Contains(Render("Alice_page"), "Vandalized."))
78
79 testing.SetRealm(testing.NewUserRealm(alice))
80 Revert(cross(cur), "Alice page", rev, "rv")
81 out := Render("Alice_page")
82 uassert.True(t, strings.Contains(out, "First body."))
83 // The vandalism is still in the history: that is the point of a wiki.
84 uassert.True(t, strings.Contains(Render("Alice_page/history"), "oops"))
85}
86
87func TestEditRejectsAnEmptyBody(cur realm, t *testing.T) {
88 testing.SetRealm(testing.NewUserRealm(alice))
89 uassert.AbortsContains(t, cur, "empty title", func() {
90 Edit(cross(cur), "", "body\n", "")
91 })
92 uassert.AbortsContains(t, cur, "empty body", func() {
93 Edit(cross(cur), "Some page", "", "")
94 })
95}
96
97func TestBannedAddressCannotEdit(cur realm, t *testing.T) {
98 testing.SetRealm(testing.NewUserRealm(steward))
99 Ban(cross(cur), bob)
100
101 testing.SetRealm(testing.NewUserRealm(bob))
102 uassert.AbortsContains(t, cur, "banned", func() {
103 Edit(cross(cur), "Ban test", "body\n", "")
104 })
105
106 testing.SetRealm(testing.NewUserRealm(steward))
107 Unban(cross(cur), bob)
108 testing.SetRealm(testing.NewUserRealm(bob))
109 uassert.True(t, Edit(cross(cur), "Ban test", "body\n", "") > 0)
110}
111
112func TestProtectionLevels(cur realm, t *testing.T) {
113 testing.SetRealm(testing.NewUserRealm(alice))
114 Edit(cross(cur), "Protected page", "body\n", "")
115
116 testing.SetRealm(testing.NewUserRealm(steward))
117 Protect(cross(cur), "Protected page", "semi")
118
119 testing.SetRealm(testing.NewUserRealm(alice))
120 uassert.AbortsContains(t, cur, "semi-protected", func() {
121 Edit(cross(cur), "Protected page", "sneaky\n", "")
122 })
123
124 testing.SetRealm(testing.NewUserRealm(steward))
125 AddEditor(cross(cur), alice)
126 testing.SetRealm(testing.NewUserRealm(alice))
127 uassert.True(t, Edit(cross(cur), "Protected page", "allowed\n", "") > 0)
128
129 testing.SetRealm(testing.NewUserRealm(steward))
130 Protect(cross(cur), "Protected page", "locked")
131 testing.SetRealm(testing.NewUserRealm(alice))
132 uassert.AbortsContains(t, cur, "caller is not owner", func() {
133 Edit(cross(cur), "Protected page", "still sneaky\n", "")
134 })
135}
136
137func TestStewardOnlyFunctionsRejectOthers(cur realm, t *testing.T) {
138 testing.SetRealm(testing.NewUserRealm(alice))
139 Edit(cross(cur), "Steward test", "body\n", "")
140
141 for _, call := range []struct {
142 name string
143 fn func()
144 }{
145 {"Protect", func() { Protect(cross(cur), "Steward test", "locked") }},
146 {"Move", func() { Move(cross(cur), "Steward test", "Elsewhere", "") }},
147 {"Blank", func() { Blank(cross(cur), "Steward test", "") }},
148 {"Purge", func() { Purge(cross(cur), "Steward test") }},
149 {"Ban", func() { Ban(cross(cur), bob) }},
150 {"SetCooldown", func() { SetCooldown(cross(cur), 5) }},
151 } {
152 testing.SetRealm(testing.NewUserRealm(alice))
153 uassert.AbortsContains(t, cur, "caller is not owner", call.fn, call.name)
154 }
155}
156
157func TestCooldown(cur realm, t *testing.T) {
158 testing.SetRealm(testing.NewUserRealm(steward))
159 SetCooldown(cross(cur), 5)
160
161 testing.SetRealm(testing.NewUserRealm(carol))
162 Edit(cross(cur), "Cooldown page", "one\n", "")
163 uassert.AbortsContains(t, cur, "cooldown", func() {
164 Edit(cross(cur), "Cooldown page", "two\n", "")
165 })
166
167 testing.SkipHeights(6)
168 testing.SetRealm(testing.NewUserRealm(carol))
169 uassert.True(t, Edit(cross(cur), "Cooldown page", "two\n", "") > 0)
170
171 testing.SetRealm(testing.NewUserRealm(steward))
172 SetCooldown(cross(cur), 0)
173}
174
175func TestBlankAndPurgeReleaseBytes(cur realm, t *testing.T) {
176 testing.SetRealm(testing.NewUserRealm(alice))
177 Edit(cross(cur), "Purge page", "a body worth some bytes\n", "")
178
179 testing.SetRealm(testing.NewUserRealm(steward))
180 Blank(cross(cur), "Purge page", "policy")
181 uassert.True(t, strings.Contains(Render("Purge_page"), "was blanked"))
182
183 testing.SetRealm(testing.NewUserRealm(steward))
184 released := Purge(cross(cur), "Purge page")
185 uassert.True(t, released > 0, "purging a page with a retained body must release bytes")
186 uassert.True(t, strings.Contains(Render("Purge_page/history"), "body evicted"))
187}
188
189func TestMoveLeavesARedirect(cur realm, t *testing.T) {
190 testing.SetRealm(testing.NewUserRealm(alice))
191 Edit(cross(cur), "Before move", "the body\n", "")
192
193 testing.SetRealm(testing.NewUserRealm(steward))
194 Move(cross(cur), "Before move", "After move", "rename")
195
196 uassert.True(t, strings.Contains(Render("After_move"), "the body"))
197 // Reading the old title follows the redirect; its history does not.
198 uassert.True(t, strings.Contains(Render("Before_move"), "the body"))
199 uassert.True(t, strings.Contains(Render("Before_move/raw"), "#REDIRECT"))
200}
201
202// TestSeedIsLinkedTogether checks the deployed starting state renders as a
203// connected wiki rather than four orphans.
204func TestSeedIsLinkedTogether(t *testing.T) {
205 uassert.True(t, strings.Contains(Render("Gno_land"), "("+basePath+":Gno)"))
206 uassert.True(t, strings.Contains(Render("Special:Backlinks?page=Gno_land"), "[Gno]"))
207 uassert.True(t, strings.Contains(Render("Category:Chains"), "[Gno land]"))
208}
209
210// TestParseTitleIsTheSameOneTheLibraryUses guards against the realm and the
211// library disagreeing about what a title is, which would make a page
212// creatable but unreachable.
213func TestParseTitleIsTheSameOneTheLibraryUses(t *testing.T) {
214 tt := wiki.MustParseTitle("Gno land")
215 uassert.Equal(t, "Gno_land", tt.Slug())
216 uassert.True(t, strings.Contains(Render(tt.Slug()), "# Gno land"))
217}
218
219func first80(s string) string {
220 if len(s) > 80 {
221 return s[:80]
222 }
223 return s
224}
225
226func TestCommentOnALockedPage(cur realm, t *testing.T) {
227 testing.SetRealm(testing.NewUserRealm(alice))
228 Edit(cross(cur), "Locked talk", "body\n", "")
229
230 testing.SetRealm(testing.NewUserRealm(steward))
231 Protect(cross(cur), "Locked talk", "locked")
232
233 // Locking an article is how a steward stops an edit war; the discussion
234 // is where that war is supposed to move, so it stays open.
235 testing.SetRealm(testing.NewUserRealm(alice))
236 uassert.AbortsContains(t, cur, "caller is not owner", func() {
237 Edit(cross(cur), "Locked talk", "sneaky\n", "")
238 })
239 testing.SetRealm(testing.NewUserRealm(alice))
240 id := Comment(cross(cur), "Locked talk", "I disagree, and here is why.\n", 0)
241 uassert.True(t, id > 0)
242 uassert.True(t, strings.Contains(Render("Locked_talk/talk"), "I disagree"))
243}
244
245func TestCommentRepliesAndModeration(cur realm, t *testing.T) {
246 testing.SetRealm(testing.NewUserRealm(alice))
247 Edit(cross(cur), "Talk target", "body\n", "")
248 root := Comment(cross(cur), "Talk target", "First.\n", 0)
249
250 testing.SetRealm(testing.NewUserRealm(bob))
251 Comment(cross(cur), "Talk target", "Replying.\n", root)
252
253 out := Render("Talk_target/talk")
254 uassert.True(t, strings.Contains(out, "First."))
255 uassert.True(t, strings.Contains(out, "Replying."))
256
257 testing.SetRealm(testing.NewUserRealm(steward))
258 released := HideComment(cross(cur), "Talk target", root)
259 uassert.True(t, released > 0, "hiding a message releases its bytes")
260 out = Render("Talk_target/talk")
261 uassert.True(t, strings.Contains(out, "removed by a steward"))
262 uassert.False(t, strings.Contains(out, "First."))
263}
264
265func TestBannedAddressCannotComment(cur realm, t *testing.T) {
266 testing.SetRealm(testing.NewUserRealm(alice))
267 Edit(cross(cur), "Ban talk", "body\n", "")
268
269 testing.SetRealm(testing.NewUserRealm(steward))
270 Ban(cross(cur), bob)
271 testing.SetRealm(testing.NewUserRealm(bob))
272 uassert.AbortsContains(t, cur, "banned", func() {
273 Comment(cross(cur), "Ban talk", "spam\n", 0)
274 })
275 testing.SetRealm(testing.NewUserRealm(steward))
276 Unban(cross(cur), bob)
277}
278
279func TestHideCommentIsStewardOnly(cur realm, t *testing.T) {
280 testing.SetRealm(testing.NewUserRealm(alice))
281 Edit(cross(cur), "Mod talk", "body\n", "")
282 id := Comment(cross(cur), "Mod talk", "hello\n", 0)
283
284 testing.SetRealm(testing.NewUserRealm(bob))
285 uassert.AbortsContains(t, cur, "caller is not owner", func() {
286 HideComment(cross(cur), "Mod talk", id)
287 })
288}