import { describe, it, expect } from "bun:test"; import { chessPlugin } from "./plugin"; import { gameRegistry } from "../registry"; const PLAYER_WHITE = "player1"; const PLAYER_BLACK = "player2"; describe("chessPlugin", () => { describe("metadata", () => { it("should have correct slug and player counts", () => { expect(chessPlugin.slug).toBe("chess"); expect(chessPlugin.minPlayers).toBe(2); expect(chessPlugin.maxPlayers).toBe(2); }); }); describe("createInitialState", () => { it("should create initial FEN position", () => { const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); expect(state.fen).toBe("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); expect(state.players.white).toBe(PLAYER_WHITE); expect(state.players.black).toBe(PLAYER_BLACK); expect(state.moveHistory).toEqual([]); expect(state.status).toBe("playing"); }); }); describe("handleAction — move", () => { it("should allow a legal pawn move", () => { const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); const result = chessPlugin.handleAction(state, { type: "move", from: "e2", to: "e4" }, PLAYER_WHITE); expect(result.ok).toBe(true); if (result.ok) { expect(result.state.fen).not.toBe(state.fen); expect(result.state.moveHistory).toEqual(["e4"]); } }); it("should reject move when it is not the player's turn", () => { const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); const result = chessPlugin.handleAction(state, { type: "move", from: "e7", to: "e5" }, PLAYER_BLACK); expect(result.ok).toBe(false); }); it("should reject an illegal move", () => { const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); const result = chessPlugin.handleAction(state, { type: "move", from: "e2", to: "e5" }, PLAYER_WHITE); expect(result.ok).toBe(false); }); it("should reject a non-player's move", () => { const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); const result = chessPlugin.handleAction(state, { type: "move", from: "e2", to: "e4" }, "random_player"); expect(result.ok).toBe(false); }); it("should detect checkmate", () => { // Scholar's mate: 1.e4 e5 2.Bc4 Nc6 3.Qh5 Nf6 4.Qxf7# let state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); const moves = [ { player: PLAYER_WHITE, from: "e2", to: "e4" }, { player: PLAYER_BLACK, from: "e7", to: "e5" }, { player: PLAYER_WHITE, from: "f1", to: "c4" }, { player: PLAYER_BLACK, from: "b8", to: "c6" }, { player: PLAYER_WHITE, from: "d1", to: "h5" }, { player: PLAYER_BLACK, from: "g8", to: "f6" }, { player: PLAYER_WHITE, from: "h5", to: "f7" }, ]; for (const m of moves) { const result = chessPlugin.handleAction(state, { type: "move", from: m.from, to: m.to }, m.player); expect(result.ok).toBe(true); if (result.ok) state = result.state; } expect(state.status).toBe("checkmate"); expect(state.winner).toBe(PLAYER_WHITE); }); }); describe("handleAction — forfeit", () => { it("should end the game with the other player as winner", () => { const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); const result = chessPlugin.handleAction(state, { type: "forfeit" }, PLAYER_WHITE); expect(result.ok).toBe(true); if (result.ok) { expect(result.state.status).toBe("forfeit"); expect(result.state.winner).toBe(PLAYER_BLACK); } }); }); describe("getPlayerView", () => { it("should return full state", () => { const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); const view = chessPlugin.getPlayerView(state, PLAYER_WHITE); expect(view).toEqual(state); }); }); describe("isGameOver", () => { it("should return null for ongoing game", () => { const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); expect(chessPlugin.isGameOver!(state)).toBeNull(); }); it("should return winner for forfeit", () => { const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); state.status = "forfeit"; state.winner = PLAYER_BLACK; expect(chessPlugin.isGameOver!(state)).toEqual({ winner: PLAYER_BLACK, reason: "forfeit" }); }); }); describe("onPlayerDisconnect", () => { it("should forfeit the disconnected player", () => { const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]); const result = chessPlugin.onPlayerDisconnect!(state, PLAYER_WHITE); expect(result.status).toBe("forfeit"); expect(result.winner).toBe(PLAYER_BLACK); }); }); describe("chess registration", () => { it("should register and retrieve from gameRegistry", () => { gameRegistry.register(chessPlugin); expect(gameRegistry.get("chess")).toBe(chessPlugin); expect(gameRegistry.list()).toContain(chessPlugin); }); }); });