144 lines
6.4 KiB
TypeScript
144 lines
6.4 KiB
TypeScript
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 a board with pieces in starting positions", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
expect(state.board.length).toBe(8);
|
|
expect(state.board[0].length).toBe(8);
|
|
expect(state.currentTurn).toBe("white");
|
|
expect(state.players.white).toBe(PLAYER_WHITE);
|
|
expect(state.players.black).toBe(PLAYER_BLACK);
|
|
expect(state.moveHistory).toEqual([]);
|
|
expect(state.status).toBe("playing");
|
|
});
|
|
|
|
it("should place white pawns on row 6 and black pawns on row 1", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
for (let col = 0; col < 8; col++) {
|
|
expect(state.board[6][col]).toEqual({ type: "pawn", color: "white" });
|
|
expect(state.board[1][col]).toEqual({ type: "pawn", color: "black" });
|
|
}
|
|
});
|
|
|
|
it("should place rooks in corners", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
expect(state.board[0][0]).toEqual({ type: "rook", color: "black" });
|
|
expect(state.board[0][7]).toEqual({ type: "rook", color: "black" });
|
|
expect(state.board[7][0]).toEqual({ type: "rook", color: "white" });
|
|
expect(state.board[7][7]).toEqual({ type: "rook", color: "white" });
|
|
});
|
|
});
|
|
|
|
describe("handleAction — move", () => {
|
|
it("should allow white pawn to move forward on white's turn", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
const result = chessPlugin.handleAction(state, { type: "move", from: [6, 4], to: [4, 4] }, PLAYER_WHITE);
|
|
expect(result.ok).toBe(true);
|
|
if (result.ok) {
|
|
expect(result.state.board[4][4]).toEqual({ type: "pawn", color: "white" });
|
|
expect(result.state.board[6][4]).toBeNull();
|
|
expect(result.state.currentTurn).toBe("black");
|
|
}
|
|
});
|
|
|
|
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: [1, 4], to: [3, 4] }, PLAYER_BLACK);
|
|
expect(result.ok).toBe(false);
|
|
});
|
|
|
|
it("should reject moving opponent's piece", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
const result = chessPlugin.handleAction(state, { type: "move", from: [1, 4], to: [3, 4] }, PLAYER_WHITE);
|
|
expect(result.ok).toBe(false);
|
|
});
|
|
|
|
it("should reject move from empty square", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
const result = chessPlugin.handleAction(state, { type: "move", from: [4, 4], to: [3, 4] }, PLAYER_WHITE);
|
|
expect(result.ok).toBe(false);
|
|
});
|
|
|
|
it("should reject out-of-bounds coordinates", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
const result = chessPlugin.handleAction(state, { type: "move", from: [8, 0], to: [7, 0] }, PLAYER_WHITE);
|
|
expect(result.ok).toBe(false);
|
|
});
|
|
});
|
|
|
|
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 (chess has no hidden info)", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
const view = chessPlugin.getPlayerView(state, PLAYER_WHITE);
|
|
expect(view).toEqual(state);
|
|
});
|
|
});
|
|
|
|
describe("getSpectatorView", () => {
|
|
it("should return full state", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
const view = chessPlugin.getSpectatorView(state);
|
|
expect(view).toEqual(state);
|
|
});
|
|
});
|
|
|
|
describe("isGameOver", () => {
|
|
it("should return null for ongoing game", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
const result = chessPlugin.isGameOver!(state);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("should return winner for forfeit", () => {
|
|
const state = chessPlugin.createInitialState([PLAYER_WHITE, PLAYER_BLACK]);
|
|
state.status = "forfeit";
|
|
state.winner = PLAYER_BLACK;
|
|
const result = chessPlugin.isGameOver!(state);
|
|
expect(result).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);
|
|
});
|
|
});
|
|
});
|