feat(api): content.load_npc + content-root resolver
This commit is contained in:
@@ -8,6 +8,7 @@ scenes into play.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -48,3 +49,30 @@ def unresolved_refs(origin: dict, world: dict[str, set[str]]) -> list[str]:
|
||||
if npc_id not in world["npcs"]:
|
||||
missing.append(f"npc:{npc_id}")
|
||||
return missing
|
||||
|
||||
|
||||
def _content_root() -> Path:
|
||||
"""Locate the authored content root. Honours CONTENT_ROOT, else walks up
|
||||
from this file for a dir containing world/ — repo-root/content in a local
|
||||
checkout, /app/content when bundled into the Docker image. Mirrors
|
||||
canon_log._schema_dir().
|
||||
"""
|
||||
env = os.environ.get("CONTENT_ROOT")
|
||||
if env:
|
||||
return Path(env)
|
||||
here = Path(__file__).resolve()
|
||||
for parent in here.parents:
|
||||
candidate = parent / "content"
|
||||
if (candidate / "world").is_dir():
|
||||
return candidate
|
||||
raise RuntimeError("content root not found")
|
||||
|
||||
|
||||
def load_npc(npc_id: str) -> dict | None:
|
||||
"""Load one NPC's authored file (persona + knowledge + capabilities), or
|
||||
None if no file resolves. Server-only fields never travel to the client."""
|
||||
path = _content_root() / "world" / "npcs" / f"{npc_id}.json"
|
||||
if not path.is_file():
|
||||
return None
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
||||
|
||||
27
api/tests/test_content_npc.py
Normal file
27
api/tests/test_content_npc.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import json
|
||||
|
||||
import app.content as content
|
||||
|
||||
|
||||
def test_load_npc_returns_parsed_file(tmp_path, monkeypatch):
|
||||
root = tmp_path / "content"
|
||||
(root / "world" / "npcs").mkdir(parents=True)
|
||||
(root / "world" / "npcs" / "fenn.json").write_text(
|
||||
json.dumps({"id": "fenn", "name": "Fenn", "persona": "clerk",
|
||||
"knowledge": ["a"], "capabilities": {}})
|
||||
)
|
||||
monkeypatch.setenv("CONTENT_ROOT", str(root))
|
||||
npc = content.load_npc("fenn")
|
||||
assert npc["name"] == "Fenn"
|
||||
assert npc["knowledge"] == ["a"]
|
||||
|
||||
|
||||
def test_load_npc_unknown_returns_none(tmp_path, monkeypatch):
|
||||
(tmp_path / "content" / "world").mkdir(parents=True)
|
||||
monkeypatch.setenv("CONTENT_ROOT", str(tmp_path / "content"))
|
||||
assert content.load_npc("nobody") is None
|
||||
|
||||
|
||||
def test_content_root_env_override(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("CONTENT_ROOT", str(tmp_path))
|
||||
assert content._content_root() == tmp_path
|
||||
Reference in New Issue
Block a user