scripts-test-provider-detector
""" Tests for Provider Detector (ADR-073).
Tests the provider detection, mode selection, and model routing for single-provider, dual-provider, and multi-provider modes. """
import os import unittest from pathlib import Path from unittest.mock import patch, MagicMock
from core.provider_detector import ( ProviderDetector, ProviderMode, Provider, ModelTier, ProviderDetectionResult, detect_provider_mode, get_diversity_report, check_provider_health, reset_default_detector, )
class TestProviderDetection(unittest.TestCase): """Test provider detection from environment variables."""
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_single_provider_detection(self):
"""Test detection with single provider available."""
detector = ProviderDetector()
result = detector.detect_mode()
self.assertEqual(result.mode, ProviderMode.SINGLE)
self.assertEqual(len(result.available_providers), 1)
self.assertIn(Provider.ANTHROPIC, result.available_providers)
self.assertEqual(result.confidence_adjustment, -0.10)
self.assertEqual(result.diversity_strategy, "model_tier_diversity")
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
"OPENAI_API_KEY": "sk-openai-test",
}, clear=True)
def test_dual_provider_detection(self):
"""Test detection with two providers available."""
detector = ProviderDetector()
result = detector.detect_mode()
self.assertEqual(result.mode, ProviderMode.DUAL)
self.assertEqual(len(result.available_providers), 2)
self.assertIn(Provider.ANTHROPIC, result.available_providers)
self.assertIn(Provider.OPENAI, result.available_providers)
self.assertEqual(result.confidence_adjustment, -0.05)
self.assertEqual(result.diversity_strategy, "provider_alternation")
@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_detection(self):
"""Test detection with three+ providers available."""
detector = ProviderDetector()
result = detector.detect_mode()
self.assertEqual(result.mode, ProviderMode.MULTI)
self.assertEqual(len(result.available_providers), 3)
self.assertEqual(result.confidence_adjustment, 0.0)
self.assertEqual(result.diversity_strategy, "full_diversity")
@patch.dict(os.environ, {}, clear=True)
def test_no_providers_available(self):
"""Test detection with no providers available."""
detector = ProviderDetector()
result = detector.detect_mode()
self.assertEqual(result.mode, ProviderMode.SINGLE)
self.assertEqual(len(result.available_providers), 0)
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
"CODITECT_PROVIDER_MODE": "multi",
}, clear=True)
def test_environment_mode_override(self):
"""Test environment variable mode override."""
detector = ProviderDetector()
result = detector.detect_mode()
# Mode is overridden to multi even though only 1 provider
self.assertEqual(result.mode, ProviderMode.MULTI)
self.assertTrue(result.override_active)
self.assertEqual(result.override_source, "CODITECT_PROVIDER_MODE")
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_force_mode_parameter(self):
"""Test force_mode parameter override."""
detector = ProviderDetector()
result = detector.detect_mode(force_mode=ProviderMode.DUAL)
self.assertEqual(result.mode, ProviderMode.DUAL)
self.assertTrue(result.override_active)
self.assertEqual(result.override_source, "parameter")
class TestModelSelection(unittest.TestCase): """Test model selection for personas."""
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_model_selection(self):
"""Test model selection in single-provider mode."""
detector = ProviderDetector()
detector.detect_mode()
# High-stakes persona should get flagship model
model = detector.get_model_for_persona("ai_risk_officer")
self.assertEqual(model, "claude-opus-4-5")
# Balanced persona should get balanced model
model = detector.get_model_for_persona("technical_architect")
self.assertEqual(model, "claude-sonnet-4-5")
# Fast persona should get fast model
model = detector.get_model_for_persona("qa_evaluator")
self.assertEqual(model, "claude-haiku-4-5")
@patch.dict(os.environ, {
"OPENAI_API_KEY": "sk-openai-test",
}, clear=True)
def test_single_provider_openai(self):
"""Test single-provider mode with OpenAI."""
detector = ProviderDetector()
detector.detect_mode()
model = detector.get_model_for_persona("ai_risk_officer")
self.assertEqual(model, "o3")
model = detector.get_model_for_persona("qa_evaluator")
self.assertEqual(model, "gpt-4.1-mini")
@patch.dict(os.environ, {
"DEEPSEEK_API_KEY": "sk-deepseek-test",
}, clear=True)
def test_single_provider_deepseek(self):
"""Test single-provider mode with DeepSeek."""
detector = ProviderDetector()
detector.detect_mode()
model = detector.get_model_for_persona("ai_risk_officer")
self.assertEqual(model, "deepseek-reasoner")
model = detector.get_model_for_persona("qa_evaluator")
self.assertEqual(model, "deepseek-chat")
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
"CODITECT_JUDGE_MODEL_TECHNICAL_ARCHITECT": "claude-opus-4-5",
}, clear=True)
def test_environment_model_override(self):
"""Test environment variable model override."""
detector = ProviderDetector()
detector.detect_mode()
model = detector.get_model_for_persona("technical_architect")
self.assertEqual(model, "claude-opus-4-5")
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_explicit_model_override(self):
"""Test explicit model override parameter."""
detector = ProviderDetector()
detector.detect_mode()
model = detector.get_model_for_persona(
"technical_architect",
override_model="custom-model"
)
self.assertEqual(model, "custom-model")
class TestConfidenceAdjustment(unittest.TestCase): """Test confidence adjustment based on provider mode."""
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(self):
"""Test -10% adjustment in single-provider mode."""
detector = ProviderDetector()
detector.detect_mode()
adjusted = detector.adjust_confidence(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(self):
"""Test -5% adjustment in dual-provider mode."""
detector = ProviderDetector()
detector.detect_mode()
adjusted = detector.adjust_confidence(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_confidence(self):
"""Test no adjustment in multi-provider mode."""
detector = ProviderDetector()
detector.detect_mode()
adjusted = detector.adjust_confidence(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 confidence is clamped to valid range."""
detector = ProviderDetector()
detector.detect_mode()
# Low confidence with adjustment shouldn't go below 0
adjusted = detector.adjust_confidence(0.05)
self.assertGreaterEqual(adjusted, 0.0)
# High confidence shouldn't go above 1
adjusted = detector.adjust_confidence(1.0)
self.assertLessEqual(adjusted, 1.0)
class TestDiversityReport(unittest.TestCase): """Test diversity reporting."""
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_diversity_report_dual(self):
"""Test diversity report with dual providers."""
detector = ProviderDetector()
detector.detect_mode()
report = detector.get_diversity_report()
self.assertEqual(report["mode"], "dual")
self.assertEqual(report["provider_count"], 2)
self.assertIn("anthropic", report["providers"])
self.assertIn("openai", report["providers"])
self.assertFalse(report["full_diversity_achieved"])
self.assertGreater(len(report["recommendations"]), 0)
@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_diversity_report_multi(self):
"""Test diversity report with multi providers."""
detector = ProviderDetector()
detector.detect_mode()
report = detector.get_diversity_report()
self.assertEqual(report["mode"], "multi")
self.assertTrue(report["full_diversity_achieved"])
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_recommended_pairs(self):
"""Test recommended pairs in diversity report."""
detector = ProviderDetector()
detector.detect_mode()
report = detector.get_diversity_report()
# Single provider should have recommendations
self.assertGreater(len(report["recommendations"]), 0)
class TestProviderHealth(unittest.TestCase): """Test provider health checks."""
def setUp(self):
reset_default_detector()
def tearDown(self):
reset_default_detector()
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test-key-12345",
"OPENAI_API_KEY": "",
}, clear=True)
def test_health_check(self):
"""Test provider health validation."""
detector = ProviderDetector()
health = detector.validate_provider_health()
# Anthropic should be configured and present
self.assertTrue(health["anthropic"]["configured"])
self.assertTrue(health["anthropic"]["api_key_present"])
self.assertGreater(health["anthropic"]["api_key_length"], 0)
# OpenAI has empty key
self.assertTrue(health["openai"]["configured"])
self.assertFalse(health["openai"]["api_key_present"])
# DeepSeek not configured
self.assertFalse(health["deepseek"]["configured"])
class TestConvenienceFunctions(unittest.TestCase): """Test module-level convenience functions."""
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_detect_provider_mode_function(self):
"""Test convenience function for mode detection."""
result = detect_provider_mode()
self.assertEqual(result.mode, ProviderMode.DUAL)
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_get_diversity_report_function(self):
"""Test convenience function for diversity report."""
report = get_diversity_report()
self.assertIn("mode", report)
self.assertIn("provider_count", report)
@patch.dict(os.environ, {
"ANTHROPIC_API_KEY": "sk-ant-test",
}, clear=True)
def test_check_provider_health_function(self):
"""Test convenience function for health check."""
health = check_provider_health()
self.assertIn("anthropic", health)
self.assertIn("openai", health)
class TestProviderDetectionResult(unittest.TestCase): """Test ProviderDetectionResult dataclass."""
def test_to_dict(self):
"""Test serialization to dictionary."""
result = ProviderDetectionResult(
mode=ProviderMode.DUAL,
available_providers=[Provider.ANTHROPIC, Provider.OPENAI],
provider_count=2,
confidence_adjustment=-0.05,
diversity_strategy="provider_alternation"
)
d = result.to_dict()
self.assertEqual(d["mode"], "dual")
self.assertEqual(d["provider_count"], 2)
self.assertIn("anthropic", d["available_providers"])
self.assertIn("openai", d["available_providers"])
if name == "main": unittest.main()