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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

devtools_test.gno

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