gsdash_test.gno
4.24 Kb · 122 lines
1package gsdash
2
3import (
4 "strings"
5 "testing"
6
7 "gno.land/p/nt/uassert/v0"
8
9 // GnoSwap's unversioned paths are proxies: the facade holds no logic and
10 // panics with "implementation is not initialized" until some version has
11 // registered itself. On chain that happened long ago; in a test VM nothing
12 // has, so these blank imports run each implementation's init, which calls
13 // RegisterInitializer, and the version manager activates the first one it
14 // sees. Importing them here rather than from the realm keeps them out of
15 // what gets deployed: the realm only ever names the proxy.
16 _ "gno.land/r/gnoswap/pool/v1"
17 _ "gno.land/r/gnoswap/position/v1"
18 _ "gno.land/r/gnoswap/staker/v1"
19)
20
21// someone is a well-formed address with nothing on GnoSwap, which is what every
22// address looks like in a fresh test VM.
23const someone = "g1manfred47kzduec920z88wfr64ylksmdcedlf5"
24
25func TestRenderIndexReadsTheProxies(t *testing.T) {
26 out := Render("")
27 uassert.True(t, len(out) > 0, "index renders")
28 uassert.True(t, strings.Contains(out, "# GnoSwap dashboard"), "index has its heading")
29 // Reaching this line at all is the point: Size() and TotalSupply() are
30 // cross-realm reads through two different GnoSwap proxies, so an index that
31 // renders proves both resolved to a live implementation.
32 uassert.True(t, strings.Contains(out, "Stored position records"), "index read the position proxy")
33 uassert.True(t, strings.Contains(out, "Position NFTs in circulation"), "index read the gnft realm")
34}
35
36func TestRenderAddressWithNothing(t *testing.T) {
37 out := Render(someone)
38 uassert.True(t, strings.Contains(out, someone), "the page names the address in full")
39 uassert.True(t, strings.Contains(out, "No positions in this window."), "an empty window says so")
40}
41
42func TestRenderRejectsAMalformedAddress(t *testing.T) {
43 out := Render("not-an-address")
44 uassert.True(t, strings.Contains(out, "Not a valid address"), "a bad address is refused")
45 uassert.False(t, strings.Contains(out, "## Positions"), "and no scan is run for it")
46}
47
48func TestRenderEscapesWhatTheReaderTyped(t *testing.T) {
49 // The path is attacker-controlled: whatever is after the colon in the URL.
50 // It must come back inert, never as a live link.
51 out := Render("[click](https://evil.example)")
52 uassert.False(t, strings.Contains(out, "](https://evil.example)"), "no live link survives")
53}
54
55func TestParsePath(t *testing.T) {
56 tests := []struct {
57 name string
58 path string
59 wantAddr string
60 wantOffset int
61 wantOK bool
62 }{
63 {"bare address", someone, someone, 0, true},
64 {"with offset", someone + "/from/250", someone, 250, true},
65 {"zero offset", someone + "/from/0", someone, 0, true},
66 {"unknown segment", someone + "/nope", "", 0, false},
67 {"offset not a number", someone + "/from/abc", "", 0, false},
68 {"negative offset", someone + "/from/-1", "", 0, false},
69 }
70 for _, tt := range tests {
71 t.Run(tt.name, func(t *testing.T) {
72 addr, offset, ok := parsePath(tt.path)
73 uassert.Equal(t, tt.wantOK, ok)
74 if !tt.wantOK {
75 return
76 }
77 uassert.Equal(t, tt.wantAddr, addr)
78 uassert.Equal(t, tt.wantOffset, offset)
79 })
80 }
81}
82
83func TestShortPool(t *testing.T) {
84 tests := []struct {
85 name string
86 key string
87 want string
88 }{
89 {
90 "a real pool key",
91 "gno.land/r/gnoland/wugnot.wugnot:gno.land/r/gnoswap/gns.GNS:3000",
92 "wugnot/GNS 3000",
93 },
94 {"not a pool key", "whatever", "whatever"},
95 {"no symbol on a side", "a:b:100", "a/b 100"},
96 }
97 for _, tt := range tests {
98 t.Run(tt.name, func(t *testing.T) {
99 uassert.Equal(t, tt.want, shortPool(tt.key))
100 })
101 }
102}
103
104// ExampleRender pins the index as a fresh chain renders it: no positions, no
105// NFTs, and the two proxy reads resolving to zero rather than panicking.
106func ExampleRender() {
107 println(Render(""))
108 // Output:
109 // # GnoSwap dashboard
110 //
111 // Everything one address has on GnoSwap, read live from the chain: GNS and xGNS balances, every liquidity position with what it holds right now, the fees it has accrued, and the emission rewards waiting to be collected.
112 //
113 // Open an address: `/r/moul/x/gsdash/v0:g1...`
114 //
115 // | What | Now |
116 // | --- | --- |
117 // | Stored position records | 0 |
118 // | Position NFTs in circulation | 0 |
119 // | Records walked per page | 250 |
120 //
121 // Records include burned positions, so the count is not a count of live liquidity.
122}