botWebWars/backend/tests/test_api.py

178 lines
6.5 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_party_formation_and_group_movement():
client = TestClient(app)
p1 = client.post("/api/players", json={"name": "LeaderBot", "color": "#00FFAA"}).json()
p2 = client.post("/api/players", json={"name": "FollowerBot", "color": "#FF00AA"}).json()
2026-09-05 21:20:39 +00:00
# Place adjacent: Leader at (10, 10), Follower at (10, 11) (within distance 1)
2026-09-05 21:20:39 +00:00
import asyncio
async def place_adjacent():
b1 = await game_engine.get_player(p1["id"])
b1.x, b1.y = 10, 10
b2 = await game_engine.get_player(p2["id"])
b2.x, b2.y = 10, 11
asyncio.run(place_adjacent())
# Form party with agreed leader p1
form_res = client.post("/api/parties", json={
"member_ids": [p1["id"], p2["id"]],
"leader_id": p1["id"],
"name": "AlphaSquad",
})
assert form_res.status_code == 201
party = form_res.json()
assert party["leader_id"] == p1["id"]
assert len(party["member_ids"]) == 2
# Follower cannot move individually
res_follow = client.post(f"/api/players/{p2['id']}/move", json={"direction": "UP"})
assert res_follow.status_code == 403
assert "Only party leader" in res_follow.json()["detail"]
# Leader moves UP: Leader moves from (10,10)->(10,9), Follower moves from (10,11)->(10,10)
res_move = client.post(f"/api/players/{p1['id']}/move", json={"direction": "UP"})
assert res_move.status_code == 200
move_data = res_move.json()
assert move_data["party_moved"] is True
assert len(move_data["affected_players"]) == 2
# Check positions in board
board = client.get("/api/board").json()
bots_by_id = {b["id"]: b for b in board["players"]}
assert bots_by_id[p1["id"]]["x"] == 10
assert bots_by_id[p1["id"]]["y"] == 9
assert bots_by_id[p2["id"]]["x"] == 10
assert bots_by_id[p2["id"]]["y"] == 10
2026-09-05 21:20:39 +00:00
def test_party_wall_collision_for_follower():
client = TestClient(app)
p1 = client.post("/api/players", json={"name": "Leader", "color": "#111111"}).json()
p2 = client.post("/api/players", json={"name": "Follower", "color": "#222222"}).json()
2026-09-05 21:20:39 +00:00
# Place Leader at (5, 1), Follower at (5, 0)
# Moving UP would push Follower to (5, -1) which hits the wall!
2026-09-05 21:20:39 +00:00
import asyncio
async def setup_near_wall():
b1 = await game_engine.get_player(p1["id"])
b1.x, b1.y = 5, 1
b2 = await game_engine.get_player(p2["id"])
b2.x, b2.y = 5, 0
asyncio.run(setup_near_wall())
client.post("/api/parties", json={
"member_ids": [p1["id"], p2["id"]],
"leader_id": p1["id"],
})
# Available moves check should mark UP as blocked due to follower hitting wall
moves = client.get(f"/api/players/{p1['id']}/available-moves").json()
assert moves["moves"]["UP"]["available"] is False
assert "boundary wall" in moves["moves"]["UP"]["reason"]
# Trying to move UP should fail
2026-09-05 21:20:39 +00:00
res = client.post(f"/api/players/{p1['id']}/move", json={"direction": "UP"})
assert res.status_code == 400
assert "Illegal move" in res.json()["detail"]
def test_party_defeat_and_leader_respawn():
client = TestClient(app)
p1 = client.post("/api/players", json={"name": "LeaderToDie", "color": "#AA0000"}).json()
p2 = client.post("/api/players", json={"name": "Survivor1", "color": "#00AA00"}).json()
p3 = client.post("/api/players", json={"name": "Survivor2", "color": "#0000AA"}).json()
2026-09-05 21:20:39 +00:00
import asyncio
async def place_trio():
b1 = await game_engine.get_player(p1["id"])
b1.x, b1.y = 20, 20
b2 = await game_engine.get_player(p2["id"])
b2.x, b2.y = 20, 21
b3 = await game_engine.get_player(p3["id"])
b3.x, b3.y = 21, 21
asyncio.run(place_trio())
party = client.post("/api/parties", json={
"member_ids": [p1["id"], p2["id"], p3["id"]],
"leader_id": p1["id"],
}).json()
# Trigger defeat on party
defeat_res = client.post(f"/api/parties/{party['id']}/defeat")
assert defeat_res.status_code == 200
data = defeat_res.json()
# 1. Leader was killed, score is -1
assert data["killed_leader_id"] == p1["id"]
assert data["killed_leader_new_score"] == -1
# 2. Leader respawned position is recorded
assert "x" in data["killed_leader_respawn_position"]
# 3. New leader randomly assigned from remaining members
assert data["new_leader_id"] in [p2["id"], p3["id"]]
assert len(data["remaining_members"]) == 2
# Verify leader is no longer part of the party
dead_leader = client.get(f"/api/players/{p1['id']}").json()
assert dead_leader["party_id"] is None
assert dead_leader["is_party_leader"] is False
assert dead_leader["score"] == -1
# Verify new leader in party
updated_party = client.get(f"/api/parties/{party['id']}").json()
assert updated_party["leader_id"] == data["new_leader_id"]
assert p1["id"] not in updated_party["member_ids"]
def test_battle_between_parties():
2026-09-05 21:20:39 +00:00
client = TestClient(app)
# Party 1 has 2 bots
p1 = client.post("/api/players", json={"name": "Warrior1", "color": "#111111"}).json()
p2 = client.post("/api/players", json={"name": "Warrior2", "color": "#222222"}).json()
# Party 2 has 1 solo bot
p3 = client.post("/api/players", json={"name": "SoloDefender", "color": "#333333"}).json()
2026-09-05 21:20:39 +00:00
import asyncio
async def place_combatants():
b1 = await game_engine.get_player(p1["id"])
b1.x, b1.y = 30, 30
b2 = await game_engine.get_player(p2["id"])
b2.x, b2.y = 30, 31
b3 = await game_engine.get_player(p3["id"])
b3.x, b3.y = 31, 30 # adjacent to b1
asyncio.run(place_combatants())
client.post("/api/parties", json={
"member_ids": [p1["id"], p2["id"]],
"leader_id": p1["id"],
})
# Initiate battle: 2-bot party vs 1-bot solo -> Party 1 wins
res = client.post("/api/battles/fight", json={
"challenger_id": p1["id"],
"defender_id": p3["id"],
})
2026-09-05 21:20:39 +00:00
assert res.status_code == 200
battle = res.json()
assert battle["winner_id"] == p1["id"]
assert battle["loser_id"] == p3["id"]
# Check loser score = -1
loser_bot = client.get(f"/api/players/{p3['id']}").json()
assert loser_bot["score"] == -1
# Check winner score = +1
winner_bot = client.get(f"/api/players/{p1['id']}").json()
assert winner_bot["score"] == 1