254 lines
9.1 KiB
Python
254 lines
9.1 KiB
Python
"""
|
|
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 == {}
|