From a2aac6e49a96fd6cd0420dc53d6ea119144e23c7 Mon Sep 17 00:00:00 2001 From: Pelle Koster <pelle.koster@geant.org> Date: Tue, 4 Feb 2025 11:03:21 +0100 Subject: [PATCH] Speed up test by implementing minimal jsonschema validation --- .../interface_stats/vendors/nokia.py | 4 +- test/interface_stats/conftest.py | 40 +++++++++++++++++++ test/interface_stats/test_interface_stats.py | 6 +-- test/interface_stats/test_juniper.py | 18 ++++----- test/interface_stats/test_nokia.py | 13 +++--- 5 files changed, 60 insertions(+), 21 deletions(-) diff --git a/brian_polling_manager/interface_stats/vendors/nokia.py b/brian_polling_manager/interface_stats/vendors/nokia.py index a8879fb..d458ed4 100644 --- a/brian_polling_manager/interface_stats/vendors/nokia.py +++ b/brian_polling_manager/interface_stats/vendors/nokia.py @@ -19,7 +19,7 @@ NCCLIENT_PARAMS = { } INTERFACE_COUNTERS = { - # Counters must be floats to be compatible with influx writing to the same + # Counters must be floats to be compatible with sensu writing to the same # measurement. cf https://github.com/sensu/sensu-go/issues/2213 "__defaults__": {"transform": float, "required": False}, "name": { @@ -51,7 +51,7 @@ INTERFACE_COUNTERS = { } INTERFACE_COUNTERS_ALT = { - # Counters must be floats to be compatible with influx writing to the same + # Counters must be floats to be compatible with sensu writing to the same # measurement. cf https://github.com/sensu/sensu-go/issues/2213 "__defaults__": {"transform": float, "required": False}, "name": { diff --git a/test/interface_stats/conftest.py b/test/interface_stats/conftest.py index 9958582..6edb22a 100644 --- a/test/interface_stats/conftest.py +++ b/test/interface_stats/conftest.py @@ -97,3 +97,43 @@ def nokia_router_fqdn(request): # do a pytest.mark.parametrize instead but we'd have to redefine NOKIA_ROUTERS in # the test module or import from this conftest directly return request.param + + +@pytest.fixture +def schemavalidate(): + """Minimal jsonschema validation. Just enough to validate the various interface + point dictionaries. Does not resolve $ref. Custom implementation because jsonschema + is slooooooow""" + + def _validate(obj, schema): + type_ = schema.get("type") + + if type_ == "object": + assert isinstance(obj, dict) + assert set(schema.get("required", [])).issubset(obj.keys()) + + additionalProperties = schema.get("additionalProperties", True) + + properties = schema.get("properties", {}) + if not additionalProperties: + assert set(obj).issubset(properties) + + for key, val in obj.items(): + if key not in properties: + if isinstance(additionalProperties, dict): + _validate(val, additionalProperties) + continue + subschema = properties[key] + if "type" not in subschema: + continue + _validate(val, subschema) + elif type_ == "number": + assert isinstance(obj, (float, int)) + elif type_ == "integer": + assert isinstance(obj, int) + elif type_ == "string": + assert isinstance(obj, str) + else: + raise ValueError(f"unsuppported type '{type_}'") + + return _validate diff --git a/test/interface_stats/test_interface_stats.py b/test/interface_stats/test_interface_stats.py index c5ada9c..169a09a 100644 --- a/test/interface_stats/test_interface_stats.py +++ b/test/interface_stats/test_interface_stats.py @@ -2,7 +2,6 @@ from datetime import datetime from unittest.mock import Mock, call, patch from brian_polling_manager import influx -import jsonschema import pytest from brian_polling_manager.interface_stats import cli from brian_polling_manager.interface_stats.cli import PointGroup, Vendor @@ -169,8 +168,9 @@ def test_no_error_point_counters(): @patch.object(cli.OutputMethod, "write_points") +@pytest.mark.usefixtures("mocked_get_netconf") def test_main_for_all_juniper_routers( - write_points, mocked_get_netconf, juniper_router_fqdn, juniper_inventory + write_points, juniper_router_fqdn, juniper_inventory, schemavalidate ): config = { "juniper": {"some": "params"}, @@ -187,7 +187,7 @@ def test_main_for_all_juniper_routers( assert points for point in points: total_points += 1 - jsonschema.validate(point, influx.INFLUX_POINT) + schemavalidate(point, influx.INFLUX_POINT) assert point["fields"] # must contain at least one field write_points.side_effect = validate diff --git a/test/interface_stats/test_juniper.py b/test/interface_stats/test_juniper.py index f6ecca5..1b4745f 100644 --- a/test/interface_stats/test_juniper.py +++ b/test/interface_stats/test_juniper.py @@ -1,7 +1,6 @@ from datetime import datetime from unittest.mock import call, patch -import jsonschema import pytest from brian_polling_manager import influx from brian_polling_manager.interface_stats.vendors import common, juniper @@ -176,9 +175,10 @@ def test_juniper_router_docs_do_not_generate_errors( def test_validate_interface_counters_and_influx_points_for_all_juniper_routers( - juniper_router_fqdn, get_netconf, juniper_inventory + juniper_router_fqdn, get_netconf, juniper_inventory, schemavalidate ): doc = get_netconf(juniper_router_fqdn) + interfaces = list( juniper.interface_counters( doc, interfaces=juniper_inventory[juniper_router_fqdn] @@ -186,7 +186,8 @@ def test_validate_interface_counters_and_influx_points_for_all_juniper_routers( ) assert interfaces for ifc in interfaces: - jsonschema.validate(ifc, common.INTERFACE_COUNTER_SCHEMA) + schemavalidate(ifc, common.INTERFACE_COUNTER_SCHEMA) + schemavalidate(ifc["brian"], common.BRIAN_POINT_FIELDS_SCHEMA) bpoints = list( common.brian_points( @@ -198,10 +199,11 @@ def test_validate_interface_counters_and_influx_points_for_all_juniper_routers( ) assert bpoints for point in bpoints: - jsonschema.validate(point, influx.INFLUX_POINT) - jsonschema.validate(point["fields"], common.BRIAN_POINT_FIELDS_SCHEMA) + schemavalidate(point, influx.INFLUX_POINT) + schemavalidate(point["fields"], common.BRIAN_POINT_FIELDS_SCHEMA) for value in point['fields'].values(): assert isinstance(value, float) + epoints = list( common.error_points( juniper_router_fqdn, @@ -212,10 +214,8 @@ def test_validate_interface_counters_and_influx_points_for_all_juniper_routers( ) assert epoints for point in epoints: - jsonschema.validate(point, influx.INFLUX_POINT) - jsonschema.validate(point["fields"], common.ERROR_POINT_FIELDS_SCHEMA) - for value in point['fields'].values(): - assert isinstance(value, int) + schemavalidate(point, influx.INFLUX_POINT) + schemavalidate(point["fields"], common.ERROR_POINT_FIELDS_SCHEMA) class TestGetJuniperNetConf: diff --git a/test/interface_stats/test_nokia.py b/test/interface_stats/test_nokia.py index 6bffba1..1b368d1 100644 --- a/test/interface_stats/test_nokia.py +++ b/test/interface_stats/test_nokia.py @@ -2,7 +2,6 @@ from datetime import datetime from unittest.mock import call, patch from brian_polling_manager import influx from brian_polling_manager.interface_stats.vendors import common, nokia -import jsonschema import pytest from lxml import etree @@ -183,13 +182,13 @@ def test_nokia_router_docs_do_not_generate_errors( def test_validate_interface_counters_and_influx_points_for_all_nokia_routers( - nokia_router_fqdn, get_netconf + nokia_router_fqdn, get_netconf, schemavalidate ): doc = get_netconf(nokia_router_fqdn) interfaces = list(nokia.interface_counters(doc)) assert interfaces for ifc in interfaces: - jsonschema.validate(ifc, common.INTERFACE_COUNTER_SCHEMA) + schemavalidate(ifc, common.INTERFACE_COUNTER_SCHEMA) bpoints = list( common.brian_points( @@ -201,8 +200,8 @@ def test_validate_interface_counters_and_influx_points_for_all_nokia_routers( ) assert bpoints for point in bpoints: - jsonschema.validate(point, influx.INFLUX_POINT) - jsonschema.validate(point["fields"], common.BRIAN_POINT_FIELDS_SCHEMA) + schemavalidate(point, influx.INFLUX_POINT) + schemavalidate(point["fields"], common.BRIAN_POINT_FIELDS_SCHEMA) epoints = list( common.error_points( @@ -214,8 +213,8 @@ def test_validate_interface_counters_and_influx_points_for_all_nokia_routers( ) assert epoints for point in epoints: - jsonschema.validate(point, influx.INFLUX_POINT) - jsonschema.validate(point["fields"], common.ERROR_POINT_FIELDS_SCHEMA) + schemavalidate(point, influx.INFLUX_POINT) + schemavalidate(point["fields"], common.ERROR_POINT_FIELDS_SCHEMA) def test_processes_specific_interfaces(get_netconf, caplog): -- GitLab