import json import shutil import pytest from app.blobs import read_bytes from app.config import get_settings from app.converter import pyexpr from app.converter.pipeline import convert_archive from app.converter.rpy_parser import RpyParser, ScriptData from tests.make_sample import build @pytest.fixture def data_dir(tmp_path, monkeypatch): monkeypatch.setenv("DATA_DIR", str(tmp_path / "data")) get_settings.cache_clear() yield tmp_path / "data" get_settings.cache_clear() def parse(src: str) -> ScriptData: d = ScriptData() RpyParser(d, "t.rpy").parse(src) return d def test_expr_translation(): e = pyexpr.parse_expr("a >= 1 and 'x' in inv or not flag") assert e["k"] == "bool" and e["op"] == "or" with pytest.raises(pyexpr.Unsupported): pyexpr.parse_expr("lambda: 1") def test_say_variants(): d = parse('label start:\n "narration"\n e happy "hi" with dissolve\n' ' "Bob" "named"\n centered "c"\n') body = d.modules["t.rpy"][0]["body"] assert body[0] == {"op": "say", "what": "narration"} assert body[1]["who"] == "e" and body[1]["attrs"] == ["happy"] and body[1]["with"]["t"] == "dissolve" assert body[2]["whoStr"] == "Bob" assert body[3]["who"] == "centered" def test_string_whitespace_and_escapes(): d = parse('label start:\n "multi\n line \\"q\\" {b}x{/b}"\n') assert d.modules["t.rpy"][0]["body"][0]["what"] == 'multi line "q" {b}x{/b}' def test_if_chain_and_menu(): d = parse("label start:\n if a:\n jump x\n elif b:\n pass\n else:\n" " return\n menu:\n \"A\" if c:\n jump y\n \"B\":\n" " pass\n") body = d.modules["t.rpy"][0]["body"] assert [b["c"] is None for b in body[0]["branches"]] == [False, False, True] assert [i["text"] for i in body[1]["items"]] == ["A", "B"] def test_unsupported_python_is_reported_not_fatal(): d = parse("init python:\n def f():\n pass\n x = 1\nlabel start:\n $ y = x\n") assert d.warnings and d.init_code[0]["op"] == "set" def test_full_pipeline(data_dir, tmp_path): archive = build(tmp_path / "sample.zip") steps = [] result = convert_archive(archive, "sample.zip", tmp_path / "work", steps.append) files = result.manifest["files"] assert "game.json" in files and "script/script.json" in files assert "images/bg room.webp" in files game = json.loads(read_bytes(files["game.json"]["hash"])) assert game["title"] == "Тестовая новелла" assert game["resolution"] == [1280, 720] assert game["images"]["bg room"]["file"] == "images/bg room.webp" assert game["images"]["eileen sad"]["props"]["alpha"] == 0.7 assert "e" in game["characters"] assert result.cover_source is not None script = json.loads(read_bytes(files["script/script.json"]["hash"])) assert [lbl["name"] for lbl in script["labels"]] == ["start", "walk", "stay_home", "ending"] if shutil.which("ffmpeg"): assert "audio/theme.ogg" in files