- Cleaned up unnecessary comments in `__all__` definitions for better readability. - Adjusted indentation and formatting across modules for improved clarity (e.g., long lines, logical grouping). - Simplified conditional expressions and inline comments for context scoring and ranking. - Replaced some hard-coded values with type-safe annotations (e.g., `ClassVar`). - Removed unused imports and ensured consistent usage across test files. - Updated `test_score_not_cached_on_context` to clarify caching behavior. - Improved truncation strategy logic and marker handling.
251 lines
7.3 KiB
Python
251 lines
7.3 KiB
Python
"""Tests for context management exceptions."""
|
|
|
|
from app.services.context.exceptions import (
|
|
AssemblyTimeoutError,
|
|
BudgetExceededError,
|
|
CacheError,
|
|
CompressionError,
|
|
ContextError,
|
|
ContextNotFoundError,
|
|
FormattingError,
|
|
InvalidContextError,
|
|
ScoringError,
|
|
TokenCountError,
|
|
)
|
|
|
|
|
|
class TestContextError:
|
|
"""Tests for base ContextError."""
|
|
|
|
def test_basic_initialization(self) -> None:
|
|
"""Test basic error initialization."""
|
|
error = ContextError("Test error")
|
|
assert error.message == "Test error"
|
|
assert error.details == {}
|
|
assert str(error) == "Test error"
|
|
|
|
def test_with_details(self) -> None:
|
|
"""Test error with details."""
|
|
error = ContextError("Test error", {"key": "value", "count": 42})
|
|
assert error.details == {"key": "value", "count": 42}
|
|
|
|
def test_to_dict(self) -> None:
|
|
"""Test conversion to dictionary."""
|
|
error = ContextError("Test error", {"key": "value"})
|
|
result = error.to_dict()
|
|
|
|
assert result["error_type"] == "ContextError"
|
|
assert result["message"] == "Test error"
|
|
assert result["details"] == {"key": "value"}
|
|
|
|
def test_inheritance(self) -> None:
|
|
"""Test that ContextError inherits from Exception."""
|
|
error = ContextError("Test")
|
|
assert isinstance(error, Exception)
|
|
|
|
|
|
class TestBudgetExceededError:
|
|
"""Tests for BudgetExceededError."""
|
|
|
|
def test_default_message(self) -> None:
|
|
"""Test default error message."""
|
|
error = BudgetExceededError()
|
|
assert "exceeded" in error.message.lower()
|
|
|
|
def test_with_budget_info(self) -> None:
|
|
"""Test with budget information."""
|
|
error = BudgetExceededError(
|
|
allocated=1000,
|
|
requested=1500,
|
|
context_type="knowledge",
|
|
)
|
|
|
|
assert error.allocated == 1000
|
|
assert error.requested == 1500
|
|
assert error.context_type == "knowledge"
|
|
assert error.details["overage"] == 500
|
|
|
|
def test_to_dict_includes_budget_info(self) -> None:
|
|
"""Test that to_dict includes budget info."""
|
|
error = BudgetExceededError(
|
|
allocated=1000,
|
|
requested=1500,
|
|
)
|
|
result = error.to_dict()
|
|
|
|
assert result["details"]["allocated"] == 1000
|
|
assert result["details"]["requested"] == 1500
|
|
assert result["details"]["overage"] == 500
|
|
|
|
|
|
class TestTokenCountError:
|
|
"""Tests for TokenCountError."""
|
|
|
|
def test_basic_error(self) -> None:
|
|
"""Test basic token count error."""
|
|
error = TokenCountError()
|
|
assert "token" in error.message.lower()
|
|
|
|
def test_with_model_info(self) -> None:
|
|
"""Test with model information."""
|
|
error = TokenCountError(
|
|
message="Failed to count",
|
|
model="claude-3-sonnet",
|
|
text_length=5000,
|
|
)
|
|
|
|
assert error.model == "claude-3-sonnet"
|
|
assert error.text_length == 5000
|
|
assert error.details["model"] == "claude-3-sonnet"
|
|
|
|
|
|
class TestCompressionError:
|
|
"""Tests for CompressionError."""
|
|
|
|
def test_basic_error(self) -> None:
|
|
"""Test basic compression error."""
|
|
error = CompressionError()
|
|
assert "compress" in error.message.lower()
|
|
|
|
def test_with_token_info(self) -> None:
|
|
"""Test with token information."""
|
|
error = CompressionError(
|
|
original_tokens=2000,
|
|
target_tokens=1000,
|
|
achieved_tokens=1500,
|
|
)
|
|
|
|
assert error.original_tokens == 2000
|
|
assert error.target_tokens == 1000
|
|
assert error.achieved_tokens == 1500
|
|
|
|
|
|
class TestAssemblyTimeoutError:
|
|
"""Tests for AssemblyTimeoutError."""
|
|
|
|
def test_basic_error(self) -> None:
|
|
"""Test basic timeout error."""
|
|
error = AssemblyTimeoutError()
|
|
assert "timed out" in error.message.lower()
|
|
|
|
def test_with_timing_info(self) -> None:
|
|
"""Test with timing information."""
|
|
error = AssemblyTimeoutError(
|
|
timeout_ms=100,
|
|
elapsed_ms=150.5,
|
|
stage="scoring",
|
|
)
|
|
|
|
assert error.timeout_ms == 100
|
|
assert error.elapsed_ms == 150.5
|
|
assert error.stage == "scoring"
|
|
assert error.details["stage"] == "scoring"
|
|
|
|
|
|
class TestScoringError:
|
|
"""Tests for ScoringError."""
|
|
|
|
def test_basic_error(self) -> None:
|
|
"""Test basic scoring error."""
|
|
error = ScoringError()
|
|
assert "score" in error.message.lower()
|
|
|
|
def test_with_scorer_info(self) -> None:
|
|
"""Test with scorer information."""
|
|
error = ScoringError(
|
|
scorer_type="relevance",
|
|
context_id="ctx-123",
|
|
)
|
|
|
|
assert error.scorer_type == "relevance"
|
|
assert error.context_id == "ctx-123"
|
|
|
|
|
|
class TestFormattingError:
|
|
"""Tests for FormattingError."""
|
|
|
|
def test_basic_error(self) -> None:
|
|
"""Test basic formatting error."""
|
|
error = FormattingError()
|
|
assert "format" in error.message.lower()
|
|
|
|
def test_with_model_info(self) -> None:
|
|
"""Test with model information."""
|
|
error = FormattingError(
|
|
model="claude-3-opus",
|
|
adapter="ClaudeAdapter",
|
|
)
|
|
|
|
assert error.model == "claude-3-opus"
|
|
assert error.adapter == "ClaudeAdapter"
|
|
|
|
|
|
class TestCacheError:
|
|
"""Tests for CacheError."""
|
|
|
|
def test_basic_error(self) -> None:
|
|
"""Test basic cache error."""
|
|
error = CacheError()
|
|
assert "cache" in error.message.lower()
|
|
|
|
def test_with_operation_info(self) -> None:
|
|
"""Test with operation information."""
|
|
error = CacheError(
|
|
operation="get",
|
|
cache_key="ctx:abc123",
|
|
)
|
|
|
|
assert error.operation == "get"
|
|
assert error.cache_key == "ctx:abc123"
|
|
|
|
|
|
class TestContextNotFoundError:
|
|
"""Tests for ContextNotFoundError."""
|
|
|
|
def test_basic_error(self) -> None:
|
|
"""Test basic not found error."""
|
|
error = ContextNotFoundError()
|
|
assert "not found" in error.message.lower()
|
|
|
|
def test_with_source_info(self) -> None:
|
|
"""Test with source information."""
|
|
error = ContextNotFoundError(
|
|
source="knowledge-base",
|
|
query="authentication flow",
|
|
)
|
|
|
|
assert error.source == "knowledge-base"
|
|
assert error.query == "authentication flow"
|
|
|
|
|
|
class TestInvalidContextError:
|
|
"""Tests for InvalidContextError."""
|
|
|
|
def test_basic_error(self) -> None:
|
|
"""Test basic invalid error."""
|
|
error = InvalidContextError()
|
|
assert "invalid" in error.message.lower()
|
|
|
|
def test_with_field_info(self) -> None:
|
|
"""Test with field information."""
|
|
error = InvalidContextError(
|
|
field="content",
|
|
value="",
|
|
reason="Content cannot be empty",
|
|
)
|
|
|
|
assert error.field == "content"
|
|
assert error.value == ""
|
|
assert error.reason == "Content cannot be empty"
|
|
|
|
def test_value_type_only_in_details(self) -> None:
|
|
"""Test that only value type is included in details (not actual value)."""
|
|
error = InvalidContextError(
|
|
field="api_key",
|
|
value="secret-key-here",
|
|
)
|
|
|
|
# Actual value should not be in details
|
|
assert "secret-key-here" not in str(error.details)
|
|
assert error.details["value_type"] == "str"
|