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

operator_faucet_test.gno

16.32 Kb · 525 lines
  1package admin
  2
  3import (
  4	"testing"
  5
  6	"chain"
  7	"chain/banker"
  8	"chain/runtime"
  9
 10	"gno.land/p/nt/uassert/v0"
 11)
 12
 13const (
 14	testFaucetFundAmount int64 = 1000000
 15)
 16
 17type faucetTestHelper struct{}
 18
 19var faucetTest = &faucetTestHelper{}
 20
 21func TestSetFaucetTargetAmount(cur realm, t *testing.T) {
 22	tests := []struct {
 23		name         string
 24		originCaller address
 25		realm        runtime.Realm
 26		amount       int64
 27		shouldPanic  bool
 28		panicMsg     string
 29	}{
 30		{"admin sets target amount", testAdmin, testing.NewUserRealm(testAdmin), 1000000, false, ""},
 31		{"unauthorized", unauthorizedUser, testing.NewUserRealm(unauthorizedUser), 1000000, true, "admin access required"},
 32		{"zero amount", testAdmin, testing.NewUserRealm(testAdmin), 0, true, "faucet target amount must be positive"},
 33		{"negative amount", testAdmin, testing.NewUserRealm(testAdmin), -1, true, "faucet target amount must be positive"},
 34	}
 35
 36	for _, tt := range tests {
 37		setupCommonAdminTests(cur, t)
 38		testing.SetOriginCaller(tt.originCaller)
 39		testing.SetRealm(tt.realm)
 40
 41		if tt.shouldPanic {
 42			uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() {
 43				SetFaucetTargetAmount(cross(cur), tt.amount)
 44			})
 45			uassert.Equal(t, int64(0), GetFaucetTargetAmount())
 46			continue
 47		}
 48
 49		SetFaucetTargetAmount(cross(cur), tt.amount)
 50		uassert.Equal(t, tt.amount, GetFaucetTargetAmount())
 51	}
 52}
 53
 54func TestRequestFaucetTransfersDeficit(cur realm, t *testing.T) {
 55	setupCommonAdminTests(cur, t)
 56
 57	adminRealm := adminRealmAddr()
 58	adminBefore := adminUgnotBalance(adminRealm)
 59	operatorBefore := adminUgnotBalance(testOperator)
 60	expectedTransfer := int64(300000)
 61	faucetTest.FundAdmin(testFaucetFundAmount)
 62
 63	testing.SetOriginCaller(testAdmin)
 64	testing.SetRealm(testing.NewUserRealm(testAdmin))
 65	SetOperator(cross(cur), testOperator)
 66	SetFaucetTargetAmount(cross(cur), operatorBefore+expectedTransfer)
 67
 68	testing.SetOriginCaller(testOperator)
 69	testing.SetRealm(testing.NewUserRealm(testOperator))
 70	transferred := RequestFaucet(cross(cur))
 71
 72	uassert.Equal(t, expectedTransfer, transferred)
 73	uassert.Equal(t, operatorBefore+expectedTransfer, adminUgnotBalance(testOperator))
 74	uassert.Equal(t, adminBefore+testFaucetFundAmount-expectedTransfer, adminUgnotBalance(adminRealm))
 75}
 76
 77func TestRequestFaucetForTransfersDeficit(cur realm, t *testing.T) {
 78	setupCommonAdminTests(cur, t)
 79
 80	adminRealm := adminRealmAddr()
 81	adminBefore := adminUgnotBalance(adminRealm)
 82	operatorBefore := adminUgnotBalance(testOperator)
 83	expectedTransfer := int64(300000)
 84	faucetTest.FundAdmin(testFaucetFundAmount)
 85
 86	testing.SetOriginCaller(testAdmin)
 87	testing.SetRealm(testing.NewUserRealm(testAdmin))
 88	SetOperator(cross(cur), testOperator)
 89	SetFaucetTargetAmount(cross(cur), operatorBefore+expectedTransfer)
 90	transferred := RequestFaucetFor(cross(cur), testOperator)
 91
 92	uassert.Equal(t, expectedTransfer, transferred)
 93	uassert.Equal(t, operatorBefore+expectedTransfer, adminUgnotBalance(testOperator))
 94	uassert.Equal(t, adminBefore+testFaucetFundAmount-expectedTransfer, adminUgnotBalance(adminRealm))
 95}
 96
 97func TestRequestFaucetsTransfersDeficits(cur realm, t *testing.T) {
 98	setupCommonAdminTests(cur, t)
 99
100	testing.SetOriginCaller(testAdmin)
101	testing.SetRealm(testing.NewUserRealm(testAdmin))
102	UnsetOperator(cross(cur), testAdmin)
103	SetOperator(cross(cur), testOperator)
104	SetOperator(cross(cur), testUser)
105	SetOperator(cross(cur), testUser2)
106
107	adminRealm := adminRealmAddr()
108	adminBefore := adminUgnotBalance(adminRealm)
109	operatorBefore := adminUgnotBalance(testOperator)
110	userBefore := adminUgnotBalance(testUser)
111	user2Before := adminUgnotBalance(testUser2)
112	targetAmount := maxInt64(operatorBefore, maxInt64(userBefore, user2Before)) + 300000
113	if user2Before < targetAmount {
114		faucetTest.Issue(testUser2, targetAmount-user2Before)
115		user2Before = targetAmount
116	}
117	operatorDeficit := targetAmount - operatorBefore
118	userDeficit := targetAmount - userBefore
119	expectedTransfer := operatorDeficit + userDeficit
120	faucetTest.FundAdmin(expectedTransfer)
121
122	testing.SetOriginCaller(testAdmin)
123	testing.SetRealm(testing.NewUserRealm(testAdmin))
124	SetFaucetTargetAmount(cross(cur), targetAmount)
125	transferred := RequestFaucets(cross(cur))
126
127	uassert.Equal(t, expectedTransfer, transferred)
128	uassert.Equal(t, targetAmount, adminUgnotBalance(testOperator))
129	uassert.Equal(t, targetAmount, adminUgnotBalance(testUser))
130	uassert.Equal(t, user2Before, adminUgnotBalance(testUser2))
131	uassert.Equal(t, adminBefore, adminUgnotBalance(adminRealm))
132}
133
134func TestRequestFaucetsReturnsZeroWithoutDeficits(cur realm, t *testing.T) {
135	setupCommonAdminTests(cur, t)
136
137	testing.SetOriginCaller(testAdmin)
138	testing.SetRealm(testing.NewUserRealm(testAdmin))
139	UnsetOperator(cross(cur), testAdmin)
140	SetOperator(cross(cur), testOperator)
141	SetOperator(cross(cur), testUser)
142
143	operatorBefore := adminUgnotBalance(testOperator)
144	userBefore := adminUgnotBalance(testUser)
145	targetAmount := maxInt64(operatorBefore, userBefore)
146	if operatorBefore < targetAmount {
147		faucetTest.Issue(testOperator, targetAmount-operatorBefore)
148	}
149	if userBefore < targetAmount {
150		faucetTest.Issue(testUser, targetAmount-userBefore)
151	}
152	if targetAmount <= 0 {
153		targetAmount = 1
154		faucetTest.Issue(testOperator, targetAmount)
155		faucetTest.Issue(testUser, targetAmount)
156	}
157
158	adminBefore := adminUgnotBalance(adminRealmAddr())
159	testing.SetOriginCaller(testAdmin)
160	testing.SetRealm(testing.NewUserRealm(testAdmin))
161	SetFaucetTargetAmount(cross(cur), targetAmount)
162	transferred := RequestFaucets(cross(cur))
163
164	uassert.Equal(t, int64(0), transferred)
165	uassert.Equal(t, adminBefore, adminUgnotBalance(adminRealmAddr()))
166}
167
168func TestRequestFaucetRejectsInvalidRequest(cur realm, t *testing.T) {
169	tests := []struct {
170		name     string
171		setup    func(cur realm) (address, runtime.Realm)
172		panicMsg string
173	}{
174		{
175			name: "non-operator direct caller",
176			setup: func(cur realm) (address, runtime.Realm) {
177				testing.SetOriginCaller(testAdmin)
178				testing.SetRealm(testing.NewUserRealm(testAdmin))
179				SetFaucetTargetAmount(cross(cur), 1000000)
180				faucetTest.FundAdmin(testFaucetFundAmount)
181				return unauthorizedUser, testing.NewUserRealm(unauthorizedUser)
182			},
183			panicMsg: "operator access required",
184		},
185		{
186			name: "operator balance already reaches target",
187			setup: func(cur realm) (address, runtime.Realm) {
188				operatorBalance := adminUgnotBalance(testOperator)
189				if operatorBalance == 0 {
190					faucetTest.Issue(testOperator, 1)
191					operatorBalance = 1
192				}
193				testing.SetOriginCaller(testAdmin)
194				testing.SetRealm(testing.NewUserRealm(testAdmin))
195				SetOperator(cross(cur), testOperator)
196				SetFaucetTargetAmount(cross(cur), operatorBalance)
197				faucetTest.FundAdmin(testFaucetFundAmount)
198				return testOperator, testing.NewUserRealm(testOperator)
199			},
200			panicMsg: "operator balance is not below faucet target",
201		},
202		{
203			name: "admin realm balance is insufficient",
204			setup: func(cur realm) (address, runtime.Realm) {
205				adminBalance := adminUgnotBalance(adminRealmAddr())
206				testing.SetOriginCaller(testAdmin)
207				testing.SetRealm(testing.NewUserRealm(testAdmin))
208				SetOperator(cross(cur), testOperator)
209				SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+adminBalance+1)
210				return testOperator, testing.NewUserRealm(testOperator)
211			},
212			panicMsg: "admin realm balance is insufficient",
213		},
214		{
215			name: "operator origin through code realm",
216			setup: func(cur realm) (address, runtime.Realm) {
217				testing.SetOriginCaller(testAdmin)
218				testing.SetRealm(testing.NewUserRealm(testAdmin))
219				SetOperator(cross(cur), testOperator)
220				SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+1)
221				faucetTest.FundAdmin(testFaucetFundAmount)
222				return testOperator, testing.NewCodeRealm("gno.land/r/akkadia/beta/operator_faucet_relay")
223			},
224			panicMsg: "contract calls not allowed: function must be called directly by user",
225		},
226		{
227			name: "target amount is not configured",
228			setup: func(cur realm) (address, runtime.Realm) {
229				testing.SetOriginCaller(testAdmin)
230				testing.SetRealm(testing.NewUserRealm(testAdmin))
231				SetOperator(cross(cur), testOperator)
232				faucetTest.FundAdmin(testFaucetFundAmount)
233				return testOperator, testing.NewUserRealm(testOperator)
234			},
235			panicMsg: "faucet target amount must be positive",
236		},
237	}
238
239	for _, tt := range tests {
240		setupCommonAdminTests(cur, t)
241		caller, rlm := tt.setup(cur)
242		testing.SetOriginCaller(caller)
243		testing.SetRealm(rlm)
244
245		uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() {
246			RequestFaucet(cross(cur))
247		})
248	}
249}
250
251func TestRequestFaucetForRejectsInvalidRequest(cur realm, t *testing.T) {
252	tests := []struct {
253		name     string
254		setup    func(cur realm) (address, runtime.Realm, address)
255		panicMsg string
256	}{
257		{
258			name: "non-admin caller",
259			setup: func(cur realm) (address, runtime.Realm, address) {
260				testing.SetOriginCaller(testAdmin)
261				testing.SetRealm(testing.NewUserRealm(testAdmin))
262				SetOperator(cross(cur), testOperator)
263				SetFaucetTargetAmount(cross(cur), 1000000)
264				faucetTest.FundAdmin(testFaucetFundAmount)
265				return unauthorizedUser, testing.NewUserRealm(unauthorizedUser), testOperator
266			},
267			panicMsg: "admin access required",
268		},
269		{
270			name: "non-operator target",
271			setup: func(cur realm) (address, runtime.Realm, address) {
272				testing.SetOriginCaller(testAdmin)
273				testing.SetRealm(testing.NewUserRealm(testAdmin))
274				SetFaucetTargetAmount(cross(cur), 1000000)
275				faucetTest.FundAdmin(testFaucetFundAmount)
276				return testAdmin, testing.NewUserRealm(testAdmin), unauthorizedUser
277			},
278			panicMsg: "operator access required",
279		},
280		{
281			name: "operator balance already reaches target",
282			setup: func(cur realm) (address, runtime.Realm, address) {
283				operatorBalance := adminUgnotBalance(testOperator)
284				if operatorBalance == 0 {
285					faucetTest.Issue(testOperator, 1)
286					operatorBalance = 1
287				}
288				testing.SetOriginCaller(testAdmin)
289				testing.SetRealm(testing.NewUserRealm(testAdmin))
290				SetOperator(cross(cur), testOperator)
291				SetFaucetTargetAmount(cross(cur), operatorBalance)
292				faucetTest.FundAdmin(testFaucetFundAmount)
293				return testAdmin, testing.NewUserRealm(testAdmin), testOperator
294			},
295			panicMsg: "operator balance is not below faucet target",
296		},
297		{
298			name: "admin realm balance is insufficient",
299			setup: func(cur realm) (address, runtime.Realm, address) {
300				adminBalance := adminUgnotBalance(adminRealmAddr())
301				testing.SetOriginCaller(testAdmin)
302				testing.SetRealm(testing.NewUserRealm(testAdmin))
303				SetOperator(cross(cur), testOperator)
304				SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+adminBalance+1)
305				return testAdmin, testing.NewUserRealm(testAdmin), testOperator
306			},
307			panicMsg: "admin realm balance is insufficient",
308		},
309		{
310			name: "target amount is not configured",
311			setup: func(cur realm) (address, runtime.Realm, address) {
312				testing.SetOriginCaller(testAdmin)
313				testing.SetRealm(testing.NewUserRealm(testAdmin))
314				SetOperator(cross(cur), testOperator)
315				faucetTest.FundAdmin(testFaucetFundAmount)
316				return testAdmin, testing.NewUserRealm(testAdmin), testOperator
317			},
318			panicMsg: "faucet target amount must be positive",
319		},
320	}
321
322	for _, tt := range tests {
323		setupCommonAdminTests(cur, t)
324		caller, rlm, operator := tt.setup(cur)
325		testing.SetOriginCaller(caller)
326		testing.SetRealm(rlm)
327
328		uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() {
329			RequestFaucetFor(cross(cur), operator)
330		})
331	}
332}
333
334func TestRequestFaucetsRejectsInvalidRequest(cur realm, t *testing.T) {
335	tests := []struct {
336		name     string
337		setup    func(cur realm) (address, runtime.Realm)
338		panicMsg string
339	}{
340		{
341			name: "non-admin caller",
342			setup: func(cur realm) (address, runtime.Realm) {
343				testing.SetOriginCaller(testAdmin)
344				testing.SetRealm(testing.NewUserRealm(testAdmin))
345				SetOperator(cross(cur), testOperator)
346				SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+1)
347				faucetTest.FundAdmin(testFaucetFundAmount)
348				return unauthorizedUser, testing.NewUserRealm(unauthorizedUser)
349			},
350			panicMsg: "admin access required",
351		},
352		{
353			name: "admin realm balance is insufficient",
354			setup: func(cur realm) (address, runtime.Realm) {
355				adminBalance := adminUgnotBalance(adminRealmAddr())
356				testing.SetOriginCaller(testAdmin)
357				testing.SetRealm(testing.NewUserRealm(testAdmin))
358				SetOperator(cross(cur), testOperator)
359				SetFaucetTargetAmount(cross(cur), adminUgnotBalance(testOperator)+adminBalance+1)
360				return testAdmin, testing.NewUserRealm(testAdmin)
361			},
362			panicMsg: "admin realm balance is insufficient",
363		},
364		{
365			name: "target amount is not configured",
366			setup: func(cur realm) (address, runtime.Realm) {
367				testing.SetOriginCaller(testAdmin)
368				testing.SetRealm(testing.NewUserRealm(testAdmin))
369				SetOperator(cross(cur), testOperator)
370				faucetTest.FundAdmin(testFaucetFundAmount)
371				return testAdmin, testing.NewUserRealm(testAdmin)
372			},
373			panicMsg: "faucet target amount must be positive",
374		},
375	}
376
377	for _, tt := range tests {
378		setupCommonAdminTests(cur, t)
379		caller, rlm := tt.setup(cur)
380		testing.SetOriginCaller(caller)
381		testing.SetRealm(rlm)
382
383		uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() {
384			RequestFaucets(cross(cur))
385		})
386	}
387}
388
389func TestWithdrawTransfersAdminRealmBalance(cur realm, t *testing.T) {
390	setupCommonAdminTests(cur, t)
391
392	adminRealm := adminRealmAddr()
393	adminBefore := adminUgnotBalance(adminRealm)
394	recipientBefore := adminUgnotBalance(testUser)
395	withdrawAmount := int64(200000)
396	faucetTest.FundAdmin(withdrawAmount)
397
398	testing.SetOriginCaller(testAdmin)
399	testing.SetRealm(testing.NewUserRealm(testAdmin))
400	transferred := Withdraw(cross(cur), testUser, withdrawAmount)
401
402	uassert.Equal(t, withdrawAmount, transferred)
403	uassert.Equal(t, recipientBefore+withdrawAmount, adminUgnotBalance(testUser))
404	uassert.Equal(t, adminBefore, adminUgnotBalance(adminRealm))
405}
406
407type withdrawRejectContext struct {
408	caller          address
409	recipient       address
410	amount          int64
411	adminBefore     int64
412	recipientBefore int64
413	checkRecipient  bool
414}
415
416func TestWithdrawRejectsInvalidRequest(cur realm, t *testing.T) {
417	tests := []struct {
418		name     string
419		setup    func() withdrawRejectContext
420		panicMsg string
421	}{
422		{
423			name: "non-admin caller",
424			setup: func() withdrawRejectContext {
425				faucetTest.FundAdmin(1)
426				return withdrawRejectContext{
427					caller:    unauthorizedUser,
428					recipient: testUser,
429					amount:    1,
430				}
431			},
432			panicMsg: "admin access required",
433		},
434		{
435			name: "invalid recipient",
436			setup: func() withdrawRejectContext {
437				faucetTest.FundAdmin(1)
438				return withdrawRejectContext{
439					caller:    testAdmin,
440					recipient: address(""),
441					amount:    1,
442				}
443			},
444			panicMsg: "invalid address",
445		},
446		{
447			name: "zero amount",
448			setup: func() withdrawRejectContext {
449				faucetTest.FundAdmin(1)
450				return withdrawRejectContext{
451					caller:    testAdmin,
452					recipient: testUser,
453					amount:    0,
454				}
455			},
456			panicMsg: "amount must be greater than 0",
457		},
458		{
459			name: "negative amount",
460			setup: func() withdrawRejectContext {
461				faucetTest.FundAdmin(1)
462				return withdrawRejectContext{
463					caller:    testAdmin,
464					recipient: testUser,
465					amount:    -1,
466				}
467			},
468			panicMsg: "amount must be greater than 0",
469		},
470		{
471			name: "admin realm balance is insufficient",
472			setup: func() withdrawRejectContext {
473				return withdrawRejectContext{
474					caller:    testAdmin,
475					recipient: testUser,
476					amount:    adminUgnotBalance(adminRealmAddr()) + 1,
477				}
478			},
479			panicMsg: "admin realm balance is insufficient",
480		},
481	}
482
483	for _, tt := range tests {
484		setupCommonAdminTests(cur, t)
485		ctx := tt.setup()
486		ctx.adminBefore = adminUgnotBalance(adminRealmAddr())
487		ctx.checkRecipient = ctx.recipient.IsValid()
488		if ctx.checkRecipient {
489			ctx.recipientBefore = adminUgnotBalance(ctx.recipient)
490		}
491		testing.SetOriginCaller(ctx.caller)
492		testing.SetRealm(testing.NewUserRealm(ctx.caller))
493
494		uassert.AbortsWithMessage(t, cur, tt.panicMsg, func() {
495			Withdraw(cross(cur), ctx.recipient, ctx.amount)
496		})
497		uassert.Equal(t, ctx.adminBefore, adminUgnotBalance(adminRealmAddr()))
498		if ctx.checkRecipient {
499			uassert.Equal(t, ctx.recipientBefore, adminUgnotBalance(ctx.recipient))
500		}
501	}
502}
503
504func adminUgnotBalance(addr address) int64 {
505	bnk := banker.NewReadonlyBanker()
506	return bnk.GetCoins(addr).AmountOf("ugnot")
507}
508
509func (h *faucetTestHelper) FundAdmin(amount int64) {
510	h.Issue(adminRealmAddr(), amount)
511}
512
513func (h *faucetTestHelper) Issue(addr address, amount int64) {
514	if amount <= 0 {
515		return
516	}
517	testing.IssueCoins(addr, chain.Coins{{"ugnot", amount}})
518}
519
520func maxInt64(a int64, b int64) int64 {
521	if a > b {
522		return a
523	}
524	return b
525}