- test_models.py: 10 tests for Room/Horse/Bet/RaceResult dataclasses - test_payout_logic.py: 12 tests for payout formula (max+round) - test_room_store_lock.py: 5 tests for get_lock() setdefault pattern - All 34 tests pass in 0.27s
159 lines
5.3 KiB
Python
159 lines
5.3 KiB
Python
"""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"
|