File size: 5,691 Bytes
ec90eae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
"""
测试:核心循环引擎
==================
测试 LoopState, DailyStatus, PregoPalLoop
"""

import sys
sys.path.insert(0, '.')

import json
import datetime
from pathlib import Path
from loop import LoopState, DailyStatus, PregoPalLoop, _TRANSITIONS, PRESETS_DIR, STATUS_FILE


class TestLoopState:
    """测试状态枚举"""

    def test_all_states_defined(self):
        """所有状态应被定义"""
        assert LoopState.LAUNCH.value == "launch"
        assert LoopState.FAMILY_QUIZ.value == "family_quiz"
        assert LoopState.SUMMARIZE.value == "summarize"
        assert LoopState.ANALYZE.value == "analyze"
        assert LoopState.BRIEF.value == "brief"
        assert LoopState.INTERACT.value == "interact"
        assert LoopState.THREE_DAY.value == "three_day"
        assert LoopState.CONSOLIDATE.value == "consolidate"
        assert LoopState.DONE.value == "done"

    def test_state_count(self):
        """应有 9 个状态"""
        assert len(LoopState) == 9


class TestTransitions:
    """测试状态转移表"""

    def test_launch_transitions(self):
        """LAUNCH 应有 2 个转移"""
        assert (LoopState.LAUNCH, "need_summary") in _TRANSITIONS
        assert (LoopState.LAUNCH, "already_done") in _TRANSITIONS

    def test_full_flow(self):
        """完整流程应能到达 DONE"""
        state = LoopState.LAUNCH
        events = ["need_summary", "ok", "ok", "ok", "ok", "day_ended", "ok"]
        for event in events:
            next_state = _TRANSITIONS.get((state, event))
            assert next_state is not None, f"No transition from {state} on '{event}'"
            state = next_state
        assert state == LoopState.DONE

    def test_three_day_flow(self):
        """触发三天总结的流程"""
        state = LoopState.LAUNCH
        events = ["need_summary", "ok", "ok", "ok", "ok", "day_ended", "need_3day", "ok"]
        for event in events:
            next_state = _TRANSITIONS.get((state, event))
            assert next_state is not None
            state = next_state
        assert state == LoopState.DONE

    def test_already_done_flow(self):
        """今日已完成的流程"""
        state = LoopState.LAUNCH
        next_state = _TRANSITIONS.get((state, "already_done"))
        assert next_state == LoopState.INTERACT


class TestDailyStatus:
    """测试状态位管理"""

    def setup_method(self):
        self._backup = None
        if STATUS_FILE.exists():
            self._backup = STATUS_FILE.read_text(encoding='utf-8')

    def teardown_method(self):
        if self._backup:
            STATUS_FILE.write_text(self._backup, encoding='utf-8')
        elif STATUS_FILE.exists():
            STATUS_FILE.unlink()

    def test_load_empty(self):
        """空文件应返回空字典"""
        if STATUS_FILE.exists():
            STATUS_FILE.unlink()
        status = DailyStatus.load()
        assert status == {}

    def test_mark_summary_done(self):
        """标记总结完成"""
        DailyStatus.mark_summary_done()
        today = datetime.date.today().isoformat()
        status = DailyStatus.load()
        assert status[today]["summary_done"] is True

    def test_is_today_done(self):
        """检查今日是否完成"""
        DailyStatus.mark_summary_done()
        assert DailyStatus.is_today_done() is True

    def test_mark_day_ended(self):
        """标记今日结束"""
        DailyStatus.mark_day_ended()
        today = datetime.date.today().isoformat()
        status = DailyStatus.load()
        assert status[today]["day_ended"] is True

    def test_get_last_summary_date(self):
        """获取最近总结日期"""
        DailyStatus.mark_summary_done()
        last = DailyStatus.get_last_summary_date()
        assert last == datetime.date.today().isoformat()

    def test_days_since_last_summary(self):
        """距离上次总结的天数"""
        DailyStatus.mark_summary_done()
        days = DailyStatus.days_since_last_summary()
        assert days == 0

    def test_should_three_day_summary_false(self):
        """刚总结完不应触发三天总结"""
        DailyStatus.mark_summary_done()
        assert DailyStatus.should_three_day_summary() is False


class TestPregoPalLoop:
    """测试循环引擎"""

    def test_loop_initialization(self):
        """初始化应处于 LAUNCH 状态"""
        loop = PregoPalLoop()
        assert loop.state == LoopState.LAUNCH

    def test_default_plugins_registered(self):
        """应注册所有默认插件"""
        loop = PregoPalLoop()
        plugins = loop.plugins.get_all()
        plugin_names = [p.name() for p in plugins]
        assert "family_recipe_quiz" in plugin_names
        assert "weight_quiz" in plugin_names
        assert "diet_summary" in plugin_names
        assert "weight_check" in plugin_names
        assert "family_memory" in plugin_names
        assert "dri_analysis" in plugin_names
        assert "briefing_generator" in plugin_names
        assert "three_day_summary" in plugin_names
        assert "preset_writer" in plugin_names

    def test_plugin_count(self):
        """应有 9 个默认插件"""
        loop = PregoPalLoop()
        assert len(loop.plugins.get_all()) == 9

    def test_get_briefing_empty(self):
        """初始简报应为空"""
        loop = PregoPalLoop()
        assert loop.get_briefing() == {}

    def test_get_thinking_keywords(self):
        """初始思考关键词"""
        loop = PregoPalLoop()
        assert loop.get_thinking_keywords() == ""

    def test_get_errors_empty(self):
        """初始错误列表为空"""
        loop = PregoPalLoop()
        assert loop.get_errors() == []