test(narrative): add end-to-end pipeline test

Full-pipeline test: fake simulation dir → actions.jsonl → two
translate_round calls → assertions on beat persistence, character
evolution, and translator offset advancement.

Final test suite: 20/20 passing across action mapper, character
engine, story store, translator (read + prose + orchestration), and
E2E.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
anadoris007 2026-04-20 22:05:08 +05:30
parent 0c9b0c025d
commit d651cb830b
1 changed files with 72 additions and 0 deletions

View File

@ -0,0 +1,72 @@
"""End-to-end test: simulated actions.jsonl → translate → verify story + state."""
import os
import json
from unittest.mock import patch
from app.services.narrative.story_store import StoryStore
from app.services.narrative.narrative_translator import translate_round
def test_full_pipeline_from_fake_simulation(tmp_path):
sim_dir = str(tmp_path / "sim_e2e")
os.makedirs(os.path.join(sim_dir, "twitter"))
actions_path = os.path.join(sim_dir, "twitter", "actions.jsonl")
actions = [
{"round": 1, "agent_id": 1, "agent_name": "Elena", "action_type": "CREATE_POST",
"action_args": {"content": "The council must fall."}, "success": True, "timestamp": "t"},
{"round": 1, "agent_id": 2, "agent_name": "Marcus", "action_type": "DISLIKE_POST",
"action_args": {"post_id": 1}, "success": True, "timestamp": "t"},
{"event_type": "round_end", "round": 1, "timestamp": "t"},
{"round": 2, "agent_id": 1, "agent_name": "Elena", "action_type": "REPOST",
"action_args": {}, "success": True, "timestamp": "t"},
{"round": 2, "agent_id": 2, "agent_name": "Marcus", "action_type": "FOLLOW",
"action_args": {"target": 3}, "success": True, "timestamp": "t"},
{"event_type": "round_end", "round": 2, "timestamp": "t"},
]
with open(actions_path, "w") as f:
for a in actions:
f.write(json.dumps(a) + "\n")
store = StoryStore(sim_dir)
neutral_emotions = {k: 0.0 for k in ["anger", "fear", "joy", "sadness", "surprise"]}
store.save_characters([
{"id": "1", "name": "Elena",
"emotional_state": {"current": {**neutral_emotions, "trust": 0.5}, "history": []}},
{"id": "2", "name": "Marcus",
"emotional_state": {"current": {**neutral_emotions, "trust": 0.5}, "history": []}},
])
with patch("app.services.narrative.narrative_translator.call_llm") as mock_llm:
mock_llm.side_effect = [
"Elena addressed the gathering. Marcus's face darkened.",
"Elena's message spread through the quarter like a spark.",
]
beat1 = translate_round(sim_dir, "twitter", 1, "dark fantasy")
beat2 = translate_round(sim_dir, "twitter", 2, "dark fantasy")
# Beats are correctly attributed and sequenced
assert beat1["round"] == 1
assert beat2["round"] == 2
assert "Elena" in beat1["characters"]
assert "Marcus" in beat1["characters"]
assert beat1["action_count"] == 2
# Both beats persisted
all_beats = store.get_all_beats()
assert len(all_beats) == 2
assert all_beats[0]["prose"] == "Elena addressed the gathering. Marcus's face darkened."
# Characters evolved per the emotional delta rules
chars = {c["name"]: c for c in store.load_characters()}
# Marcus DISLIKE_POST'd in round 1 → anger should be > 0 (delta 0.08)
assert chars["Marcus"]["emotional_state"]["current"]["anger"] > 0.0
# Marcus FOLLOW'd in round 2 → trust should have climbed above baseline (delta 0.08)
assert chars["Marcus"]["emotional_state"]["current"]["trust"] > 0.5
# Elena CREATE_POST'd then REPOST'd → joy and surprise both bumped
assert chars["Elena"]["emotional_state"]["current"]["joy"] > 0.0
assert chars["Elena"]["emotional_state"]["current"]["surprise"] > 0.0
# Translator state advanced — next call for round 3 would resume, not re-read
assert store.get_file_offset("twitter") > 0