from __future__ import annotations from datetime import date from app.schemas.steward import StewardPlanResponse, StewardTask, StewardThinkingEvent from app.services.steward_context_resume import ( RESUME_CONFIRMATION_KEYWORDS, attach_resumed_task, resume_task_from_flow, should_resume_recent_task, ) def _state_with_travel_application(fields: dict | None = None) -> dict: return { "active_flow": "travel_application", "flows": { "travel_application": { "flow_id": "travel_application", "status": "ready_for_confirmation", "fields": fields or {"location": "上海", "time_range": "2026-02-20 至 2026-02-23"}, "missing_fields": [], } }, } def test_should_resume_returns_flow_id_for_confirmation_keyword_with_state(): state = _state_with_travel_application() for keyword in ("再提交", "继续提交", "重新提交", "再申请", "重新申请", "那就提交", "继续吧", "再试一次"): assert should_resume_recent_task(keyword, state) == "travel_application", f"keyword={keyword}" def test_should_resume_returns_none_when_state_empty(): assert should_resume_recent_task("再提交", {}) is None assert should_resume_recent_task("再提交", None) is None def test_should_resume_returns_none_for_non_confirmation_message(): state = _state_with_travel_application() assert should_resume_recent_task("今天天气不错", state) is None assert should_resume_recent_task("你好", state) is None assert should_resume_recent_task("查一下差旅标准", state) is None assert should_resume_recent_task("", state) is None def test_should_resume_returns_none_when_flow_has_no_fields(): state = { "active_flow": "travel_application", "flows": {"travel_application": {"fields": {}, "missing_fields": []}}, } assert should_resume_recent_task("再提交", state) is None def test_should_resume_finds_flow_when_active_flow_empty(): # active_flow 已清空,但 flows 里仍有可恢复的 flow state = { "active_flow": "", "flows": { "travel_application": { "fields": {"location": "武汉"}, } }, } assert should_resume_recent_task("再提交", state) == "travel_application" def test_resume_task_from_flow_restores_travel_application(): flow = { "flow_id": "travel_application", "fields": {"location": "上海", "time_range": "2026-02-20 至 2026-02-23"}, "missing_fields": [], } task = resume_task_from_flow("travel_application", flow, task_index=1) assert task.task_type == "expense_application" assert task.assigned_agent == "application_assistant" assert task.ontology_fields["location"] == "上海" assert task.requested_action == "submit" assert task.status == "ready_to_delegate" # 无 missing_fields def test_resume_task_from_flow_marks_needs_confirmation_when_missing_fields(): flow = { "fields": {"location": "武汉"}, "missing_fields": ["time_range", "reason"], } task = resume_task_from_flow("travel_application", flow) assert task.missing_fields == ["time_range", "reason"] assert task.status == "needs_confirmation" def test_attach_resumed_task_adds_task_and_thinking_event(): plan = StewardPlanResponse( plan_id="plan_test", planning_source="rule_fallback", summary="占位", tasks=[], thinking_events=[], pending_flow_confirmation={"status": "none"}, ) state = _state_with_travel_application({"location": "上海", "time_range": "2026-02-20 至 2026-02-23"}) updated = attach_resumed_task(plan, state, "travel_application") assert len(updated.tasks) == 1 assert updated.tasks[0].task_type == "expense_application" assert updated.tasks[0].ontology_fields["location"] == "上海" assert updated.planning_source == "context_resume" # thinking_event 应说明上下文已恢复 assert any("恢复" in event.title or "恢复" in event.content for event in updated.thinking_events) def test_attach_resumed_task_returns_unchanged_when_flow_missing(): plan = StewardPlanResponse( plan_id="plan_test", planning_source="rule_fallback", summary="占位", tasks=[], thinking_events=[], pending_flow_confirmation={"status": "none"}, ) updated = attach_resumed_task(plan, {"flows": {}}, "travel_application") assert updated is plan # 原样返回 def test_resume_keywords_cover_common_variants(): # 确认关键词覆盖场景里常见的表述 assert "再提交" in RESUME_CONFIRMATION_KEYWORDS assert "继续提交" in RESUME_CONFIRMATION_KEYWORDS assert "重新申请" in RESUME_CONFIRMATION_KEYWORDS # "提交" 单独不在列表里(避免把"首次提交"误判为恢复) assert "提交" not in RESUME_CONFIRMATION_KEYWORDS