"""The two-phase turn: play_turn_text must return dialogue without painting, play_turn_images must complete the view afterwards — including from a fresh Engine (the ZeroGPU cross-worker contract via the state file).""" from __future__ import annotations import pytest from visualnovel import engine as engine_mod from visualnovel.engine import Engine from visualnovel.schemas import SetupForm @pytest.fixture() def isolated_state_file(monkeypatch, tmp_path): monkeypatch.setattr(engine_mod, "_STATE_FILE", tmp_path / "vn_game_state.json") def _started_engine() -> Engine: eng = Engine() eng.start(SetupForm(theme="school", tone="romantic", seed=7)) return eng def test_turn_text_then_images(isolated_state_file): eng = _started_engine() v1 = eng.play_turn_text("hello there") assert v1.dialogue assert v1.backdrop_url is None assert v1.present == [] v2 = eng.play_turn_images() assert v2.backdrop_url assert v2.present assert v2.dialogue == v1.dialogue def test_turn_images_survives_worker_restart(isolated_state_file): eng = _started_engine() v1 = eng.play_turn_text("hello there") # ZeroGPU may dispatch the second call to a fresh worker: a new Engine must # recover the pending turn from the state file. fresh = Engine() v2 = fresh.play_turn_images() assert v2.backdrop_url assert v2.dialogue == v1.dialogue def test_composed_play_turn_still_full(isolated_state_file): eng = _started_engine() v = eng.play_turn("hello there") assert v.dialogue assert v.backdrop_url assert v.present def test_resume_roundtrip(isolated_state_file): eng = _started_engine() v1 = eng.play_turn("hello there") fresh = Engine() v = fresh.resume() assert v is not None assert v.turn_index == v1.turn_index assert v.dialogue == v1.dialogue assert v.backdrop_url # journal recap: the recent exchanges travel with display names, not ids assert v.history assert v.history[-1].dialogue == v1.dialogue assert v.history[-1].speaker == "Hana" # mock first character's name, not "hana" def test_resume_without_state_returns_none(isolated_state_file): assert Engine().resume() is None def test_session_info(isolated_state_file, monkeypatch): from visualnovel import engine as engine_mod assert engine_mod.session_info() == {"exists": False} eng = _started_engine() eng.play_turn("hello there") info = engine_mod.session_info() assert info["exists"] is True assert info["turn_index"] == eng.state.turn_index assert info["place"] == eng.state.scene.place assert info["ended"] is False