This commit renames the 'laborious' package to 'model_manager' across the entire project. This includes renaming directories, modules, references in code, configuration files, and documentation to reflect the new package name. This change improves clarity and consistency within the project.
370 lines
11 KiB
Python
370 lines
11 KiB
Python
from unittest.mock import patch, MagicMock, ANY, call, AsyncMock
|
|
from pandas import DataFrame
|
|
from pytest import fixture, mark
|
|
import pytest_asyncio
|
|
from sientia_do.notifications.models import NotificationLevel
|
|
|
|
from model_manager.activities.opc import OPC
|
|
|
|
metadata = {
|
|
"metadata": {
|
|
"model_id": "test_model",
|
|
"model_name": "test_model",
|
|
"workflow_name": "test_workflow",
|
|
"schema_name": "test_schedule",
|
|
},
|
|
}
|
|
|
|
|
|
def test__init__():
|
|
servers = {
|
|
'server1': 'config'
|
|
}
|
|
opc = OPC(
|
|
opc_servers=servers,
|
|
logger=MagicMock(),
|
|
notification_handler=MagicMock()
|
|
)
|
|
|
|
assert opc.opc_servers == servers
|
|
assert opc.opc_repository == {}
|
|
|
|
|
|
@mark.asyncio
|
|
@patch("model_manager.activities.opc.OpcRepository")
|
|
@patch("model_manager.activities.opc.OPC.send_notification")
|
|
async def test_init_opc(mock_send_notification, mock_opc_repository):
|
|
mock_logger = MagicMock()
|
|
server1 = MagicMock(
|
|
connect=AsyncMock(return_value=(True, {})),
|
|
write_data=AsyncMock(return_value=(True, {}))
|
|
)
|
|
server2 = MagicMock(
|
|
connect=AsyncMock(return_value=(True, {})),
|
|
write_data=AsyncMock(return_value=(True, {}))
|
|
)
|
|
server3 = MagicMock(
|
|
connect=AsyncMock(return_value=(False, {
|
|
'notification_id': 'OPC_CONNECTION_ERROR_server3',
|
|
'message': 'Failed to connect to OPC server: Test error',
|
|
'block': 'opc_repository',
|
|
'level': NotificationLevel.ERROR,
|
|
'attachment_content': 'Test error'
|
|
})),
|
|
write_data=AsyncMock(return_value=(True, {}))
|
|
)
|
|
mock_opc_repository.side_effect = [server1, server2, server3]
|
|
mock_notification_handler = MagicMock()
|
|
servers = {
|
|
'server1': {
|
|
'id': 'server1',
|
|
'url': 'http://localhost:8080',
|
|
'server_uri': 'opc.tcp://localhost:4840',
|
|
'cert_path': '',
|
|
'private_key_path': '',
|
|
'server_cert_path': '',
|
|
'reconnection_interval': 60,
|
|
},
|
|
'server2': {
|
|
'id': 'server2',
|
|
'url': 'http://localhost:8080',
|
|
'server_uri': 'opc.tcp://localhost:4840',
|
|
'cert_path': '',
|
|
'private_key_path': '',
|
|
'server_cert_path': '',
|
|
'reconnection_interval': 60,
|
|
},
|
|
'server3': {
|
|
'id': 'server3',
|
|
'url': 'http://localhost:8080',
|
|
'server_uri': 'opc.tcp://localhost:4840',
|
|
'cert_path': '',
|
|
'private_key_path': '',
|
|
'server_cert_path': '',
|
|
'reconnection_interval': 60,
|
|
}
|
|
}
|
|
opc = OPC(
|
|
opc_servers=servers,
|
|
logger=mock_logger,
|
|
notification_handler=mock_notification_handler
|
|
)
|
|
await opc.init_opc()
|
|
|
|
assert opc.opc_servers == servers
|
|
assert opc.logger == mock_logger
|
|
assert opc.notification_handler == mock_notification_handler
|
|
assert opc.opc_repository['server1'] == server1
|
|
assert opc.opc_repository['server2'] == server2
|
|
|
|
mock_opc_repository.assert_has_calls([
|
|
call(
|
|
id="server1",
|
|
url="http://localhost:8080",
|
|
logger=mock_logger,
|
|
server_uri="opc.tcp://localhost:4840",
|
|
cert_path="",
|
|
private_key_path="",
|
|
server_cert_path="",
|
|
notification_handler=mock_notification_handler,
|
|
reconnection_interval=60,
|
|
pod_id='localhost'
|
|
),
|
|
])
|
|
mock_opc_repository.assert_has_calls([
|
|
call(
|
|
id="server2",
|
|
url="http://localhost:8080",
|
|
logger=mock_logger,
|
|
server_uri="opc.tcp://localhost:4840",
|
|
cert_path="",
|
|
private_key_path="",
|
|
server_cert_path="",
|
|
notification_handler=mock_notification_handler,
|
|
reconnection_interval=60,
|
|
pod_id='localhost'
|
|
)
|
|
])
|
|
|
|
server1.connect.assert_called_once()
|
|
server2.connect.assert_called_once()
|
|
|
|
mock_send_notification.assert_has_calls([
|
|
call(
|
|
metadata={
|
|
'model_id': '-',
|
|
'model_name': '-',
|
|
'workflow_name': '-',
|
|
'schedule_name': 'INITIALIZATION'
|
|
},
|
|
notification_id="OPC_CONNECTION_ERROR_server3",
|
|
message="Failed to connect to OPC server: Test error",
|
|
block="opc_repository",
|
|
level=NotificationLevel.ERROR,
|
|
attachment_content=ANY
|
|
)
|
|
])
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
@patch("model_manager.activities.opc.OpcRepository")
|
|
async def opc(mock_opc_repository):
|
|
servers = {
|
|
'server1': {
|
|
'id': 'server1',
|
|
'url': 'http://localhost:8080',
|
|
'server_uri': 'opc.tcp://localhost:4840',
|
|
'cert_path': '',
|
|
'private_key_path': '',
|
|
'server_cert_path': '',
|
|
'reconnection_interval': 60,
|
|
}
|
|
}
|
|
|
|
mock_opc_repository.return_value.write_data = AsyncMock(
|
|
return_value=(True, {})
|
|
)
|
|
mock_opc_repository.return_value.connect = AsyncMock(
|
|
return_value=(True, {})
|
|
)
|
|
opc = OPC(
|
|
opc_servers=servers,
|
|
logger=MagicMock(),
|
|
notification_handler=MagicMock()
|
|
)
|
|
await opc.init_opc()
|
|
opc.send_notification = MagicMock()
|
|
return opc
|
|
|
|
|
|
WRITE_DATA_CASES = [
|
|
('tag1', 'int', 50),
|
|
('tag2', 'float', 50.5),
|
|
('tag3', 'bool', True),
|
|
('tag4', 'string', 'test'),
|
|
]
|
|
|
|
|
|
@mark.parametrize('tag,data_type,data', WRITE_DATA_CASES)
|
|
@mark.asyncio
|
|
async def test_write_data_success(opc, tag, data_type, data):
|
|
result = await opc.write_data(server_id='server1', tag=tag, data=data,
|
|
data_type=data_type, tag_type='prediction', metadata=metadata)
|
|
assert result is True
|
|
opc.opc_repository['server1'].write_data.assert_called_once_with(
|
|
tag, data, data_type, opc.logger, metadata)
|
|
|
|
|
|
@mark.asyncio
|
|
async def test_write_data_failed(opc):
|
|
opc.opc_repository['server1'].write_data.return_value = (False, {
|
|
'notification_id': 'OPC_WRITE_DATA_ERROR_server1',
|
|
'message': 'Failed to write data to OPC server: Test error',
|
|
'block': 'opc_repository',
|
|
'level': NotificationLevel.ERROR,
|
|
'attachment_content': 'Test error'
|
|
})
|
|
|
|
result = await opc.write_data(server_id='server1', tag='tag1', data=50,
|
|
data_type='int', tag_type='prediction', metadata=metadata)
|
|
assert result is False
|
|
|
|
opc.send_notification.assert_called_once_with(
|
|
metadata=metadata,
|
|
notification_id="OPC_WRITE_DATA_ERROR_server1",
|
|
message="Failed to write data to OPC server: Test error",
|
|
block="opc_repository",
|
|
level=NotificationLevel.ERROR,
|
|
attachment_content=ANY
|
|
)
|
|
|
|
|
|
@mark.asyncio
|
|
async def test_write_data_exception(opc):
|
|
opc.opc_repository['server1'].write_data.side_effect = Exception(
|
|
"Test error")
|
|
|
|
try:
|
|
await opc.write_data(server_id='server1', tag='tag1', data=50,
|
|
data_type='int', tag_type='prediction', metadata=metadata)
|
|
|
|
except Exception:
|
|
opc.send_notification.assert_called_once_with(
|
|
metadata=metadata,
|
|
notification_id="WRITE_OPC_PREDICTION_ERROR",
|
|
message="Error writing data to OPC server: Test error",
|
|
block="write_opc_data",
|
|
level=NotificationLevel.ERROR,
|
|
attachment_content=ANY
|
|
)
|
|
|
|
else:
|
|
assert False, "Expected an exception to be raised"
|
|
|
|
|
|
@mark.asyncio
|
|
async def test_write_opc_data_success(opc):
|
|
# Arrange
|
|
input_data = {
|
|
**metadata,
|
|
'data': {
|
|
'prediction': [0.75],
|
|
'prediction_confidence': [0.95]
|
|
},
|
|
'opc_output_config': {
|
|
'server1': {
|
|
'prediction_tags': {
|
|
'tag1': {'data_type': 'float'}
|
|
},
|
|
'confidence_tags': {
|
|
'tag2': {'data_type': 'float'}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Act
|
|
opc.write_data = AsyncMock(return_value=True)
|
|
opc.process_confidence = MagicMock(return_value={'data': 'data'})
|
|
output = await opc.write_opc_data(input_data)
|
|
|
|
# Assert
|
|
assert output == {'data': 'data'}
|
|
opc.write_data.assert_has_calls([
|
|
call(
|
|
server_id='server1',
|
|
tag='tag1',
|
|
data=0.75,
|
|
data_type='float',
|
|
tag_type='prediction',
|
|
metadata=metadata['metadata']
|
|
)])
|
|
opc.write_data.assert_has_calls([
|
|
call(
|
|
server_id='server1',
|
|
tag='tag2',
|
|
data=0.95,
|
|
data_type='float',
|
|
tag_type='confidence',
|
|
metadata=metadata['metadata']
|
|
)
|
|
])
|
|
assert opc.write_data.call_count == 2
|
|
|
|
|
|
@mark.asyncio
|
|
async def test_write_opc_data_empty_config(opc):
|
|
# Arrange
|
|
input_data = {
|
|
**metadata,
|
|
'data': {
|
|
'prediction': [0.75],
|
|
'prediction_confidence': [0.95]
|
|
},
|
|
'opc_servers': ['server1'],
|
|
'opc_output_config': {
|
|
'server1': {
|
|
'prediction_tags': {},
|
|
'confidence_tags': {}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Act
|
|
await opc.write_opc_data(input_data)
|
|
|
|
# Assert
|
|
opc.opc_repository['server1'].write_data.assert_not_called()
|
|
|
|
|
|
@mark.asyncio
|
|
async def test_write_opc_data_no_validate_server(opc):
|
|
opc.validate_server = MagicMock(return_value=False)
|
|
input_data = {
|
|
**metadata,
|
|
'data': {
|
|
'prediction': [0.75],
|
|
'prediction_confidence': [0.95]
|
|
},
|
|
'opc_output_config': {
|
|
'server1': {
|
|
'prediction_tags': {
|
|
'tag1': {'data_type': 'float'}
|
|
},
|
|
'confidence_tags': {
|
|
'tag2': {'data_type': 'float'}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Act
|
|
await opc.write_opc_data(input_data)
|
|
|
|
# Assert
|
|
opc.opc_repository['server1'].write_data.assert_not_called()
|
|
|
|
|
|
@mark.parametrize('data,success,expected', [
|
|
(DataFrame({'prediction_confidence': [0]}), True, 0),
|
|
(DataFrame({'prediction_confidence': [0]}), False, 12),
|
|
])
|
|
def test_process_confidence(opc, data, success, expected):
|
|
# Act
|
|
result = opc.process_confidence(data, success, metadata)
|
|
|
|
# Assert
|
|
assert result['prediction_confidence'][0] == expected
|
|
|
|
|
|
def test_validate_server(opc):
|
|
assert opc.validate_server('server1', metadata) is True
|
|
assert opc.validate_server('server2', metadata) is False
|
|
|
|
|
|
@mark.asyncio
|
|
async def test_shutdown(opc):
|
|
opc.opc_repository['server1'].disconnect = AsyncMock(return_value=True)
|
|
await opc.shutdown()
|
|
opc.opc_repository['server1'].disconnect.assert_called_once()
|