Feature/endgame play (#1)

* create endgame play section

* add cache tests

* normalise FENs - trim and fuill move to 1 on addition

* remove svg on root and allow navbar to exist across all pages

* update nav items
This commit is contained in:
2026-08-11 22:38:18 +02:00
committed by GitHub
parent ca51546be0
commit 7717278561
20 changed files with 771 additions and 78 deletions
+39
View File
@@ -0,0 +1,39 @@
defmodule Chesstrainer.CacheTest do
use ExUnit.Case, async: true
alias Chesstrainer.Cache
setup do
table = :"test_#{System.unique_integer([:positive])}"
{:ok, pid} = Cache.start_link(table)
on_exit(fn -> if Process.alive?(pid), do: GenServer.stop(pid) end)
{:ok, %{pid: pid, table: table}}
end
describe "ChessTrainer.Cache function tests" do
test "cache is started", %{pid: pid, table: table} do
assert is_pid(pid)
assert 0 = Cache.size(table)
end
test "put/3", %{table: table} do
assert :ok = Cache.put(table, "key1", "value1")
assert {:ok, "value1"} = Cache.get(table, "key1")
assert 1 = Cache.size(table)
assert :ok = Cache.put(table, "key2", "value2")
assert {:ok, "value2"} = Cache.get(table, "key2")
assert 2 = Cache.size(table)
end
test "delete/2", %{table: table} do
assert :ok = Cache.put(table, "key1", "value1")
assert :ok = Cache.put(table, "key2", "value2")
assert 2 = Cache.size(table)
assert :ok = Cache.delete(table, "key1")
assert 1 = Cache.size(table)
assert :error = Cache.get(table, "key1")
end
end
end
+88
View File
@@ -0,0 +1,88 @@
defmodule Chesstrainer.EndgamesTest do
use Chesstrainer.DataCase
alias Chesstrainer.Endgames
alias Chesstrainer.Endgames.Endgame
describe "create_endgame/1 FEN validation" do
test "accepts a standard valid FEN" do
attrs = %{
fen: "8/8/3k4/8/8/3K1R2/8/8 w - - 0 1",
color: :white,
key: "KR v K",
result: :win,
rating: 1500
}
assert {:ok, %Endgame{}} = Endgames.create_endgame(attrs)
end
test "normalizes fullmove 0 to 1 before inserting" do
attrs = %{
fen: "6k1/5p2/6p1/8/7p/8/6PP/6K1 b - - 0 0",
color: :black,
key: "KPPP v KPP",
result: :win,
rating: 1500
}
assert {:ok, endgame} = Endgames.create_endgame(attrs)
assert endgame.fen == "6k1/5p2/6p1/8/7p/8/6PP/6K1 b - - 0 1"
end
test "trims whitespace around the FEN before inserting" do
attrs = %{
fen: " 8/8/3k4/8/8/3K1R2/8/8 w - - 0 1\t\n",
color: :white,
key: "KR v K",
result: :win,
rating: 1500
}
assert {:ok, endgame} = Endgames.create_endgame(attrs)
assert endgame.fen == "8/8/3k4/8/8/3K1R2/8/8 w - - 0 1"
end
test "rejects a FEN with a zero digit in a rank" do
attrs = %{
fen: "4k3/8/8/8/8/8/8/0K7 w - - 0 1",
color: :white,
key: "KQ v K",
result: :win,
rating: 1500
}
assert {:error, changeset} = Endgames.create_endgame(attrs)
assert [msg] = errors_on(changeset).fen
assert msg =~ "board must be 8 ranks"
end
test "rejects a FEN with the wrong number of fields" do
attrs = %{
fen: "8/8/3k4/8/8/3K1R2/8/8",
color: :white,
key: "KQ v K",
result: :win,
rating: 1500
}
assert {:error, changeset} = Endgames.create_endgame(attrs)
assert [msg] = errors_on(changeset).fen
assert msg =~ "6 space-separated"
end
test "rejects a FEN with an unknown active color" do
attrs = %{
fen: "8/8/3k4/8/8/3K1R2/8/8 x - - 0 1",
color: :white,
key: "KQ v K",
result: :win,
rating: 1500
}
assert {:error, changeset} = Endgames.create_endgame(attrs)
assert [msg] = errors_on(changeset).fen
assert msg =~ "active color"
end
end
end
+62
View File
@@ -0,0 +1,62 @@
defmodule Chesstrainer.FENTest do
use ExUnit.Case, async: true
alias Chesstrainer.FEN
describe "color_from_fen/1" do
test "extracts white from the active-color field" do
assert FEN.color_from_fen("8/8/3k4/8/8/3K1R2/8/8 w - - 0 1") == :white
end
test "extracts black from the active-color field" do
assert FEN.color_from_fen("6k1/5p2/6p1/8/7p/8/6PP/6K1 b - - 0 1") == :black
end
test "returns nil when the active-color field is missing" do
assert FEN.color_from_fen("8/8/8/8/8/8/8/8") == nil
end
end
describe "normalize/1" do
test "trims surrounding whitespace" do
fen = "8/8/3k4/8/8/3K1R2/8/8 w - - 0 1"
assert FEN.normalize(" \n" <> fen <> "\t\n") == fen
end
test "rewrites fullmove 0 to 1" do
fen = "6k1/5p2/6p1/8/7p/8/6PP/6K1 b - - 0 0"
assert FEN.normalize(fen) == "6k1/5p2/6p1/8/7p/8/6PP/6K1 b - - 0 1"
end
test "rewrites fullmove 0 even when the FEN has surrounding whitespace" do
fen = "\n 6k1/5p2/6p1/8/7p/8/6PP/6K1 b - - 0 0 \t"
assert FEN.normalize(fen) == "6k1/5p2/6p1/8/7p/8/6PP/6K1 b - - 0 1"
end
test "leaves a non-zero fullmove untouched" do
fen = "8/8/3k4/8/8/3K1R2/8/8 w - - 0 5"
assert FEN.normalize(fen) == fen
end
test "leaves a standard starting position untouched" do
fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
assert FEN.normalize(fen) == fen
end
test "passes through a non-FEN string unchanged" do
assert FEN.normalize("not a fen") == "not a fen"
end
test "passes through a partial FEN (fewer than 6 fields) unchanged" do
fen = "8/8/3k4/8/8/3K1R2/8/8 w - -"
assert FEN.normalize(fen) == fen
end
test "passes through a FEN with a leading-zero fullmove unchanged" do
# Fullmove 0 only matches the literal "0"; leading zeros stay so the
# validator can surface them rather than silently masking the typo.
fen = "8/8/3k4/8/8/3K1R2/8/8 w - - 0 00"
assert FEN.normalize(fen) == fen
end
end
end