""" 测试:核心循环引擎 ================== 测试 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() == []