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

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}