Files
ai-tax-agent/tests/unit/test_event_metrics.py
harkon fdba81809f
Some checks failed
CI/CD Pipeline / Generate SBOM (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Code Quality & Linting (push) Has been cancelled
CI/CD Pipeline / Policy Validation (push) Has been cancelled
CI/CD Pipeline / Test Suite (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-coverage) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-extract) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-firm-connectors) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-forms) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-hmrc) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-ingestion) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-kg) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-normalize-map) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-ocr) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rag-indexer) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rag-retriever) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-reason) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (svc-rpa) (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (ui-review) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-coverage) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-extract) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-kg) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (svc-rag-retriever) (push) Has been cancelled
CI/CD Pipeline / Security Scanning (ui-review) (push) Has been cancelled
CI/CD Pipeline / Notifications (push) Has been cancelled
completed local setup with compose
2025-11-26 13:17:17 +00:00

275 lines
9.9 KiB
Python

"""Tests for event metrics."""
from unittest.mock import MagicMock, patch
from libs.events.metrics import (
EventMetricsCollector,
event_consumed_total,
event_dlq_total,
event_processing_duration_seconds,
event_processing_errors_total,
event_publish_errors_total,
event_published_total,
event_publishing_duration_seconds,
event_retry_total,
event_schema_validation_errors_total,
get_event_metrics_registry,
nats_consumer_lag_messages,
nats_stream_messages_total,
)
class TestEventMetrics:
"""Test cases for event metrics."""
def test_get_event_metrics_registry(self) -> None:
"""Test getting the metrics registry."""
registry = get_event_metrics_registry()
assert registry is not None
def test_metrics_exist(self) -> None:
"""Test that all expected metrics are defined."""
# Publishing metrics
assert event_published_total is not None
assert event_publish_errors_total is not None
assert event_publishing_duration_seconds is not None
# Consumption metrics
assert event_consumed_total is not None
assert event_processing_duration_seconds is not None
assert event_processing_errors_total is not None
# DLQ metrics
assert event_dlq_total is not None
assert event_retry_total is not None
# Schema validation metrics
assert event_schema_validation_errors_total is not None
# NATS metrics
assert nats_stream_messages_total is not None
assert nats_consumer_lag_messages is not None
class TestEventMetricsCollector:
"""Test cases for EventMetricsCollector."""
def test_record_publish_success(self) -> None:
"""Test recording successful publish."""
with patch.object(event_published_total, "labels") as mock_labels:
mock_counter = MagicMock()
mock_labels.return_value = mock_counter
EventMetricsCollector.record_publish(
topic="test.topic",
duration_seconds=0.05,
success=True,
)
mock_labels.assert_called_once_with(topic="test.topic")
mock_counter.inc.assert_called_once()
def test_record_publish_failure(self) -> None:
"""Test recording failed publish."""
with patch.object(event_publish_errors_total, "labels") as mock_labels:
mock_counter = MagicMock()
mock_labels.return_value = mock_counter
EventMetricsCollector.record_publish(
topic="test.topic",
duration_seconds=0.1,
success=False,
error_type="ConnectionError",
)
mock_labels.assert_called_once_with(
topic="test.topic", error_type="ConnectionError"
)
mock_counter.inc.assert_called_once()
def test_record_publish_duration(self) -> None:
"""Test recording publish duration."""
with patch.object(event_publishing_duration_seconds, "labels") as mock_labels:
mock_histogram = MagicMock()
mock_labels.return_value = mock_histogram
duration = 0.123
EventMetricsCollector.record_publish(
topic="test.topic",
duration_seconds=duration,
success=True,
)
mock_labels.assert_called_once_with(topic="test.topic")
mock_histogram.observe.assert_called_once_with(duration)
def test_record_consume_success(self) -> None:
"""Test recording successful event consumption."""
with patch.object(event_consumed_total, "labels") as mock_labels:
mock_counter = MagicMock()
mock_labels.return_value = mock_counter
EventMetricsCollector.record_consume(
topic="test.topic",
consumer_group="test-group",
duration_seconds=0.5,
success=True,
)
mock_labels.assert_called_once_with(
topic="test.topic", consumer_group="test-group"
)
mock_counter.inc.assert_called_once()
def test_record_consume_failure(self) -> None:
"""Test recording failed event consumption."""
with patch.object(event_processing_errors_total, "labels") as mock_labels:
mock_counter = MagicMock()
mock_labels.return_value = mock_counter
EventMetricsCollector.record_consume(
topic="test.topic",
consumer_group="test-group",
duration_seconds=1.0,
success=False,
error_type="ValidationError",
)
mock_labels.assert_called_once_with(
topic="test.topic",
consumer_group="test-group",
error_type="ValidationError",
)
mock_counter.inc.assert_called_once()
def test_record_consume_duration(self) -> None:
"""Test recording consumption duration."""
with patch.object(event_processing_duration_seconds, "labels") as mock_labels:
mock_histogram = MagicMock()
mock_labels.return_value = mock_histogram
duration = 2.5
EventMetricsCollector.record_consume(
topic="test.topic",
consumer_group="test-group",
duration_seconds=duration,
success=True,
)
mock_labels.assert_called_once_with(
topic="test.topic", consumer_group="test-group"
)
mock_histogram.observe.assert_called_once_with(duration)
def test_record_dlq(self) -> None:
"""Test recording DLQ event."""
with patch.object(event_dlq_total, "labels") as mock_labels:
mock_counter = MagicMock()
mock_labels.return_value = mock_counter
EventMetricsCollector.record_dlq(
topic="test.topic", error_type="TimeoutError"
)
mock_labels.assert_called_once_with(
topic="test.topic", error_type="TimeoutError"
)
mock_counter.inc.assert_called_once()
def test_record_retry(self) -> None:
"""Test recording retry attempt."""
with patch.object(event_retry_total, "labels") as mock_labels:
mock_counter = MagicMock()
mock_labels.return_value = mock_counter
EventMetricsCollector.record_retry(topic="test.topic", retry_attempt=2)
mock_labels.assert_called_once_with(topic="test.topic", retry_attempt="2")
mock_counter.inc.assert_called_once()
def test_record_schema_validation_error(self) -> None:
"""Test recording schema validation error."""
with patch.object(
event_schema_validation_errors_total, "labels"
) as mock_labels:
mock_counter = MagicMock()
mock_labels.return_value = mock_counter
EventMetricsCollector.record_schema_validation_error(
topic="test.topic", validation_error="missing_required_field"
)
mock_labels.assert_called_once_with(
topic="test.topic", validation_error="missing_required_field"
)
mock_counter.inc.assert_called_once()
def test_record_nats_stream_message(self) -> None:
"""Test recording NATS stream message."""
with patch.object(nats_stream_messages_total, "labels") as mock_labels:
mock_counter = MagicMock()
mock_labels.return_value = mock_counter
EventMetricsCollector.record_nats_stream_message(
stream_name="TAX_AGENT_EVENTS"
)
mock_labels.assert_called_once_with(stream_name="TAX_AGENT_EVENTS")
mock_counter.inc.assert_called_once()
def test_record_consumer_lag(self) -> None:
"""Test recording consumer lag."""
with patch.object(nats_consumer_lag_messages, "labels") as mock_labels:
mock_histogram = MagicMock()
mock_labels.return_value = mock_histogram
EventMetricsCollector.record_consumer_lag(
stream_name="TAX_AGENT_EVENTS",
consumer_group="tax-agent",
lag_messages=150,
)
mock_labels.assert_called_once_with(
stream_name="TAX_AGENT_EVENTS", consumer_group="tax-agent"
)
mock_histogram.observe.assert_called_once_with(150)
def test_record_publish_with_default_error_type(self) -> None:
"""Test recording publish failure with default error type."""
with patch.object(event_publish_errors_total, "labels") as mock_labels:
mock_counter = MagicMock()
mock_labels.return_value = mock_counter
EventMetricsCollector.record_publish(
topic="test.topic",
duration_seconds=0.1,
success=False,
error_type=None, # No error type provided
)
mock_labels.assert_called_once_with(
topic="test.topic", error_type="unknown" # Should default to "unknown"
)
mock_counter.inc.assert_called_once()
def test_record_consume_with_default_error_type(self) -> None:
"""Test recording consume failure with default error type."""
with patch.object(event_processing_errors_total, "labels") as mock_labels:
mock_counter = MagicMock()
mock_labels.return_value = mock_counter
EventMetricsCollector.record_consume(
topic="test.topic",
consumer_group="test-group",
duration_seconds=1.0,
success=False,
error_type=None, # No error type provided
)
mock_labels.assert_called_once_with(
topic="test.topic",
consumer_group="test-group",
error_type="unknown", # Should default to "unknown"
)
mock_counter.inc.assert_called_once()