"""Test models.py dataclasses - direct import bypassing __init__.py. Uses importlib.util to load models.py directly, avoiding nonebot dependency. """ import pytest import importlib.util import sys from pathlib import Path from datetime import datetime # Load models.py directly without triggering __init__.py project_root = Path(__file__).parent.parent models_path = project_root / "danding_bot" / "plugins" / "group_horse_racing" / "models.py" spec = importlib.util.spec_from_file_location("models", models_path) models = importlib.util.module_from_spec(spec) spec.loader.exec_module(models) RoomState = models.RoomState HorseState = models.HorseState Horse = models.Horse Bet = models.Bet Room = models.Room RaceResult = models.RaceResult class TestEnums: def test_room_states(self): assert RoomState.WAITING.value == "waiting" assert RoomState.RUNNING.value == "running" assert RoomState.FINISHED.value == "finished" assert RoomState.INTERRUPTED.value == "interrupted" def test_horse_states(self): assert HorseState.READY.value == "ready" assert HorseState.RACING.value == "racing" assert HorseState.FINISHED.value == "finished" def test_room_state_string_enum(self): """RoomState is str enum, should work in string comparisons""" assert RoomState.WAITING == "waiting" def test_horse_state_string_enum(self): assert HorseState.READY == "ready" class TestHorseDataclass: def test_default_construction(self): h = Horse(owner_id="u1", name="闪电") assert h.owner_id == "u1" assert h.name == "闪电" assert h.position == 0.0 assert h.state == HorseState.READY assert h.index == 0 def test_custom_values(self): h = Horse(owner_id="u2", name="旋风", index=3, position=5.5, state=HorseState.RACING) assert h.index == 3 assert h.position == 5.5 assert h.state == HorseState.RACING def test_finished_state(self): h = Horse(owner_id="u3", name="飞龙", state=HorseState.FINISHED, position=100.0) assert h.state == HorseState.FINISHED class TestBetDataclass: def test_construction(self): b = Bet(user_id="u1", horse_name="闪电", amount=100) assert b.user_id == "u1" assert b.horse_name == "闪电" assert b.amount == 100 def test_zero_bet(self): b = Bet(user_id="u2", horse_name="旋风", amount=0) assert b.amount == 0 def test_negative_bet_boundary(self): """Negative bet shouldn't happen but dataclass allows it""" b = Bet(user_id="u3", horse_name="x", amount=-50) assert b.amount == -50 class TestRoomDataclass: def test_default_room(self): r = Room(scope="test_group") assert r.scope == "test_group" assert r.state == RoomState.WAITING assert r.horses == {} assert r.bets == [] assert r.champion_name is None assert r.tick_count == 0 assert r.next_horse_index == 1 assert isinstance(r.created_at, datetime) def test_room_with_horses(self): r = Room( scope="g1", horses={"闪电": Horse(owner_id="u1", name="闪电")} ) assert "闪电" in r.horses assert len(r.horses) == 1 def test_room_state_transitions(self): r = Room(scope="g1") assert r.state == RoomState.WAITING r.state = RoomState.RUNNING assert r.state == RoomState.RUNNING r.state = RoomState.FINISHED assert r.state == RoomState.FINISHED def test_independent_rooms_have_independent_horses(self): """Verify dict default_factory creates independent instances""" r1 = Room(scope="g1") r2 = Room(scope="g2") r1.horses["test"] = Horse(owner_id="u1", name="test") assert "test" not in r2.horses def test_independent_rooms_have_independent_bets(self): r1 = Room(scope="g1") r2 = Room(scope="g2") r1.bets.append(Bet(user_id="u1", horse_name="x", amount=10)) assert len(r2.bets) == 0 class TestRaceResultDataclass: def test_basic_construction(self): rr = RaceResult( race_id="race1", scope="g1", champion_name="闪电", champion_owner="u1", participants=["u1", "u2"], bet_distribution={"u1": 100, "u2": 50}, duration_ticks=30, completed_at=datetime.now(), ) assert rr.race_id == "race1" assert rr.champion_name == "闪电" assert len(rr.participants) == 2 assert rr.point_changes == {} assert rr.point_change_summaries == {} def test_with_point_changes(self): rr = RaceResult( race_id="race2", scope="g1", champion_name="旋风", champion_owner="u2", participants=["u1", "u2"], bet_distribution={"u1": 100}, duration_ticks=25, completed_at=datetime.now(), point_changes={"u1": -100, "u2": 200}, point_change_summaries={"u1": "-100", "u2": "+200"}, ) assert rr.point_changes["u2"] == 200 assert rr.point_change_summaries["u1"] == "-100"