from unittest.mock import MagicMock, patch, call import pytest from sientia_do.notifications.handlers import CoreNotificationHandler as NotificationHandler from scouter.activities.faker import Faker @pytest.fixture def mock_kafka_producer(): with patch('scouter.activities.faker.KafkaProducer') as mock: producer = MagicMock() mock.return_value = producer yield producer @pytest.fixture def mock_datetime(): with patch('scouter.activities.faker.datetime') as mock_dt: mock_dt.now.return_value.strftime.return_value = '2025-05-14 14:54:24' yield mock_dt @pytest.fixture def faker_instance(mock_kafka_producer): logger = MagicMock() notification_handler = MagicMock(spec=NotificationHandler) return Faker( bootstrap_servers='localhost:9092', logger=logger, notification_handler=notification_handler ) @pytest.mark.asyncio async def test_faker_init(faker_instance, mock_kafka_producer): """Test Faker initialization with correct parameters""" assert faker_instance.producer is not None assert len(faker_instance.tags) == 6 metadata = { 'metadata': { 'model_id': 'test_model_id', 'model_name': 'test_model', 'schedule_name': 'test_schedule', 'workflow_name': 'scouter' } } @pytest.mark.asyncio async def test_generate_and_send_data_default_count(faker_instance, mock_kafka_producer, mock_datetime): """Test generating data with default message count""" # Mock random.choice to control the output with patch('random.choice') as mock_choice, \ patch('random.uniform', return_value=42.5), \ patch('random.randint', return_value=3), \ patch('random.random', return_value=0.5): # Setup mock for tag and name selection mock_choice.side_effect = [ 'ns=1;i=1001', 'ns=1;i=1002', 'ns=1;i=1003' ] # Call the method await faker_instance.generate_and_send_data({'topic': 'test_topic', **metadata}) # Verify the producer was called 3 times (default count) assert mock_kafka_producer.send.call_count == 3 mock_kafka_producer.flush.assert_called_once() # Verify the message format expected_data = [{ 'tag': 'ns=1;i=1001', 'name': 'Temperature Sensor', 'timestamp': '2025-05-14 14:54:24', 'value': 42.5 }, { 'tag': 'ns=1;i=1002', 'name': 'Vibration Meter', 'timestamp': '2025-05-14 14:54:24', 'value': 42.5 }, { 'tag': 'ns=1;i=1003', 'name': 'Pressure Gauge', 'timestamp': '2025-05-14 14:54:24', 'value': 42.5 }] mock_kafka_producer.send.assert_has_calls([ call('test_topic', value=expected_data[0]), call('test_topic', value=expected_data[1]), call('test_topic', value=expected_data[2]) ]) @pytest.mark.asyncio async def test_generate_and_send_data_custom_count(faker_instance, mock_kafka_producer): """Test generating data with custom message count""" # Call the method with custom count await faker_instance.generate_and_send_data({ 'topic': 'test_topic', 'num_messages': 2, **metadata }) # Verify the producer was called 2 times assert mock_kafka_producer.send.call_count == 2 mock_kafka_producer.flush.assert_called_once() @pytest.mark.asyncio async def test_generate_and_send_data_no_topic(faker_instance): """Test that ValueError is raised when no topic is provided""" with pytest.raises(ValueError, match="Topic must be specified in input_data"): await faker_instance.generate_and_send_data({**metadata}) @pytest.mark.asyncio @patch('scouter.activities.faker.random.random', return_value=0.5) async def test_generate_and_send_data_random_values(_random, faker_instance, mock_kafka_producer): """Test that random values are within expected ranges""" # Call the method await faker_instance.generate_and_send_data({'topic': 'test_topic', **metadata}) # Get the call arguments call_args = mock_kafka_producer.send.call_args[1]['value'] # Verify the data structure assert 'tag' in call_args assert call_args['tag'] in faker_instance.tags assert 'value' in call_args assert 0 <= call_args['value'] <= 100 @pytest.mark.asyncio @patch('scouter.activities.faker.random.random', return_value=0.05) async def test_generate_and_send_data_generate_null_values( _random_mock, faker_instance, mock_kafka_producer): # Call the method await faker_instance.generate_and_send_data({'topic': 'test_topic', 'num_messages': 1, **metadata}) # Get the call arguments call_args = mock_kafka_producer.send.call_args[1]['value'] assert call_args['value'] is None assert call_args['tag'] in faker_instance.tags assert 'name' in call_args assert 'timestamp' in call_args