"""Tests for LearningOrchestrator opt-in skill optimization (Plan 2A).""" from __future__ import annotations from pathlib import Path from unittest.mock import MagicMock, patch class TestOrchestratorSkillAutoOptimize: def test_auto_optimize_disabled_by_default_does_not_call_skill_optimizer( self, tmp_path: Path ) -> None: from openjarvis.learning.learning_orchestrator import ( LearningOrchestrator, ) store = MagicMock() store.list_traces.return_value = [] orchestrator = LearningOrchestrator( trace_store=store, config_dir=tmp_path, ) with patch( "openjarvis.learning.agents.skill_optimizer.SkillOptimizer.optimize" ) as mock_optimize: orchestrator._maybe_optimize_skills(auto_optimize=False) mock_optimize.assert_not_called() def test_auto_optimize_enabled_calls_skill_optimizer(self, tmp_path: Path) -> None: from openjarvis.learning.learning_orchestrator import ( LearningOrchestrator, ) store = MagicMock() store.list_traces.return_value = [] orchestrator = LearningOrchestrator( trace_store=store, config_dir=tmp_path, ) with patch( "openjarvis.learning.agents.skill_optimizer.SkillOptimizer.optimize", return_value={}, ) as mock_optimize: orchestrator._maybe_optimize_skills( auto_optimize=True, optimizer="dspy", min_traces_per_skill=5, ) mock_optimize.assert_called_once() class TestOrchestratorRunSkillTrigger: """End-to-end: LearningOrchestrator.run() invokes _maybe_optimize_skills when learning.skills.auto_optimize is true (Plan 2A C2 fix).""" def _make_store(self) -> MagicMock: store = MagicMock() # Just enough surface area for orchestrator.run() to short-circuit store.list_traces.return_value = [] return store def _make_config(self, *, auto_optimize: bool): from openjarvis.core.config import ( JarvisConfig, LearningConfig, SkillsLearningConfig, ) cfg = JarvisConfig() cfg.learning = LearningConfig() cfg.learning.skills = SkillsLearningConfig( auto_optimize=auto_optimize, optimizer="dspy", min_traces_per_skill=5, ) return cfg def test_run_does_not_call_skill_optimizer_when_disabled( self, tmp_path: Path ) -> None: from openjarvis.learning.learning_orchestrator import ( LearningOrchestrator, ) store = self._make_store() orchestrator = LearningOrchestrator( trace_store=store, config_dir=tmp_path, ) with patch( "openjarvis.core.config.load_config", return_value=self._make_config(auto_optimize=False), ): with patch( "openjarvis.learning.agents.skill_optimizer.SkillOptimizer.optimize" ) as mock_optimize: orchestrator.run() mock_optimize.assert_not_called() def test_run_calls_skill_optimizer_when_enabled( self, tmp_path: Path ) -> None: from openjarvis.learning.agents.skill_optimizer import ( SkillOptimizationResult, ) from openjarvis.learning.learning_orchestrator import ( LearningOrchestrator, ) store = self._make_store() orchestrator = LearningOrchestrator( trace_store=store, config_dir=tmp_path, ) fake_results = { "research-skill": SkillOptimizationResult( skill_name="research-skill", status="optimized", trace_count=10, ), } with patch( "openjarvis.core.config.load_config", return_value=self._make_config(auto_optimize=True), ): with patch( "openjarvis.learning.agents.skill_optimizer.SkillOptimizer.optimize", return_value=fake_results, ) as mock_optimize: result = orchestrator.run() mock_optimize.assert_called_once() # The orchestrator should record the skill optimization # results in the returned dict assert "skill_optimization" in result assert "research-skill" in result["skill_optimization"] assert ( result["skill_optimization"]["research-skill"]["status"] == "optimized" )