76 lines
2.7 KiB
Python
76 lines
2.7 KiB
Python
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from content_build.emit import build_trees, check_secrecy, write_trees, dump_json
|
|
from content_build.model import entry_from_raw, Entry
|
|
from content_build.parse import parse_bible
|
|
from content_build.resolve import resolve
|
|
from content_build.errors import BuildError
|
|
|
|
FIXTURE = Path(__file__).parent / "fixtures" / "valid" / "world.md"
|
|
|
|
|
|
def _world():
|
|
raws = parse_bible(FIXTURE)
|
|
return resolve([entry_from_raw(r) for r in raws])
|
|
|
|
|
|
def test_npc_skeleton_has_no_persona_client_side():
|
|
client, _ = build_trees(_world())
|
|
npc = client["npcs/tess.json"]
|
|
assert "persona" not in npc and "body" not in npc
|
|
assert npc["start_disposition"] == "cold"
|
|
assert {"fact_id": "secret.big-twist", "gate": "never"} in npc["knows"]
|
|
|
|
|
|
def test_persona_goes_server_side():
|
|
_, server = build_trees(_world())
|
|
assert server["npcs/tess.json"]["persona"].startswith("Tess")
|
|
|
|
|
|
def test_secret_body_only_server_side():
|
|
client, server = build_trees(_world())
|
|
assert "topics/big-twist.json" in server
|
|
assert server["topics/big-twist.json"]["body"].startswith("The twist")
|
|
# client topic skeleton carries no body
|
|
assert "body" not in client["topics/big-twist.json"]
|
|
|
|
|
|
def test_canon_entity_body_ships_client_side():
|
|
client, _ = build_trees(_world())
|
|
assert client["canon/testburg.json"]["body"].startswith("A test town")
|
|
assert client["canon/disposition-ladder.json"]["rungs"][0] == "hostile"
|
|
|
|
|
|
def test_check_secrecy_passes_on_valid_world():
|
|
client, _ = build_trees(_world())
|
|
check_secrecy(client, _world()) # no raise
|
|
|
|
|
|
def test_check_secrecy_fails_when_secret_body_reaches_client():
|
|
world = _world()
|
|
client, _ = build_trees(world)
|
|
# simulate a routing regression: a secrecy-4 body in a client record
|
|
client["canon/leak.json"] = {"id": "secret.big-twist", "body": "leak"}
|
|
with pytest.raises(BuildError):
|
|
check_secrecy(client, world)
|
|
|
|
|
|
def test_write_trees_serializes_deterministically(tmp_path):
|
|
client, server = build_trees(_world())
|
|
write_trees(client, server, tmp_path / "world", tmp_path / "server")
|
|
written = (tmp_path / "world" / "npcs" / "tess.json").read_text()
|
|
assert written == dump_json(client["npcs/tess.json"])
|
|
|
|
|
|
def test_candidate_entries_are_excluded_from_both_trees():
|
|
world = _world()
|
|
world.entries.append(Entry(id="town.draft", type="town", status="candidate",
|
|
secrecy=0, related=[], body="draft town",
|
|
source="t:99"))
|
|
client, server = build_trees(world)
|
|
# a candidate entry ships in NEITHER tree
|
|
assert "canon/draft.json" not in client
|
|
assert all("draft" not in relpath for relpath in list(client) + list(server))
|