import pytest from investibook.core.diagnosis_invocation import ( normalize_diagnosis_invocation, render_diagnosis_invocation, ) from investibook.models.requests import AgentTurnCreateRequest def test_missing_invocation_preserves_legacy_autopilot() -> None: invocation = normalize_diagnosis_invocation(None) assert invocation == { "diagnosis_mode": "autopilot", "trigger_type": "unknown", "supports_followup": False, "channel": "unknown", "preferred_route": "", "interaction_action": "start", "selected_routes": [], "confirmed_time_range": None, } body = AgentTurnCreateRequest(input="diagnose this") assert body.diagnosis_invocation is None def test_human_invocation_renders_copilot_policy() -> None: invocation = normalize_diagnosis_invocation( { "diagnosis_mode": "copilot", "trigger_type": "human_message", "supports_followup": True, "preferred_route": "transaction_or_lock_contention", "interaction_action": "select_routes", "selected_routes": [ "transaction_or_lock_contention", "sql_plan_or_query", ], }, channel="lark", ) rendered = render_diagnosis_invocation(invocation) assert "diagnosis_mode: copilot" in rendered assert "trigger_type: human_message" in rendered assert "channel: lark" in rendered assert "supports_followup: true" in rendered assert "preferred_route: transaction_or_lock_contention" in rendered assert "interaction_action: select_routes" in rendered assert 'selected_routes: ["transaction_or_lock_contention","sql_plan_or_query"]' in rendered def test_time_confirmation_is_normalized_and_rendered() -> None: body = AgentTurnCreateRequest( input="confirm", diagnosis_invocation={ "diagnosis_mode": "copilot", "interaction_action": "confirm_time", "confirmed_time_range": { "start": " 2026-09-03T01:00:00Z ", "end": "2026-09-03T02:00:00Z", "timezone": "UTC", }, }, ) invocation = normalize_diagnosis_invocation( body.diagnosis_invocation.model_dump() if body.diagnosis_invocation else None ) assert invocation["confirmed_time_range"] == { "start": "2026-09-03T01:00:00Z", "end": "2026-09-03T02:00:00Z", "timezone": "UTC", } assert 'confirmed_time_range: {"start":"2026-09-03T01:00:00Z"' in render_diagnosis_invocation(invocation) def test_time_confirmation_rejects_partial_range() -> None: with pytest.raises(ValueError, match="confirmed_time_range requires"): AgentTurnCreateRequest( input="confirm", diagnosis_invocation={ "interaction_action": "confirm_time", "confirmed_time_range": { "start": "2026-09-03T01:00:00Z", "end": "", "timezone": "UTC", }, }, ) def test_unknown_runtime_values_fall_back_safely() -> None: invocation = normalize_diagnosis_invocation( {"diagnosis_mode": "surprise", "trigger_type": "magic"}, channel="api", ) assert invocation["diagnosis_mode"] == "autopilot" assert invocation["trigger_type"] == "unknown" assert invocation["channel"] == "api" @pytest.mark.asyncio async def test_auto_chat_marks_alert_as_unattended(monkeypatch) -> None: seen: dict = {} async def _fake_ask_and_wait(*args, **kwargs): seen.update(kwargs) return object() monkeypatch.setattr( "investibook.core.agent_runtime.ask_and_wait", _fake_ask_and_wait ) from investibook.core.auto_chat import _ask_workspace_question await _ask_workspace_question("ws-alert", "diagnose the alert") assert seen["diagnosis_invocation"] == { "diagnosis_mode": "autopilot", "trigger_type": "alert", "supports_followup": False, "channel": "auto_chat", }