botWebWars/backend/tests/test_api.py

155 lines
5.4 KiB
Python
Raw Normal View History

import pytest
from fastapi.testclient import TestClient
from app.main import app
from app.game import game_engine
@pytest.fixture(autouse=True)
def reset_board_state():
import asyncio
asyncio.run(game_engine.reset())
yield
asyncio.run(game_engine.reset())
def test_health():
client = TestClient(app)
response = client.get("/api/health")
assert response.status_code == 200
data = response.json()
assert data["success"] is True
def test_register_player():
client = TestClient(app)
res = client.post("/api/players", json={"name": "CyberBot", "color": "#00FFAA"})
assert res.status_code == 201
player = res.json()
assert player["name"] == "CyberBot"
assert 0 <= player["x"] <= 64
assert 0 <= player["y"] <= 64
2026-09-05 21:20:39 +00:00
def test_turn_order_and_movement():
client = TestClient(app)
2026-09-05 21:20:39 +00:00
p1 = client.post("/api/players", json={"name": "Bot1", "color": "#FF0000"}).json()
p2 = client.post("/api/players", json={"name": "Bot2", "color": "#0000FF"}).json()
2026-09-05 21:20:39 +00:00
# Board state should show Bot1 as active turn
board = client.get("/api/board").json()
assert board["turn"]["current_player_id"] == p1["id"]
assert board["turn"]["round_number"] == 1
2026-09-05 21:20:39 +00:00
# Bot2 cannot move yet (not their turn)
res_fail = client.post(f"/api/players/{p2['id']}/move", json={"direction": "UP"})
assert res_fail.status_code == 403
# Manually place Bot1 at (10, 10) for deterministic movement test
import asyncio
async def place_p1():
player = await game_engine.get_player(p1["id"])
player.x = 10
player.y = 10
asyncio.run(place_p1())
# Check available moves for Bot1
moves_res = client.get(f"/api/players/{p1['id']}/available-moves")
assert moves_res.status_code == 200
moves_data = moves_res.json()
assert moves_data["is_turn"] is True
assert moves_data["moves"]["UP"]["available"] is True
assert moves_data["moves"]["UP"]["target_x"] == 10
assert moves_data["moves"]["UP"]["target_y"] == 9
assert moves_data["moves"]["UP_RIGHT"]["target_x"] == 11
assert moves_data["moves"]["UP_RIGHT"]["target_y"] == 9
# Bot1 moves UP
move_res = client.post(f"/api/players/{p1['id']}/move", json={"direction": "UP"})
assert move_res.status_code == 200
res_json = move_res.json()
assert res_json["new_position"]["x"] == 10
assert res_json["new_position"]["y"] == 9
# Turn should advance to Bot2
assert res_json["turn"]["current_player_id"] == p2["id"]
def test_diagonal_movement_and_turn_cycle():
client = TestClient(app)
2026-09-05 21:20:39 +00:00
p1 = client.post("/api/players", json={"name": "DiagBot", "color": "#123456"}).json()
import asyncio
async def place():
player = await game_engine.get_player(p1["id"])
player.x = 20
player.y = 20
asyncio.run(place())
# Move diagonal UP_LEFT
res = client.post(f"/api/players/{p1['id']}/move", json={"direction": "UP_LEFT"})
assert res.status_code == 200
2026-09-05 21:20:39 +00:00
assert res.json()["new_position"] == {"x": 19, "y": 19}
2026-09-05 21:20:39 +00:00
# Move diagonal DOWN_RIGHT (single player cycle wrapped around)
res2 = client.post(f"/api/players/{p1['id']}/move", json={"direction": "SE"})
assert res2.status_code == 200
assert res2.json()["new_position"] == {"x": 20, "y": 20}
2026-09-05 21:20:39 +00:00
def test_wall_collision():
client = TestClient(app)
2026-09-05 21:20:39 +00:00
p1 = client.post("/api/players", json={"name": "CornerBot", "color": "#123456"}).json()
import asyncio
async def place_corner():
player = await game_engine.get_player(p1["id"])
player.x = 0
player.y = 0
asyncio.run(place_corner())
2026-09-05 21:20:39 +00:00
# Query single move check
check_up = client.get(f"/api/players/{p1['id']}/check-move?direction=UP")
assert check_up.status_code == 200
assert check_up.json()["available"] is False
assert "Wall collision" in check_up.json()["reason"]
2026-09-05 21:20:39 +00:00
# Attempt to move UP through boundary wall (y < 0)
res = client.post(f"/api/players/{p1['id']}/move", json={"direction": "UP"})
assert res.status_code == 400
assert "Illegal move" in res.json()["detail"]
2026-09-05 21:20:39 +00:00
def test_player_collision():
client = TestClient(app)
2026-09-05 21:20:39 +00:00
p1 = client.post("/api/players", json={"name": "Attacker", "color": "#123456"}).json()
p2 = client.post("/api/players", json={"name": "Defender", "color": "#654321"}).json()
2026-09-05 21:20:39 +00:00
import asyncio
async def place_neighbors():
p_att = await game_engine.get_player(p1["id"])
p_att.x = 10
p_att.y = 10
p_def = await game_engine.get_player(p2["id"])
p_def.x = 11
p_def.y = 10
asyncio.run(place_neighbors())
# Attacker tries to move RIGHT into Defender's space
check_right = client.get(f"/api/players/{p1['id']}/check-move?direction=RIGHT")
assert check_right.status_code == 200
assert check_right.json()["available"] is False
assert "occupied" in check_right.json()["reason"].lower()
move_fail = client.post(f"/api/players/{p1['id']}/move", json={"direction": "RIGHT"})
assert move_fail.status_code == 400
assert "Illegal move" in move_fail.json()["detail"]
def test_pass_turn():
client = TestClient(app)
p1 = client.post("/api/players", json={"name": "Passer1", "color": "#111111"}).json()
p2 = client.post("/api/players", json={"name": "Passer2", "color": "#222222"}).json()
res = client.post(f"/api/players/{p1['id']}/pass")
assert res.status_code == 200
assert res.json()["current_player_id"] == p2["id"]