"""Conversation control never revises a goal or starts a model/browser call.""" import copy from unittest.mock import AsyncMock import pytest from backend.prospecting import orchestrator as module @pytest.fixture def engine(monkeypatch, tmp_path): row = {"id": "control", "knowledge_id": "xiaohongshu_prospecting", "status": "running", "current_step": "search_read", "inputs": {"goal": "寻找装修需求", "_goal_version": 1}, "artifacts": {"sources": [{"source_id": "n", "source_type": "note"}]}, "pause_reason": None} def update(tid, **fields): row.update(copy.deepcopy(fields)) return copy.deepcopy(row) monkeypatch.setattr(module.db, "get_task", lambda tid: copy.deepcopy(row)) monkeypatch.setattr(module.db, "update_task", update) monkeypatch.setattr(module, "task_doc_dir", lambda tid: tmp_path) monkeypatch.setattr(module, "append_progress", lambda *args, **kwargs: None) monkeypatch.setattr(module.runner, "close_live_session", AsyncMock()) monkeypatch.setattr(module, "interpret_message", AsyncMock(side_effect=AssertionError("control must not call model"))) return module.ProspectingOrchestrator(), row async def test_status_does_not_interrupt_running_goal(engine, monkeypatch): subject, row = engine interrupt = AsyncMock(side_effect=AssertionError("status must not interrupt")) monkeypatch.setattr(subject, "_interrupt_for_revision", interrupt) result = await subject.message("control", "进度怎么样?") assert result["status"] == "running" assert result["inputs"] == {"goal": "寻找装修需求", "_goal_version": 1} assert "1篇笔记" in result["artifacts"]["messages"][-1]["text"] async def test_natural_stop_preserves_goal_and_sources(engine): subject, row = engine result = await subject.message("control", "先停一下") assert result["status"] == "cancelled" assert result["inputs"]["goal"] == "寻找装修需求" assert result["artifacts"]["sources"][0]["source_id"] == "n" assert result["artifacts"]["stop_reason"] == "user_stopped"