| """Tests for attract/replay serialization. No GPU, no network, no Modal. |
| |
| Run: python -m pytest tests/ -q |
| """ |
|
|
| import sys |
| from pathlib import Path |
|
|
| import pandas as pd |
|
|
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) |
|
|
| from ttw.dummy import make_random_policy |
| from ttw.game import Game |
| from ttw.replay import FRAME_KEYS, frame_to_views, load_attract, record_frame, save_frames |
|
|
|
|
| def _game_after_a_turn() -> Game: |
| g = Game(make_random_policy(seed=7), deck_seed=7) |
| g.tempt_fate() |
| g.step() |
| return g |
|
|
|
|
| def test_record_frame_has_all_view_keys(): |
| frame = record_frame(_game_after_a_turn()) |
| assert sorted(frame.keys()) == sorted(FRAME_KEYS) |
|
|
|
|
| def test_frame_to_views_arity_and_chart_types(): |
| frame = record_frame(_game_after_a_turn()) |
| views = frame_to_views(frame) |
| assert len(views) == 15 |
| assert isinstance(views[1], pd.DataFrame) |
| assert isinstance(views[2], pd.DataFrame) |
| assert list(views[1].columns) == ["turn", "good", "price"] |
| assert list(views[2].columns) == ["turn", "gini"] |
|
|
|
|
| def test_roundtrip_save_load(tmp_path): |
| g = _game_after_a_turn() |
| frames = [record_frame(g)] |
| path = tmp_path / "attract.json" |
| save_frames(frames, path) |
| loaded = load_attract(path) |
| assert len(loaded) == 1 |
| assert loaded[0]["town"] == frames[0]["town"] |
|
|
|
|
| def test_load_attract_missing_file_is_empty(tmp_path): |
| assert load_attract(tmp_path / "nope.json") == [] |
|
|
|
|
| def test_frame_to_views_empty_frame_yields_valid_empty_plots(): |
| |
| views = frame_to_views({}) |
| assert isinstance(views[1], pd.DataFrame) and views[1].empty |
| assert isinstance(views[2], pd.DataFrame) and views[2].empty |
|
|