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

talk_test.gno

4.71 Kb · 136 lines
  1package wiki
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"gno.land/p/nt/uassert/v0"
  8)
  9
 10func comment(t *testing.T, w *Wiki, n int, author address, title, body string, replyTo uint64) *Comment {
 11	t.Helper()
 12	now, h := at(n)
 13	c, err := w.Comment(author, now, h, title, body, replyTo)
 14	uassert.NoError(t, err, title)
 15	return c
 16}
 17
 18func TestCommentThreads(t *testing.T) {
 19	w := New(3, DefaultMaxBody)
 20	edit(t, w, 1, alice, "A", "the article\n", "")
 21
 22	root1 := comment(t, w, 2, alice, "A", "First point.\n", 0)
 23	root2 := comment(t, w, 3, bob, "A", "Second point.\n", 0)
 24	reply := comment(t, w, 4, mod, "A", "Answering the first.\n", root1.ID)
 25
 26	title := MustParseTitle("A")
 27	uassert.Equal(t, 3, w.NumComments(title))
 28
 29	threads := w.Comments(title, 0, 10)
 30	uassert.Equal(t, 2, len(threads), "replies do not count as threads")
 31	uassert.Equal(t, uint64(root1.ID), threads[0].Root.ID)
 32	uassert.Equal(t, 1, len(threads[0].Replies))
 33	uassert.Equal(t, uint64(reply.ID), threads[0].Replies[0].ID)
 34	uassert.Equal(t, uint64(root2.ID), threads[1].Root.ID)
 35	uassert.Equal(t, 0, len(threads[1].Replies))
 36
 37	uassert.Equal(t, 1, len(w.Comments(title, 1, 10)), "offset skips whole threads")
 38}
 39
 40func TestCommentRejectsBadInput(t *testing.T) {
 41	w := New(3, DefaultMaxBody)
 42	edit(t, w, 1, alice, "A", "the article\n", "")
 43	now, h := at(2)
 44
 45	_, err := w.Comment(alice, now, h, "Missing page", "hi\n", 0)
 46	uassert.ErrorIs(t, err, ErrNoSuchPage, "a discussion needs a page to be about")
 47
 48	_, err = w.Comment(alice, now, h, "A", "", 0)
 49	uassert.ErrorIs(t, err, ErrEmptyComment)
 50
 51	_, err = w.Comment(alice, now, h, "A", strings.Repeat("x", MaxCommentLen+1), 0)
 52	uassert.ErrorIs(t, err, ErrCommentTooLong)
 53
 54	_, err = w.Comment(alice, now, h, "A", "hi\n", 999)
 55	uassert.ErrorIs(t, err, ErrNoSuchComment)
 56}
 57
 58// TestRepliesNestOneLevel pins MaxReplyDepth. Unbounded nesting means
 59// unbounded recursive rendering, which is the shape the query gas ceiling
 60// punishes hardest.
 61func TestRepliesNestOneLevel(t *testing.T) {
 62	w := New(3, DefaultMaxBody)
 63	edit(t, w, 1, alice, "A", "the article\n", "")
 64	root := comment(t, w, 2, alice, "A", "root\n", 0)
 65	reply := comment(t, w, 3, bob, "A", "reply\n", root.ID)
 66
 67	now, h := at(4)
 68	_, err := w.Comment(mod, now, h, "A", "reply to a reply\n", reply.ID)
 69	uassert.ErrorIs(t, err, ErrReplyToReply)
 70}
 71
 72func TestHideComment(t *testing.T) {
 73	w := New(3, DefaultMaxBody)
 74	edit(t, w, 1, alice, "A", "the article\n", "")
 75	before := w.Stats().BytesHeld
 76	c := comment(t, w, 2, bob, "A", "unpleasant\n", 0)
 77	uassert.Equal(t, before+len("unpleasant\n"), w.Stats().BytesHeld,
 78		"a comment costs its author the bytes it adds")
 79
 80	released, err := w.HideComment("A", c.ID)
 81	uassert.NoError(t, err)
 82	uassert.Equal(t, len("unpleasant\n"), released)
 83	uassert.Equal(t, before, w.Stats().BytesHeld)
 84
 85	threads := w.Comments(MustParseTitle("A"), 0, 10)
 86	uassert.Equal(t, 1, len(threads), "a hidden message stays in the thread")
 87	uassert.True(t, threads[0].Root.Hidden)
 88	uassert.Equal(t, "", threads[0].Root.Body)
 89
 90	// Hiding twice must not double-count the refund.
 91	released, err = w.HideComment("A", c.ID)
 92	uassert.NoError(t, err)
 93	uassert.Equal(t, 0, released)
 94	uassert.Equal(t, before, w.Stats().BytesHeld)
 95}
 96
 97func TestRenderTalk(t *testing.T) {
 98	w := New(3, DefaultMaxBody)
 99	edit(t, w, 1, alice, "A", "the article\n", "")
100	root := comment(t, w, 2, alice, "A", "Is [[B]] the right link?\n", 0)
101	comment(t, w, 3, bob, "A", "Yes.\n", root.ID)
102	hidden := comment(t, w, 4, mod, "A", "spam\n", 0)
103	w.HideComment("A", hidden.ID)
104
105	p, _ := w.Page("A")
106	c := Ctx{Base: "/r/moul/x/wiki/v0", Exists: w.Exists}
107	out := RenderTalk(c, w, p, 0, 10, act)
108
109	uassert.True(t, strings.Contains(out, "# Discussion: A"))
110	uassert.True(t, strings.Contains(out, "func=Comment"), "an add-a-message action")
111	uassert.True(t, strings.Contains(out, "Yes."), "replies render under their root")
112	uassert.True(t, strings.Contains(out, "removed by a steward"))
113	uassert.False(t, strings.Contains(out, "spam"), "a hidden body is gone from the render")
114
115	// A comment is user markdown too: it goes through the sanitizer, and it
116	// is NOT a wikilink slot, so brackets stay literal rather than linking.
117	uassert.True(t, strings.Contains(out, `\[\[B\]\]`), out)
118}
119
120func TestRenderTalkEmpty(t *testing.T) {
121	w := New(3, DefaultMaxBody)
122	edit(t, w, 1, alice, "A", "the article\n", "")
123	p, _ := w.Page("A")
124	out := RenderTalk(Ctx{Base: "/r/moul/x/wiki/v0"}, w, p, 0, 10, nil)
125	uassert.True(t, strings.Contains(out, "No messages yet"))
126}
127
128func TestArticleLinksToItsDiscussion(t *testing.T) {
129	w := New(3, DefaultMaxBody)
130	edit(t, w, 1, alice, "A", "the article\n", "")
131	comment(t, w, 2, alice, "A", "hi\n", 0)
132
133	p, _ := w.Page("A")
134	out := RenderArticle(Ctx{Base: "/r/moul/x/wiki/v0"}, w, p, nil)
135	uassert.True(t, strings.Contains(out, "[discussion](/r/moul/x/wiki/v0:A/talk) (1)"), out)
136}