fomo3d_test.gno

9.66 Kb ยท 293 lines
  1package fomo3d
  2
  3import (
  4	"std"
  5	"testing"
  6
  7	"gno.land/p/demo/avl"
  8	"gno.land/p/demo/grc/grc721"
  9	"gno.land/p/demo/ownable"
 10	"gno.land/p/demo/testutils"
 11	"gno.land/p/demo/urequire"
 12)
 13
 14// Reset game state
 15func setupTestGame(t *testing.T) {
 16	gameState = GameState{
 17		StartBlock:   0,
 18		EndBlock:     0,
 19		LastKeyBlock: 0,
 20		LastBuyer:    "",
 21		Jackpot:      0,
 22		KeyPrice:     MIN_KEY_PRICE,
 23		TotalKeys:    0,
 24		Ended:        true,
 25		CurrentRound: 0,
 26		NextPot:      0,
 27		OwnerFee:     0,
 28	}
 29	players = avl.NewTree()
 30	Ownable = ownable.New()
 31}
 32
 33// Test ownership functionality
 34func TestOwnership(t *testing.T) {
 35	owner := testutils.TestAddress("owner")
 36	nonOwner := testutils.TestAddress("nonOwner")
 37
 38	// Set up initial owner
 39	testing.SetOriginCaller(owner)
 40	setupTestGame(t)
 41
 42	// Transfer ownership to nonOwner first to test ownership functions
 43	testing.SetOriginCaller(owner)
 44	urequire.NotPanics(t, func() {
 45		Ownable.TransferOwnership(nonOwner)
 46	})
 47
 48	// Test fee accumulation
 49	StartGame()
 50	payment := MIN_KEY_PRICE * 10
 51	testing.SetOriginCaller(owner)
 52	testing.SetOriginSend(std.Coins{{"ugnot", payment}})
 53	testing.IssueCoins(owner, std.Coins{{"ugnot", payment}})
 54	testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"ugnot", payment}})
 55	BuyKeys()
 56
 57	// Verify fee accumulation
 58	_, fees := GetOwnerInfo()
 59	expectedFees := payment * OWNER_FEE_PERCENT / 100
 60	urequire.Equal(t, expectedFees, fees)
 61
 62	// Test unauthorized fee claim (using old owner)
 63	testing.SetOriginCaller(owner)
 64	urequire.PanicsWithMessage(t, "ownable: caller is not owner", ClaimOwnerFee)
 65
 66	// Test authorized fee claim (using new owner)
 67	testing.SetOriginCaller(nonOwner)
 68	initialBalance := std.NewBanker(std.BankerTypeRealmSend).GetCoins(nonOwner)
 69	testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"ugnot", expectedFees}})
 70	urequire.NotPanics(t, ClaimOwnerFee)
 71
 72	// Verify fees were claimed
 73	_, feesAfter := GetOwnerInfo()
 74	urequire.Equal(t, int64(0), feesAfter)
 75
 76	finalBalance := std.NewBanker(std.BankerTypeRealmSend).GetCoins(nonOwner)
 77	urequire.Equal(t, initialBalance.AmountOf("ugnot")+expectedFees, finalBalance.AmountOf("ugnot"))
 78}
 79
 80// Test full game flow
 81func TestFullGameFlow(t *testing.T) {
 82	setupTestGame(t)
 83
 84	player1 := testutils.TestAddress("player1")
 85	player2 := testutils.TestAddress("player2")
 86	player3 := testutils.TestAddress("player3")
 87
 88	// Test initial state
 89	urequire.Equal(t, int64(0), gameState.CurrentRound)
 90	urequire.Equal(t, MIN_KEY_PRICE, gameState.KeyPrice)
 91	urequire.Equal(t, true, gameState.Ended)
 92
 93	// Start game
 94	urequire.NotPanics(t, StartGame)
 95	urequire.Equal(t, false, gameState.Ended)
 96	urequire.Equal(t, std.ChainHeight(), gameState.StartBlock)
 97	urequire.Equal(t, int64(1), gameState.CurrentRound)
 98
 99	t.Run("buying keys", func(t *testing.T) {
100		// Test insufficient payment
101		testing.SetOriginCaller(player1)
102		testing.IssueCoins(player1, std.Coins{{"ugnot", MIN_KEY_PRICE - 1}})
103		testing.SetOriginSend(std.Coins{{"ugnot", MIN_KEY_PRICE - 1}})
104		testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"ugnot", MIN_KEY_PRICE - 1}})
105		urequire.PanicsWithMessage(t, ErrInsufficientPayment.Error(), BuyKeys)
106
107		// Test successful key purchase
108		payment := MIN_KEY_PRICE * 3
109		testing.SetOriginSend(std.Coins{{"ugnot", payment}})
110		testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"ugnot", payment}})
111
112		currentBlock := std.ChainHeight()
113		urequire.NotPanics(t, BuyKeys)
114
115		// Verify time extension
116		_, endBlock, _, _, _, _, _, _, _, _ := GetGameState()
117		urequire.Equal(t, currentBlock+TIME_EXTENSION, endBlock)
118
119		// Verify player state
120		keys, dividends := GetPlayerInfo(player1.String())
121
122		urequire.Equal(t, int64(3), keys)
123		urequire.Equal(t, int64(0), dividends)
124		urequire.Equal(t, player1, gameState.LastBuyer)
125
126		// Verify game state
127		_, endBlock, _, buyer, pot, price, keys, isEnded, nextPot, round := GetGameState()
128		urequire.Equal(t, player1, buyer)
129		urequire.Equal(t, int64(3), keys)
130		urequire.Equal(t, false, isEnded)
131
132		urequire.Equal(t, payment*JACKPOT_PERCENT/100, pot)
133
134		// Verify owner fee
135		_, ownerFees := GetOwnerInfo()
136		urequire.Equal(t, payment*OWNER_FEE_PERCENT/100, ownerFees)
137	})
138
139	t.Run("dividend distribution and claiming", func(t *testing.T) {
140		// Player 2 buys keys
141		testing.SetOriginCaller(player2)
142		payment := gameState.KeyPrice * 2 // Buy 2 keys using current keyPrice
143		testing.SetOriginSend(std.Coins{{"ugnot", payment}})
144		testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"ugnot", payment}})
145		urequire.NotPanics(t, BuyKeys)
146
147		// Check player1 received dividends
148		keys1, dividends1 := GetPlayerInfo(player1.String())
149
150		urequire.Equal(t, int64(3), keys1)
151		expectedDividends := payment * DIVIDENDS_PERCENT / 100 * 3 / gameState.TotalKeys
152		urequire.Equal(t, expectedDividends, dividends1)
153
154		// Test claiming dividends
155		{
156			// Player1 claims dividends
157			testing.SetOriginCaller(player1)
158			initialBalance := std.NewBanker(std.BankerTypeRealmSend).GetCoins(player1)
159			urequire.NotPanics(t, ClaimDividends)
160
161			// Verify dividends were claimed
162			_, dividendsAfter := GetPlayerInfo(player1.String())
163			urequire.Equal(t, int64(0), dividendsAfter)
164
165			lastBuyerBalance := std.NewBanker(std.BankerTypeRealmSend).GetCoins(player1)
166			urequire.Equal(t, initialBalance.AmountOf("ugnot")+expectedDividends, lastBuyerBalance.AmountOf("ugnot"))
167		}
168	})
169
170	t.Run("game ending", func(t *testing.T) {
171		// Try ending too early
172		urequire.PanicsWithMessage(t, ErrGameNotInProgress.Error(), EndGame)
173
174		// Skip to end of current time window
175		currentEndBlock := gameState.EndBlock
176		testing.SkipHeights(currentEndBlock - std.ChainHeight() + 1)
177
178		// End game successfully
179		urequire.NotPanics(t, EndGame)
180		urequire.Equal(t, true, gameState.Ended)
181		urequire.Equal(t, int64(1), gameState.CurrentRound)
182
183		// Verify winner received jackpot
184		lastBuyerBalance := std.NewBanker(std.BankerTypeRealmSend).GetCoins(gameState.LastBuyer)
185		urequire.Equal(t, gameState.Jackpot, lastBuyerBalance.AmountOf("ugnot"))
186
187		// Verify NFT was minted to winner
188		balance, err := BalanceOf(gameState.LastBuyer)
189		urequire.NoError(t, err)
190		urequire.Equal(t, uint64(1), balance)
191
192		// Check NFT metadata
193		tokenID := grc721.TokenID("1")
194		metadata, err := TokenMetadata(tokenID)
195
196		urequire.NoError(t, err)
197		urequire.Equal(t, "Fomo3D Winner - Round #1", metadata.Name)
198	})
199
200	// Test new round
201	t.Run("new round", func(t *testing.T) {
202		// Calculate expected next pot from previous round
203		payment1 := MIN_KEY_PRICE * 3
204		// After buying 3 keys, price increased by 3% (1% per key)
205		secondKeyPrice := MIN_KEY_PRICE + (MIN_KEY_PRICE * 3 / 100)
206		payment2 := secondKeyPrice * 2
207		expectedNextPot := (payment1 * NEXT_ROUND_POT / 100) + (payment2 * NEXT_ROUND_POT / 100)
208
209		// Start new round
210		urequire.NotPanics(t, StartGame)
211		urequire.Equal(t, false, gameState.Ended)
212		urequire.Equal(t, int64(2), gameState.CurrentRound)
213
214		start, end, last, buyer, pot, price, keys, isEnded, nextPot, round := GetGameState()
215		urequire.Equal(t, int64(2), round)
216		urequire.Equal(t, expectedNextPot, pot)
217		urequire.Equal(t, int64(0), nextPot)
218	})
219}
220
221// Test individual components
222func TestStartGame(t *testing.T) {
223	setupTestGame(t)
224
225	// Test starting first game
226	urequire.NotPanics(t, StartGame)
227	urequire.Equal(t, false, gameState.Ended)
228	urequire.Equal(t, std.ChainHeight(), gameState.StartBlock)
229
230	// Test cannot start while game in progress
231	urequire.PanicsWithMessage(t, ErrGameInProgress.Error(), StartGame)
232}
233
234func TestBuyKeys(t *testing.T) {
235	setupTestGame(t)
236	StartGame()
237
238	player := testutils.TestAddress("player")
239	testing.SetOriginCaller(player)
240
241	// Test invalid coin denomination
242	testing.IssueCoins(player, std.Coins{{"invalid", MIN_KEY_PRICE}})
243	testing.SetOriginSend(std.Coins{{"invalid", MIN_KEY_PRICE}})
244	testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"invalid", MIN_KEY_PRICE}})
245	urequire.PanicsWithMessage(t, ErrInvalidPayment.Error(), BuyKeys)
246
247	// Test multiple coin types
248	testing.IssueCoins(player, std.Coins{{"ugnot", MIN_KEY_PRICE}, {"other", 100}})
249	testing.SetOriginSend(std.Coins{{"ugnot", MIN_KEY_PRICE}, {"other", 100}})
250	testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"ugnot", MIN_KEY_PRICE}, {"other", 100}})
251	urequire.PanicsWithMessage(t, ErrInvalidPayment.Error(), BuyKeys)
252
253	// Test insufficient payment
254	testing.IssueCoins(player, std.Coins{{"ugnot", MIN_KEY_PRICE - 1}})
255	testing.SetOriginSend(std.Coins{{"ugnot", MIN_KEY_PRICE - 1}})
256	testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"ugnot", MIN_KEY_PRICE - 1}})
257	urequire.PanicsWithMessage(t, ErrInsufficientPayment.Error(), BuyKeys)
258
259	// Test successful purchase
260	testing.IssueCoins(player, std.Coins{{"ugnot", MIN_KEY_PRICE * 2}})
261	testing.SetOriginSend(std.Coins{{"ugnot", MIN_KEY_PRICE * 2}})
262	testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"ugnot", MIN_KEY_PRICE * 2}})
263	urequire.NotPanics(t, BuyKeys)
264}
265
266func TestClaimDividends(t *testing.T) {
267	setupTestGame(t)
268	StartGame()
269
270	player := testutils.TestAddress("player")
271	testing.SetOriginCaller(player)
272
273	// Test claiming with no dividends
274	urequire.PanicsWithMessage(t, ErrNoDividendsToClaim.Error(), ClaimDividends)
275
276	// Setup player with dividends
277	testing.IssueCoins(player, std.Coins{{"ugnot", MIN_KEY_PRICE}})
278	testing.SetOriginSend(std.Coins{{"ugnot", MIN_KEY_PRICE}})
279	testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"ugnot", MIN_KEY_PRICE}})
280	BuyKeys()
281
282	// Have another player buy to generate dividends
283	player2 := testutils.TestAddress("player2")
284	testing.SetOriginCaller(player2)
285	testing.IssueCoins(player2, std.Coins{{"ugnot", MIN_KEY_PRICE * 2}})
286	testing.SetOriginSend(std.Coins{{"ugnot", MIN_KEY_PRICE * 2}})
287	testing.IssueCoins(std.CurrentRealm().Address(), std.Coins{{"ugnot", MIN_KEY_PRICE * 2}})
288	BuyKeys()
289
290	// Test successful claim
291	testing.SetOriginCaller(player)
292	urequire.NotPanics(t, ClaimDividends)
293}