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
275 lines
9.9 KiB
Python
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()
|