diff --git a/ingestor/metrics.py b/ingestor/metrics.py index 14ce00c..a9ee17f 100644 --- a/ingestor/metrics.py +++ b/ingestor/metrics.py @@ -142,35 +142,49 @@ NOTIFICATIONS_SENT = Counter( # --- Gateway: Write metrics --- OPC_WRITE_TOTAL = Counter( - 'opc_write_total', 'OPC active write attempts', + 'opc_write_total', + 'OPC active write attempts', ['pod_id', 'server_name', 'model_id', 'result'], # result: success|error ) OPC_WRITE_LATENCY = Histogram( - 'opc_write_latency_seconds', 'OPC active write latency', ['pod_id', 'server_name'], + 'opc_write_latency_seconds', + 'OPC active write latency', + ['pod_id', 'server_name'], ) OPC_WRITE_STATUS_BY_MODEL = Gauge( - 'opc_write_status_by_model', 'Last write status per model (1=ok,0=error)', + 'opc_write_status_by_model', + 'Last write status per model (1=ok,0=error)', ['pod_id', 'model_id', 'status'], ) # --- Gateway: Active read metrics --- OPC_ACTIVE_READ_TOTAL = Counter( - 'opc_active_read_total', 'OPC active read attempts', + 'opc_active_read_total', + 'OPC active read attempts', ['pod_id', 'server_name', 'result'], ) OPC_ACTIVE_READ_LATENCY = Histogram( - 'opc_active_read_latency_seconds', 'OPC active read latency', ['pod_id', 'server_name'], + 'opc_active_read_latency_seconds', + 'OPC active read latency', + ['pod_id', 'server_name'], ) OPC_READ_LAG = Gauge( - 'opc_read_lag_seconds', 'Lag between OPC source timestamp and machine time', + 'opc_read_lag_seconds', + 'Lag between OPC source timestamp and machine time', ['pod_id', 'server_name'], ) OPC_TAG_VALUE = Gauge( - 'opc_tag_value', 'Last value read per tag/model', ['pod_id', 'tag_name', 'model_id'], + 'opc_tag_value', + 'Last value read per tag/model', + ['pod_id', 'tag_name', 'model_id'], ) # --- Gateway: connection/error metrics --- OPC_DISCONNECTIONS_TOTAL = Counter( - 'opc_disconnections_total', 'OPC disconnections', ['pod_id', 'server_name'], + 'opc_disconnections_total', + 'OPC disconnections', + ['pod_id', 'server_name'], ) OPC_ERROR_BY_TYPE = Counter( - 'opc_error_by_type_total', 'OPC errors by type', ['pod_id', 'server_name', 'error_type'], + 'opc_error_by_type_total', + 'OPC errors by type', + ['pod_id', 'server_name', 'error_type'], ) diff --git a/tests/unit/engines/test_subscription.py b/tests/unit/engines/test_subscription.py index a088657..5d8cdc8 100644 --- a/tests/unit/engines/test_subscription.py +++ b/tests/unit/engines/test_subscription.py @@ -150,9 +150,7 @@ async def test_subscribe_without_subscription_raises(): async def test_unsubscribe_missing_logs_warning(): eng = make_engine() await eng.unsubscribe('sub1') - eng.logger.warning.assert_called_once_with( - "Subscription 'sub1' not found. Cannot unsubscribe." - ) + eng.logger.warning.assert_called_once_with("Subscription 'sub1' not found. Cannot unsubscribe.") @pytest.mark.asyncio diff --git a/tests/unit/io/test_slots.py b/tests/unit/io/test_slots.py index ccbf17b..f168b50 100644 --- a/tests/unit/io/test_slots.py +++ b/tests/unit/io/test_slots.py @@ -1,10 +1,33 @@ from ingestor.io.slots import parse_slot -RAW = {'server1': {'server_id': '1', 'url': 'opc.tcp://h:4840', 'use_security': True, - 'subscription_period_ms': 1000, 'tags': {'ns=2;i=2': {'frequency': '15000', - 'active_read': {'enabled': True, 'cycle_multiple': 3}, - 'names': [{'collection': 'raw_x', 'tag_name': 'Counter', 'model_name': 'm1', 'model_id': '1'}, - {'collection': 'raw_y', 'tag_name': 'Contador', 'model_name': 'm2', 'model_id': '2'}]}}}} +RAW = { + 'server1': { + 'server_id': '1', + 'url': 'opc.tcp://h:4840', + 'use_security': True, + 'subscription_period_ms': 1000, + 'tags': { + 'ns=2;i=2': { + 'frequency': '15000', + 'active_read': {'enabled': True, 'cycle_multiple': 3}, + 'names': [ + { + 'collection': 'raw_x', + 'tag_name': 'Counter', + 'model_name': 'm1', + 'model_id': '1', + }, + { + 'collection': 'raw_y', + 'tag_name': 'Contador', + 'model_name': 'm2', + 'model_id': '2', + }, + ], + } + }, + } +} def test_parse_slot_multi_name(): @@ -15,8 +38,20 @@ def test_parse_slot_multi_name(): def test_parse_slot_defaults_active_read_disabled(): - raw = {'s': {'server_id': '1', 'url': 'u', 'tags': {'ns=2;i=1': { - 'frequency': '1000', 'names': [{'collection': 'c', 'tag_name': 't', 'model_name': 'm', 'model_id': '1'}]}}}} + raw = { + 's': { + 'server_id': '1', + 'url': 'u', + 'tags': { + 'ns=2;i=1': { + 'frequency': '1000', + 'names': [ + {'collection': 'c', 'tag_name': 't', 'model_name': 'm', 'model_id': '1'} + ], + } + }, + } + } tag = parse_slot(raw)['s']['tags']['ns=2;i=1'] assert tag['active_read'] == {'enabled': False} @@ -24,5 +59,6 @@ def test_parse_slot_defaults_active_read_disabled(): def test_parse_slot_rejects_tag_without_names(): raw = {'s': {'server_id': '1', 'url': 'u', 'tags': {'ns=2;i=1': {'frequency': '1000'}}}} import pytest + with pytest.raises((KeyError, ValueError)): parse_slot(raw) diff --git a/tests/unit/test_converters.py b/tests/unit/test_converters.py index b5a4ff3..4df64e7 100644 --- a/tests/unit/test_converters.py +++ b/tests/unit/test_converters.py @@ -3,10 +3,20 @@ import pytest from ingestor.converters import ConversionError, to_typed_value -@pytest.mark.parametrize('raw,expected', [ - ('false', False), ('False', False), ('0', False), ('no', False), - ('off', False), ('', False), ('true', True), ('1', True), (True, True), -]) +@pytest.mark.parametrize( + 'raw,expected', + [ + ('false', False), + ('False', False), + ('0', False), + ('no', False), + ('off', False), + ('', False), + ('true', True), + ('1', True), + (True, True), + ], +) def test_bool_from_json_string(raw, expected): assert to_typed_value(raw, 'boolean') is expected @@ -16,9 +26,15 @@ def test_float_and_int(): assert to_typed_value('7', 'int') == 7 -@pytest.mark.parametrize('raw,dtype', [ - (None, 'int'), ('abc', 'float'), ('123.123', 'int'), (None, 'float'), -]) +@pytest.mark.parametrize( + 'raw,dtype', + [ + (None, 'int'), + ('abc', 'float'), + ('123.123', 'int'), + (None, 'float'), + ], +) def test_invalid_raises_conversion_error(raw, dtype): with pytest.raises(ConversionError): to_typed_value(raw, dtype)