""" Unit tests for Routing Configuration. [AC-AISVC-RES-01~15] Tests for strategy routing configuration models. """ import pytest from app.services.retrieval.routing_config import ( RagRuntimeMode, StrategyType, RoutingConfig, StrategyContext, StrategyResult, ) class TestStrategyType: """[AC-AISVC-RES-01,02] Tests for StrategyType enum.""" def test_strategy_type_default(self): """[AC-AISVC-RES-01] Default strategy type should exist.""" assert StrategyType.DEFAULT.value == "default" def test_strategy_type_enhanced(self): """[AC-AISVC-RES-02] Enhanced strategy type should exist.""" assert StrategyType.ENHANCED.value == "enhanced" def test_strategy_type_from_string(self): """Should create StrategyType from string.""" assert StrategyType("default") == StrategyType.DEFAULT assert StrategyType("enhanced") == StrategyType.ENHANCED class TestRagRuntimeMode: """[AC-AISVC-RES-09,10,11] Tests for RagRuntimeMode enum.""" def test_mode_direct(self): """[AC-AISVC-RES-09] Direct mode should exist.""" assert RagRuntimeMode.DIRECT.value == "direct" def test_mode_react(self): """[AC-AISVC-RES-10] React mode should exist.""" assert RagRuntimeMode.REACT.value == "react" def test_mode_auto(self): """[AC-AISVC-RES-11] Auto mode should exist.""" assert RagRuntimeMode.AUTO.value == "auto" def test_mode_from_string(self): """Should create RagRuntimeMode from string.""" assert RagRuntimeMode("direct") == RagRuntimeMode.DIRECT assert RagRuntimeMode("react") == RagRuntimeMode.REACT assert RagRuntimeMode("auto") == RagRuntimeMode.AUTO class TestRoutingConfig: """[AC-AISVC-RES-01~15] Tests for RoutingConfig.""" def test_default_config(self): """[AC-AISVC-RES-01] Default config should use default strategy and auto mode.""" config = RoutingConfig() assert config.enabled is True assert config.strategy == StrategyType.DEFAULT assert config.rag_runtime_mode == RagRuntimeMode.AUTO assert config.grayscale_percentage == 0.0 assert config.grayscale_allowlist == [] def test_config_with_custom_values(self): """[AC-AISVC-RES-02,03] Config should accept custom values.""" config = RoutingConfig( enabled=False, strategy=StrategyType.ENHANCED, rag_runtime_mode=RagRuntimeMode.REACT, grayscale_percentage=0.3, grayscale_allowlist=["tenant_a", "tenant_b"], ) assert config.enabled is False assert config.strategy == StrategyType.ENHANCED assert config.rag_runtime_mode == RagRuntimeMode.REACT assert config.grayscale_percentage == 0.3 assert "tenant_a" in config.grayscale_allowlist def test_should_use_enhanced_strategy_default(self): """[AC-AISVC-RES-01] Default strategy should not use enhanced.""" config = RoutingConfig() assert config.should_use_enhanced_strategy("tenant_a") is False def test_should_use_enhanced_strategy_explicit_enhanced(self): """[AC-AISVC-RES-02] Explicit enhanced strategy should use enhanced for all tenants.""" config = RoutingConfig(strategy=StrategyType.ENHANCED) assert config.should_use_enhanced_strategy("tenant_a") is True assert config.should_use_enhanced_strategy("tenant_b") is True assert config.should_use_enhanced_strategy(None) is True def test_should_fallback_direct_to_react_disabled(self): """[AC-AISVC-RES-14] Fallback should be disabled when configured.""" config = RoutingConfig(direct_fallback_on_low_confidence=False) assert config.should_fallback_direct_to_react(0.1) is False assert config.should_fallback_direct_to_react(0.0) is False def test_should_fallback_direct_to_react_enabled(self): """[AC-AISVC-RES-14] Fallback should trigger on low confidence.""" config = RoutingConfig( direct_fallback_on_low_confidence=True, direct_fallback_confidence_threshold=0.4, ) assert config.should_fallback_direct_to_react(0.3) is True assert config.should_fallback_direct_to_react(0.4) is False assert config.should_fallback_direct_to_react(0.5) is False def test_should_trigger_react_in_auto_mode_low_confidence(self): """[AC-AISVC-RES-12] Low confidence should trigger react in auto mode.""" config = RoutingConfig( react_trigger_confidence_threshold=0.6, ) assert config.should_trigger_react_in_auto_mode( confidence=0.5, complexity_score=0.3 ) is True assert config.should_trigger_react_in_auto_mode( confidence=0.7, complexity_score=0.3 ) is False def test_should_trigger_react_in_auto_mode_high_complexity(self): """[AC-AISVC-RES-13] High complexity should trigger react in auto mode.""" config = RoutingConfig( react_trigger_complexity_score=0.5, ) assert config.should_trigger_react_in_auto_mode( confidence=0.8, complexity_score=0.6 ) is True assert config.should_trigger_react_in_auto_mode( confidence=0.8, complexity_score=0.4 ) is False def test_validate_valid_config(self): """[AC-AISVC-RES-06] Valid config should pass validation.""" config = RoutingConfig() is_valid, errors = config.validate() assert is_valid is True assert len(errors) == 0 def test_validate_invalid_grayscale_percentage(self): """[AC-AISVC-RES-06] Invalid grayscale percentage should fail validation.""" config = RoutingConfig(grayscale_percentage=1.5) is_valid, errors = config.validate() assert is_valid is False assert any("grayscale_percentage" in e for e in errors) def test_validate_invalid_confidence_threshold(self): """[AC-AISVC-RES-06] Invalid confidence threshold should fail validation.""" config = RoutingConfig(react_trigger_confidence_threshold=1.5) is_valid, errors = config.validate() assert is_valid is False assert any("react_trigger_confidence_threshold" in e for e in errors) def test_validate_invalid_react_max_steps(self): """[AC-AISVC-RES-06] Invalid react max steps should fail validation.""" config = RoutingConfig(react_max_steps=2) is_valid, errors = config.validate() assert is_valid is False assert any("react_max_steps" in e for e in errors) def test_validate_invalid_performance_budget(self): """[AC-AISVC-RES-06] Invalid performance budget should fail validation.""" config = RoutingConfig(performance_budget_ms=500) is_valid, errors = config.validate() assert is_valid is False assert any("performance_budget_ms" in e for e in errors) class TestStrategyContext: """[AC-AISVC-RES-01~15] Tests for StrategyContext.""" def test_context_creation(self): """Should create context with all fields.""" ctx = StrategyContext( tenant_id="tenant_a", query="Test query", metadata_filter={"category": "product"}, metadata_confidence=0.8, complexity_score=0.3, kb_ids=["kb_1", "kb_2"], top_k=10, ) assert ctx.tenant_id == "tenant_a" assert ctx.query == "Test query" assert ctx.metadata_filter == {"category": "product"} assert ctx.metadata_confidence == 0.8 assert ctx.complexity_score == 0.3 assert ctx.kb_ids == ["kb_1", "kb_2"] assert ctx.top_k == 10 def test_context_minimal(self): """Should create context with minimal fields.""" ctx = StrategyContext(tenant_id="tenant_a", query="Test query") assert ctx.tenant_id == "tenant_a" assert ctx.query == "Test query" assert ctx.metadata_filter is None assert ctx.metadata_confidence == 1.0 assert ctx.complexity_score == 0.0 assert ctx.kb_ids is None assert ctx.top_k == 5 class TestStrategyResult: """[AC-AISVC-RES-01,02] Tests for StrategyResult.""" def test_result_creation(self): """Should create result with all fields.""" result = StrategyResult( strategy=StrategyType.ENHANCED, mode=RagRuntimeMode.REACT, should_fallback=True, fallback_reason="Low confidence", diagnostics={"key": "value"}, ) assert result.strategy == StrategyType.ENHANCED assert result.mode == RagRuntimeMode.REACT assert result.should_fallback is True assert result.fallback_reason == "Low confidence" assert result.diagnostics == {"key": "value"} def test_result_defaults(self): """Should create result with default values.""" result = StrategyResult( strategy=StrategyType.DEFAULT, mode=RagRuntimeMode.AUTO, ) assert result.should_fallback is False assert result.fallback_reason is None assert result.diagnostics == {}