devtools_test.gno
4.08 Kb · 114 lines
1package devtools
2
3import (
4 "testing"
5
6 "gno.land/p/moul/fifo/v0"
7 "gno.land/r/moul/config/v1"
8 "gno.land/p/nt/uassert/v0"
9)
10
11// reset puts the realm back to a just-deployed state. Realm globals live for
12// the whole test binary and gno runs every Example AFTER every Test, so without
13// this the pinned output would depend on which tests ran before it.
14func reset() { notes = fifo.New(maxNotes) }
15
16func TestAdd(cur realm, t *testing.T) {
17 reset()
18
19 Add(cross(cur), "hello")
20 uassert.Equal(t, 1, notes.Size())
21 uassert.Equal(t, "hello", notes.Get(0).(string))
22
23 uassert.AbortsWithMessage(t, cur, "empty note", func() { Add(cross(cur), " ") })
24 uassert.AbortsContains(t, cur, "note too long", func() {
25 long := ""
26 for i := 0; i < 201; i++ {
27 long += "x"
28 }
29 Add(cross(cur), long)
30 })
31}
32
33// TestAddEscapes is the guard that matters most in a realm anyone can write to:
34// a note is a string a caller typed, and it lands in a markdown list. Without
35// ui.Inline the first line below would close the list item and render as a
36// heading, and the second would become a link.
37func TestAddEscapes(cur realm, t *testing.T) {
38 reset()
39
40 Add(cross(cur), "# not a heading")
41 Add(cross(cur), "[not](https://evil.example.com)")
42
43 for _, got := range []string{notes.Get(0).(string), notes.Get(1).(string)} {
44 uassert.False(t, got == "# not a heading", "markdown survived unescaped")
45 uassert.False(t, got == "[not](https://evil.example.com)", "a link survived unescaped")
46 }
47 uassert.True(t, len(notes.Get(0).(string)) > len("# not a heading"),
48 "escaping should have added characters")
49}
50
51// TestNotesAreBounded pins the reason fifo is here rather than a slice: storage
52// is paid for and never refunded, so a list anyone can append to needs a
53// ceiling in the type, not in a comment.
54func TestNotesAreBounded(cur realm, t *testing.T) {
55 reset()
56
57 for i := 0; i < maxNotes+5; i++ {
58 Add(cross(cur), "note "+string(rune('a'+i)))
59 }
60 uassert.Equal(t, maxNotes, notes.Size(), "the list stops growing at maxNotes")
61 uassert.Equal(t, maxNotes, notes.MaxSize())
62}
63
64// TestAddHonoursThePause is the whole point of importing r/moul/config: this
65// realm can be stopped from outside, by a transaction against another realm,
66// with no redeploy here. It cannot set that from this package (config has its
67// own authority), so it asserts the wiring: Add goes through the pause check
68// and passes while nothing is set.
69func TestAddHonoursThePause(cur realm, t *testing.T) {
70 reset()
71 uassert.False(t, config.IsPausedFor(realmPath), "nothing configured means running")
72 Add(cross(cur), "allowed while running")
73 uassert.Equal(t, 1, notes.Size())
74}
75
76func TestReset(cur realm, t *testing.T) {
77 reset()
78 Add(cross(cur), "one")
79 Reset(cross(cur))
80 uassert.Equal(t, 0, notes.Size())
81}
82
83// ExampleRender pins the root view. It is free of the height and the chain id
84// on purpose: those live under :chain, so this one is stable.
85func ExampleRender() {
86 reset()
87 print(Render(""))
88 // Output:
89 // # allinone: devtools
90 //
91 // Eight packages, used together rather than one at a time. [source](https://mygnoscan.moul.p2p.team/realm/r/moul/x/allinone/devtools/v0)
92 //
93 // ## Notes
94 //
95 // *no notes yet*
96 //
97 // - [add a note](/r/moul/x/allinone/devtools/v0$help&func=Add&body=hello)
98 // - [clear them](/r/moul/x/allinone/devtools/v0$help&func=Reset)
99 // - [live chain values](/r/moul/x/allinone/devtools/v0:chain)
100 // - [toggle the debug panel](/r/moul/x/allinone/devtools/v0:?debug=1)
101 //
102 // ## What each package does here
103 //
104 // | package | what it contributed |
105 // | --- | --- |
106 // | `r/moul/config` | the notice block above, the pause guard on `Add`, and which explorer to link |
107 // | `p/moul/mygnoscan` | the explorer routes, so a link survives a route change |
108 // | `p/moul/debug` | the `?debug=1` panel, empty otherwise |
109 // | `p/moul/realmpath` | splitting the render path into a view and a query |
110 // | `p/moul/kit/ui` | escaping a caller's note, and this table |
111 // | `p/moul/md` | every heading, link and list on the page |
112 // | `p/moul/txlink` | the call links above, relative to this realm |
113 // | `p/moul/fifo` | the note list, bounded so storage cannot grow forever |
114}