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

games_test.gno

9.21 Kb · 343 lines
  1package kewrchess
  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 := Resign(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 := CreateGame(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 = MakeMove(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 = MakeMove(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 = OfferDraw(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 = AcceptDraw(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 := JoinQueue(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 = JoinQueue(p1)
102	if err == nil {
103		t.Fatal("expected error when joining queue twice")
104	}
105
106	// 4. Test LeaveQueue
107	err = LeaveQueue(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 = LeaveQueue(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 = JoinQueue(p1)
127	if err != nil {
128		t.Fatalf("p1 failed to rejoin queue: %v", err)
129	}
130
131	err = JoinQueue(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 := CreateGame(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 = SettleGame(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 = SettleGame(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 = SettleGame(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 = SettleGame(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 = SettleGame(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 = SettleGame(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 := CreateGame(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 = ProposeSettlement(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 = ConfirmSettlement(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 = DisputeSettlement(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 = ProposeSettlement(p1, gameID, scholarsMate)
275	if err != nil {
276		t.Fatalf("failed to repropose settlement: %v", err)
277	}
278
279	err = ConfirmSettlement(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 := CreateGame(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 = StartTurnTimer(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 = StartTurnTimer(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 = ClaimTimeout(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 = ClaimTimeout(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}
342
343