home_test.gno
6.63 Kb · 177 lines
1package home
2
3import (
4 "strings"
5 "testing"
6
7 "gno.land/p/nt/avl/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 mallory = testutils.TestAddress("mallory")
16)
17
18func reset() {
19 galleries = avl.NewTree()
20 slots = avl.NewTree()
21 rev = 0
22 hearts = avl.NewTree()
23 recent = nil
24}
25
26func seed(slug, body string) {
27 rev++
28 slots.Set(slug, &slot{body: body, rev: rev})
29}
30
31func TestDefaultPageRendersEverySection(t *testing.T) {
32 // State as left by init: every default slot, no heart yet.
33 page := Render("")
34 for _, want := range []string{"data:image/svg+xml;base64,", "Founder, CEO & CTO", "## 今 Now", "## 組 Inside Memba", "BARRICADE", "256 merged pull requests", "Paris P2P Festival", "Contributing on Gno since | 2022", "## 源 Open source", "(https://www.youtube.com/watch?v=aG4M37FX4II)", "**0** hearts so far.", "gno.land/r/g1747t5m2f08plqjlrjk2q0qld7465hxz8gkx59c/home"} {
35 uassert.True(t, strings.Contains(page, want), "missing: "+want)
36 }
37 for _, s := range defaultSlots {
38 uassert.False(t, strings.Contains(page, ":"+s.slug+":"), "unfilled placeholder :"+s.slug+":")
39 }
40 for _, g := range defaultGalleries {
41 uassert.False(t, strings.Contains(page, ":gallery."+g.name+":"), "unfilled gallery :gallery."+g.name+":")
42 }
43 uassert.False(t, strings.Contains(page, ":hero:"))
44}
45
46func TestSetGetDelete(cur realm, t *testing.T) {
47 reset()
48 testing.SetRealm(testing.NewUserRealm(admin))
49
50 Set(cross(cur), "bio", "Building on gno.")
51 uassert.Equal(t, "Building on gno.", Get("bio"))
52 Append(cross(cur), "bio", " Still.")
53 uassert.Equal(t, "Building on gno. Still.", Get("bio"))
54 uassert.Equal(t, 2, Revision())
55
56 Delete(cross(cur), "bio")
57 uassert.Equal(t, "", Get("bio"))
58 uassert.AbortsWithMessage(t, cur, "no such slot: bio", func() {
59 Delete(cross(cur), "bio")
60 })
61}
62
63func TestWritesAreAdminOnly(cur realm, t *testing.T) {
64 reset()
65 testing.SetRealm(testing.NewUserRealm(mallory))
66 uassert.AbortsWithMessage(t, cur, "restricted to admin", func() { Set(cross(cur), "bio", "pwned") })
67 uassert.AbortsWithMessage(t, cur, "restricted to admin", func() { Append(cross(cur), "bio", "pwned") })
68 uassert.AbortsWithMessage(t, cur, "restricted to admin", func() { Delete(cross(cur), "bio") })
69}
70
71func TestSlugRules(cur realm, t *testing.T) {
72 reset()
73 testing.SetRealm(testing.NewUserRealm(admin))
74 uassert.AbortsContains(t, cur, "invalid slug", func() { Set(cross(cur), "Bad", "x") })
75 uassert.AbortsContains(t, cur, "invalid slug", func() { Set(cross(cur), "a:b", "x") })
76 uassert.AbortsContains(t, cur, "invalid slug", func() { Set(cross(cur), "", "x") })
77 uassert.AbortsWithMessage(t, cur, "reserved slug: hearts", func() { Set(cross(cur), "hearts", "fake 9999") })
78 uassert.AbortsContains(t, cur, "reserved slug: gallery.", func() { Set(cross(cur), "gallery.x", "fake") })
79}
80
81func TestFillIsSinglePass(t *testing.T) {
82 reset()
83 seed("layout", "A :a: B :missing: C 10:30 :b:")
84 seed("a", "[:b:]")
85 seed("b", "bee")
86 uassert.Equal(t, "A [:b:] B :missing: C 10:30 bee", Render(""))
87}
88
89func TestFallbackLayout(t *testing.T) {
90 reset()
91 seed("bio", "hi")
92 uassert.True(t, strings.HasPrefix(Render(""), "# zôÖma\n\nhi\n\n**0** hearts"))
93}
94
95func TestHeartOncePerAddress(cur realm, t *testing.T) {
96 reset()
97 seed("layout", ":hearts:")
98
99 testing.SetRealm(testing.NewUserRealm(alice))
100 Heart(cross(cur))
101 uassert.Equal(t, 1, Hearts())
102 uassert.True(t, HasHeart(alice))
103 uassert.AbortsWithMessage(t, cur, "already sent: one <3 per address", func() { Heart(cross(cur)) })
104
105 testing.SetRealm(testing.NewUserRealm(bob))
106 Heart(cross(cur))
107 uassert.Equal(t, 2, Hearts())
108 uassert.Equal(t, bob, recent[0], "newest first")
109
110 page := Render("")
111 uassert.True(t, strings.Contains(page, "**2** hearts so far."))
112 uassert.True(t, strings.Contains(page, "/u/"+bob.String()))
113 uassert.True(t, strings.Contains(Render("hearts"), "/u/"+alice.String()))
114}
115
116func TestRecentIsBounded(cur realm, t *testing.T) {
117 reset()
118 for i := 0; i < maxRecent+3; i++ {
119 testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("fan" + string(rune('a'+i)))))
120 Heart(cross(cur))
121 }
122 uassert.Equal(t, maxRecent+3, Hearts())
123 uassert.Equal(t, maxRecent, len(recent))
124}
125
126func TestOtherViews(t *testing.T) {
127 reset()
128 seed("bio", "hello")
129 uassert.True(t, strings.Contains(Render("slots"), "[bio]"))
130 uassert.True(t, strings.Contains(Render("slots/bio"), "hello"))
131 uassert.True(t, strings.Contains(Render("slots/nope"), "Not found"))
132 uassert.True(t, strings.Contains(Render("nope"), "Not found"))
133}
134
135func TestGallery(cur realm, t *testing.T) {
136 reset()
137 seed("layout", ":gallery.demo:")
138
139 testing.SetRealm(testing.NewUserRealm(mallory))
140 uassert.AbortsWithMessage(t, cur, "restricted to admin", func() {
141 SetGallery(cross(cur), "demo", "film | A | b | https://x.y")
142 })
143
144 testing.SetRealm(testing.NewUserRealm(admin))
145 uassert.AbortsContains(t, cur, "needs 4 or 5 fields", func() { SetGallery(cross(cur), "demo", "film | A | b") })
146 uassert.AbortsContains(t, cur, "must be https://", func() { SetGallery(cross(cur), "demo", "film | A | b | javascript:alert(1)") })
147 uassert.AbortsContains(t, cur, "markdown would break", func() { SetGallery(cross(cur), "demo", "film | A | b | https://x.y/a)b") })
148
149 SetGallery(cross(cur), "demo", "# comment\nfilm | One <&> | meta | https://a.b/1\nproduct | Two | m | /r/x\nevent | Three | m | https://a.b/3\nreport | Four | m | https://a.b/4")
150 page := Render("")
151 uassert.Equal(t, 2, strings.Count(page, "<gno-columns>"), "3 per row")
152 uassert.Equal(t, 4, strings.Count(page, "data:image/svg+xml;base64,"))
153 uassert.True(t, strings.Contains(page, "](https://a.b/1)"))
154 uassert.True(t, strings.Contains(page, "](/r/x)"))
155
156 // Titles are XML-escaped inside the poster.
157 svg := poster(card{kind: "film", title: "One <&>", meta: "m", url: "https://a.b"})
158 uassert.True(t, strings.Contains(svg, "One <&>"))
159
160 // An optional fifth field becomes the link's hover text.
161 SetGallery(cross(cur), "demo", "memba | DAO Management | m | https://a.b/dao | Vote, execute, create.")
162 uassert.True(t, strings.Contains(Render(""), `](https://a.b/dao "Vote, execute, create.")`))
163 uassert.Equal(t, "MEMBA · PRODUCT", styleOf("memba").label, "every seeded kind has its own style")
164 uassert.AbortsContains(t, cur, "without quotes", func() {
165 SetGallery(cross(cur), "demo", "memba | A | m | https://a.b | say \"hi\"")
166 })
167
168 DeleteGallery(cross(cur), "demo")
169 uassert.Equal(t, ":gallery.demo:", Render(""), "a missing gallery stays visible")
170}
171
172func TestWrap(t *testing.T) {
173 uassert.Equal(t, 1, len(wrap("Short title", 24)))
174 lines := wrap("The Underground Sistine Chapel is a very long title indeed", 24)
175 uassert.Equal(t, 2, len(lines))
176 uassert.True(t, strings.HasSuffix(lines[1], "…"))
177}