refactor(tests): streamline test cases for ApiResponse, Board, GameContext, Piece, and Square
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import glob,re,os
|
import glob,re
|
||||||
rows=[]
|
mods=['api','core','io','rule','ui']
|
||||||
for f in glob.glob('modules/*/build/test-results/test/TEST-*.xml'):
|
tot=0
|
||||||
txt=open(f,encoding='utf-8').read(500)
|
for m in mods:
|
||||||
m1=re.search(r'name="([^"]+)"',txt)
|
s=0
|
||||||
m2=re.search(r'tests="(\d+)"',txt)
|
for f in glob.glob(f'modules/{m}/build/test-results/test/TEST-*.xml'):
|
||||||
if m1 and m2:
|
txt=open(f,encoding='utf-8').read(300)
|
||||||
rows.append((int(m2.group(1)),f,m1.group(1)))
|
m2=re.search(r'tests="(\d+)"',txt)
|
||||||
for n,f,name in sorted(rows, reverse=True)[:20]:
|
if m2:s+=int(m2.group(1))
|
||||||
print(f'{n:3} {name} ({f})')
|
print(f'{m}: {s}')
|
||||||
|
tot+=s
|
||||||
|
print('overall:',tot)
|
||||||
@@ -8,13 +8,9 @@ class BoardTest extends AnyFunSuite with Matchers:
|
|||||||
|
|
||||||
private val e2 = Square(File.E, Rank.R2)
|
private val e2 = Square(File.E, Rank.R2)
|
||||||
private val e4 = Square(File.E, Rank.R4)
|
private val e4 = Square(File.E, Rank.R4)
|
||||||
private val d7 = Square(File.D, Rank.R7)
|
|
||||||
|
|
||||||
test("pieceAt returns Some for occupied square") {
|
test("pieceAt resolves occupied and empty squares") {
|
||||||
Board.initial.pieceAt(e2) shouldBe Some(Piece.WhitePawn)
|
Board.initial.pieceAt(e2) shouldBe Some(Piece.WhitePawn)
|
||||||
}
|
|
||||||
|
|
||||||
test("pieceAt returns None for empty square") {
|
|
||||||
Board.initial.pieceAt(e4) shouldBe None
|
Board.initial.pieceAt(e4) shouldBe None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,38 +31,20 @@ class BoardTest extends AnyFunSuite with Matchers:
|
|||||||
board.pieceAt(from) shouldBe None
|
board.pieceAt(from) shouldBe None
|
||||||
}
|
}
|
||||||
|
|
||||||
test("pieces returns the underlying map") {
|
test("Board.apply and pieces expose the wrapped map") {
|
||||||
val map = Map(e2 -> Piece.WhitePawn)
|
|
||||||
val b = Board(map)
|
|
||||||
b.pieces shouldBe map
|
|
||||||
}
|
|
||||||
|
|
||||||
test("Board.apply constructs board from map") {
|
|
||||||
val map = Map(e2 -> Piece.WhitePawn)
|
val map = Map(e2 -> Piece.WhitePawn)
|
||||||
val b = Board(map)
|
val b = Board(map)
|
||||||
b.pieceAt(e2) shouldBe Some(Piece.WhitePawn)
|
b.pieceAt(e2) shouldBe Some(Piece.WhitePawn)
|
||||||
|
b.pieces shouldBe map
|
||||||
}
|
}
|
||||||
|
|
||||||
test("initial board has 32 pieces") {
|
test("initial board has expected material and pawn placement") {
|
||||||
Board.initial.pieces should have size 32
|
Board.initial.pieces should have size 32
|
||||||
}
|
|
||||||
|
|
||||||
test("initial board has 16 white pieces") {
|
|
||||||
Board.initial.pieces.values.count(_.color == Color.White) shouldBe 16
|
Board.initial.pieces.values.count(_.color == Color.White) shouldBe 16
|
||||||
}
|
|
||||||
|
|
||||||
test("initial board has 16 black pieces") {
|
|
||||||
Board.initial.pieces.values.count(_.color == Color.Black) shouldBe 16
|
Board.initial.pieces.values.count(_.color == Color.Black) shouldBe 16
|
||||||
}
|
|
||||||
|
|
||||||
test("initial board white pawns on rank 2") {
|
|
||||||
File.values.foreach { file =>
|
File.values.foreach { file =>
|
||||||
Board.initial.pieceAt(Square(file, Rank.R2)) shouldBe Some(Piece.WhitePawn)
|
Board.initial.pieceAt(Square(file, Rank.R2)) shouldBe Some(Piece.WhitePawn)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test("initial board black pawns on rank 7") {
|
|
||||||
File.values.foreach { file =>
|
|
||||||
Board.initial.pieceAt(Square(file, Rank.R7)) shouldBe Some(Piece.BlackPawn)
|
Board.initial.pieceAt(Square(file, Rank.R7)) shouldBe Some(Piece.BlackPawn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,17 +80,14 @@ class BoardTest extends AnyFunSuite with Matchers:
|
|||||||
Board.initial.pieceAt(Square(file, rank)) shouldBe None
|
Board.initial.pieceAt(Square(file, rank)) shouldBe None
|
||||||
}
|
}
|
||||||
|
|
||||||
test("updated adds or replaces piece at square") {
|
test("updated adds and replaces piece at squares") {
|
||||||
val b = Board(Map(e2 -> Piece.WhitePawn))
|
val b = Board(Map(e2 -> Piece.WhitePawn))
|
||||||
val updated = b.updated(e4, Piece.WhiteKnight)
|
val added = b.updated(e4, Piece.WhiteKnight)
|
||||||
updated.pieceAt(e2) shouldBe Some(Piece.WhitePawn)
|
added.pieceAt(e2) shouldBe Some(Piece.WhitePawn)
|
||||||
updated.pieceAt(e4) shouldBe Some(Piece.WhiteKnight)
|
added.pieceAt(e4) shouldBe Some(Piece.WhiteKnight)
|
||||||
}
|
|
||||||
|
|
||||||
test("updated replaces existing piece") {
|
val replaced = b.updated(e2, Piece.WhiteKnight)
|
||||||
val b = Board(Map(e2 -> Piece.WhitePawn))
|
replaced.pieceAt(e2) shouldBe Some(Piece.WhiteKnight)
|
||||||
val updated = b.updated(e2, Piece.WhiteKnight)
|
|
||||||
updated.pieceAt(e2) shouldBe Some(Piece.WhiteKnight)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("removed deletes piece from board") {
|
test("removed deletes piece from board") {
|
||||||
|
|||||||
@@ -11,50 +11,23 @@ class PieceTest extends AnyFunSuite with Matchers:
|
|||||||
p.pieceType shouldBe PieceType.Queen
|
p.pieceType shouldBe PieceType.Queen
|
||||||
}
|
}
|
||||||
|
|
||||||
test("WhitePawn convenience constant") {
|
test("all convenience constants map to expected color and piece type") {
|
||||||
Piece.WhitePawn shouldBe Piece(Color.White, PieceType.Pawn)
|
val expected = List(
|
||||||
}
|
Piece.WhitePawn -> Piece(Color.White, PieceType.Pawn),
|
||||||
|
Piece.WhiteKnight -> Piece(Color.White, PieceType.Knight),
|
||||||
|
Piece.WhiteBishop -> Piece(Color.White, PieceType.Bishop),
|
||||||
|
Piece.WhiteRook -> Piece(Color.White, PieceType.Rook),
|
||||||
|
Piece.WhiteQueen -> Piece(Color.White, PieceType.Queen),
|
||||||
|
Piece.WhiteKing -> Piece(Color.White, PieceType.King),
|
||||||
|
Piece.BlackPawn -> Piece(Color.Black, PieceType.Pawn),
|
||||||
|
Piece.BlackKnight -> Piece(Color.Black, PieceType.Knight),
|
||||||
|
Piece.BlackBishop -> Piece(Color.Black, PieceType.Bishop),
|
||||||
|
Piece.BlackRook -> Piece(Color.Black, PieceType.Rook),
|
||||||
|
Piece.BlackQueen -> Piece(Color.Black, PieceType.Queen),
|
||||||
|
Piece.BlackKing -> Piece(Color.Black, PieceType.King)
|
||||||
|
)
|
||||||
|
|
||||||
test("WhiteKnight convenience constant") {
|
expected.foreach { case (actual, wanted) =>
|
||||||
Piece.WhiteKnight shouldBe Piece(Color.White, PieceType.Knight)
|
actual shouldBe wanted
|
||||||
}
|
}
|
||||||
|
|
||||||
test("WhiteBishop convenience constant") {
|
|
||||||
Piece.WhiteBishop shouldBe Piece(Color.White, PieceType.Bishop)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("WhiteRook convenience constant") {
|
|
||||||
Piece.WhiteRook shouldBe Piece(Color.White, PieceType.Rook)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("WhiteQueen convenience constant") {
|
|
||||||
Piece.WhiteQueen shouldBe Piece(Color.White, PieceType.Queen)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("WhiteKing convenience constant") {
|
|
||||||
Piece.WhiteKing shouldBe Piece(Color.White, PieceType.King)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("BlackPawn convenience constant") {
|
|
||||||
Piece.BlackPawn shouldBe Piece(Color.Black, PieceType.Pawn)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("BlackKnight convenience constant") {
|
|
||||||
Piece.BlackKnight shouldBe Piece(Color.Black, PieceType.Knight)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("BlackBishop convenience constant") {
|
|
||||||
Piece.BlackBishop shouldBe Piece(Color.Black, PieceType.Bishop)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("BlackRook convenience constant") {
|
|
||||||
Piece.BlackRook shouldBe Piece(Color.Black, PieceType.Rook)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("BlackQueen convenience constant") {
|
|
||||||
Piece.BlackQueen shouldBe Piece(Color.Black, PieceType.Queen)
|
|
||||||
}
|
|
||||||
|
|
||||||
test("BlackKing convenience constant") {
|
|
||||||
Piece.BlackKing shouldBe Piece(Color.Black, PieceType.King)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,67 +5,32 @@ import org.scalatest.matchers.should.Matchers
|
|||||||
|
|
||||||
class SquareTest extends AnyFunSuite with Matchers:
|
class SquareTest extends AnyFunSuite with Matchers:
|
||||||
|
|
||||||
test("Square.toString produces lowercase file and rank number") {
|
test("toString renders algebraic notation for edge and middle squares") {
|
||||||
Square(File.E, Rank.R4).toString shouldBe "e4"
|
|
||||||
}
|
|
||||||
|
|
||||||
test("Square.toString for a1") {
|
|
||||||
Square(File.A, Rank.R1).toString shouldBe "a1"
|
Square(File.A, Rank.R1).toString shouldBe "a1"
|
||||||
}
|
Square(File.E, Rank.R4).toString shouldBe "e4"
|
||||||
|
|
||||||
test("Square.toString for h8") {
|
|
||||||
Square(File.H, Rank.R8).toString shouldBe "h8"
|
Square(File.H, Rank.R8).toString shouldBe "h8"
|
||||||
}
|
}
|
||||||
|
|
||||||
test("fromAlgebraic parses valid square e4") {
|
test("fromAlgebraic parses valid coordinates including case-insensitive files") {
|
||||||
Square.fromAlgebraic("e4") shouldBe Some(Square(File.E, Rank.R4))
|
val expected = List(
|
||||||
|
"a1" -> Square(File.A, Rank.R1),
|
||||||
|
"e4" -> Square(File.E, Rank.R4),
|
||||||
|
"h8" -> Square(File.H, Rank.R8),
|
||||||
|
"E4" -> Square(File.E, Rank.R4)
|
||||||
|
)
|
||||||
|
expected.foreach { case (raw, sq) =>
|
||||||
|
Square.fromAlgebraic(raw) shouldBe Some(sq)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test("fromAlgebraic parses valid square a1") {
|
test("fromAlgebraic rejects malformed coordinates") {
|
||||||
Square.fromAlgebraic("a1") shouldBe Some(Square(File.A, Rank.R1))
|
List("", "e", "e42", "z4", "ex", "e0", "e9").foreach { raw =>
|
||||||
|
Square.fromAlgebraic(raw) shouldBe None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test("fromAlgebraic parses valid square h8") {
|
test("offset returns Some in-bounds and None out-of-bounds") {
|
||||||
Square.fromAlgebraic("h8") shouldBe Some(Square(File.H, Rank.R8))
|
|
||||||
}
|
|
||||||
|
|
||||||
test("fromAlgebraic is case-insensitive for file") {
|
|
||||||
Square.fromAlgebraic("E4") shouldBe Some(Square(File.E, Rank.R4))
|
|
||||||
}
|
|
||||||
|
|
||||||
test("fromAlgebraic returns None for empty string") {
|
|
||||||
Square.fromAlgebraic("") shouldBe None
|
|
||||||
}
|
|
||||||
|
|
||||||
test("fromAlgebraic returns None for string too short") {
|
|
||||||
Square.fromAlgebraic("e") shouldBe None
|
|
||||||
}
|
|
||||||
|
|
||||||
test("fromAlgebraic returns None for string too long") {
|
|
||||||
Square.fromAlgebraic("e42") shouldBe None
|
|
||||||
}
|
|
||||||
|
|
||||||
test("fromAlgebraic returns None for invalid file character") {
|
|
||||||
Square.fromAlgebraic("z4") shouldBe None
|
|
||||||
}
|
|
||||||
|
|
||||||
test("fromAlgebraic returns None for non-digit rank") {
|
|
||||||
Square.fromAlgebraic("ex") shouldBe None
|
|
||||||
}
|
|
||||||
|
|
||||||
test("fromAlgebraic returns None for rank 0") {
|
|
||||||
Square.fromAlgebraic("e0") shouldBe None
|
|
||||||
}
|
|
||||||
|
|
||||||
test("fromAlgebraic returns None for rank 9") {
|
|
||||||
Square.fromAlgebraic("e9") shouldBe None
|
|
||||||
}
|
|
||||||
|
|
||||||
test("offset returns target square for in-bounds delta") {
|
|
||||||
Square(File.E, Rank.R4).offset(1, 2) shouldBe Some(Square(File.F, Rank.R6))
|
Square(File.E, Rank.R4).offset(1, 2) shouldBe Some(Square(File.F, Rank.R6))
|
||||||
}
|
|
||||||
|
|
||||||
test("offset returns None for out-of-bounds delta") {
|
|
||||||
Square(File.A, Rank.R1).offset(-1, 0) shouldBe None
|
Square(File.A, Rank.R1).offset(-1, 0) shouldBe None
|
||||||
Square(File.H, Rank.R8).offset(0, 1) shouldBe None
|
Square(File.H, Rank.R8).offset(0, 1) shouldBe None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,23 +7,15 @@ import org.scalatest.matchers.should.Matchers
|
|||||||
|
|
||||||
class GameContextTest extends AnyFunSuite with Matchers:
|
class GameContextTest extends AnyFunSuite with Matchers:
|
||||||
|
|
||||||
test("GameContext.initial has initial board"):
|
test("GameContext.initial exposes expected default state"):
|
||||||
GameContext.initial.board shouldBe Board.initial
|
val initial = GameContext.initial
|
||||||
|
|
||||||
test("GameContext.initial active color is White"):
|
initial.board shouldBe Board.initial
|
||||||
GameContext.initial.turn shouldBe Color.White
|
initial.turn shouldBe Color.White
|
||||||
|
initial.castlingRights shouldBe CastlingRights.Initial
|
||||||
test("GameContext.initial has full castling rights"):
|
initial.enPassantSquare shouldBe None
|
||||||
GameContext.initial.castlingRights shouldBe CastlingRights.Initial
|
initial.halfMoveClock shouldBe 0
|
||||||
|
initial.moves shouldBe List.empty
|
||||||
test("GameContext.initial en-passant square is None"):
|
|
||||||
GameContext.initial.enPassantSquare shouldBe None
|
|
||||||
|
|
||||||
test("GameContext.initial half-move clock is 0"):
|
|
||||||
GameContext.initial.halfMoveClock shouldBe 0
|
|
||||||
|
|
||||||
test("GameContext.initial move history is empty"):
|
|
||||||
GameContext.initial.moves shouldBe List.empty
|
|
||||||
|
|
||||||
test("withBoard updates only board"):
|
test("withBoard updates only board"):
|
||||||
val square = Square(File.E, Rank.R4)
|
val square = Square(File.E, Rank.R4)
|
||||||
@@ -36,26 +28,31 @@ class GameContextTest extends AnyFunSuite with Matchers:
|
|||||||
updated.halfMoveClock shouldBe GameContext.initial.halfMoveClock
|
updated.halfMoveClock shouldBe GameContext.initial.halfMoveClock
|
||||||
updated.moves shouldBe GameContext.initial.moves
|
updated.moves shouldBe GameContext.initial.moves
|
||||||
|
|
||||||
test("withTurn updates only turn"):
|
test("withers update only targeted fields"):
|
||||||
val updated = GameContext.initial.withTurn(Color.Black)
|
val initial = GameContext.initial
|
||||||
updated.turn shouldBe Color.Black
|
|
||||||
updated.board shouldBe GameContext.initial.board
|
|
||||||
|
|
||||||
test("withCastlingRights updates castling rights"):
|
|
||||||
val rights = CastlingRights(
|
val rights = CastlingRights(
|
||||||
whiteKingSide = true,
|
whiteKingSide = true,
|
||||||
whiteQueenSide = false,
|
whiteQueenSide = false,
|
||||||
blackKingSide = false,
|
blackKingSide = false,
|
||||||
blackQueenSide = true
|
blackQueenSide = true
|
||||||
)
|
)
|
||||||
GameContext.initial.withCastlingRights(rights).castlingRights shouldBe rights
|
|
||||||
|
|
||||||
test("withEnPassantSquare updates en-passant square"):
|
|
||||||
val square = Some(Square(File.E, Rank.R3))
|
val square = Some(Square(File.E, Rank.R3))
|
||||||
GameContext.initial.withEnPassantSquare(square).enPassantSquare shouldBe square
|
val updatedTurn = initial.withTurn(Color.Black)
|
||||||
|
val updatedRights = initial.withCastlingRights(rights)
|
||||||
|
val updatedEp = initial.withEnPassantSquare(square)
|
||||||
|
val updatedClock = initial.withHalfMoveClock(17)
|
||||||
|
|
||||||
test("withHalfMoveClock updates half-move clock"):
|
updatedTurn.turn shouldBe Color.Black
|
||||||
GameContext.initial.withHalfMoveClock(17).halfMoveClock shouldBe 17
|
updatedTurn.board shouldBe initial.board
|
||||||
|
|
||||||
|
updatedRights.castlingRights shouldBe rights
|
||||||
|
updatedRights.turn shouldBe initial.turn
|
||||||
|
|
||||||
|
updatedEp.enPassantSquare shouldBe square
|
||||||
|
updatedEp.castlingRights shouldBe initial.castlingRights
|
||||||
|
|
||||||
|
updatedClock.halfMoveClock shouldBe 17
|
||||||
|
updatedClock.moves shouldBe initial.moves
|
||||||
|
|
||||||
test("withMove appends move to history"):
|
test("withMove appends move to history"):
|
||||||
val move = Move(Square(File.E, Rank.R2), Square(File.E, Rank.R4))
|
val move = Move(Square(File.E, Rank.R2), Square(File.E, Rank.R4))
|
||||||
|
|||||||
@@ -5,52 +5,26 @@ import org.scalatest.matchers.should.Matchers
|
|||||||
|
|
||||||
class ApiResponseTest extends AnyFunSuite with Matchers:
|
class ApiResponseTest extends AnyFunSuite with Matchers:
|
||||||
|
|
||||||
test("ApiResponse.Success carries data") {
|
test("ApiResponse factories and payload wrappers keep values") {
|
||||||
val r = ApiResponse.Success(42)
|
val r = ApiResponse.Success(42)
|
||||||
r.data shouldBe 42
|
r.data shouldBe 42
|
||||||
}
|
|
||||||
|
|
||||||
test("ApiResponse.Failure carries error list") {
|
|
||||||
val err = ApiError("CODE", "msg")
|
val err = ApiError("CODE", "msg")
|
||||||
val r = ApiResponse.Failure(List(err))
|
ApiResponse.Failure(List(err)).errors shouldBe List(err)
|
||||||
r.errors shouldBe List(err)
|
ApiResponse.error(err) shouldBe ApiResponse.Failure(List(err))
|
||||||
}
|
|
||||||
|
|
||||||
test("ApiResponse.error creates single-error Failure") {
|
|
||||||
val err = ApiError("NOT_FOUND", "not found")
|
|
||||||
val f = ApiResponse.error(err)
|
|
||||||
f shouldBe ApiResponse.Failure(List(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
test("ApiError holds code and message") {
|
|
||||||
val e = ApiError("CODE", "message")
|
val e = ApiError("CODE", "message")
|
||||||
e.code shouldBe "CODE"
|
e.code shouldBe "CODE"
|
||||||
e.message shouldBe "message"
|
e.message shouldBe "message"
|
||||||
e.field shouldBe None
|
e.field shouldBe None
|
||||||
|
ApiError("INVALID", "bad value", Some("email")).field shouldBe Some("email")
|
||||||
}
|
}
|
||||||
|
|
||||||
test("ApiError holds optional field") {
|
test("Pagination.totalPages handles normal and guarded inputs") {
|
||||||
val e = ApiError("INVALID", "bad value", Some("email"))
|
|
||||||
e.field shouldBe Some("email")
|
|
||||||
}
|
|
||||||
|
|
||||||
test("Pagination.totalPages with exact division") {
|
|
||||||
Pagination(page = 0, pageSize = 10, totalItems = 30).totalPages shouldBe 3
|
Pagination(page = 0, pageSize = 10, totalItems = 30).totalPages shouldBe 3
|
||||||
}
|
|
||||||
|
|
||||||
test("Pagination.totalPages rounds up") {
|
|
||||||
Pagination(page = 0, pageSize = 10, totalItems = 25).totalPages shouldBe 3
|
Pagination(page = 0, pageSize = 10, totalItems = 25).totalPages shouldBe 3
|
||||||
}
|
|
||||||
|
|
||||||
test("Pagination.totalPages is 0 when totalItems is 0") {
|
|
||||||
Pagination(page = 0, pageSize = 10, totalItems = 0).totalPages shouldBe 0
|
Pagination(page = 0, pageSize = 10, totalItems = 0).totalPages shouldBe 0
|
||||||
}
|
|
||||||
|
|
||||||
test("Pagination.totalPages is 0 when pageSize is 0") {
|
|
||||||
Pagination(page = 0, pageSize = 0, totalItems = 100).totalPages shouldBe 0
|
Pagination(page = 0, pageSize = 0, totalItems = 100).totalPages shouldBe 0
|
||||||
}
|
|
||||||
|
|
||||||
test("Pagination.totalPages is 0 when pageSize is negative") {
|
|
||||||
Pagination(page = 0, pageSize = -1, totalItems = 100).totalPages shouldBe 0
|
Pagination(page = 0, pageSize = -1, totalItems = 100).totalPages shouldBe 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-6
@@ -141,12 +141,6 @@ class GameEngineCoverageRegressionTest extends AnyFunSuite with Matchers:
|
|||||||
engine.replayMoves(List(normalMove), engine.context) shouldBe Right(())
|
engine.replayMoves(List(normalMove), engine.context) shouldBe Right(())
|
||||||
engine.context.moves.lastOption shouldBe Some(normalMove)
|
engine.context.moves.lastOption shouldBe Some(normalMove)
|
||||||
|
|
||||||
test("loadGame replay will stop on errors"):
|
|
||||||
val normalMove = Move(sq("e2"), sq("e4"), MoveType.Normal())
|
|
||||||
val engine = new GameEngine()
|
|
||||||
|
|
||||||
engine.replayMoves(List(normalMove), engine.context) shouldBe Right(())
|
|
||||||
engine.context.moves.lastOption shouldBe Some(normalMove)
|
|
||||||
|
|
||||||
test("normalMoveNotation handles missing source piece"):
|
test("normalMoveNotation handles missing source piece"):
|
||||||
val engine = new GameEngine()
|
val engine = new GameEngine()
|
||||||
|
|||||||
@@ -6,33 +6,22 @@ import org.scalatest.matchers.should.Matchers
|
|||||||
|
|
||||||
class RendererAndUnicodeTest extends AnyFunSuite with Matchers:
|
class RendererAndUnicodeTest extends AnyFunSuite with Matchers:
|
||||||
|
|
||||||
private val whiteKing = Piece(Color.White, PieceType.King)
|
test("unicode mapping covers representative white and black pieces"):
|
||||||
private val blackPawn = Piece(Color.Black, PieceType.Pawn)
|
Piece(Color.White, PieceType.King).unicode shouldBe "\u2654"
|
||||||
|
Piece(Color.White, PieceType.Queen).unicode shouldBe "\u2655"
|
||||||
|
Piece(Color.Black, PieceType.King).unicode shouldBe "\u265A"
|
||||||
|
Piece(Color.Black, PieceType.Pawn).unicode shouldBe "\u265F"
|
||||||
|
|
||||||
test("unicode returns the correct symbol for white king"):
|
test("render outputs coordinates ranks ansi escapes and piece glyphs"):
|
||||||
whiteKing.unicode shouldBe "\u2654"
|
val board = Board(Map(Square(File.E, Rank.R4) -> Piece(Color.White, PieceType.Queen)))
|
||||||
|
|
||||||
test("unicode returns the correct symbol for black pawn"):
|
|
||||||
blackPawn.unicode shouldBe "\u265F"
|
|
||||||
|
|
||||||
test("render includes board coordinates on top and bottom"):
|
|
||||||
val rendered = Renderer.render(Board(Map.empty))
|
val rendered = Renderer.render(Board(Map.empty))
|
||||||
val lines = rendered.trim.split("\\n").toList.map(_.trim)
|
val lines = rendered.trim.split("\\n").toList.map(_.trim)
|
||||||
|
|
||||||
lines.head shouldBe "a b c d e f g h"
|
lines.head shouldBe "a b c d e f g h"
|
||||||
lines.last shouldBe "a b c d e f g h"
|
lines.last shouldBe "a b c d e f g h"
|
||||||
|
|
||||||
test("render includes rank labels from 8 down to 1"):
|
|
||||||
val rendered = Renderer.render(Board(Map.empty))
|
|
||||||
|
|
||||||
rendered should include("8")
|
rendered should include("8")
|
||||||
rendered should include("1")
|
rendered should include("1")
|
||||||
|
Renderer.render(board) should include("\u2655")
|
||||||
test("render places a piece unicode glyph on occupied square"):
|
Renderer.render(board) should include("\u001b[")
|
||||||
val board = Board(Map(Square(File.E, Rank.R4) -> Piece(Color.White, PieceType.Queen)))
|
|
||||||
val rendered = Renderer.render(board)
|
|
||||||
|
|
||||||
rendered should include("\u2655")
|
|
||||||
rendered should include("\u001b[")
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user