""" Tests for MoEOrchestrator and ProviderDetector Integration (ADR-073).
Tests the integration between MoEOrchestrator and ProviderDetector for provider-aware confidence adjustment and statistics tracking. """
import os import unittest from dataclasses import dataclass from pathlib import Path from typing import List, Optional from unittest.mock import patch, MagicMock
from core.orchestrator import ( MoEOrchestrator, OrchestratorConfig, OrchestratorStats, ) from core.models import ( Document, DocumentType, AnalystVote, JudgeDecision, ConsensusResult, ApprovalType, ) from core.provider_detector import ( ProviderMode, Provider, reset_default_detector, )
Mock analyst for testing
@dataclass class MockAnalyst: """Mock analyst for testing.""" name: str = "mock_analyst" confidence: float = 0.85
def analyze(self, document: Document) -> AnalystVote:
return AnalystVote(
analyst=self.name,
classification="guide",
confidence=self.confidence,
reasoning="Test reasoning",
duration_ms=10
)
Mock judge for testing
@dataclass class MockJudge: """Mock judge for testing.""" name: str = "mock_judge" approved: bool = True
def evaluate(self, document: Document, votes: List[AnalystVote]) -> JudgeDecision:
return JudgeDecision(
judge=self.name,
approved=self.approved,
reason="Test evaluation",
confidence=0.90,
metadata={'duration_ms': 5}
)
class TestOrchestratorProviderDetection(unittest.TestCase): """Test MoEOrchestrator provider detection integration."""
def setUp(self):
"""Reset detector before each test."""
reset_default_detector()
def tearDown(self):
"""Clean up after each test."""
reset_default_detector()
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_orchestrator_detects_single_provider(self):
"""Test that orchestrator detects single provider mode."""
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst()],
judges=[MockJudge()],
)
self.assertEqual(orchestrator.provider_mode, ProviderMode.SINGLE)
self.assertEqual(orchestrator.stats.provider_mode, "single")
self.assertEqual(orchestrator.stats.provider_count, 1)
self.assertIn("anthropic", orchestrator.stats.available_providers)
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
"OPENAI_API_KEY": "sk-openai-test",
}, clear=True)
def test_orchestrator_detects_dual_provider(self):
"""Test that orchestrator detects dual provider mode."""
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst()],
judges=[MockJudge()],
)
self.assertEqual(orchestrator.provider_mode, ProviderMode.DUAL)
self.assertEqual(orchestrator.stats.provider_mode, "dual")
self.assertEqual(orchestrator.stats.provider_count, 2)
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
"OPENAI_API_KEY": "sk-openai-test",
"DEEPSEEK_API_KEY": "sk-deepseek-test",
}, clear=True)
def test_orchestrator_detects_multi_provider(self):
"""Test that orchestrator detects multi provider mode."""
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst()],
judges=[MockJudge()],
)
self.assertEqual(orchestrator.provider_mode, ProviderMode.MULTI)
self.assertEqual(orchestrator.stats.provider_mode, "multi")
self.assertEqual(orchestrator.stats.provider_count, 3)
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_provider_detection_disabled(self):
"""Test that provider detection can be disabled."""
config = OrchestratorConfig(enable_provider_detection=False)
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst()],
judges=[MockJudge()],
config=config,
)
self.assertEqual(orchestrator.stats.provider_mode, "unknown")
self.assertEqual(orchestrator.stats.provider_count, 0)
class TestOrchestratorConfidenceAdjustment(unittest.TestCase): """Test MoEOrchestrator confidence adjustment."""
def setUp(self):
reset_default_detector()
def tearDown(self):
reset_default_detector()
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_single_provider_confidence_adjustment(self):
"""Test -10% confidence adjustment in single provider mode."""
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst(confidence=0.90)],
judges=[MockJudge()],
)
# Verify adjustment is -0.10 for single provider
self.assertAlmostEqual(
orchestrator.stats.confidence_adjustment,
-0.10,
places=2
)
# Test internal adjustment method
adjusted = orchestrator._apply_confidence_adjustment(0.90)
self.assertAlmostEqual(adjusted, 0.80, places=2)
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
"OPENAI_API_KEY": "sk-openai-test",
}, clear=True)
def test_dual_provider_confidence_adjustment(self):
"""Test -5% confidence adjustment in dual provider mode."""
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst(confidence=0.90)],
judges=[MockJudge()],
)
self.assertAlmostEqual(
orchestrator.stats.confidence_adjustment,
-0.05,
places=2
)
adjusted = orchestrator._apply_confidence_adjustment(0.90)
self.assertAlmostEqual(adjusted, 0.85, places=2)
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
"OPENAI_API_KEY": "sk-openai-test",
"DEEPSEEK_API_KEY": "sk-deepseek-test",
}, clear=True)
def test_multi_provider_no_adjustment(self):
"""Test no confidence adjustment in multi provider mode."""
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst(confidence=0.90)],
judges=[MockJudge()],
)
self.assertAlmostEqual(
orchestrator.stats.confidence_adjustment,
0.0,
places=2
)
adjusted = orchestrator._apply_confidence_adjustment(0.90)
self.assertAlmostEqual(adjusted, 0.90, places=2)
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_confidence_adjustment_disabled(self):
"""Test that confidence adjustment can be disabled."""
config = OrchestratorConfig(apply_confidence_adjustment=False)
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst()],
judges=[MockJudge()],
config=config,
)
# Adjustment should not be applied
adjusted = orchestrator._apply_confidence_adjustment(0.90)
self.assertAlmostEqual(adjusted, 0.90, places=2)
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_confidence_clamping(self):
"""Test that confidence is clamped to valid range."""
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst()],
judges=[MockJudge()],
)
# Low confidence shouldn't go below 0
adjusted = orchestrator._apply_confidence_adjustment(0.05)
self.assertGreaterEqual(adjusted, 0.0)
# High confidence shouldn't go above 1
adjusted = orchestrator._apply_confidence_adjustment(1.0)
self.assertLessEqual(adjusted, 1.0)
class TestOrchestratorStats(unittest.TestCase): """Test MoEOrchestrator statistics with provider info."""
def setUp(self):
reset_default_detector()
def tearDown(self):
reset_default_detector()
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
"OPENAI_API_KEY": "sk-openai-test",
}, clear=True)
def test_stats_include_provider_info(self):
"""Test that get_stats includes provider information."""
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst()],
judges=[MockJudge()],
)
stats = orchestrator.get_stats()
self.assertIn('provider_mode', stats)
self.assertIn('provider_count', stats)
self.assertIn('available_providers', stats)
self.assertIn('diversity_strategy', stats)
self.assertIn('confidence_adjustment', stats)
self.assertIn('total_confidence_adjustments', stats)
self.assertIn('avg_raw_confidence', stats)
self.assertIn('avg_adjusted_confidence', stats)
self.assertEqual(stats['provider_mode'], 'dual')
self.assertEqual(stats['provider_count'], 2)
self.assertEqual(stats['diversity_strategy'], 'provider_alternation')
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_provider_info_property(self):
"""Test provider_info property."""
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst()],
judges=[MockJudge()],
)
info = orchestrator.provider_info
self.assertIn('mode', info)
self.assertIn('provider_count', info)
self.assertEqual(info['mode'], 'single')
class TestOrchestratorForceMode(unittest.TestCase): """Test MoEOrchestrator force mode override."""
def setUp(self):
reset_default_detector()
def tearDown(self):
reset_default_detector()
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_force_mode_override(self):
"""Test that force_provider_mode overrides detection."""
config = OrchestratorConfig(force_provider_mode=ProviderMode.MULTI)
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst()],
judges=[MockJudge()],
config=config,
)
# Mode should be forced to MULTI despite only having 1 provider
self.assertEqual(orchestrator.provider_mode, ProviderMode.MULTI)
self.assertEqual(orchestrator.stats.provider_mode, "multi")
class TestOrchestratorRefreshDetection(unittest.TestCase): """Test MoEOrchestrator refresh provider detection."""
def setUp(self):
reset_default_detector()
def tearDown(self):
reset_default_detector()
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_refresh_provider_detection(self):
"""Test refreshing provider detection."""
orchestrator = MoEOrchestrator(
analysts=[MockAnalyst()],
judges=[MockJudge()],
)
# Initially single provider
self.assertEqual(orchestrator.provider_mode, ProviderMode.SINGLE)
# Simulate adding a new provider
with patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
"OPENAI_API_KEY": "sk-openai-test",
}, clear=True):
result = orchestrator.refresh_provider_detection()
# Should now detect dual provider
self.assertEqual(orchestrator.provider_mode, ProviderMode.DUAL)
self.assertEqual(result.mode, ProviderMode.DUAL)
class TestConsensusResultProviderFields(unittest.TestCase): """Test ConsensusResult provider adjustment fields."""
def test_consensus_result_has_provider_fields(self):
"""Test that ConsensusResult has provider adjustment fields."""
result = ConsensusResult(
classification="guide",
confidence=0.85,
agreement_ratio=0.90,
approval_type=ApprovalType.JUDGE_APPROVED,
provider_adjustment_applied=True,
raw_confidence=0.95,
provider_mode="single"
)
self.assertTrue(result.provider_adjustment_applied)
self.assertEqual(result.raw_confidence, 0.95)
self.assertEqual(result.provider_mode, "single")
def test_consensus_result_default_values(self):
"""Test ConsensusResult default values for provider fields."""
result = ConsensusResult(
classification="guide",
confidence=0.85,
agreement_ratio=0.90,
approval_type=ApprovalType.AUTO_APPROVED,
)
self.assertFalse(result.provider_adjustment_applied)
self.assertIsNone(result.raw_confidence)
self.assertIsNone(result.provider_mode)
if name == "main": unittest.main()