diff --git a/ingestor/managers/opc_manager.py b/ingestor/managers/opc_manager.py index 45bacd1..db77e35 100644 --- a/ingestor/managers/opc_manager.py +++ b/ingestor/managers/opc_manager.py @@ -210,19 +210,8 @@ class OpcManager(BaseActivity): ).set(1) self.logger.info(f'Connection to {self.name} successful.') except Exception as e: - try: - await self.disconnect() - except Exception as internal_e: - self.logger.error(f'And error occurred while creating connection from {self.name}, and an exception occurred while disconnecting: {internal_e}') + await self.disconnect() - del self.client - self.client = None - - metrics.OPC_CONNECTION_STATUS.labels( - pod_id=self.pod_id, server_name=self.name, server_url=self.url - ).set(0) - metrics.OPC_CONNECTIONS_FAILED.labels(pod_id=self.pod_id, server_name=self.name).inc() - self.logger.error(f'Failed to connect to {self.name}: {e}') raise async def create_subscription(self, name: str): diff --git a/tests/unit/managers/test_ingestor_manager.py b/tests/unit/managers/test_ingestor_manager.py index 2625fd3..d6dc973 100644 --- a/tests/unit/managers/test_ingestor_manager.py +++ b/tests/unit/managers/test_ingestor_manager.py @@ -94,6 +94,7 @@ async def test_initialize_opc_from_config(opc_manager, ingestor_manager): server_config = { 'name': 'server1', 'url': 'opc.tcp://localhost:4840', + 'subscription_period_ms': 1000, 'server_uri': 'http://opcua-server.simulator', 'cert_path': '/path/to/cert', 'private_key_path': '/path/to/private_key', @@ -109,6 +110,7 @@ async def test_initialize_opc_from_config(opc_manager, ingestor_manager): url=server_config['url'], data_manager=ingestor_manager.data_manager, logger=ingestor_manager.logger, + subscription_period_ms=server_config['subscription_period_ms'], server_uri=server_config['server_uri'], notification_handler=ingestor_manager.notification_handler, cert_path=server_config['cert_path'], @@ -128,6 +130,7 @@ async def test_initialize_opc_from_config_exception(traceback_mock, opc_manager, server_config = { 'name': 'server1', 'url': 'opc.tcp://localhost:4840', + 'subscription_period_ms': 1000, 'server_uri': 'http://opcua-server.simulator', 'cert_path': '/path/to/cert', 'private_key_path': '/path/to/private_key', diff --git a/tests/unit/managers/test_opc_manager.py b/tests/unit/managers/test_opc_manager.py index d7a2eac..fe72f46 100644 --- a/tests/unit/managers/test_opc_manager.py +++ b/tests/unit/managers/test_opc_manager.py @@ -1,6 +1,6 @@ import json from datetime import datetime -from unittest.mock import AsyncMock, MagicMock, call, patch +from unittest.mock import ANY, AsyncMock, MagicMock, call, patch import pytest from asyncua.crypto.security_policies import SecurityPolicyBasic256 @@ -142,7 +142,7 @@ async def test_connect_no_security(client, mock_metrics, raw_opc_manager): await raw_opc_manager.connect() - client.assert_called_once_with(raw_opc_manager.url, watchdog_intervall=3600000) + client.assert_called_once_with(raw_opc_manager.url, timeout=10, watchdog_intervall=3600000) raw_opc_manager.client.connect.assert_called_once() raw_opc_manager.set_security.assert_not_called() mock_metrics.OPC_CONNECTIONS_TOTAL.labels.assert_called_once_with( @@ -169,7 +169,7 @@ async def test_connect_with_security(client, raw_opc_manager): await raw_opc_manager.connect() - client.assert_called_once_with(raw_opc_manager.url, watchdog_intervall=3600000) + client.assert_called_once_with(raw_opc_manager.url, timeout=10, watchdog_intervall=3600000) raw_opc_manager.client.connect.assert_called_once() raw_opc_manager.set_security.assert_called_once() @@ -184,33 +184,15 @@ async def test_connect_exception_handling_and_metrics( simulated_error_message = 'Erro de conexão simulado' mock_client_instance.connect.side_effect = Exception(simulated_error_message) + raw_opc_manager.disconnect = AsyncMock() + opc_manager_instance = raw_opc_manager opc_manager_instance.cert_path = None with pytest.raises(Exception, match=simulated_error_message): await opc_manager_instance.connect() - - mock_metrics_module.OPC_CONNECTIONS_TOTAL.labels.assert_called_once_with( - pod_id=opc_manager_instance.pod_id, server_name=opc_manager_instance.name - ) - mock_metrics_module.OPC_CONNECTIONS_TOTAL.labels.return_value.inc.assert_called_once() - - mock_metrics_module.OPC_CONNECTION_STATUS.labels.assert_called_once_with( - pod_id=opc_manager_instance.pod_id, - server_name=opc_manager_instance.name, - server_url=opc_manager_instance.url, - ) - mock_metrics_module.OPC_CONNECTION_STATUS.labels.return_value.set.assert_called_once_with(0) - - mock_metrics_module.OPC_CONNECTIONS_FAILED.labels.assert_called_once_with( - pod_id=opc_manager_instance.pod_id, server_name=opc_manager_instance.name - ) - mock_metrics_module.OPC_CONNECTIONS_FAILED.labels.return_value.inc.assert_called_once() - - opc_manager_instance.logger.error.assert_called_once_with( - f'Failed to connect to {opc_manager_instance.name}: {simulated_error_message}' - ) - + + opc_manager_instance.disconnect.assert_called_once() @mark.asyncio async def test_create_subscription_no_client(raw_opc_manager): @@ -260,16 +242,15 @@ async def test_create_subscription_exception_during_client_call( opc_manager_instance.client = AsyncMock() subscription_name = 'test_sub_client_error' - simulated_period = 750 simulated_error_message = 'Falha ao criar subscrição no cliente OPC' opc_manager_instance.client.create_subscription.side_effect = Exception(simulated_error_message) with pytest.raises(Exception, match=simulated_error_message): - await opc_manager_instance.create_subscription(subscription_name, period=simulated_period) + await opc_manager_instance.create_subscription(subscription_name) opc_manager_instance.client.create_subscription.assert_called_once_with( - simulated_period, opc_manager_instance + opc_manager_instance.subscription_period_ms, opc_manager_instance ) opc_manager_instance.logger.error.assert_called_once_with( @@ -321,6 +302,28 @@ async def test_unsubscribe_success(opc_manager_subscribed): assert opc_manager_subscribed.subscriptions.get('sub1') is None + +@mark.asyncio +async def test_disconnection_fallback_success(opc_manager): + opc_manager.client = AsyncMock() + opc_manager.client.disconnect.return_value = True + result = await opc_manager.disconnection_fallback() + assert result == [] + +@mark.asyncio +async def test_disconnection_fallback_fail(opc_manager): + opc_manager.client = AsyncMock() + opc_manager.client.disconnect.side_effect = Exception('Test error') + result = await opc_manager.disconnection_fallback() + assert result == [ + {'attempt': 1, 'error': 'Test error', 'traceback': ANY}, + {'attempt': 2, 'error': 'Test error', 'traceback': ANY}, + {'attempt': 3, 'error': 'Test error', 'traceback': ANY}, + {'attempt': 4, 'error': 'Test error', 'traceback': ANY}, + {'attempt': 5, 'error': 'Test error', 'traceback': ANY}, ] + assert opc_manager.client.disconnect.call_count == 5 + + @mark.asyncio async def test_disconnect_success(opc_manager_subscribed): opc_manager_subscribed.client = MagicMock() @@ -368,9 +371,7 @@ async def test_disconnect_error(opc_manager_subscribed): opc_manager_subscribed.subscriptions['sub1'].delete.assert_called_once() opc_manager_subscribed.client = None - opc_manager_subscribed.logger.error.assert_called_once_with( - 'Failed to disconnect from OPC UA server: Test error' - ) + @patch('ingestor.managers.opc_manager.metrics')