Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

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}