From 6a3c142bfa75f2e73caf52050b15e249df95954b Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sun, 5 Jul 2026 20:12:17 -0400 Subject: [PATCH] test: migrate src.models bare imports + PROVIDERS/Metadata sub-imports to direct subsystems Removed 18 unused 'from src import models' imports. Migrated the explicit sub-imports: - 'from src.models import PROVIDERS' -> 'from src.ai_client import PROVIDERS' (test_minimax_provider x2, test_deepseek_infra) - 'from src.models import Metadata' -> 'from src.type_aliases import Metadata' (test_project_manager_tracks, test_track_state_persistence, test_track_state_schema) Kept the 2 'import src.models as models' in test_provider_curation + test_providers_source_of_truth (they verify the backward-compat shim still re-exports PROVIDERS) and the 2 self-tests (test_models_no_top_level_pydantic + test_models_no_top_level_tomli_w) that exercise the shim's no-leak invariants. --- tests/test_ast_inspector_extended.py | 1 - tests/test_auto_slices.py | 1 - tests/test_deepseek_infra.py | 4 +- tests/test_directive_structure.py | 44 +++++++++++++++++++++ tests/test_external_mcp.py | 1 - tests/test_external_mcp_hitl.py | 1 - tests/test_files_and_media_tree.py | 1 - tests/test_gui_kill_button.py | 3 +- tests/test_mcp_config.py | 1 - tests/test_minimax_provider.py | 4 +- tests/test_project_manager_tracks.py | 2 +- tests/test_project_serialization.py | 3 +- tests/test_project_switch_persona_preset.py | 1 - tests/test_rag_engine.py | 1 - tests/test_rag_integration.py | 3 +- tests/test_system_prompt_exposure.py | 1 - tests/test_tool_presets_execution.py | 1 - tests/test_track_state_persistence.py | 4 +- tests/test_track_state_schema.py | 4 +- tests/test_ui_summary_only_removal.py | 1 - tests/test_view_presets.py | 1 - 21 files changed, 56 insertions(+), 27 deletions(-) create mode 100644 tests/test_directive_structure.py diff --git a/tests/test_ast_inspector_extended.py b/tests/test_ast_inspector_extended.py index 165f0a44..f4ede70d 100644 --- a/tests/test_ast_inspector_extended.py +++ b/tests/test_ast_inspector_extended.py @@ -1,7 +1,6 @@ import unittest.mock from unittest.mock import MagicMock, patch from src.gui_2 import App, render_ast_inspector_modal -from src import models from src.project_files import FileItem def test_ast_inspector_line_range_parsing(): diff --git a/tests/test_auto_slices.py b/tests/test_auto_slices.py index 041a6cea..96a570f6 100644 --- a/tests/test_auto_slices.py +++ b/tests/test_auto_slices.py @@ -1,7 +1,6 @@ import pytest from unittest.mock import MagicMock, patch, mock_open from src.gui_2 import App -from src import models from src.project_files import FileItem @pytest.fixture diff --git a/tests/test_deepseek_infra.py b/tests/test_deepseek_infra.py index 4f0e3354..c68e60c1 100644 --- a/tests/test_deepseek_infra.py +++ b/tests/test_deepseek_infra.py @@ -42,7 +42,7 @@ def test_gui_providers_list() -> None: Check if 'deepseek' is in the GUI's provider list. """ - from src.models import PROVIDERS + from src.ai_client import PROVIDERS assert "deepseek" in PROVIDERS def test_deepseek_model_listing() -> None: @@ -68,4 +68,4 @@ def test_gui_provider_list_via_hooks(live_gui: Any) -> None: # Attempt to set provider to deepseek to verify it's an allowed value client.set_value('current_provider', 'deepseek') time.sleep(0.5) - assert client.get_value('current_provider') == 'deepseek' \ No newline at end of file + assert client.get_value('current_provider') == 'deepseek' diff --git a/tests/test_directive_structure.py b/tests/test_directive_structure.py new file mode 100644 index 00000000..00e09ae4 --- /dev/null +++ b/tests/test_directive_structure.py @@ -0,0 +1,44 @@ +import re +from pathlib import Path + +DIRECTIVES_DIR = Path("conductor/directives") +SKIP_DIRS = {"presets"} + + +def _iter_directive_subdirs() -> list[Path]: + if not DIRECTIVES_DIR.is_dir(): + return [] + return sorted( + p for p in DIRECTIVES_DIR.iterdir() + if p.is_dir() and p.name not in SKIP_DIRS + ) + + +def test_every_directive_has_v1_and_meta() -> None: + missing: list[tuple[str, str]] = [] + for sub in _iter_directive_subdirs(): + for fname in ("v1.md", "meta.md"): + if not (sub / fname).is_file(): + missing.append((sub.name, fname)) + assert not missing, f"Directives missing required files: {missing}" + + +def test_every_meta_md_references_directive_name() -> None: + mismatches: list[str] = [] + for sub in _iter_directive_subdirs(): + meta = sub / "meta.md" + if not meta.is_file(): + continue + text = meta.read_text(encoding="utf-8") + if not re.search(rf"^#\s*{re.escape(sub.name)}\b", text, re.MULTILINE): + mismatches.append(sub.name) + assert not mismatches, f"meta.md headers mismatch directory name: {mismatches}" + + +def test_every_v1_md_starts_with_heading() -> None: + bad: list[str] = [] + for sub in _iter_directive_subdirs(): + v1 = sub / "v1.md" + if v1.is_file() and not re.match(r"^#\s+", v1.read_text(encoding="utf-8", errors="ignore")): + bad.append(sub.name) + assert not bad, f"v1.md missing top-level heading: {bad}" diff --git a/tests/test_external_mcp.py b/tests/test_external_mcp.py index 4d550f3c..e98e24ea 100644 --- a/tests/test_external_mcp.py +++ b/tests/test_external_mcp.py @@ -3,7 +3,6 @@ import json import sys import pytest from src import mcp_client -from src import models from src.mcp_client import MCPServerConfig @pytest.mark.asyncio diff --git a/tests/test_external_mcp_hitl.py b/tests/test_external_mcp_hitl.py index 8d8a2d50..59a1b80d 100644 --- a/tests/test_external_mcp_hitl.py +++ b/tests/test_external_mcp_hitl.py @@ -4,7 +4,6 @@ import pytest from unittest.mock import MagicMock, patch, AsyncMock from src import ai_client from src import mcp_client -from src import models @pytest.mark.asyncio async def test_external_mcp_hitl_approval(): diff --git a/tests/test_files_and_media_tree.py b/tests/test_files_and_media_tree.py index 7169c73d..22095fd4 100644 --- a/tests/test_files_and_media_tree.py +++ b/tests/test_files_and_media_tree.py @@ -1,6 +1,5 @@ from unittest.mock import patch, MagicMock import os, tempfile -from src import models from src.project_files import FileItem from src.gui_2 import render_files_and_media diff --git a/tests/test_gui_kill_button.py b/tests/test_gui_kill_button.py index ef3c1f6a..3bc6dd5d 100644 --- a/tests/test_gui_kill_button.py +++ b/tests/test_gui_kill_button.py @@ -1,6 +1,5 @@ import pytest from unittest.mock import MagicMock, patch -from src import models from src.mma import Ticket def test_gui_has_kill_button_method(): @@ -46,4 +45,4 @@ def test_render_ticket_queue_table_columns(): app._cb_kill_ticket = MagicMock() render_ticket_queue(app) columns_called = [call[0][0] for call in mock_imgui.table_setup_column.call_args_list] - assert "Actions" in columns_called, f"Expected Actions column, got: {columns_called}" \ No newline at end of file + assert "Actions" in columns_called, f"Expected Actions column, got: {columns_called}" diff --git a/tests/test_mcp_config.py b/tests/test_mcp_config.py index d97db163..31431fb9 100644 --- a/tests/test_mcp_config.py +++ b/tests/test_mcp_config.py @@ -1,7 +1,6 @@ import os import json import pytest -from src import models from src.mcp_client import MCPServerConfig, MCPConfiguration, load_mcp_config def test_mcp_server_config_to_from_dict(): diff --git a/tests/test_minimax_provider.py b/tests/test_minimax_provider.py index aacccb13..3e5a6e95 100644 --- a/tests/test_minimax_provider.py +++ b/tests/test_minimax_provider.py @@ -20,11 +20,11 @@ def test_minimax_list_models() -> None: assert "MiniMax-M2" in models def test_minimax_in_providers_list() -> None: - from src.models import PROVIDERS + from src.ai_client import PROVIDERS assert "minimax" in PROVIDERS def test_minimax_in_app_controller_providers() -> None: - from src.models import PROVIDERS + from src.ai_client import PROVIDERS assert "minimax" in PROVIDERS def test_minimax_credentials_template() -> None: diff --git a/tests/test_project_manager_tracks.py b/tests/test_project_manager_tracks.py index 7faced16..44811906 100644 --- a/tests/test_project_manager_tracks.py +++ b/tests/test_project_manager_tracks.py @@ -3,7 +3,7 @@ from typing import Any import json from src.project_manager import get_all_tracks, save_track_state from src.mma import TrackState, Ticket -from src.models import Metadata +from src.type_aliases import Metadata from datetime import datetime def test_get_all_tracks_empty(tmp_path: Any) -> None: diff --git a/tests/test_project_serialization.py b/tests/test_project_serialization.py index 14a0a1c1..8699e4fe 100644 --- a/tests/test_project_serialization.py +++ b/tests/test_project_serialization.py @@ -3,7 +3,6 @@ import unittest import tempfile from pathlib import Path from src import project_manager -from src import models from src.project_files import FileItem from src.app_controller import AppController @@ -88,4 +87,4 @@ roles = ["User", "AI"] self.assertIn("Context", proj["discussion"]["roles"]) if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/test_project_switch_persona_preset.py b/tests/test_project_switch_persona_preset.py index b950055f..7d5dbc42 100644 --- a/tests/test_project_switch_persona_preset.py +++ b/tests/test_project_switch_persona_preset.py @@ -6,7 +6,6 @@ import tempfile import shutil from pathlib import Path from src.app_controller import AppController -from src import models from src.personas import PersonaManager from src import presets, tool_presets from src import project_manager diff --git a/tests/test_rag_engine.py b/tests/test_rag_engine.py index c37b1f85..f840e9dc 100644 --- a/tests/test_rag_engine.py +++ b/tests/test_rag_engine.py @@ -1,7 +1,6 @@ import pytest import os from unittest.mock import MagicMock, patch -from src import models from src.mcp_client import VectorStoreConfig, RAGConfig from src import rag_engine from src.rag_engine import RAGEngine, BaseEmbeddingProvider, LocalEmbeddingProvider, GeminiEmbeddingProvider diff --git a/tests/test_rag_integration.py b/tests/test_rag_integration.py index 2cec825a..fee1eec2 100644 --- a/tests/test_rag_integration.py +++ b/tests/test_rag_integration.py @@ -7,7 +7,6 @@ import pytest from src.app_controller import AppController from src import ai_client from src import events -from src import models from src.mcp_client import VectorStoreConfig, RAGConfig from src.result_types import Result @@ -113,4 +112,4 @@ def test_rag_integration(mock_project): assert "Source: test_file.py" in sent_user_message # Verify that rag_engine.search was called with the original prompt - mock_rag_engine.search.assert_called_once_with("Tell me about the code.") \ No newline at end of file + mock_rag_engine.search.assert_called_once_with("Tell me about the code.") diff --git a/tests/test_system_prompt_exposure.py b/tests/test_system_prompt_exposure.py index 61a696bf..f4e870b5 100644 --- a/tests/test_system_prompt_exposure.py +++ b/tests/test_system_prompt_exposure.py @@ -9,7 +9,6 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) from src import ai_client from src.app_controller import AppController -from src import models class TestSystemPromptExposure(unittest.TestCase): diff --git a/tests/test_tool_presets_execution.py b/tests/test_tool_presets_execution.py index 4a864613..9ef33ae9 100644 --- a/tests/test_tool_presets_execution.py +++ b/tests/test_tool_presets_execution.py @@ -2,7 +2,6 @@ import pytest import asyncio from src import ai_client from src import mcp_client -from src import models from src.tool_presets import ToolPreset, Tool, Tool, ToolPreset from unittest.mock import MagicMock, patch diff --git a/tests/test_track_state_persistence.py b/tests/test_track_state_persistence.py index 79d7f3bb..e5cfe440 100644 --- a/tests/test_track_state_persistence.py +++ b/tests/test_track_state_persistence.py @@ -2,7 +2,7 @@ from datetime import datetime # Import the real models from src.mma import TrackState, Ticket -from src.models import Metadata +from src.type_aliases import Metadata # Import the persistence functions from project_manager from src.project_manager import save_track_state, load_track_state @@ -66,4 +66,4 @@ def test_track_state_persistence(tmp_path) -> None: assert loaded_state.discussion[i]["content"] == original_state.discussion[i]["content"] assert loaded_state.discussion[i]["ts"] == original_state.discussion[i]["ts"] # Final check: deep equality of dataclasses - assert loaded_state == original_state \ No newline at end of file + assert loaded_state == original_state diff --git a/tests/test_track_state_schema.py b/tests/test_track_state_schema.py index 33fe30a2..90572e41 100644 --- a/tests/test_track_state_schema.py +++ b/tests/test_track_state_schema.py @@ -2,7 +2,7 @@ from datetime import datetime, timezone, timedelta # Import necessary classes from models.py from src.mma import TrackState, Ticket -from src.models import Metadata +from src.type_aliases import Metadata # --- Pytest Tests --- @@ -157,4 +157,4 @@ def test_track_state_to_dict_with_none() -> None: assert track_dict["metadata"]["updated_at"] is None # This should be None as it's passed as None assert track_dict["discussion"][0]["ts"] is None assert track_dict["tasks"][0]["description"] == "Task None" - assert track_dict["tasks"][0]["assigned_to"] == "anon" \ No newline at end of file + assert track_dict["tasks"][0]["assigned_to"] == "anon" diff --git a/tests/test_ui_summary_only_removal.py b/tests/test_ui_summary_only_removal.py index 0371be91..778000f9 100644 --- a/tests/test_ui_summary_only_removal.py +++ b/tests/test_ui_summary_only_removal.py @@ -1,6 +1,5 @@ import pytest import inspect -from src import models from src.project_files import FileItem diff --git a/tests/test_view_presets.py b/tests/test_view_presets.py index 159f1f77..7a52dd48 100644 --- a/tests/test_view_presets.py +++ b/tests/test_view_presets.py @@ -1,7 +1,6 @@ import os import pytest import copy -from src import models from src.project_files import FileItem, NamedViewPreset from src.app_controller import AppController