games_test.gno
9.45 Kb · 341 lines
1package kewrchess_v2
2
3import (
4 "testing"
5)
6
7func TestGameInitAndRender(t *testing.T) {
8 if GetGamesCount() != 1 {
9 t.Fatalf("expected 1 game in init, got %d", GetGamesCount())
10 }
11
12 rendered := Render("")
13 if len(rendered) == 0 {
14 t.Fatal("rendered output is empty")
15 }
16}
17
18func TestResign(t *testing.T) {
19 white := GetWhitePlayer(0)
20 err := ResignInternal(white, 0)
21 if err != nil {
22 t.Fatalf("unexpected error on resign: %v", err)
23 }
24
25 if GetGameStatus(0) != "BLACK_WON" {
26 t.Fatalf("expected status BLACK_WON, got %s", GetGameStatus(0))
27 }
28}
29
30func TestNewGameAndMoves(t *testing.T) {
31 p1 := address("g1p177777777777777777777777777777777777777")
32 p2 := address("g1p277777777777777777777777777777777777777")
33
34 err := CreateGameInternal(p1, p2)
35 if err != nil {
36 t.Fatalf("failed to create game: %v", err)
37 }
38
39 gameID := GetGamesCount() - 1
40
41 // White moves pawn e2 to e4 (from 6,4 to 4,4)
42 err = MakeMoveInternal(p1, gameID, 6, 4, 4, 4)
43 if err != nil {
44 t.Fatalf("failed to make move e2e4: %v", err)
45 }
46
47 if IsWhiteTurn(gameID) {
48 t.Fatal("expected Black's turn after White move")
49 }
50
51 // Black moves pawn e7 to e5 (from 1,4 to 3,4)
52 err = MakeMoveInternal(p2, gameID, 1, 4, 3, 4)
53 if err != nil {
54 t.Fatalf("failed to make move e7e5: %v", err)
55 }
56
57 // Test draw offer
58 err = OfferDrawInternal(p1, gameID)
59 if err != nil {
60 t.Fatalf("p1 failed to offer draw: %v", err)
61 }
62
63 if GetDrawOfferedBy(gameID) != p1 {
64 t.Fatalf("expected draw offered by p1, got %s", GetDrawOfferedBy(gameID))
65 }
66
67 // p2 accepts draw
68 err = AcceptDrawInternal(p2, gameID)
69 if err != nil {
70 t.Fatalf("p2 failed to accept draw: %v", err)
71 }
72
73 if GetGameStatus(gameID) != "DRAW" {
74 t.Fatalf("expected status DRAW, got %s", GetGameStatus(gameID))
75 }
76}
77
78func TestMatchmakingQueue(t *testing.T) {
79 p1 := address("g1player11111111111111111111111111111111111")
80 p2 := address("g1player22222222222222222222222222222222222")
81
82 // 1. Initial queue count must be 0
83 if GetQueueCount() != 0 {
84 t.Fatalf("expected queue count 0, got %d", GetQueueCount())
85 }
86
87 // 2. Player 1 joins queue
88 err := JoinQueueInternal(p1)
89 if err != nil {
90 t.Fatalf("p1 failed to join queue: %v", err)
91 }
92
93 if GetQueueCount() != 1 {
94 t.Fatalf("expected queue count 1, got %d", GetQueueCount())
95 }
96 if !IsInQueue(p1) {
97 t.Fatal("expected p1 to be in queue")
98 }
99
100 // 3. Joining twice must be rejected
101 err = JoinQueueInternal(p1)
102 if err == nil {
103 t.Fatal("expected error when joining queue twice")
104 }
105
106 // 4. Test LeaveQueue
107 err = LeaveQueueInternal(p1)
108 if err != nil {
109 t.Fatalf("p1 failed to leave queue: %v", err)
110 }
111 if GetQueueCount() != 0 {
112 t.Fatalf("expected queue count 0 after leave, got %d", GetQueueCount())
113 }
114 if IsInQueue(p1) {
115 t.Fatal("expected p1 to not be in queue after leave")
116 }
117
118 // 5. Leaving when not in queue should return an error
119 err = LeaveQueueInternal(p1)
120 if err == nil {
121 t.Fatal("expected error when leaving queue while not in it")
122 }
123
124 // 6. Test Matchmaking: P1 joins, then P2 joins -> match automatically created
125 initialGameCount := GetGamesCount()
126 err = JoinQueueInternal(p1)
127 if err != nil {
128 t.Fatalf("p1 failed to rejoin queue: %v", err)
129 }
130
131 err = JoinQueueInternal(p2)
132 if err != nil {
133 t.Fatalf("p2 failed to join queue: %v", err)
134 }
135
136 // Queue should be empty again after matching
137 if GetQueueCount() != 0 {
138 t.Fatalf("expected queue count 0 after match, got %d", GetQueueCount())
139 }
140
141 // New game should be created
142 if GetGamesCount() != initialGameCount+1 {
143 t.Fatalf("expected game count %d, got %d", initialGameCount+1, GetGamesCount())
144 }
145
146 newGameID := GetGamesCount() - 1
147 if GetWhitePlayer(newGameID) != p1 {
148 t.Fatalf("expected White to be p1, got %s", GetWhitePlayer(newGameID))
149 }
150 if GetBlackPlayer(newGameID) != p2 {
151 t.Fatalf("expected Black to be p2, got %s", GetBlackPlayer(newGameID))
152 }
153 if GetGameStatus(newGameID) != "PLAYING" {
154 t.Fatalf("expected status PLAYING, got %s", GetGameStatus(newGameID))
155 }
156
157 // Verify GetActiveGameForPlayer
158 if GetActiveGameForPlayer(p1) != newGameID {
159 t.Fatalf("expected active game for p1 to be %d, got %d", newGameID, GetActiveGameForPlayer(p1))
160 }
161 if GetActiveGameForPlayer(p2) != newGameID {
162 t.Fatalf("expected active game for p2 to be %d, got %d", newGameID, GetActiveGameForPlayer(p2))
163 }
164}
165
166func TestSettleGame(t *testing.T) {
167 // For testing instant auto-settlement
168 oldWindow := ChallengeWindowSeconds
169 ChallengeWindowSeconds = 0
170 defer func() { ChallengeWindowSeconds = oldWindow }()
171
172 p1 := address("g1winner111111111111111111111111111111111")
173 p2 := address("g1loser2222222222222222222222222222222222")
174
175 err := CreateGameInternal(p1, p2)
176 if err != nil {
177 t.Fatalf("failed to create game: %v", err)
178 }
179
180 gameID := GetGamesCount() - 1
181 scholarsMate := "e2e4 e7e5 d1h5 b8c6 f1c4 g8f6 h5f7"
182
183 // 1. Third party caller should not be able to settle
184 p3 := address("g1intruder3333333333333333333333333333333")
185 err = SettleGameInternal(p3, gameID, scholarsMate)
186 if err == nil {
187 t.Fatal("expected error when non-player tries to settle")
188 }
189
190 // 2. Incomplete game (not checkmate) should fail
191 err = SettleGameInternal(p1, gameID, "e2e4 e7e5")
192 if err == nil {
193 t.Fatal("expected error when moves do not end in checkmate or draw")
194 }
195
196 // 3. Illegal move sequence should fail
197 err = SettleGameInternal(p1, gameID, "e2e5")
198 if err == nil {
199 t.Fatal("expected error for illegal move sequence")
200 }
201
202 // 4. White won checkmate, but Black tries to claim
203 err = SettleGameInternal(p2, gameID, scholarsMate)
204 if err == nil {
205 t.Fatal("expected error when losing player tries to claim victory")
206 }
207
208 // 5. Legitimate Winner (p1 / White) settles with valid Scholar's Mate checkmate
209 err = SettleGameInternal(p1, gameID, scholarsMate)
210 if err != nil {
211 t.Fatalf("failed to settle game: %v", err)
212 }
213
214 if GetGameStatus(gameID) != "WHITE_WON" {
215 t.Fatalf("expected status WHITE_WON, got %s", GetGameStatus(gameID))
216 }
217
218 if GetSettledBy(gameID) != p1 {
219 t.Fatalf("expected settledBy p1, got %s", GetSettledBy(gameID))
220 }
221
222 // 6. Settling again should fail
223 err = SettleGameInternal(p1, gameID, scholarsMate)
224 if err == nil {
225 t.Fatal("expected error when trying to settle already settled game")
226 }
227}
228
229func TestTwoPhaseSettlementAndDispute(t *testing.T) {
230 p1 := address("g1proposer111111111111111111111111111111")
231 p2 := address("g1victim22222222222222222222222222222222")
232
233 err := CreateGameInternal(p1, p2)
234 if err != nil {
235 t.Fatalf("failed to create game: %v", err)
236 }
237
238 gameID := GetGamesCount() - 1
239 scholarsMate := "e2e4 e7e5 d1h5 b8c6 f1c4 g8f6 h5f7"
240
241 // 1. Proposer submits settlement proposal
242 err = ProposeSettlementInternal(p1, gameID, scholarsMate)
243 if err != nil {
244 t.Fatalf("p1 failed to propose settlement: %v", err)
245 }
246
247 if GetGameStatus(gameID) != "PROPOSED" {
248 t.Fatalf("expected status PROPOSED, got %s", GetGameStatus(gameID))
249 }
250 if GetProposedBy(gameID) != p1 {
251 t.Fatalf("expected proposedBy p1, got %s", GetProposedBy(gameID))
252 }
253
254 // 2. Proposer cannot confirm their own proposal
255 err = ConfirmSettlementInternal(p1, gameID)
256 if err == nil {
257 t.Fatal("expected error when proposer tries to confirm their own proposal")
258 }
259
260 // 3. Victim disputes fraudulent proposal -> status reverts to PLAYING
261 err = DisputeSettlementInternal(p2, gameID)
262 if err != nil {
263 t.Fatalf("victim failed to dispute proposal: %v", err)
264 }
265
266 if GetGameStatus(gameID) != "PLAYING" {
267 t.Fatalf("expected status PLAYING after dispute, got %s", GetGameStatus(gameID))
268 }
269 if GetProposedBy(gameID) != "" {
270 t.Fatal("expected proposedBy to be cleared after dispute")
271 }
272
273 // 4. Repropose and victim legitimately confirms -> instant settlement
274 err = ProposeSettlementInternal(p1, gameID, scholarsMate)
275 if err != nil {
276 t.Fatalf("failed to repropose settlement: %v", err)
277 }
278
279 err = ConfirmSettlementInternal(p2, gameID)
280 if err != nil {
281 t.Fatalf("victim failed to confirm settlement: %v", err)
282 }
283
284 if GetGameStatus(gameID) != "WHITE_WON" {
285 t.Fatalf("expected status WHITE_WON after confirmation, got %s", GetGameStatus(gameID))
286 }
287}
288
289func TestTurnTimeoutAbandonment(t *testing.T) {
290 oldTimeout := TurnTimeoutSeconds
291 TurnTimeoutSeconds = 0
292 defer func() { TurnTimeoutSeconds = oldTimeout }()
293
294 p1 := address("g1activeplayer11111111111111111111111111")
295 p2 := address("g1ragequitter22222222222222222222222222")
296
297 err := CreateGameInternal(p1, p2)
298 if err != nil {
299 t.Fatalf("failed to create game: %v", err)
300 }
301
302 gameID := GetGamesCount() - 1
303
304 // White played e2e4 offchain, Black rage-quitted on move 1!
305 currentMoves := "e2e4"
306
307 // 1. Black cannot start turn timer when it is Black's turn
308 err = StartTurnTimerInternal(p2, gameID, currentMoves)
309 if err == nil {
310 t.Fatal("expected error when AFK player tries to start timer against opponent")
311 }
312
313 // 2. White legitimately starts turn timer for AFK Black
314 err = StartTurnTimerInternal(p1, gameID, currentMoves)
315 if err != nil {
316 t.Fatalf("white failed to start turn timer: %v", err)
317 }
318
319 if GetTurnChallengedBy(gameID) != p1 {
320 t.Fatalf("expected turnChallengedBy p1, got %s", GetTurnChallengedBy(gameID))
321 }
322
323 // 3. Other player or Black cannot claim timeout
324 err = ClaimTimeoutInternal(p2, gameID)
325 if err == nil {
326 t.Fatal("expected error when non-challenger tries to claim timeout")
327 }
328
329 // 4. White claims timeout (since TurnTimeoutSeconds=0) -> White wins by Abandonment!
330 err = ClaimTimeoutInternal(p1, gameID)
331 if err != nil {
332 t.Fatalf("white failed to claim timeout: %v", err)
333 }
334
335 if GetGameStatus(gameID) != "WHITE_WON" {
336 t.Fatalf("expected status WHITE_WON from abandonment, got %s", GetGameStatus(gameID))
337 }
338 if GetSettledBy(gameID) != p1 {
339 t.Fatalf("expected settledBy p1, got %s", GetSettledBy(gameID))
340 }
341}