"""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()