interactive-chat / tests /test_story.py
Junhoee's picture
Upload 162 files
1c9c13f verified
Raw
History Blame Contribute Delete
4.15 kB
"""M2 결정적 리딩 루프 νšŒκ·€ ν…ŒμŠ€νŠΈ β€” LLM 없이 전체 μ„œμ‚¬κ°€ μ½”λ“œλ§ŒμœΌλ‘œ λˆλ‹€."""
import pytest
from engine.repositories.content import ContentError, ContentRepository
from engine.services.state import HiddenState
from engine.services.story import StoryEngine, eval_condition
@pytest.fixture(scope="module")
def repo():
return ContentRepository("content")
def play(repo, policy: str) -> StoryEngine:
engine = StoryEngine(repo)
step = engine.start()
while step.ending is None:
if step.choices:
idx = next((i for i, c in enumerate(step.choices) if c.axis == policy),
next((i for i, c in enumerate(step.choices)
if c.axis in (None, "neutral")), 0))
_, step = engine.choose(idx)
else:
step = engine.advance()
return engine
# ── μ—”λ”© 3μ’… 도달 (수렴 νšŒκ·€μ˜ 핡심) ─────────────────────────
def test_trust_policy_reaches_eternal(repo):
e = play(repo, "trust")
assert e.ending.id == "ending_eternal"
def test_doubt_policy_reaches_reckoning(repo):
e = play(repo, "doubt")
assert e.ending.id == "ending_reckoning"
assert e.ending.canonical # μ²˜λ‹¨μ΄ μ›μž‘ 캐논
def test_neutral_policy_reaches_release(repo):
e = play(repo, "neutral")
assert e.ending.id == "ending_release"
# ── 캐논 보μž₯ ────────────────────────────────────────────────
def test_reach_point_visited_on_every_policy(repo):
"""E06-10(두 점 발견)은 μ–΄λ–€ μ„±ν–₯이든 λ°˜λ“œμ‹œ 도달 (ν—Œλ²• 0번)."""
for policy in ("trust", "doubt", "neutral"):
e = play(repo, policy)
assert "E06-10" in e.visited, policy
assert e.state.frailty >= 1 # μ‡ μ•½λ„λŠ” 신뒰와 λ¬΄κ΄€ν•˜κ²Œ μ§„ν–‰
def test_awareness_established_at_entry(repo):
e = StoryEngine(repo)
e.start()
assert e.state.awareness_established # μ—”μ§„ A: λ„μž… 1회 확립
def test_trust_path_commits_hidden_branch(repo):
"""E06-09 동쑰(μ‹ λ’°) β†’ 도달점 톡과 ν›„ E06-10C(μˆ¨κΉ€) λΆ„κΈ°κ°€ μ˜ˆμ•½λœλ‹€."""
e = play(repo, "trust")
assert "E06-10C" in e.visited
assert "E06-10B" not in e.visited
# ── μˆ¨μ€ μƒνƒœ κ·œμΉ™ ───────────────────────────────────────────
def test_frailty_is_monotonic():
s = HiddenState()
s.advance_frailty()
with pytest.raises(ValueError):
s.advance_frailty(-1)
def test_trust_levels():
s = HiddenState()
assert s.trust_level == "neutral"
s.trust_score = 40
assert s.trust_level == "trust_high" and s.deep_unlock
s.doubt_score = 80
assert s.trust_level == "doubt_high"
def test_condition_parser_rejects_arbitrary_code():
s = HiddenState()
assert eval_condition("default", s)
assert not eval_condition("doubt_score >= 30", s)
s.doubt_score = 30
assert eval_condition("doubt_score >= 30", s)
with pytest.raises(ValueError):
eval_condition("__import__('os')", s)
# ── 슀포일러 ν•„ν„° ────────────────────────────────────────────
def test_spoiler_filter_blocks_future_cards(repo):
with pytest.raises(ContentError):
repo.get_card("E06-10", current_id="E01-01")
assert repo.get_card("E01-01", current_id="E06-10").id == "E01-01"
def test_deep_gate_hidden_until_trust_high(repo):
"""E06-09 심측 κ³ λ°±(μ—”μ§„ D)은 μ‹ λ’°ι«˜μ—μ„œλ§Œ λ…ΈμΆœ."""
card = repo.get_card("E06-09")
gated = [b for b in card.narration if b.gate == "trust_high"]
assert gated, "E06-09에 심측 게이트 블둝이 μžˆμ–΄μ•Ό 함"
cold = StoryEngine(repo)
cold.current_id = "E06-09"
assert all(b.gate == "all" for b in cold.visible_narration(card))
warm = StoryEngine(repo, HiddenState(trust_score=60))
assert any(b.gate == "trust_high" for b in warm.visible_narration(card))