| """Mock-mode end-to-end test of the branching state machine, driving the same |
| handler functions Gradio wires to the UI. Run: STORY_MOCK=1 python test_flow.py |
| """ |
|
|
| import os |
|
|
| assert os.environ.get("STORY_MOCK") == "1", "run with STORY_MOCK=1" |
|
|
| import app |
|
|
|
|
| def unpack(result): |
| state = result[0] |
| return state |
|
|
|
|
| def current(state): |
| return state["nodes"][state["path"]] |
|
|
|
|
| def test_full_flow(): |
| |
| state = unpack( |
| app.start_story( |
| "Mia", "3-6", "en", |
| "Hop", "little rabbit", "curious", "a small white rabbit", |
| "Sage", "old tortoise", "wise", "an old green tortoise", |
| "Didn't want to share toys today.", |
| ) |
| ) |
| node = current(state) |
| assert state["path"] == "" |
| assert node["title"], "opening must have a title" |
| assert node["choice"], "opening must have a choice" |
| assert state["images"].get("") is not None, "opening must have an illustration" |
|
|
| |
| state = unpack(app.choose(state, "B")) |
| assert state["path"] == "B" |
| assert current(state)["choice"], "depth 1 must still have a choice" |
|
|
| state = unpack(app.choose(state, "B")) |
| assert state["path"] == "BB" |
| node = current(state) |
| assert node["isEnding"] and node["endingTone"] == "gentle_setback", f"expected setback, got {node['endingTone']}" |
|
|
| |
| state = unpack(app.back_to_choice(state)) |
| assert state["path"] == "B" |
| assert state["disabled_option"] == "B" |
|
|
| |
| state = unpack(app.choose(state, "A")) |
| node = current(state) |
| assert state["path"] == "BA" |
| assert node["isEnding"] and node["endingTone"] == "warm", f"replay must end warm, got {node['endingTone']}" |
| assert node["moral"], "warm ending must carry a moral" |
|
|
| |
| nodes_before = dict(state["nodes"]) |
| state = unpack(app.back_to_choice(state)) |
| state = unpack(app.choose(state, "A")) |
| assert state["nodes"] is not nodes_before or len(state["nodes"]) == len(nodes_before) |
| assert state["path"] == "BA" |
|
|
| |
| md = app._story_markdown(state) |
| assert "π" in md or len(md) > 50 |
|
|
| print("β
full branching flow passed:", sorted(state["nodes"].keys())) |
|
|
|
|
| def test_render_shapes(): |
| state = unpack( |
| app.start_story( |
| "Mia", "3-6", "de", |
| "Hop", "rabbit", "curious", "white rabbit", |
| "", "", "", "", |
| "A small quarrel happened.", |
| ) |
| ) |
| rendered = app._render(state) |
| assert len(rendered) == 12, f"render tuple arity changed: {len(rendered)}" |
| audio = rendered[5] |
| assert audio is None or isinstance(audio, tuple) |
| print("β
render shapes ok (German TTS degraded gracefully)") |
|
|
|
|
| if __name__ == "__main__": |
| test_full_flow() |
| test_render_shapes() |
| print("π mock e2e: PASS") |
|
|