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}