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