from __future__ import annotations import sys from pathlib import Path from unittest.mock import patch from cognitive_agent.application.assistant_capabilities import AssistantCapabilityCatalog from cognitive_agent.application.codex_assistant import CodexAssistantService from cognitive_agent.application.mode_profile import ModeProfileRegistry from cognitive_agent.application.seat_service import REPOSITORY_ROOT, SeatDemoService from cognitive_agent.infrastructure.codex.app_server import CodexAppServerClient FAKE_SERVER = REPOSITORY_ROOT / "tests" / "fixtures" / "fake_codex_app_server.py" MCP_SERVER = ( REPOSITORY_ROOT / "src/cognitive_agent/infrastructure/codex/vehicle_mcp_server.py" ) CALLBACK_URL = "http://127.0.0.1:8765/api/internal/codex/vehicle-control" def test_structured_scene_turn_reaches_the_complete_local_dry_run_pipeline( tmp_path: Path, ) -> None: service = SeatDemoService(REPOSITORY_ROOT) service.artifact_root = tmp_path / "runs" service.mode_profiles = ModeProfileRegistry( REPOSITORY_ROOT / "config" / "modes", user_root=tmp_path / "user-modes", ) client = CodexAppServerClient( cwd=tmp_path / "codex-workspace", codex_command=[sys.executable, "-u", str(FAKE_SERVER)], model="gpt-test-fast", reasoning_effort="low", request_timeout=2, startup_timeout=2, environment={"OPENAI_BASE_URL": "https://api.openai.com/v1"}, mcp_server_path=MCP_SERVER, ) assistant = CodexAssistantService(client) catalog = AssistantCapabilityCatalog(service) snapshot = catalog.snapshot() events: list[dict[str, object]] = [] def execute_control( instruction: str, progress: object, assistant_plan: object, ) -> dict[str, object]: assert isinstance(assistant_plan, dict) return service.preview_native_can( instruction, provider="local", conversation_id="conv-scene-e2e-pipeline", execution_mode="hardware", vehicle_id="denza_n7", on_commands_validated=progress if callable(progress) else None, assistant_plan=assistant_plan, ) try: assistant.start(CALLBACK_URL) with patch.object( service.hardware_execution, "reserve", side_effect=AssertionError("scene preview must never reserve hardware"), ): response = assistant.stream( "构建一个星夜模式", conversation_id="conv-scene-e2e-pipeline", principal="scene-e2e-user", history=[], emit=lambda event: events.append(event) or True, execute_control=execute_control, capability_snapshot=snapshot, capability_context=catalog.compact_context(snapshot), scene_capability_context=catalog.scene_composition_context(snapshot), control_terms=catalog.control_terms(snapshot), ) finally: assistant.close() result = response["result"] draft = result["mode_profile_draft"] assert response["route"] == "scene_composer" assert response["success"] is True assert result["status"] == "succeeded" assert result["execution_mode"] == "dry_run" assert result["hardware_execution_allowed"] is False assert result["orchestration_route"] == "mode_profile_create_preview" assert draft["name"] == "星夜模式" assert draft["generation_source"] == "codex_scene_composition" assert draft["operation_count"] == 2 capabilities = { action["capability"] for action in result["actions"] } assert capabilities == { "climate.set_temperature", "roof.set_shade_position", } assert not list((tmp_path / "user-modes").rglob("*.yaml")) assert any(event.get("type") == "action.completed" for event in events) assert all("request_token" not in str(event) for event in events)