178 lines
6.5 KiB
Python
178 lines
6.5 KiB
Python
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()
|
|
|
|
# Place adjacent: Leader at (10, 10), Follower at (10, 11) (within distance 1)
|
|
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
|
|
|
|
|
|
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()
|
|
|
|
# Place Leader at (5, 1), Follower at (5, 0)
|
|
# Moving UP would push Follower to (5, -1) which hits the wall!
|
|
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
|
|
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()
|
|
|
|
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():
|
|
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()
|
|
|
|
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"],
|
|
})
|
|
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
|