1
0
This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/tests/bannerlord/test_gabs_client.py
Alexander Whitestone 7385c46a78 WIP: Claude Code progress on #1095
Automated salvage commit — agent session ended (exit 124).
Work in progress, may need continuation.
2026-03-23 14:25:01 -04:00

163 lines
5.4 KiB
Python

"""Tests for the GABSClient — uses mocked asyncio streams, no real TCP."""
from __future__ import annotations
import json
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from bannerlord.gabs_client import GABSClient
from bannerlord.types import GameState
@pytest.fixture
def client():
return GABSClient(host="127.0.0.1", port=4825, timeout=2.0)
class TestGABSClientConnection:
@pytest.mark.asyncio
async def test_connect_returns_false_when_refused(self, client):
with patch(
"asyncio.open_connection",
side_effect=ConnectionRefusedError("refused"),
):
result = await client.connect()
assert result is False
assert not client.is_connected
@pytest.mark.asyncio
async def test_connect_success(self, client):
mock_reader = AsyncMock()
mock_writer = MagicMock()
mock_writer.drain = AsyncMock()
mock_writer.close = MagicMock()
mock_writer.wait_closed = AsyncMock()
# Simulate tools/list response on connect
tools_response = json.dumps({"jsonrpc": "2.0", "id": 1, "result": []}) + "\n"
mock_reader.readline = AsyncMock(return_value=tools_response.encode())
with patch("asyncio.open_connection", return_value=(mock_reader, mock_writer)):
with patch("asyncio.wait_for", side_effect=_passthrough_wait_for):
result = await client.connect()
assert result is True
assert client.is_connected
@pytest.mark.asyncio
async def test_disconnect_when_not_connected(self, client):
# Should not raise
await client.disconnect()
assert not client.is_connected
class TestGABSClientCall:
@pytest.mark.asyncio
async def test_call_returns_none_when_disconnected(self, client):
result = await client._call("game/get_state")
assert result is None
@pytest.mark.asyncio
async def test_call_id_increments(self, client):
assert client._next_id() == 1
assert client._next_id() == 2
assert client._next_id() == 3
@pytest.mark.asyncio
async def test_get_game_state_returns_empty_when_disconnected(self, client):
state = await client.get_game_state()
assert isinstance(state, GameState)
assert state.tick == 0
assert not state.has_kingdom()
@pytest.mark.asyncio
async def test_move_party_returns_false_when_disconnected(self, client):
result = await client.move_party("Vlandia")
assert result is False
@pytest.mark.asyncio
async def test_propose_peace_returns_false_when_disconnected(self, client):
result = await client.propose_peace("Vlandia")
assert result is False
@pytest.mark.asyncio
async def test_assess_prices_returns_empty_dict_when_disconnected(self, client):
result = await client.assess_prices("Pravend")
assert result == {}
@pytest.mark.asyncio
async def test_map_patrol_routes_returns_empty_list_when_disconnected(self, client):
result = await client.map_patrol_routes("Vlandia")
assert result == []
class TestGameStateParsing:
def test_parse_game_state_full(self):
client = GABSClient()
raw = {
"tick": 5,
"in_game_day": 42,
"party": {
"location": "Pravend",
"troops": 200,
"food_days": 8,
"wounded_pct": 0.1,
"denars": 15000,
"morale": 85.0,
"prisoners": 3,
},
"kingdom": {
"name": "House Timmerson",
"fiefs": ["Pravend", "Epicrotea"],
"daily_income": 500,
"daily_expenses": 300,
"vassal_lords": ["Lord A"],
"active_wars": ["Sturgia"],
"active_alliances": ["Battania"],
},
"factions": [
{
"name": "Sturgia",
"leader": "Raganvad",
"fiefs": ["Varcheg"],
"army_strength": 250,
"treasury": 5000,
"is_at_war_with": ["House Timmerson"],
"relations": {"House Timmerson": -50},
}
],
}
state = client._parse_game_state(raw)
assert state.tick == 5
assert state.in_game_day == 42
assert state.party.location == "Pravend"
assert state.party.troops == 200
assert state.kingdom.name == "House Timmerson"
assert state.fief_count() == 2
assert len(state.factions) == 1
assert state.factions[0].name == "Sturgia"
assert state.has_kingdom()
assert not state.is_two_front_war()
def test_parse_game_state_minimal(self):
client = GABSClient()
state = client._parse_game_state({})
assert isinstance(state, GameState)
assert not state.has_kingdom()
def test_tool_count_zero_before_connect(self):
client = GABSClient()
assert client.tool_count() == 0
assert client.available_tools == []
# ---------------------------------------------------------------------------
# Helper
# ---------------------------------------------------------------------------
async def _passthrough_wait_for(coro, timeout=None):
"""Stand-in for asyncio.wait_for that just awaits the coroutine."""
import asyncio
return await coro