Spaces:
Running on Zero
Running on Zero
File size: 2,668 Bytes
1ea8154 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | """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
|