This commit is contained in:
2026-03-05 17:13:59 -05:00
parent d4923c5198
commit fd36aad539
11 changed files with 48 additions and 48 deletions

View File

@@ -10,7 +10,7 @@ from src import ai_client
def test_agent_capabilities_listing() -> None: def test_agent_capabilities_listing() -> None:
# Mock credentials # Mock credentials
with patch("ai_client._load_credentials") as mock_creds: with patch("src.ai_client._load_credentials") as mock_creds:
mock_creds.return_value = {"gemini": {"api_key": "fake-key"}} mock_creds.return_value = {"gemini": {"api_key": "fake-key"}}
# Mock the google-genai Client and models.list # Mock the google-genai Client and models.list

View File

@@ -36,8 +36,8 @@ def test_event_emission() -> None:
def test_send_emits_events_proper() -> None: def test_send_emits_events_proper() -> None:
ai_client.reset_session() ai_client.reset_session()
with patch("ai_client._ensure_gemini_client"), \ with patch("src.ai_client._ensure_gemini_client"), \
patch("ai_client._gemini_client") as mock_client: patch("src.ai_client._gemini_client") as mock_client:
mock_chat = MagicMock() mock_chat = MagicMock()
mock_client.chats.create.return_value = mock_chat mock_client.chats.create.return_value = mock_chat
mock_response = MagicMock() mock_response = MagicMock()
@@ -57,9 +57,9 @@ def test_send_emits_events_proper() -> None:
def test_send_emits_tool_events() -> None: def test_send_emits_tool_events() -> None:
ai_client.reset_session() # Clear caches and chats to avoid test pollution ai_client.reset_session() # Clear caches and chats to avoid test pollution
with patch("ai_client._ensure_gemini_client"), \ with patch("src.ai_client._ensure_gemini_client"), \
patch("ai_client._gemini_client") as mock_client, \ patch("src.ai_client._gemini_client") as mock_client, \
patch("mcp_client.dispatch") as mock_dispatch: patch("src.mcp_client.dispatch") as mock_dispatch:
mock_chat = MagicMock() mock_chat = MagicMock()
mock_client.chats.create.return_value = mock_chat mock_client.chats.create.return_value = mock_chat
# 1. Setup mock response with a tool call # 1. Setup mock response with a tool call

View File

@@ -24,7 +24,7 @@ def test_telemetry_data_updates_correctly(app_instance: Any) -> None:
"percentage": 75.0, "percentage": 75.0,
} }
# 3. Patch the dependencies # 3. Patch the dependencies
with patch('ai_client.get_token_stats', return_value=mock_stats) as mock_get_stats: with patch('src.ai_client.get_token_stats', return_value=mock_stats) as mock_get_stats:
# 4. Call the method under test # 4. Call the method under test
app_instance._refresh_api_metrics({}, md_content="test content") app_instance._refresh_api_metrics({}, md_content="test content")
# 5. Assert the results # 5. Assert the results
@@ -41,7 +41,7 @@ def test_performance_history_updates(app_instance: Any) -> None:
def test_gui_updates_on_event(app_instance: App) -> None: def test_gui_updates_on_event(app_instance: App) -> None:
mock_stats = {"percentage": 50.0, "current": 500, "limit": 1000} mock_stats = {"percentage": 50.0, "current": 500, "limit": 1000}
app_instance.last_md = "mock_md" app_instance.last_md = "mock_md"
with patch('ai_client.get_token_stats', return_value=mock_stats): with patch('src.ai_client.get_token_stats', return_value=mock_stats):
app_instance._on_api_event(payload={"text": "test"}) app_instance._on_api_event(payload={"text": "test"})
app_instance._process_pending_gui_tasks() app_instance._process_pending_gui_tasks()
assert app_instance._token_stats["percentage"] == 50.0 assert app_instance._token_stats["percentage"] == 50.0

View File

@@ -1,4 +1,4 @@
import pytest import pytest
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from pathlib import Path from pathlib import Path
@@ -32,9 +32,9 @@ history = []
@pytest.fixture @pytest.fixture
def app_instance(mock_config: Path, mock_project: Path, monkeypatch: pytest.MonkeyPatch) -> App: def app_instance(mock_config: Path, mock_project: Path, monkeypatch: pytest.MonkeyPatch) -> App:
monkeypatch.setattr("gui_2.CONFIG_PATH", mock_config) monkeypatch.setattr("src.models.CONFIG_PATH", mock_config)
with patch("project_manager.load_project") as mock_load, \ with patch("src.project_manager.load_project") as mock_load, \
patch("session_logger.open_session"): patch("src.session_logger.open_session"):
mock_load.return_value = { mock_load.return_value = {
"project": {"name": "test"}, "project": {"name": "test"},
"discussion": {"roles": ["User", "AI"], "active": "main", "discussions": {"main": {"history": []}}}, "discussion": {"roles": ["User", "AI"], "active": "main", "discussions": {"main": {"history": []}}},
@@ -63,19 +63,19 @@ def test_render_log_management_logic(app_instance: App) -> None:
app = app_instance app = app_instance
app.show_windows["Log Management"] = True app.show_windows["Log Management"] = True
# Mock LogRegistry # Mock LogRegistry
with patch("gui_2.LogRegistry") as MockRegistry, \ with patch("src.log_registry.LogRegistry") as MockRegistry, \
patch("gui_2.imgui.begin") as mock_begin, \ patch("src.gui_2.imgui.begin") as mock_begin, \
patch("gui_2.imgui.begin_table") as mock_begin_table, \ patch("src.gui_2.imgui.begin_table") as mock_begin_table, \
patch("gui_2.imgui.text") as mock_text, \ patch("src.gui_2.imgui.text") as mock_text, \
patch("gui_2.imgui.end_table"), \ patch("src.gui_2.imgui.end_table"), \
patch("gui_2.imgui.end"), \ patch("src.gui_2.imgui.end"), \
patch("gui_2.imgui.push_style_color"), \ patch("src.gui_2.imgui.push_style_color"), \
patch("gui_2.imgui.pop_style_color"), \ patch("src.gui_2.imgui.pop_style_color"), \
patch("gui_2.imgui.table_setup_column"), \ patch("src.gui_2.imgui.table_setup_column"), \
patch("gui_2.imgui.table_headers_row"), \ patch("src.gui_2.imgui.table_headers_row"), \
patch("gui_2.imgui.table_next_row"), \ patch("src.gui_2.imgui.table_next_row"), \
patch("gui_2.imgui.table_next_column"), \ patch("src.gui_2.imgui.table_next_column"), \
patch("gui_2.imgui.button"): patch("src.gui_2.imgui.button"):
mock_reg = MockRegistry.return_value mock_reg = MockRegistry.return_value
mock_reg.data = { mock_reg.data = {
"session_1": { "session_1": {

View File

@@ -4,7 +4,7 @@ from pathlib import Path
from datetime import datetime, timedelta from datetime import datetime, timedelta
from src import session_logger from src import session_logger
from src.log_registry import LogRegistry from src.log_registry import LogRegistry
from log_pruner import LogPruner from src.log_pruner import LogPruner
@pytest.fixture @pytest.fixture
def e2e_setup(tmp_path: Path, monkeypatch: Any) -> Any: def e2e_setup(tmp_path: Path, monkeypatch: Any) -> Any:

View File

@@ -11,7 +11,7 @@ from src import mcp_client
def test_mcp_perf_tool_retrieval() -> None: def test_mcp_perf_tool_retrieval() -> None:
mock_metrics = {"fps": 60, "last_frame_time_ms": 16.6} mock_metrics = {"fps": 60, "last_frame_time_ms": 16.6}
# Simulate tool call by patching the callback # Simulate tool call by patching the callback
with patch('mcp_client.perf_monitor_callback', return_value=mock_metrics): with patch('src.mcp_client.perf_monitor_callback', return_value=mock_metrics):
result = mcp_client.get_ui_performance() result = mcp_client.get_ui_performance()
assert "60" in result assert "60" in result
assert "16.6" in result assert "16.6" in result

View File

@@ -70,7 +70,7 @@ class TestMMAApprovalIndicators:
_pending_ask_dialog=False, _pending_ask_dialog=False,
) )
imgui_mock = _make_imgui_mock() imgui_mock = _make_imgui_mock()
with patch("gui_2.imgui", imgui_mock): with patch("src.gui_2.imgui", imgui_mock):
App._render_mma_dashboard(app) App._render_mma_dashboard(app)
combined = _collect_text_colored_args(imgui_mock) combined = _collect_text_colored_args(imgui_mock)
assert "APPROVAL PENDING" not in combined, ( assert "APPROVAL PENDING" not in combined, (
@@ -81,7 +81,7 @@ class TestMMAApprovalIndicators:
"""'APPROVAL PENDING' badge must appear when _pending_mma_spawn is set.""" """'APPROVAL PENDING' badge must appear when _pending_mma_spawn is set."""
app = _make_app(_pending_mma_spawn={"ticket_id": "T-001"}) app = _make_app(_pending_mma_spawn={"ticket_id": "T-001"})
imgui_mock = _make_imgui_mock() imgui_mock = _make_imgui_mock()
with patch("gui_2.imgui", imgui_mock), patch("gui_2.math") as mock_math: with patch("src.gui_2.imgui", imgui_mock), patch("src.gui_2.math") as mock_math:
mock_math.sin.return_value = 0.8 mock_math.sin.return_value = 0.8
App._render_mma_dashboard(app) App._render_mma_dashboard(app)
combined = _collect_text_colored_args(imgui_mock) combined = _collect_text_colored_args(imgui_mock)
@@ -93,7 +93,7 @@ class TestMMAApprovalIndicators:
"""'APPROVAL PENDING' badge must appear when _pending_mma_approval is set.""" """'APPROVAL PENDING' badge must appear when _pending_mma_approval is set."""
app = _make_app(_pending_mma_approval={"step": "test"}) app = _make_app(_pending_mma_approval={"step": "test"})
imgui_mock = _make_imgui_mock() imgui_mock = _make_imgui_mock()
with patch("gui_2.imgui", imgui_mock), patch("gui_2.math") as mock_math: with patch("src.gui_2.imgui", imgui_mock), patch("src.gui_2.math") as mock_math:
mock_math.sin.return_value = 0.8 mock_math.sin.return_value = 0.8
App._render_mma_dashboard(app) App._render_mma_dashboard(app)
combined = _collect_text_colored_args(imgui_mock) combined = _collect_text_colored_args(imgui_mock)
@@ -105,7 +105,7 @@ class TestMMAApprovalIndicators:
"""'APPROVAL PENDING' badge must appear when _pending_ask_dialog is True.""" """'APPROVAL PENDING' badge must appear when _pending_ask_dialog is True."""
app = _make_app(_pending_ask_dialog=True) app = _make_app(_pending_ask_dialog=True)
imgui_mock = _make_imgui_mock() imgui_mock = _make_imgui_mock()
with patch("gui_2.imgui", imgui_mock), patch("gui_2.math") as mock_math: with patch("src.gui_2.imgui", imgui_mock), patch("src.gui_2.math") as mock_math:
mock_math.sin.return_value = 0.8 mock_math.sin.return_value = 0.8
App._render_mma_dashboard(app) App._render_mma_dashboard(app)
combined = _collect_text_colored_args(imgui_mock) combined = _collect_text_colored_args(imgui_mock)

View File

@@ -8,11 +8,11 @@ from src.gui_2 import App
def app_instance() -> Any: def app_instance() -> Any:
with ( with (
patch("src.models.load_config", return_value={"ai": {}, "projects": {}}), patch("src.models.load_config", return_value={"ai": {}, "projects": {}}),
patch("gui_2.save_config"), patch("src.gui_2.save_config"),
patch("gui_2.project_manager"), patch("src.gui_2.project_manager"),
patch("app_controller.project_manager") as mock_pm, patch("src.app_controller.project_manager") as mock_pm,
patch("gui_2.session_logger"), patch("src.gui_2.session_logger"),
patch("gui_2.immapp.run"), patch("src.gui_2.immapp.run"),
patch("src.app_controller.AppController._load_active_project"), patch("src.app_controller.AppController._load_active_project"),
patch("src.app_controller.AppController._fetch_models"), patch("src.app_controller.AppController._fetch_models"),
patch.object(App, "_load_fonts"), patch.object(App, "_load_fonts"),

View File

@@ -56,7 +56,7 @@ class TestMMADashboardStreams:
app = _make_app(mma_streams={"Tier 1": "hello"}) app = _make_app(mma_streams={"Tier 1": "hello"})
imgui_mock = _make_imgui_mock() imgui_mock = _make_imgui_mock()
imgui_mock.begin_child.return_value = True imgui_mock.begin_child.return_value = True
with patch("gui_2.imgui", imgui_mock): with patch("src.gui_2.imgui", imgui_mock):
App._render_tier_stream_panel(app, "Tier 1", "Tier 1") App._render_tier_stream_panel(app, "Tier 1", "Tier 1")
text_wrapped_args = " ".join(str(c) for c in imgui_mock.text_wrapped.call_args_list) text_wrapped_args = " ".join(str(c) for c in imgui_mock.text_wrapped.call_args_list)
assert "hello" in text_wrapped_args, "text_wrapped not called with stream content 'hello'" assert "hello" in text_wrapped_args, "text_wrapped not called with stream content 'hello'"
@@ -69,7 +69,7 @@ class TestMMADashboardStreams:
}) })
imgui_mock = _make_imgui_mock() imgui_mock = _make_imgui_mock()
imgui_mock.begin_child.return_value = True imgui_mock.begin_child.return_value = True
with patch("gui_2.imgui", imgui_mock): with patch("src.gui_2.imgui", imgui_mock):
App._render_tier_stream_panel(app, "Tier 3", None) App._render_tier_stream_panel(app, "Tier 3", None)
text_args = " ".join(str(c) for c in imgui_mock.text.call_args_list) text_args = " ".join(str(c) for c in imgui_mock.text.call_args_list)
assert "T-001" in text_args, "imgui.text not called with 'T-001' worker sub-header" assert "T-001" in text_args, "imgui.text not called with 'T-001' worker sub-header"

View File

@@ -7,8 +7,8 @@ from src import mma_prompts
class TestOrchestratorPM(unittest.TestCase): class TestOrchestratorPM(unittest.TestCase):
@patch('summarize.build_summary_markdown') @patch('src.summarize.build_summary_markdown')
@patch('ai_client.send') @patch('src.ai_client.send')
def test_generate_tracks_success(self, mock_send: Any, mock_summarize: Any) -> None: def test_generate_tracks_success(self, mock_send: Any, mock_summarize: Any) -> None:
# Setup mocks # Setup mocks
mock_summarize.return_value = "REPO_MAP_CONTENT" mock_summarize.return_value = "REPO_MAP_CONTENT"
@@ -43,8 +43,8 @@ class TestOrchestratorPM(unittest.TestCase):
# Verify result # Verify result
self.assertEqual(result[0]['id'], mock_response_data[0]['id']) self.assertEqual(result[0]['id'], mock_response_data[0]['id'])
@patch('summarize.build_summary_markdown') @patch('src.summarize.build_summary_markdown')
@patch('ai_client.send') @patch('src.ai_client.send')
def test_generate_tracks_markdown_wrapped(self, mock_send: Any, mock_summarize: Any) -> None: def test_generate_tracks_markdown_wrapped(self, mock_send: Any, mock_summarize: Any) -> None:
mock_summarize.return_value = "REPO_MAP" mock_summarize.return_value = "REPO_MAP"
mock_response_data = [{"id": "track_1"}] mock_response_data = [{"id": "track_1"}]
@@ -58,8 +58,8 @@ class TestOrchestratorPM(unittest.TestCase):
result = orchestrator_pm.generate_tracks("req", {}, []) result = orchestrator_pm.generate_tracks("req", {}, [])
self.assertEqual(result, expected_result) self.assertEqual(result, expected_result)
@patch('summarize.build_summary_markdown') @patch('src.summarize.build_summary_markdown')
@patch('ai_client.send') @patch('src.ai_client.send')
def test_generate_tracks_malformed_json(self, mock_send: Any, mock_summarize: Any) -> None: def test_generate_tracks_malformed_json(self, mock_send: Any, mock_summarize: Any) -> None:
mock_summarize.return_value = "REPO_MAP" mock_summarize.return_value = "REPO_MAP"
mock_send.return_value = "NOT A JSON" mock_send.return_value = "NOT A JSON"

View File

@@ -28,7 +28,7 @@ class TestOrchestratorPMHistory(unittest.TestCase):
with open(track_path / "spec.md", "w") as f: with open(track_path / "spec.md", "w") as f:
f.write(spec_content) f.write(spec_content)
@patch('orchestrator_pm.CONDUCTOR_PATH', Path("test_conductor")) @patch('src.orchestrator_pm.CONDUCTOR_PATH', Path("test_conductor"))
def test_get_track_history_summary(self) -> None: def test_get_track_history_summary(self) -> None:
self.create_track(self.archive_dir, "track_001", "Initial Setup", "completed", "Setting up the project structure.") self.create_track(self.archive_dir, "track_001", "Initial Setup", "completed", "Setting up the project structure.")
self.create_track(self.tracks_dir, "track_002", "Feature A", "in_progress", "Implementing Feature A.") self.create_track(self.tracks_dir, "track_002", "Feature A", "in_progress", "Implementing Feature A.")
@@ -40,7 +40,7 @@ class TestOrchestratorPMHistory(unittest.TestCase):
self.assertIn("in_progress", summary) self.assertIn("in_progress", summary)
self.assertIn("Implementing Feature A.", summary) self.assertIn("Implementing Feature A.", summary)
@patch('orchestrator_pm.CONDUCTOR_PATH', Path("test_conductor")) @patch('src.orchestrator_pm.CONDUCTOR_PATH', Path("test_conductor"))
def test_get_track_history_summary_missing_files(self) -> None: def test_get_track_history_summary_missing_files(self) -> None:
track_path = self.tracks_dir / "track_003" track_path = self.tracks_dir / "track_003"
track_path.mkdir(exist_ok=True) track_path.mkdir(exist_ok=True)
@@ -51,8 +51,8 @@ class TestOrchestratorPMHistory(unittest.TestCase):
self.assertIn("pending", summary) self.assertIn("pending", summary)
self.assertIn("No overview available", summary) self.assertIn("No overview available", summary)
@patch('orchestrator_pm.summarize.build_summary_markdown') @patch('src.orchestrator_pm.summarize.build_summary_markdown')
@patch('ai_client.send') @patch('src.ai_client.send')
def test_generate_tracks_with_history(self, mock_send: MagicMock, mock_summarize: MagicMock) -> None: def test_generate_tracks_with_history(self, mock_send: MagicMock, mock_summarize: MagicMock) -> None:
mock_summarize.return_value = "REPO_MAP" mock_summarize.return_value = "REPO_MAP"
mock_send.return_value = "[]" mock_send.return_value = "[]"