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

tellers_test.gno

7.29 Kb · 290 lines
  1package grc721
  2
  3import (
  4	"testing"
  5
  6	"gno.land/p/nt/uassert/v0"
  7	"gno.land/p/nt/urequire/v0"
  8)
  9
 10type evilTeller struct{ Teller }
 11
 12func TestIsCanonicalTeller(cur realm, t *testing.T) {
 13	tok, led := newTestToken("Foo", "FOO", 0, cur)
 14
 15	var realmTeller Teller
 16	func(cur realm) {
 17		realmTeller = tok.RealmTeller(0, cur)
 18	}(cross(cur))
 19
 20	tests := []struct {
 21		name string
 22		te   Teller
 23		want bool
 24	}{
 25		{"CallerTeller is canonical", led.CallerTeller(), true},
 26		{"ReadonlyTeller is canonical", tok.ReadonlyTeller(), true},
 27		{"RealmTeller is canonical", realmTeller, true},
 28		{"ImpersonateTeller is canonical", led.ImpersonateTeller(alice), true},
 29		{"embedding wrapper is rejected", &evilTeller{led.CallerTeller()}, false},
 30	}
 31
 32	for _, tc := range tests {
 33		tc := tc
 34		t.Run(tc.name, func(t *testing.T) {
 35			uassert.Equal(t, tc.want, IsCanonicalTeller(tc.te))
 36		})
 37	}
 38}
 39
 40func TestTellerNilGuards(cur realm, t *testing.T) {
 41	var nilTok *Token
 42	var nilLed *PrivateLedger
 43
 44	tests := []struct {
 45		name string
 46		msg  string
 47		fn   func()
 48	}{
 49		{"CallerTeller on nil ledger panics", "Ledger cannot be nil", func() {
 50			nilLed.CallerTeller()
 51		}},
 52		{"ReadonlyTeller on nil token panics", "Token cannot be nil", func() {
 53			nilTok.ReadonlyTeller()
 54		}},
 55		{"RealmTeller on nil token panics", "Token cannot be nil", func() {
 56			nilTok.RealmTeller(0, cur)
 57		}},
 58		{"RealmSubTeller on nil token panics", "Token cannot be nil", func() {
 59			nilTok.RealmSubTeller(0, cur, "slug")
 60		}},
 61		{"ImpersonateTeller on nil ledger panics", "Ledger cannot be nil", func() {
 62			nilLed.ImpersonateTeller(alice)
 63		}},
 64	}
 65
 66	for _, tc := range tests {
 67		tc := tc
 68		t.Run(tc.name, func(t *testing.T) {
 69			uassert.PanicsWithMessage(t, cur, tc.msg, tc.fn)
 70		})
 71	}
 72}
 73
 74func TestReadonlyTeller(cur realm, t *testing.T) {
 75	tok, led := newTestToken("Foo", "FOO", 0, cur)
 76	urequire.NoError(t, led.Mint(alice, "1"))
 77	ro := tok.ReadonlyTeller()
 78
 79	tests := []struct {
 80		name string
 81		op   string
 82	}{
 83		{"Approve rejected with ErrReadonly", "approve"},
 84		{"TransferFrom rejected with ErrReadonly", "transfer"},
 85		{"SetApprovalForAll rejected with ErrReadonly", "setall"},
 86	}
 87
 88	for _, tc := range tests {
 89		tc := tc
 90		t.Run(tc.name, func(t *testing.T) {
 91			var err error
 92			testing.SetRealm(testing.NewUserRealm(alice))
 93			func(cur realm) {
 94				switch tc.op {
 95				case "approve":
 96					err = ro.Approve(0, cur, bob, "1")
 97				case "transfer":
 98					err = ro.TransferFrom(0, cur, alice, bob, "1")
 99				case "setall":
100					err = ro.SetApprovalForAll(0, cur, bob, true)
101				}
102			}(cross(cur))
103			uassert.ErrorIs(t, err, ErrReadonly)
104		})
105	}
106}
107
108func TestCallerTellerFlow(cur realm, t *testing.T) {
109	tok, led := newTestToken("Foo", "FOO", 0, cur)
110	urequire.NoError(t, led.Mint(alice, "1"))
111	urequire.NoError(t, led.Mint(alice, "2"))
112
113	teller := led.CallerTeller()
114
115	testing.SetRealm(testing.NewUserRealm(alice))
116	func(cur realm) {
117		urequire.NoError(t, teller.Approve(0, cur, bob, "1"))
118	}(cross(cur))
119
120	testing.SetRealm(testing.NewUserRealm(alice))
121	func(cur realm) {
122		urequire.NoError(t, teller.SetApprovalForAll(0, cur, bob, true))
123	}(cross(cur))
124	uassert.True(t, tok.IsApprovedForAll(alice, bob))
125
126	testing.SetRealm(testing.NewUserRealm(bob))
127	func(cur realm) {
128		urequire.NoError(t, teller.TransferFrom(0, cur, alice, carl, "1"))
129	}(cross(cur))
130
131	owner, err := tok.OwnerOf("1")
132	urequire.NoError(t, err)
133	uassert.Equal(t, carl, owner)
134
135	testing.SetRealm(testing.NewUserRealm(bob))
136	func(cur realm) {
137		uassert.ErrorIs(t, teller.TransferFrom(0, cur, carl, bob, "1"), ErrCallerIsNotOwnerOrApproved)
138	}(cross(cur))
139}
140
141func TestCallerTellerSpoofedRealm(cur realm, t *testing.T) {
142	_, led := newTestToken("Foo", "FOO", 0, cur)
143	urequire.NoError(t, led.Mint(alice, "1"))
144	teller := led.CallerTeller()
145
146	// Stale outer cur while a fresh frame is current → ErrSpoofedRealm.
147	tests := []struct {
148		name string
149		op   string
150	}{
151		{"Approve rejects spoofed realm", "approve"},
152		{"TransferFrom rejects spoofed realm", "transfer"},
153		{"SetApprovalForAll rejects spoofed realm", "setall"},
154	}
155
156	for _, tc := range tests {
157		tc := tc
158		t.Run(tc.name, func(t *testing.T) {
159			var err error
160			stale := cur
161			func(cur realm) {
162				switch tc.op {
163				case "approve":
164					err = teller.Approve(0, stale, bob, "1")
165				case "transfer":
166					err = teller.TransferFrom(0, stale, alice, bob, "1")
167				case "setall":
168					err = teller.SetApprovalForAll(0, stale, bob, true)
169				}
170			}(cross(cur))
171			uassert.ErrorIs(t, err, ErrSpoofedRealm)
172		})
173	}
174}
175
176func TestRealmTellerFlow(cur realm, t *testing.T) {
177	tok, led := newTestToken("Foo", "FOO", 0, cur)
178
179	var teller Teller
180	var realmAddr address
181	func(cur realm) {
182		teller = tok.RealmTeller(0, cur)
183		realmAddr = cur.Address()
184	}(cross(cur))
185
186	urequire.NoError(t, led.Mint(realmAddr, "1"))
187
188	testing.SetRealm(testing.NewUserRealm(alice))
189	func(cur realm) {
190		urequire.NoError(t, teller.TransferFrom(0, cur, realmAddr, bob, "1"))
191	}(cross(cur))
192
193	owner, err := tok.OwnerOf("1")
194	urequire.NoError(t, err)
195	uassert.Equal(t, bob, owner)
196}
197
198func TestRealmSubTellerFlow(cur realm, t *testing.T) {
199	tok, led := newTestToken("Foo", "FOO", 0, cur)
200
201	tests := []struct {
202		name           string
203		slug           string
204		tid            TokenID
205		wantsRealmAddr bool
206	}{
207		{"empty slug derives the realm address itself", "", "10", true},
208		{"non-empty slug derives a sub-account", "vault", "11", false},
209	}
210
211	for _, tc := range tests {
212		tc := tc
213		t.Run(tc.name, func(t *testing.T) {
214			var teller Teller
215			var realmAddr, account address
216			func(cur realm) {
217				teller = tok.RealmSubTeller(0, cur, tc.slug)
218				realmAddr = cur.Address()
219				account = accountSlugAddr(cur.Address(), tc.slug)
220			}(cross(cur))
221
222			if tc.wantsRealmAddr {
223				uassert.Equal(t, realmAddr, account)
224			} else {
225				uassert.True(t, realmAddr != account, "sub-account must differ from realm address")
226			}
227
228			urequire.NoError(t, led.Mint(account, tc.tid))
229			testing.SetRealm(testing.NewUserRealm(alice))
230			func(cur realm) {
231				urequire.NoError(t, teller.TransferFrom(0, cur, account, bob, tc.tid))
232			}(cross(cur))
233
234			owner, err := tok.OwnerOf(tc.tid)
235			urequire.NoError(t, err)
236			uassert.Equal(t, bob, owner)
237		})
238	}
239}
240
241func TestRealmTellerSpoofedRealm(cur realm, t *testing.T) {
242	tok, _ := newTestToken("Foo", "FOO", 0, cur)
243
244	tests := []struct {
245		name string
246		fn   func()
247	}{
248		{"RealmTeller rejects spoofed realm", func() {
249			stale := cur
250			func(cur realm) {
251				tok.RealmTeller(0, stale)
252			}(cross(cur))
253		}},
254		{"RealmSubTeller rejects spoofed realm", func() {
255			stale := cur
256			func(cur realm) {
257				tok.RealmSubTeller(0, stale, "vault")
258			}(cross(cur))
259		}},
260	}
261
262	for _, tc := range tests {
263		tc := tc
264		t.Run(tc.name, func(t *testing.T) {
265			urequire.AbortsContains(t, cur, "rlm does not match the current crossing frame", tc.fn)
266		})
267	}
268}
269
270func TestImpersonateTeller(cur realm, t *testing.T) {
271	tok, led := newTestToken("Foo", "FOO", 0, cur)
272	urequire.NoError(t, led.Mint(alice, "1"))
273
274	teller := led.ImpersonateTeller(alice)
275	testing.SetRealm(testing.NewUserRealm(bob))
276	func(cur realm) {
277		urequire.NoError(t, teller.TransferFrom(0, cur, alice, bob, "1"))
278	}(cross(cur))
279
280	owner, err := tok.OwnerOf("1")
281	urequire.NoError(t, err)
282	uassert.Equal(t, bob, owner)
283}
284
285func TestImpersonateTellerRejectsInvalidAddress(cur realm, t *testing.T) {
286	_, led := newTestToken("Foo", "FOO", 0, cur)
287	uassert.PanicsWithMessage(t, cur, "invalid address", func() {
288		led.ImpersonateTeller(zeroAddress)
289	})
290}