home_test.gno
5.97 Kb · 192 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 stranger = testutils.TestAddress("mallory")
13
14// reset clears realm state. Realm globals live for the whole test binary, so
15// every test that reads Render or Manifest starts from a known tree.
16func reset() {
17 slots = avl.NewTree()
18 rev = 0
19}
20
21// seed writes a slot without going through the admin check, for tests (and the
22// Example) that only care about the rendering side.
23func seed(slug, body string) {
24 rev++
25 slots.Set(slug, &slot{body: body, rev: rev, updated: 0})
26}
27
28func TestSetGetDelete(cur realm, t *testing.T) {
29 reset()
30 testing.SetRealm(testing.NewUserRealm(admin))
31
32 Set(cross(cur), "bio", "Building on gno.")
33 uassert.Equal(t, "Building on gno.", Get("bio"))
34 uassert.Equal(t, 1, Revision())
35
36 // Set is idempotent in shape: a second write replaces, never appends.
37 Set(cross(cur), "bio", "Still building.")
38 uassert.Equal(t, "Still building.", Get("bio"))
39 uassert.Equal(t, 2, Revision())
40
41 Delete(cross(cur), "bio")
42 uassert.Equal(t, "", Get("bio"), "a deleted slot reads as empty")
43 uassert.Equal(t, 3, Revision())
44
45 uassert.AbortsWithMessage(t, cur, "no such slot: bio", func() {
46 Delete(cross(cur), "bio")
47 })
48}
49
50func TestAppendChunks(cur realm, t *testing.T) {
51 reset()
52 testing.SetRealm(testing.NewUserRealm(admin))
53
54 // The escape hatch for a body too large for one transaction.
55 Set(cross(cur), "long", "part one. ")
56 Append(cross(cur), "long", "part two.")
57 uassert.Equal(t, "part one. part two.", Get("long"))
58
59 // Append creates the slot when it is absent.
60 Append(cross(cur), "fresh", "hello")
61 uassert.Equal(t, "hello", Get("fresh"))
62}
63
64func TestWritesAreAdminOnly(cur realm, t *testing.T) {
65 reset()
66 testing.SetRealm(testing.NewUserRealm(stranger))
67
68 uassert.AbortsWithMessage(t, cur, "restricted to admin", func() {
69 Set(cross(cur), "bio", "pwned")
70 })
71 uassert.AbortsWithMessage(t, cur, "restricted to admin", func() {
72 Append(cross(cur), "bio", "pwned")
73 })
74 uassert.AbortsWithMessage(t, cur, "restricted to admin", func() {
75 Delete(cross(cur), "bio")
76 })
77 uassert.Equal(t, "", Get("bio"))
78}
79
80func TestSlugRules(cur realm, t *testing.T) {
81 reset()
82 testing.SetRealm(testing.NewUserRealm(admin))
83
84 uassert.True(t, validSlug("bio"))
85 uassert.True(t, validSlug("now.2026-09"))
86 uassert.True(t, validSlug("latest_packages"))
87 uassert.False(t, validSlug(""), "empty")
88 uassert.False(t, validSlug("Bio"), "uppercase: a slot must have one name")
89 uassert.False(t, validSlug("a b"), "space")
90 uassert.False(t, validSlug("a:b"), "a colon would break out of the :slug: placeholder")
91 uassert.False(t, validSlug(strings.Repeat("a", maxSlugLen+1)), "too long")
92
93 uassert.AbortsContains(t, cur, "invalid slug", func() {
94 Set(cross(cur), "Bad Slug", "x")
95 })
96
97 // A reserved name would shadow a value computed from chain state.
98 for _, name := range reservedSlugs {
99 uassert.AbortsContains(t, cur, "reserved slug", func() {
100 Set(cross(cur), name, "x")
101 })
102 }
103
104 // "layout" is special but NOT reserved: it is a real, writable slot.
105 Set(cross(cur), layoutSlug, "# hi")
106 uassert.Equal(t, "# hi", Get(layoutSlug))
107}
108
109func TestManifestIsTheDiffSurface(cur realm, t *testing.T) {
110 reset()
111 testing.SetRealm(testing.NewUserRealm(admin))
112
113 Set(cross(cur), "bio", "hello")
114 Set(cross(cur), "alpha", "")
115
116 // Sorted by slug (avl order), one line each, four tab-separated fields.
117 lines := strings.Split(strings.TrimSuffix(Manifest(), "\n"), "\n")
118 uassert.Equal(t, 2, len(lines))
119
120 alpha := strings.Split(lines[0], "\t")
121 uassert.Equal(t, 4, len(alpha))
122 uassert.Equal(t, "alpha", alpha[0])
123 uassert.Equal(t, "0", alpha[2], "byte length of an empty body")
124 // sha256(""), pinning that the hash is over the body, not over a wrapper.
125 uassert.Equal(t, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", alpha[3])
126
127 bio := strings.Split(lines[1], "\t")
128 uassert.Equal(t, "bio", bio[0])
129 uassert.Equal(t, "5", bio[2])
130 // sha256("hello")
131 uassert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", bio[3])
132}
133
134func TestRenderFillsTheLayoutSlot(cur realm, t *testing.T) {
135 reset()
136 testing.SetRealm(testing.NewUserRealm(admin))
137
138 Set(cross(cur), layoutSlug, "# :owner:\n\n:bio:\n\n:missing:\n")
139 Set(cross(cur), "bio", "Building on gno.")
140
141 out := Render("")
142 uassert.True(t, strings.Contains(out, "# "+admin.String()),
143 "a computed placeholder is filled from chain state")
144 uassert.True(t, strings.Contains(out, "Building on gno."),
145 "a slot placeholder is filled from the tree")
146 uassert.True(t, strings.Contains(out, ":missing:"),
147 "an unmatched placeholder survives verbatim, so a gap is visible")
148}
149
150func TestRenderDoesNotRecurse(cur realm, t *testing.T) {
151 reset()
152 testing.SetRealm(testing.NewUserRealm(admin))
153
154 // A slot body that looks like a placeholder must not expand: substitution
155 // is one pass, which is what makes cycles impossible.
156 Set(cross(cur), layoutSlug, ":a:")
157 Set(cross(cur), "a", ":b:")
158 Set(cross(cur), "b", "should not appear")
159
160 uassert.Equal(t, ":b:", Render(""))
161}
162
163func TestRenderUnsetLayoutFallsBack(t *testing.T) {
164 reset()
165 out := Render("")
166 uassert.True(t, strings.Contains(out, "No layout slot yet"))
167 uassert.False(t, strings.Contains(out, ":slots:"),
168 "the default layout must not leave its own placeholders unresolved")
169 uassert.False(t, strings.Contains(out, ":rev:"))
170 uassert.False(t, strings.Contains(out, ":height:"))
171 uassert.False(t, strings.Contains(out, ":chainid:"))
172}
173
174func TestRenderSubPaths(t *testing.T) {
175 reset()
176 seed("bio", "Building on gno.")
177
178 idx := Render("slots")
179 uassert.True(t, strings.Contains(idx, "| [bio](/r/moul/home:slots/bio) |"))
180
181 raw := Render("slots/bio")
182 uassert.True(t, strings.Contains(raw, "Building on gno."))
183 uassert.True(t, strings.Contains(raw, "16 bytes"))
184
185 uassert.True(t, strings.Contains(Render("slots/nope"), "No slot named"))
186 uassert.True(t, strings.Contains(Render("whatever"), "No such path"))
187}
188
189func TestRenderEmptyIndex(t *testing.T) {
190 reset()
191 uassert.True(t, strings.Contains(Render("slots"), "_no slots yet_"))
192}