02cd9d519c
Lint & Test / test (push) Successful in 1m56s
- Rename Python package: wled_controller -> ledgrab - Rename env var prefix: WLED_ -> LEDGRAB_ (with auto-migration for old vars) - Rename localStorage key: wled_api_key -> ledgrab_api_key (with migration) - Rename HA integration domain: wled_screen_controller -> ledgrab - Update all imports, build scripts, Docker, installer, config, docs - Remove HA integration (moved to ledgrab-haos-integration repo) - Remove hacs.json (belongs in HA repo now) - Add startup warning for users with old WLED_ env vars - All tests pass (715/715), ruff clean, tsc clean, frontend builds
205 lines
7.3 KiB
Python
205 lines
7.3 KiB
Python
"""Tests for Dota 2 Game State Integration adapter."""
|
|
|
|
import pytest
|
|
|
|
from ledgrab.core.game_integration.adapters.dota2_adapter import Dota2Adapter
|
|
|
|
|
|
def _make_dota2_payload(
|
|
*,
|
|
health: int = 1000,
|
|
max_health: int = 1000,
|
|
mana: float = 500.0,
|
|
max_mana: float = 500.0,
|
|
gold: int = 625,
|
|
kills: int = 0,
|
|
deaths: int = 0,
|
|
game_state: str | None = None,
|
|
auth_token: str | None = None,
|
|
) -> dict:
|
|
"""Build a realistic Dota 2 GSI payload."""
|
|
payload: dict = {
|
|
"hero": {
|
|
"xpos": -6624,
|
|
"ypos": -6592,
|
|
"id": 1,
|
|
"name": "npc_dota_hero_antimage",
|
|
"level": 12,
|
|
"alive": True,
|
|
"respawn_seconds": 0,
|
|
"buyback_cost": 874,
|
|
"buyback_cooldown": 0,
|
|
"health": health,
|
|
"max_health": max_health,
|
|
"health_percent": int(100.0 * health / max_health) if max_health else 0,
|
|
"mana": mana,
|
|
"max_mana": max_mana,
|
|
"mana_percent": int(100.0 * mana / max_mana) if max_mana else 0,
|
|
},
|
|
"player": {
|
|
"steamid": "76561198012345678",
|
|
"name": "TestPlayer",
|
|
"activity": "playing",
|
|
"kills": kills,
|
|
"deaths": deaths,
|
|
"assists": 0,
|
|
"last_hits": 120,
|
|
"denies": 10,
|
|
"kill_streak": 0,
|
|
"gold": gold,
|
|
"gold_reliable": 200,
|
|
"gold_unreliable": gold - 200,
|
|
"gpm": 450,
|
|
"xpm": 520,
|
|
},
|
|
}
|
|
|
|
if game_state is not None:
|
|
payload["map"] = {
|
|
"name": "start",
|
|
"matchid": "12345",
|
|
"game_time": 1200,
|
|
"clock_time": 1200,
|
|
"daytime": True,
|
|
"game_state": game_state,
|
|
"win_team": "none",
|
|
}
|
|
|
|
if auth_token is not None:
|
|
payload["auth"] = {"token": auth_token}
|
|
|
|
return payload
|
|
|
|
|
|
class TestDota2ContinuousEvents:
|
|
def test_health_normalized(self) -> None:
|
|
payload = _make_dota2_payload(health=750, max_health=1500)
|
|
events, _ = Dota2Adapter.parse_payload(payload, {"adapter_id": "d2"}, {})
|
|
hp = [e for e in events if e.event_type == "health"]
|
|
assert len(hp) == 1
|
|
assert hp[0].value == pytest.approx(0.5)
|
|
|
|
def test_mana_normalized(self) -> None:
|
|
payload = _make_dota2_payload(mana=300.0, max_mana=600.0)
|
|
events, _ = Dota2Adapter.parse_payload(payload, {"adapter_id": "d2"}, {})
|
|
mp = [e for e in events if e.event_type == "mana"]
|
|
assert len(mp) == 1
|
|
assert mp[0].value == pytest.approx(0.5)
|
|
|
|
def test_gold_normalized(self) -> None:
|
|
payload = _make_dota2_payload(gold=5000)
|
|
events, _ = Dota2Adapter.parse_payload(payload, {"adapter_id": "d2"}, {})
|
|
g = [e for e in events if e.event_type == "gold"]
|
|
assert len(g) == 1
|
|
assert g[0].value == pytest.approx(5000.0 / 99999.0)
|
|
|
|
def test_gold_custom_max(self) -> None:
|
|
payload = _make_dota2_payload(gold=5000)
|
|
events, _ = Dota2Adapter.parse_payload(
|
|
payload,
|
|
{"adapter_id": "d2", "max_gold": 10000},
|
|
{},
|
|
)
|
|
g = [e for e in events if e.event_type == "gold"]
|
|
assert g[0].value == pytest.approx(0.5)
|
|
|
|
|
|
class TestDota2DiffDetection:
|
|
def test_kill_detected(self) -> None:
|
|
p1 = _make_dota2_payload(kills=3)
|
|
_, s1 = Dota2Adapter.parse_payload(p1, {"adapter_id": "d2"}, {})
|
|
|
|
p2 = _make_dota2_payload(kills=4)
|
|
events, _ = Dota2Adapter.parse_payload(p2, {"adapter_id": "d2"}, s1)
|
|
kills = [e for e in events if e.event_type == "kill"]
|
|
assert len(kills) == 1
|
|
|
|
def test_no_kill_on_first_payload(self) -> None:
|
|
p = _make_dota2_payload(kills=5)
|
|
events, _ = Dota2Adapter.parse_payload(p, {"adapter_id": "d2"}, {})
|
|
kills = [e for e in events if e.event_type == "kill"]
|
|
assert len(kills) == 0
|
|
|
|
def test_death_detected(self) -> None:
|
|
p1 = _make_dota2_payload(deaths=1)
|
|
_, s1 = Dota2Adapter.parse_payload(p1, {"adapter_id": "d2"}, {})
|
|
|
|
p2 = _make_dota2_payload(deaths=2)
|
|
events, _ = Dota2Adapter.parse_payload(p2, {"adapter_id": "d2"}, s1)
|
|
deaths = [e for e in events if e.event_type == "death"]
|
|
assert len(deaths) == 1
|
|
|
|
def test_multiple_kills(self) -> None:
|
|
p1 = _make_dota2_payload(kills=0)
|
|
_, s1 = Dota2Adapter.parse_payload(p1, {"adapter_id": "d2"}, {})
|
|
|
|
p2 = _make_dota2_payload(kills=3)
|
|
events, _ = Dota2Adapter.parse_payload(p2, {"adapter_id": "d2"}, s1)
|
|
kills = [e for e in events if e.event_type == "kill"]
|
|
assert len(kills) == 3
|
|
|
|
|
|
class TestDota2MatchFlow:
|
|
def test_match_start_pre_game(self) -> None:
|
|
p = _make_dota2_payload(game_state="DOTA_GAMERULES_STATE_PRE_GAME")
|
|
events, _ = Dota2Adapter.parse_payload(p, {"adapter_id": "d2"}, {})
|
|
ms = [e for e in events if e.event_type == "match_start"]
|
|
assert len(ms) == 1
|
|
|
|
def test_match_start_in_progress(self) -> None:
|
|
prev = {"game_state": "DOTA_GAMERULES_STATE_PRE_GAME"}
|
|
p = _make_dota2_payload(game_state="DOTA_GAMERULES_STATE_GAME_IN_PROGRESS")
|
|
events, _ = Dota2Adapter.parse_payload(p, {"adapter_id": "d2"}, prev)
|
|
ms = [e for e in events if e.event_type == "match_start"]
|
|
assert len(ms) == 1
|
|
|
|
def test_match_end(self) -> None:
|
|
prev = {"game_state": "DOTA_GAMERULES_STATE_GAME_IN_PROGRESS"}
|
|
p = _make_dota2_payload(game_state="DOTA_GAMERULES_STATE_POST_GAME")
|
|
events, _ = Dota2Adapter.parse_payload(p, {"adapter_id": "d2"}, prev)
|
|
me = [e for e in events if e.event_type == "match_end"]
|
|
assert len(me) == 1
|
|
|
|
def test_no_event_on_same_state(self) -> None:
|
|
prev = {"game_state": "DOTA_GAMERULES_STATE_GAME_IN_PROGRESS"}
|
|
p = _make_dota2_payload(game_state="DOTA_GAMERULES_STATE_GAME_IN_PROGRESS")
|
|
events, _ = Dota2Adapter.parse_payload(p, {"adapter_id": "d2"}, prev)
|
|
flow = [e for e in events if e.event_type in ("match_start", "match_end")]
|
|
assert len(flow) == 0
|
|
|
|
|
|
class TestDota2Auth:
|
|
def test_auth_valid(self) -> None:
|
|
p = _make_dota2_payload(auth_token="secret")
|
|
result = Dota2Adapter.validate_auth({}, p, {"auth_token": "secret"})
|
|
assert result is True
|
|
|
|
def test_auth_invalid(self) -> None:
|
|
p = _make_dota2_payload(auth_token="wrong")
|
|
result = Dota2Adapter.validate_auth({}, p, {"auth_token": "secret"})
|
|
assert result is False
|
|
|
|
def test_auth_no_config_accepts_all(self) -> None:
|
|
p = _make_dota2_payload()
|
|
result = Dota2Adapter.validate_auth({}, p, {})
|
|
assert result is True
|
|
|
|
|
|
class TestDota2Metadata:
|
|
def test_adapter_type(self) -> None:
|
|
assert Dota2Adapter.ADAPTER_TYPE == "dota2"
|
|
|
|
def test_supported_events(self) -> None:
|
|
assert "health" in Dota2Adapter.SUPPORTED_EVENTS
|
|
assert "mana" in Dota2Adapter.SUPPORTED_EVENTS
|
|
assert "gold" in Dota2Adapter.SUPPORTED_EVENTS
|
|
|
|
def test_config_schema(self) -> None:
|
|
schema = Dota2Adapter.get_config_schema()
|
|
assert "auth_token" in schema["properties"]
|
|
assert "max_gold" in schema["properties"]
|
|
|
|
def test_setup_instructions(self) -> None:
|
|
instructions = Dota2Adapter.get_setup_instructions()
|
|
assert "Dota 2" in instructions
|