File size: 4,153 Bytes
1c9c13f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"""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))