SIENTIAPDE-1646

Update project configuration and dependencies

- Added .mypy_cache and .cursor to .gitignore.
- Changed asyncio_default_fixture_loop_scope and asyncio_default_test_loop_scope to "session" in pyproject.toml.
- Updated e2e testing dependencies in requirements-dev.txt, replacing fakeredis and mongomock with pytest-httpserver.
- Updated requirements.txt to use sientia_do instead of a specific git commit.
- Modified sonar-project.properties to remove a file from coverage exclusions.
- Enhanced E2E test fixtures in e2e/conftest.py for better container management.
- Cleaned up e2e test files related to CoreScouter and PIWebAPIScouter workflows.
This commit is contained in:
vitor-aignosi
2026-05-25 12:58:03 -03:00
parent 909ad25b63
commit 34dbc886f3
65 changed files with 2591 additions and 2849 deletions

View File

@@ -1,6 +1,7 @@
import inspect
from unittest.mock import ANY, MagicMock, patch
from sientia_do.temporal.activities.postgres import Postgres
from sientia_do.temporal.activities.postgres_sync import Postgres
from scouter.activities.activities import Activities
from scouter.activities.api import API
@@ -181,3 +182,13 @@ def test_shutdown(
mock_redis_close.assert_called()
mock_gates_close.assert_called()
mock_api_close.assert_called()
def test_activity_methods_are_sync():
"""Every @activity.defn method on Activities must be a synchronous def."""
for cls in Activities.__mro__:
for name, member in vars(cls).items():
if getattr(member, '__temporal_activity_definition', None) is not None:
assert not inspect.iscoroutinefunction(member), (
f'{cls.__name__}.{name} must not be async'
)

View File

@@ -1,4 +1,4 @@
from unittest.mock import ANY, AsyncMock, MagicMock, patch
from unittest.mock import ANY, MagicMock, Mock, patch
import pandas as pd
import pytest
@@ -86,8 +86,7 @@ def test_close(mock_sientia_monitoring, api_activity):
mock_sientia_monitoring.shutdown.assert_called_once()
@pytest.mark.asyncio
async def test_get_tag_values_success(api_activity):
def test_get_tag_values_success(api_activity):
"""Test get_tag_values with successful data retrieval."""
# Setup test data
test_data = {
@@ -131,10 +130,10 @@ async def test_get_tag_values_success(api_activity):
mock_df['timestamp'] = pd.to_datetime(mock_df['timestamp'])
api_activity.pi_web_api_client.get_latest_values_df = AsyncMock(return_value=mock_df)
api_activity.pi_web_api_client.get_latest_values_df = Mock(return_value=mock_df)
# Execute
result = await api_activity.get_tag_values(test_data)
result = api_activity.get_tag_values(test_data)
# Verify
api_activity.pi_web_api_client.get_latest_values_df.assert_called_once_with(
@@ -161,8 +160,7 @@ async def test_get_tag_values_success(api_activity):
assert result[2]['timestamp'] == '2023-01-01 12:02:00+0000'
@pytest.mark.asyncio
async def test_get_tag_values_with_default_max_count(api_activity):
def test_get_tag_values_with_default_max_count(api_activity):
"""Test get_tag_values with default max_count value."""
# Setup test data without max_count
test_data = {
@@ -190,10 +188,10 @@ async def test_get_tag_values_with_default_max_count(api_activity):
mock_df['timestamp'] = pd.to_datetime(mock_df['timestamp'])
api_activity.pi_web_api_client.get_latest_values_df = AsyncMock(return_value=mock_df)
api_activity.pi_web_api_client.get_latest_values_df = Mock(return_value=mock_df)
# Execute
result = await api_activity.get_tag_values(test_data)
result = api_activity.get_tag_values(test_data)
# Verify default max_count is 1
api_activity.pi_web_api_client.get_latest_values_df.assert_called_once_with(
@@ -209,8 +207,7 @@ async def test_get_tag_values_with_default_max_count(api_activity):
assert len(result) == 1
@pytest.mark.asyncio
async def test_get_tag_values_with_none_webids(api_activity):
def test_get_tag_values_with_none_webids(api_activity):
"""Test get_tag_values with some None WebIds."""
# Setup test data with None values
test_data = {
@@ -245,18 +242,17 @@ async def test_get_tag_values_with_none_webids(api_activity):
mock_df['timestamp'] = pd.to_datetime(mock_df['timestamp'])
api_activity.pi_web_api_client.get_latest_values_df = AsyncMock(return_value=mock_df)
api_activity.pi_web_api_client.get_latest_values_df = Mock(return_value=mock_df)
# Execute
result = await api_activity.get_tag_values(test_data)
result = api_activity.get_tag_values(test_data)
# Verify - should only query non-None WebIds
assert len(result) == 2
assert all(r['name'] in ['tag1', 'tag3'] for r in result)
@pytest.mark.asyncio
async def test_get_tag_values_api_error(api_activity):
def test_get_tag_values_api_error(api_activity):
"""Test get_tag_values when PI Web API client raises an error and sends notification."""
# Setup test data
test_data = {
@@ -275,19 +271,19 @@ async def test_get_tag_values_api_error(api_activity):
}
# Mock API error
api_activity.pi_web_api_client.get_latest_values_df = AsyncMock(
api_activity.pi_web_api_client.get_latest_values_df = Mock(
side_effect=Exception('PI Web API connection error')
)
api_activity.send_notification_async = AsyncMock()
api_activity.send_notification = MagicMock()
# Execute and verify exception is raised
with pytest.raises(Exception) as exc_info:
await api_activity.get_tag_values(test_data)
api_activity.get_tag_values(test_data)
assert str(exc_info.value) == 'PI Web API connection error'
# Verify notification was sent
api_activity.send_notification_async.assert_called_once_with(
api_activity.send_notification.assert_called_once_with(
metadata=metadata['metadata'],
notification_id='PI_WEB_API_REQUEST_ERROR',
message='Error getting tag values from PI Web API: PI Web API connection error',
@@ -297,8 +293,7 @@ async def test_get_tag_values_api_error(api_activity):
)
@pytest.mark.asyncio
async def test_get_tag_values_with_nan_values(api_activity):
def test_get_tag_values_with_nan_values(api_activity):
"""Test get_tag_values handling NaN values in the DataFrame."""
# Setup test data
test_data = {
@@ -333,10 +328,10 @@ async def test_get_tag_values_with_nan_values(api_activity):
mock_df['timestamp'] = pd.to_datetime(mock_df['timestamp'])
api_activity.pi_web_api_client.get_latest_values_df = AsyncMock(return_value=mock_df)
api_activity.pi_web_api_client.get_latest_values_df = Mock(return_value=mock_df)
# Execute
result = await api_activity.get_tag_values(test_data)
result = api_activity.get_tag_values(test_data)
# Verify
assert len(result) == 2

View File

@@ -1,5 +1,5 @@
from typing import Any
from unittest.mock import ANY, AsyncMock, MagicMock, Mock, call, patch
from unittest.mock import ANY, MagicMock, Mock, call, patch
import numpy as np
import pandas as pd
@@ -21,8 +21,6 @@ def gates_fixture():
metrics_controller=metrics_controller,
)
gates.send_notification = MagicMock()
gates.send_notification_async = AsyncMock()
gates.emit_metric = AsyncMock()
gates.logger = logger
gates.notification_handler = notification_handler
@@ -48,8 +46,7 @@ def test_close(mock_sientia_monitoring, gates_fixture):
mock_sientia_monitoring.shutdown.assert_called_once()
@pytest.mark.asyncio
async def test_data_quality_gate_with_null_values_filter_discard(gates_fixture):
def test_data_quality_gate_with_null_values_filter_discard(gates_fixture):
"""Test data_quality_gate with NULL_VALUES_FILTER and DISCARD policy."""
# Setup test data
input_data = {
@@ -69,16 +66,15 @@ async def test_data_quality_gate_with_null_values_filter_discard(gates_fixture):
}
# Execute
result = await gates_fixture.data_quality_gate(input_data)
result = gates_fixture.data_quality_gate(input_data)
# Verify
assert len(result['tag']) == 2
assert 'tag2' not in result['tag']
gates_fixture.send_notification_async.assert_called_once()
gates_fixture.send_notification.assert_called_once()
@pytest.mark.asyncio
async def test_data_quality_gate_with_out_of_bounds_filter_keep(gates_fixture):
def test_data_quality_gate_with_out_of_bounds_filter_keep(gates_fixture):
"""Test data_quality_gate with OUT_OF_BOUNDS_FILTER and KEEP policy."""
# Setup test data with out of bounds values
input_data = {
@@ -103,15 +99,14 @@ async def test_data_quality_gate_with_out_of_bounds_filter_keep(gates_fixture):
{'OUT_OF_BOUNDS_FILTER': lambda df: df[df['tag'] == 'tag2']},
):
# Execute
result = await gates_fixture.data_quality_gate(input_data)
result = gates_fixture.data_quality_gate(input_data)
# Verify data is kept but notification is sent
assert len(result['tag']) == 3 # All rows kept
gates_fixture.send_notification_async.assert_called_once()
gates_fixture.send_notification.assert_called_once()
@pytest.mark.asyncio
async def test_data_quality_gate_with_multiple_filters(gates_fixture):
def test_data_quality_gate_with_multiple_filters(gates_fixture):
"""Test data_quality_gate with multiple filters."""
# Setup test data
input_data = {
@@ -134,7 +129,7 @@ async def test_data_quality_gate_with_multiple_filters(gates_fixture):
**metadata,
}
result = await gates_fixture.data_quality_gate(input_data)
result = gates_fixture.data_quality_gate(input_data)
# Verify only tag1 and tag4 remain (tag2 has null, tag3 is out of bounds)
assert result == {
@@ -144,11 +139,10 @@ async def test_data_quality_gate_with_multiple_filters(gates_fixture):
'timestamp': {0: '2023-01-01', 3: '2023-01-04'},
}
# Should be called twice (once for each filter)
assert gates_fixture.send_notification_async.call_count == 2
assert gates_fixture.send_notification.call_count == 2
@pytest.mark.asyncio
async def test_data_quality_gate_with_unknown_filter(gates_fixture):
def test_data_quality_gate_with_unknown_filter(gates_fixture):
"""Test data_quality_gate with an unknown filter."""
# Setup test data with unknown filter
gates_fixture.warning = MagicMock()
@@ -160,7 +154,7 @@ async def test_data_quality_gate_with_unknown_filter(gates_fixture):
}
# Execute
result = await gates_fixture.data_quality_gate(input_data)
result = gates_fixture.data_quality_gate(input_data)
# Verify data is unchanged and warning is logged
assert len(result['tag']) == 1
@@ -169,8 +163,7 @@ async def test_data_quality_gate_with_unknown_filter(gates_fixture):
)
@pytest.mark.asyncio
async def test_data_quality_gate_with_filter_error(gates_fixture):
def test_data_quality_gate_with_filter_error(gates_fixture):
"""Test data_quality_gate when a filter raises an exception."""
# Setup test data
input_data = {
@@ -188,19 +181,18 @@ async def test_data_quality_gate_with_filter_error(gates_fixture):
'scouter.activities.gates.quality_gate_filters', {'NULL_VALUES_FILTER': failing_filter}
):
# Execute
result = await gates_fixture.data_quality_gate(input_data)
result = gates_fixture.data_quality_gate(input_data)
# Verify error notification is sent and data is unchanged
assert len(result['tag']) == 1
gates_fixture.send_notification_async.assert_called_once()
call_args = gates_fixture.send_notification_async.call_args[1]
gates_fixture.send_notification.assert_called_once()
call_args = gates_fixture.send_notification.call_args[1]
assert call_args['notification_id'] == 'DATA_QUALITY_GATE_ISSUES'
assert call_args['level'] == NotificationLevel.ERROR
assert 'Filter error' in call_args['message']
@pytest.mark.asyncio
async def test_data_quality_gate_with_empty_data(gates_fixture):
def test_data_quality_gate_with_empty_data(gates_fixture):
"""Test data_quality_gate with empty input data."""
# Setup empty input data
input_data = {
@@ -211,15 +203,14 @@ async def test_data_quality_gate_with_empty_data(gates_fixture):
}
# Execute
result = await gates_fixture.data_quality_gate(input_data)
result = gates_fixture.data_quality_gate(input_data)
# Verify empty result and no notifications
assert len(result['tag']) == 0
gates_fixture.send_notification.assert_not_called()
@pytest.mark.asyncio
async def test_data_quality_gate_with_no_filters(gates_fixture):
def test_data_quality_gate_with_no_filters(gates_fixture):
"""Test data_quality_gate with no filters specified."""
# Setup test data with no filters
input_data = {
@@ -230,7 +221,7 @@ async def test_data_quality_gate_with_no_filters(gates_fixture):
}
# Execute
result = await gates_fixture.data_quality_gate(input_data)
result = gates_fixture.data_quality_gate(input_data)
# Verify data is unchanged and no notifications
assert len(result['tag']) == 1
@@ -254,23 +245,22 @@ async def test_data_quality_gate_with_no_filters(gates_fixture):
(pd.DataFrame({'value': [np.nan, np.nan]}), 'avg', None),
# Invalid aggregation function
(pd.DataFrame({'value': [1.0, 2.0]}), 'invalid', 'continue'),
(pd.DataFrame({'value': [10.0]}), 'invalid', 'continue'),
],
)
@pytest.mark.asyncio
async def test_apply_aggregation(gates_fixture, group_data, aggr_function, expected_result):
def test_apply_aggregation(gates_fixture, group_data, aggr_function, expected_result):
"""Test apply_aggregation method with various scenarios."""
result = await gates_fixture.apply_aggregation(group_data, aggr_function, metadata)
result = gates_fixture.apply_aggregation(group_data, aggr_function, metadata)
assert result == expected_result
# Check notification was sent for invalid function
if aggr_function == 'invalid':
gates_fixture.send_notification_async.assert_called_once()
gates_fixture.send_notification.assert_called_once()
else:
gates_fixture.send_notification_async.assert_not_called()
gates_fixture.send_notification.assert_not_called()
@pytest.mark.asyncio
async def test_aggregate_data(gates_fixture):
def test_aggregate_data(gates_fixture):
"""Test aggregate_data method with multiple groups and aggregation functions."""
input_data = {
'data': [
@@ -299,16 +289,15 @@ async def test_aggregate_data(gates_fixture):
}
# Execute
result = await gates_fixture.aggregate_data(input_data)
result = gates_fixture.aggregate_data(input_data)
# Verify
assert result == expected_result
gates_fixture.send_notification.assert_not_called()
@pytest.mark.asyncio
async def test_aggregate_data_with_continue(gates_fixture):
gates_fixture.apply_aggregation = AsyncMock(return_value='continue')
def test_aggregate_data_with_continue(gates_fixture):
gates_fixture.apply_aggregation = MagicMock(return_value='continue')
input_data = {
'data': [
@@ -331,15 +320,14 @@ async def test_aggregate_data_with_continue(gates_fixture):
expected_result: dict[str, Any] = {}
# Execute
result = await gates_fixture.aggregate_data(input_data)
result = gates_fixture.aggregate_data(input_data)
# Verify
assert result == expected_result
gates_fixture.send_notification_async.assert_not_called()
gates_fixture.send_notification.assert_not_called()
@pytest.mark.asyncio
async def test_aggregate_data_raise_exception(gates_fixture):
def test_aggregate_data_raise_exception(gates_fixture):
gates_fixture.apply_aggregation = MagicMock(side_effect=Exception('Test exception'))
input_data = {
@@ -360,10 +348,10 @@ async def test_aggregate_data_raise_exception(gates_fixture):
}
try:
await gates_fixture.aggregate_data(input_data)
gates_fixture.aggregate_data(input_data)
except Exception as e:
assert str(e) == 'Test exception'
gates_fixture.send_notification_async.assert_called_once_with(
gates_fixture.send_notification.assert_called_once_with(
metadata=metadata['metadata'],
notification_id='AGGREGATION_ISSUES',
message='Error aggregating data: Test exception',
@@ -375,9 +363,8 @@ async def test_aggregate_data_raise_exception(gates_fixture):
raise AssertionError('Exception not raised')
@pytest.mark.asyncio
@patch('scouter.activities.gates.metrics')
async def test_write_metrics(mock_metrics, gates_fixture):
def test_write_metrics(mock_metrics, gates_fixture):
"""Test write_metrics method."""
input_data = {
'metadata': metadata['metadata'],
@@ -386,7 +373,7 @@ async def test_write_metrics(mock_metrics, gates_fixture):
'value': [1.0, 2.0, None],
},
}
await gates_fixture.write_metrics(input_data)
gates_fixture.write_metrics(input_data)
mock_metrics.LABORIOUS_DATA_WRITTEN_COUNT.labels.assert_called_once_with(
pod_id=gates_fixture.pod_id,
model_name=metadata['metadata']['model_name'],

View File

@@ -1,7 +1,7 @@
from datetime import datetime
from unittest.mock import ANY, AsyncMock, MagicMock, patch
from unittest.mock import ANY, MagicMock, Mock, patch
from pytest import fixture, mark
from pytest import fixture
from sientia_do.notifications.models import NotificationLevel
from sientia_do.temporal.constants import DATETIME_FORMAT_MS_WITH_TZ
@@ -44,16 +44,18 @@ def mongodb_activity(mock_mongodb_repository):
database_name='test_db',
logger=MagicMock(),
notification_handler=MagicMock(),
metrics_controller=AsyncMock(),
metrics_controller=MagicMock(),
)
return mongo
def test_close(mongodb_activity):
@patch('scouter.activities.mongodb.SientiaMonitoring')
def test_close(mock_sientia_monitoring, mongodb_activity):
"""Test close"""
mongodb_activity.close()
mongodb_activity.mongodb_repository.close.assert_called_once()
mock_sientia_monitoring.shutdown.assert_called_once()
def test_del(mongodb_activity):
@@ -64,11 +66,10 @@ def test_del(mongodb_activity):
mongodb_activity.close.assert_called_once()
@mark.asyncio
async def test_load_latest_data_none_last_data_timestamp(mongodb_activity):
def test_load_latest_data_none_last_data_timestamp(mongodb_activity):
"""Test load_latest_data"""
mongodb_activity.mongodb_repository.find = AsyncMock(
mongodb_activity.mongodb_repository.find = Mock(
return_value=[
{
'name': 'test1',
@@ -80,7 +81,7 @@ async def test_load_latest_data_none_last_data_timestamp(mongodb_activity):
]
)
result = await mongodb_activity.load_latest_data(
result = mongodb_activity.load_latest_data(
{
'metadata': {'workflow_name': 'test_pipeline', 'schedule_name': 'test_schedule'},
'collection_name': 'test_collection',
@@ -103,11 +104,10 @@ async def test_load_latest_data_none_last_data_timestamp(mongodb_activity):
]
@mark.asyncio
async def test_load_latest_data_not_none_last_data_timestamp(mongodb_activity):
def test_load_latest_data_not_none_last_data_timestamp(mongodb_activity):
"""Test load_latest_data"""
mongodb_activity.mongodb_repository.find = AsyncMock(
mongodb_activity.mongodb_repository.find = Mock(
return_value=[
{
'name': 'test1',
@@ -119,7 +119,7 @@ async def test_load_latest_data_not_none_last_data_timestamp(mongodb_activity):
]
)
result = await mongodb_activity.load_latest_data(
result = mongodb_activity.load_latest_data(
{
'metadata': {'workflow_name': 'test_pipeline', 'schedule_name': 'test_schedule'},
'collection_name': 'test_collection',
@@ -148,16 +148,13 @@ async def test_load_latest_data_not_none_last_data_timestamp(mongodb_activity):
]
@mark.asyncio
async def test_load_latest_data_error(mongodb_activity):
def test_load_latest_data_error(mongodb_activity):
"""Test load_latest_data"""
mongodb_activity.mongodb_repository.find.side_effect = Exception('test')
mongodb_activity.send_notification = MagicMock()
mongodb_activity.send_notification_async = AsyncMock()
mongodb_activity.emit_metric = AsyncMock()
try:
await mongodb_activity.load_latest_data(
mongodb_activity.load_latest_data(
{
'metadata': {'workflow_name': 'test_pipeline', 'schedule_name': 'test_schedule'},
'collection_name': 'test_collection',
@@ -167,7 +164,7 @@ async def test_load_latest_data_error(mongodb_activity):
except Exception as e:
assert str(e) == 'test'
mongodb_activity.send_notification_async.assert_called_once_with(
mongodb_activity.send_notification.assert_called_once_with(
metadata={'workflow_name': 'test_pipeline', 'schedule_name': 'test_schedule'},
notification_id='MONGO_LOAD_ERROR',
message='Error loading data from MongoDB: test',

View File

@@ -1,5 +1,5 @@
from datetime import datetime
from unittest.mock import ANY, AsyncMock, MagicMock, patch
from unittest.mock import ANY, MagicMock, Mock, patch
import numpy as np
import pytest
@@ -79,8 +79,7 @@ def test_redis_initialization(mock_redis_repository):
assert activity.redis_repository is not None
@pytest.mark.asyncio
async def test_get_last_data_timestamp_none(redis_activity):
def test_get_last_data_timestamp_none(redis_activity):
"""Test get_last_data_timestamp"""
test_data = {
**metadata,
@@ -88,9 +87,9 @@ async def test_get_last_data_timestamp_none(redis_activity):
'schedule_name': 'test_schedule',
}
redis_activity.redis_repository.get = AsyncMock(return_value=None)
redis_activity.redis_repository.get = Mock(return_value=None)
result = await redis_activity.get_last_data_timestamp(test_data)
result = redis_activity.get_last_data_timestamp(test_data)
redis_activity.redis_repository.get.assert_called_once_with(
'last_data_timestamp:test_pipeline:test_schedule',
@@ -100,14 +99,13 @@ async def test_get_last_data_timestamp_none(redis_activity):
assert result is None
@pytest.mark.asyncio
async def test_get_last_data_timestamp_not_none(redis_activity):
def test_get_last_data_timestamp_not_none(redis_activity):
"""Test get_last_data_timestamp"""
test_data = {**metadata, 'workflow_name': 'test_pipeline', 'schedule_name': 'test_schedule'}
redis_activity.redis_repository.get = AsyncMock(return_value='2023-01-01 12:00:00')
redis_activity.redis_repository.get = Mock(return_value='2023-01-01 12:00:00')
result = await redis_activity.get_last_data_timestamp(test_data)
result = redis_activity.get_last_data_timestamp(test_data)
redis_activity.redis_repository.get.assert_called_once_with(
'last_data_timestamp:test_pipeline:test_schedule',
@@ -117,21 +115,20 @@ async def test_get_last_data_timestamp_not_none(redis_activity):
assert result == '2023-01-01 12:00:00'
@pytest.mark.asyncio
async def test_get_last_data_timestamp_error(redis_activity):
def test_get_last_data_timestamp_error(redis_activity):
"""Test get_last_data_timestamp error"""
test_data = {**metadata, 'workflow_name': 'test_pipeline', 'schedule_name': 'test_schedule'}
redis_activity.send_notification_async = AsyncMock()
redis_activity.send_notification = Mock()
redis_activity.redis_repository.get.side_effect = Exception('test')
try:
await redis_activity.get_last_data_timestamp(test_data)
redis_activity.get_last_data_timestamp(test_data)
except Exception as e:
assert str(e) == 'test'
redis_activity.send_notification_async.assert_called_once_with(
redis_activity.send_notification.assert_called_once_with(
metadata=metadata['metadata'],
notification_id='REDIS_GET_ERROR',
message='Error getting last data timestamp: test',
@@ -144,8 +141,7 @@ async def test_get_last_data_timestamp_error(redis_activity):
raise AssertionError('Expected exception')
@pytest.mark.asyncio
async def test_put_last_data_timestamp_empty_dataframe(redis_activity):
def test_put_last_data_timestamp_empty_dataframe(redis_activity):
"""Test put_last_data_timestamp with empty dataframe"""
test_data = {
**metadata,
@@ -156,15 +152,14 @@ async def test_put_last_data_timestamp_empty_dataframe(redis_activity):
redis_activity.redis_repository.set = MagicMock()
result = await redis_activity.put_last_data_timestamp(test_data)
result = redis_activity.put_last_data_timestamp(test_data)
assert result is None
redis_activity.redis_repository.set.assert_not_called()
@pytest.mark.asyncio
async def test_put_last_data_timestamp_not_empty_dataframe(redis_activity):
def test_put_last_data_timestamp_not_empty_dataframe(redis_activity):
"""Test put_last_data_timestamp with not empty dataframe"""
data = DataFrame(
@@ -181,9 +176,9 @@ async def test_put_last_data_timestamp_not_empty_dataframe(redis_activity):
'data': data.to_dict('records'),
}
redis_activity.redis_repository.set = AsyncMock()
redis_activity.redis_repository.set = Mock()
result = await redis_activity.put_last_data_timestamp(test_data)
result = redis_activity.put_last_data_timestamp(test_data)
assert result == '2023-01-01 12:00:01'
@@ -195,8 +190,7 @@ async def test_put_last_data_timestamp_not_empty_dataframe(redis_activity):
)
@pytest.mark.asyncio
async def test_put_last_data_timestamp_error(redis_activity):
def test_put_last_data_timestamp_error(redis_activity):
"""Test put_last_data_timestamp error"""
test_data = {
**metadata,
@@ -211,16 +205,16 @@ async def test_put_last_data_timestamp_error(redis_activity):
).to_dict('records'),
}
redis_activity.send_notification_async = AsyncMock()
redis_activity.redis_repository.set = AsyncMock(side_effect=Exception('test'))
redis_activity.send_notification = Mock()
redis_activity.redis_repository.set = Mock(side_effect=Exception('test'))
try:
await redis_activity.put_last_data_timestamp(test_data)
redis_activity.put_last_data_timestamp(test_data)
except Exception as e:
assert str(e) == 'test'
redis_activity.send_notification_async.assert_called_once_with(
redis_activity.send_notification.assert_called_once_with(
metadata=metadata['metadata'],
notification_id='REDIS_SET_ERROR',
message='Error setting last data timestamp: test',
@@ -233,8 +227,7 @@ async def test_put_last_data_timestamp_error(redis_activity):
raise AssertionError('Expected exception')
@pytest.mark.asyncio
async def test_group_and_hold_data_new_key(redis_activity):
def test_group_and_hold_data_new_key(redis_activity):
"""Test group_and_hold_data with a new key"""
# Setup
test_data = {
@@ -255,11 +248,11 @@ async def test_group_and_hold_data_new_key(redis_activity):
}
# Mock get to return None for new key
redis_activity.redis_repository.get = AsyncMock(return_value=None)
redis_activity.redis_repository.set = AsyncMock()
redis_activity.redis_repository.get = Mock(return_value=None)
redis_activity.redis_repository.set = Mock()
# Call the method
result = await redis_activity.group_and_hold_data(test_data)
result = redis_activity.group_and_hold_data(test_data)
# Verify the result
expected_result = {
@@ -279,8 +272,7 @@ async def test_group_and_hold_data_new_key(redis_activity):
)
@pytest.mark.asyncio
async def test_group_and_hold_data_update_existing_fill_missing(redis_activity):
def test_group_and_hold_data_update_existing_fill_missing(redis_activity):
"""Test updating existing data with group_and_hold_data"""
# Setup initial data in Redis
existing_data = {'sensor1': 20.0, 'sensor2': 28.0, 'timestamp': '2023-01-01 11:00:00'}
@@ -309,11 +301,11 @@ async def test_group_and_hold_data_update_existing_fill_missing(redis_activity):
}
# Mock get to return existing data
redis_activity.redis_repository.get = AsyncMock(return_value=existing_data)
redis_activity.redis_repository.set = AsyncMock()
redis_activity.redis_repository.get = Mock(return_value=existing_data)
redis_activity.redis_repository.set = Mock()
# Call the method
result = await redis_activity.group_and_hold_data(test_data)
result = redis_activity.group_and_hold_data(test_data)
# Verify the result
expected_result = {
@@ -344,8 +336,7 @@ async def test_group_and_hold_data_update_existing_fill_missing(redis_activity):
)
@pytest.mark.asyncio
async def test_group_and_hold_data_with_none_values(redis_activity):
def test_group_and_hold_data_with_none_values(redis_activity):
"""Test handling of None values in group_and_hold_data"""
# Setup test data with None values
test_data = {
@@ -366,19 +357,18 @@ async def test_group_and_hold_data_with_none_values(redis_activity):
}
# Mock get to return None for new key
redis_activity.redis_repository.get = AsyncMock(return_value=None)
redis_activity.redis_repository.set = AsyncMock()
redis_activity.redis_repository.get = Mock(return_value=None)
redis_activity.redis_repository.set = Mock()
# Call the method
result = await redis_activity.group_and_hold_data(test_data)
result = redis_activity.group_and_hold_data(test_data)
# Verify None was converted to np.nan and values are as expected
assert np.isnan(result['value'][0])
assert result['value'][1] == pytest.approx(30.0)
@pytest.mark.asyncio
async def test_group_and_hold_data_empty_dataframe(redis_activity):
def test_group_and_hold_data_empty_dataframe(redis_activity):
"""Test group_and_hold_data with empty DataFrame"""
# Setup test with empty data
test_data = {
@@ -391,16 +381,15 @@ async def test_group_and_hold_data_empty_dataframe(redis_activity):
'fill_missing_tags': False,
}
redis_activity.redis_repository.get = AsyncMock(return_value=None)
redis_activity.redis_repository.get = Mock(return_value=None)
# Call the method
result = await redis_activity.group_and_hold_data(test_data)
result = redis_activity.group_and_hold_data(test_data)
assert result == {}
@pytest.mark.asyncio
async def test_group_and_hold_data_error_get(redis_activity):
def test_group_and_hold_data_error_get(redis_activity):
"""Test group_and_hold_data error"""
test_data = {
**metadata,
@@ -413,16 +402,16 @@ async def test_group_and_hold_data_error_get(redis_activity):
'fill_missing_tags': False,
}
redis_activity.redis_repository.get = AsyncMock(side_effect=Exception('test'))
redis_activity.send_notification_async = AsyncMock()
redis_activity.redis_repository.get = Mock(side_effect=Exception('test'))
redis_activity.send_notification = Mock()
try:
await redis_activity.group_and_hold_data(test_data)
redis_activity.group_and_hold_data(test_data)
except Exception as e:
assert str(e) == 'test'
redis_activity.send_notification_async.assert_called_once_with(
redis_activity.send_notification.assert_called_once_with(
metadata=metadata['metadata'],
notification_id='REDIS_GET_ERROR',
message='Error getting held data: test',
@@ -435,8 +424,7 @@ async def test_group_and_hold_data_error_get(redis_activity):
raise AssertionError('Expected exception')
@pytest.mark.asyncio
async def test_group_and_hold_data_error_set(redis_activity):
def test_group_and_hold_data_error_set(redis_activity):
"""Test group_and_hold_data error"""
test_data = {
**metadata,
@@ -452,21 +440,20 @@ async def test_group_and_hold_data_error_set(redis_activity):
existing_data = {'sensor1': 20.0, 'sensor2': 28.0, 'timestamp': '2023-01-01 11:00:00'}
# Mock get to return existing data
redis_activity.redis_repository.get = AsyncMock(return_value=existing_data)
redis_activity.redis_repository.set = AsyncMock(side_effect=Exception('test'))
redis_activity.send_notification_async = AsyncMock()
redis_activity.redis_repository.get = Mock(return_value=existing_data)
redis_activity.redis_repository.set = Mock(side_effect=Exception('test'))
redis_activity.send_notification = Mock()
try:
await redis_activity.group_and_hold_data(test_data)
redis_activity.group_and_hold_data(test_data)
except Exception as e:
assert str(e) == 'test'
@pytest.mark.asyncio
async def test_store_data_package(redis_activity):
def test_store_data_package(redis_activity):
"""Test store_data_package"""
redis_activity.redis_repository.set = AsyncMock()
redis_activity.redis_repository.set = Mock()
test_data = {
**metadata,
@@ -489,7 +476,7 @@ async def test_store_data_package(redis_activity):
'model_tags': {'sensor1': 'sensor1', 'sensor2': 'sensor2'},
}
await redis_activity.store_data_package(test_data)
redis_activity.store_data_package(test_data)
redis_activity.redis_repository.set.assert_called_once_with(
ANY,
@@ -499,11 +486,10 @@ async def test_store_data_package(redis_activity):
)
@pytest.mark.asyncio
async def test_store_data_package_error(redis_activity):
def test_store_data_package_error(redis_activity):
"""Test store_data_package error"""
redis_activity.redis_repository.set = AsyncMock(side_effect=ValueError('test'))
redis_activity.send_notification_async = AsyncMock()
redis_activity.redis_repository.set = Mock(side_effect=ValueError('test'))
redis_activity.send_notification = Mock()
test_data = {
**metadata,
@@ -527,9 +513,9 @@ async def test_store_data_package_error(redis_activity):
}
with pytest.raises(ValueError):
await redis_activity.store_data_package(test_data)
redis_activity.store_data_package(test_data)
redis_activity.send_notification_async.assert_called_once_with(
redis_activity.send_notification.assert_called_once_with(
metadata=metadata['metadata'],
notification_id='REDIS_SET_ERROR',
message='Error setting data package: test',