From c4ecf16927ef14aa3577e8eb2a50afdadfb3ea27 Mon Sep 17 00:00:00 2001 From: Claus Holbech Date: Tue, 13 Aug 2024 14:46:54 +0200 Subject: [PATCH] CI: Refactor tests and enable Ruff (#4354) --- pyproject.toml | 6 +- python-avd/pyavd/_schema/avdschema.py | 4 +- .../pyavd/eos_cli_config_gen/conftest.py | 31 ++-- .../test_get_device_config.py | 8 +- .../eos_cli_config_gen/test_get_device_doc.py | 7 +- .../test_validate_structured_config.py | 18 +-- .../tests/pyavd/eos_designs/conftest.py | 38 ++--- .../pyavd/eos_designs/test_get_avd_facts.py | 6 +- .../eos_designs/test_get_device_config.py | 7 +- .../test_get_device_structured_config.py | 6 +- .../pyavd/eos_designs/test_validate_inputs.py | 12 +- .../test_validate_structured_config.py | 18 +-- .../tests/pyavd/j2filters/test_add_md_toc.py | 47 +++--- .../tests/pyavd/j2filters/test_decrypt.py | 4 +- .../tests/pyavd/j2filters/test_default.py | 4 +- .../tests/pyavd/j2filters/test_encrypt.py | 8 +- .../pyavd/j2filters/test_hide_passwords.py | 14 +- .../pyavd/j2filters/test_is_in_filter.py | 5 +- .../pyavd/j2filters/test_list_compress.py | 12 +- .../pyavd/j2filters/test_natural_sort.py | 11 +- .../pyavd/j2filters/test_range_expand.py | 12 +- .../tests/pyavd/j2filters/test_snmp_hash.py | 24 ++- .../pyavd/j2filters/test_status_render.py | 4 +- .../tests/pyavd/j2tests/test_contains.py | 6 +- .../pyavd/j2tests/test_defined_plugin.py | 146 +++++++++--------- .../tests/pyavd/schema/test_avdschema.py | 135 +++++++--------- .../tests/pyavd/utils/merge/test_merge.py | 23 ++- .../pyavd/utils/password/test_password.py | 114 ++++++-------- .../utils/password/test_password_utils.py | 40 ++--- python-avd/tests/pyavd/utils/test_get.py | 15 +- .../pyavd/utils/test_get_ip_from_pool.py | 17 +- .../utils/test_short_esi_to_route_target.py | 6 +- .../tests/pyavd/utils/test_strip_empties.py | 26 ++-- python-avd/tests/schema_tools/conftest.py | 14 +- .../generate_docs/test_mdtabsgen.py | 9 +- .../metaschema/test_meta_schema_model.py | 8 +- python-avd/tests/utils.py | 29 ++-- 37 files changed, 405 insertions(+), 489 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 154a9fd339d..c2bf7ee9541 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,6 @@ known_first_party = ["pyavd", "schema_tools"] line-length = 160 extend-exclude = [ "python-avd/pyavd/_cv/api/**/*", - "python-avd/tests/**/*", "ansible_collections/arista/avd/tests/**/*", ] target-version = "py310" @@ -78,6 +77,11 @@ convention = "google" "python-avd/pyavd/_cv/client/*.py" = [ "B904", # Within an `except` clause, raise exceptions with `raise - TODO: Improve code ] +"python-avd/tests/**/*" = [ + "S101", # Accept 'assert' in pytest. + "INP001", # implicit namespace package. Add an `__init__.py` - Tests are not in packages +] + [tool.ruff.lint.pylint] max-args = 12 diff --git a/python-avd/pyavd/_schema/avdschema.py b/python-avd/pyavd/_schema/avdschema.py index b235dbfb910..6363f7f1b89 100644 --- a/python-avd/pyavd/_schema/avdschema.py +++ b/python-avd/pyavd/_schema/avdschema.py @@ -3,7 +3,7 @@ # that can be found in the LICENSE file. from __future__ import annotations -from typing import TYPE_CHECKING, Any, NoReturn +from typing import TYPE_CHECKING, Any import jsonschema from deepmerge import always_merger @@ -86,7 +86,7 @@ def load_schema(self, schema: dict | None = None, schema_id: str | None = None) msg = "An error occurred during creation of the validator" raise AristaAvdError(msg) from e - def extend_schema(self, schema: dict) -> NoReturn: + def extend_schema(self, schema: dict) -> None: for validation_error in self.validate_schema(schema): raise validation_error always_merger.merge(self._schema, schema) diff --git a/python-avd/tests/pyavd/eos_cli_config_gen/conftest.py b/python-avd/tests/pyavd/eos_cli_config_gen/conftest.py index 149100b6e0d..79787a99201 100644 --- a/python-avd/tests/pyavd/eos_cli_config_gen/conftest.py +++ b/python-avd/tests/pyavd/eos_cli_config_gen/conftest.py @@ -1,32 +1,28 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from glob import iglob from pathlib import Path import pytest -from ...utils import read_file, read_vars +from tests.utils import read_file, read_vars VARS_PATH = Path(Path(__file__).parent, "../artifacts/eos_cli_config_gen/vars") CONFIGS_PATH = Path(Path(__file__).parent, "../artifacts/eos_cli_config_gen/configs") DOCS_PATH = Path(Path(__file__).parent, "../artifacts/eos_cli_config_gen/documentation") -def get_hostnames(): +def get_hostnames() -> list: assert Path(VARS_PATH).is_dir() - hostnames = [] - for device_var_file in iglob(f"{VARS_PATH}/*"): - hostnames.append(Path(device_var_file).name.removesuffix(".yaml").removesuffix(".yml").removesuffix(".json")) - - return hostnames + return [Path(device_var_file).name.removesuffix(".yaml").removesuffix(".yml").removesuffix(".json") for device_var_file in Path(VARS_PATH).glob("*")] @pytest.fixture(scope="module") def all_inputs() -> dict[str, dict]: """ - Return dict with all inputs like + Return dict with all inputs. + { "hostname1": dict "hostname2": dict @@ -36,7 +32,7 @@ def all_inputs() -> dict[str, dict]: assert Path(VARS_PATH).is_dir() inputs = {} - for device_var_file in iglob(f"{VARS_PATH}/*"): + for device_var_file in Path(VARS_PATH).glob("*"): hostname = Path(device_var_file).name.removesuffix(".yaml").removesuffix(".yml").removesuffix(".json") inputs[hostname] = read_vars(device_var_file) @@ -44,15 +40,15 @@ def all_inputs() -> dict[str, dict]: @pytest.fixture(scope="module", params=get_hostnames()) -def hostname(request) -> dict: - hostname = request.param - return hostname +def hostname(request: pytest.FixtureRequest) -> dict: + return request.param @pytest.fixture(scope="module") def configs() -> dict: """ - Return dict with all configs like + Return dict with all configs. + { "hostname1": str "hostname2": str @@ -62,7 +58,7 @@ def configs() -> dict: assert Path(CONFIGS_PATH).is_dir() result = {} - for filename in iglob(f"{CONFIGS_PATH}/*"): + for filename in Path(CONFIGS_PATH).glob("*"): hostname = Path(filename).name.removesuffix(".cfg") result[hostname] = read_file(filename) @@ -72,7 +68,8 @@ def configs() -> dict: @pytest.fixture(scope="module") def device_docs() -> dict: """ - Return dict with all docs like + Return dict with all docs. + { "hostname1": str "hostname2": str @@ -82,7 +79,7 @@ def device_docs() -> dict: assert Path(DOCS_PATH).is_dir() result = {} - for filename in iglob(f"{DOCS_PATH}/*"): + for filename in Path(DOCS_PATH).glob("*"): hostname = Path(filename).name.removesuffix(".md") result[hostname] = read_file(filename) diff --git a/python-avd/tests/pyavd/eos_cli_config_gen/test_get_device_config.py b/python-avd/tests/pyavd/eos_cli_config_gen/test_get_device_config.py index 2714f855843..67e5b60ed33 100644 --- a/python-avd/tests/pyavd/eos_cli_config_gen/test_get_device_config.py +++ b/python-avd/tests/pyavd/eos_cli_config_gen/test_get_device_config.py @@ -4,11 +4,8 @@ from pyavd import get_device_config, validate_structured_config -def test_get_device_config(hostname: str, all_inputs: dict, configs: dict): - """ - Test get_device_config - """ - +def test_get_device_config(hostname: str, all_inputs: dict, configs: dict) -> None: + """Test get_device_config.""" structured_config: dict = all_inputs[hostname] expected_config: str = configs[hostname] @@ -18,6 +15,5 @@ def test_get_device_config(hostname: str, all_inputs: dict, configs: dict): device_config = get_device_config(structured_config) assert isinstance(device_config, str) - # assert f"hostname {hostname}\n" in eos_config assert device_config == expected_config diff --git a/python-avd/tests/pyavd/eos_cli_config_gen/test_get_device_doc.py b/python-avd/tests/pyavd/eos_cli_config_gen/test_get_device_doc.py index 29d2a4abc38..21e6edc30a1 100644 --- a/python-avd/tests/pyavd/eos_cli_config_gen/test_get_device_doc.py +++ b/python-avd/tests/pyavd/eos_cli_config_gen/test_get_device_doc.py @@ -4,11 +4,8 @@ from pyavd import get_device_doc -def test_get_device_doc(hostname: str, all_inputs: dict, device_docs: dict): - """ - Test get_device_config - """ - +def test_get_device_doc(hostname: str, all_inputs: dict, device_docs: dict) -> None: + """Test get_device_config.""" structured_config: dict = all_inputs[hostname] if not structured_config.get("generate_device_documentation", True): return diff --git a/python-avd/tests/pyavd/eos_cli_config_gen/test_validate_structured_config.py b/python-avd/tests/pyavd/eos_cli_config_gen/test_validate_structured_config.py index b63b6554dd1..47499a0a351 100644 --- a/python-avd/tests/pyavd/eos_cli_config_gen/test_validate_structured_config.py +++ b/python-avd/tests/pyavd/eos_cli_config_gen/test_validate_structured_config.py @@ -8,20 +8,18 @@ SCHEMA = AvdSchemaTools(schema_id="eos_cli_config_gen").avdschema._schema -def test_validate_structured_config_with_valid_data(hostname: str, all_inputs: dict): - """ - Test validate_structured_config - """ +def test_validate_structured_config_with_valid_data(hostname: str, all_inputs: dict) -> None: + """Test validate_structured_config.""" structured_config = all_inputs[hostname] validation_result = validate_structured_config(structured_config) - assert hostname and validation_result.validation_errors == [] - assert hostname and validation_result.failed is False + assert hostname + assert validation_result.validation_errors == [] + assert hostname + assert validation_result.failed is False -def test_validate_structured_config_with_invalid_data(hostname: str, all_inputs: dict): - """ - Test validate_structured_config - """ +def test_validate_structured_config_with_invalid_data(hostname: str, all_inputs: dict) -> None: + """Test validate_structured_config.""" structured_config: dict = all_inputs[hostname] updated = False diff --git a/python-avd/tests/pyavd/eos_designs/conftest.py b/python-avd/tests/pyavd/eos_designs/conftest.py index bc045a68180..6afc0fe5b6c 100644 --- a/python-avd/tests/pyavd/eos_designs/conftest.py +++ b/python-avd/tests/pyavd/eos_designs/conftest.py @@ -1,34 +1,29 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from glob import iglob from pathlib import Path import pytest from pyavd import get_avd_facts - -from ...utils import read_file, read_vars +from tests.utils import read_file, read_vars VARS_PATH = Path(Path(__file__).parent, "../artifacts/eos_designs_unit_tests/vars") STRUCTURED_CONFIGS_PATH = Path(Path(__file__).parent, "../artifacts/eos_designs_unit_tests/structured_configs") CONFIGS_PATH = Path(Path(__file__).parent, "../artifacts/eos_designs_unit_tests/configs") -def get_hostnames(): +def get_hostnames() -> list: assert Path(VARS_PATH).is_dir() - hostnames = [] - for device_var_file in iglob(f"{VARS_PATH}/*"): - hostnames.append(Path(device_var_file).name.removesuffix(".yaml").removesuffix(".yml").removesuffix(".json")) - - return hostnames + return [Path(device_var_file).name.removesuffix(".yaml").removesuffix(".yml").removesuffix(".json") for device_var_file in Path(VARS_PATH).glob("*")] @pytest.fixture(scope="module") def all_inputs() -> dict[str, dict]: """ - Return dict with all inputs like + Return dict with all inputs. + { "hostname1": dict "hostname2": dict @@ -38,7 +33,7 @@ def all_inputs() -> dict[str, dict]: assert Path(VARS_PATH).is_dir() inputs = {} - for device_var_file in iglob(f"{VARS_PATH}/*"): + for device_var_file in Path(VARS_PATH).glob("*"): hostname = Path(device_var_file).name.removesuffix(".yaml").removesuffix(".yml").removesuffix(".json") inputs[hostname] = read_vars(device_var_file) @@ -46,23 +41,21 @@ def all_inputs() -> dict[str, dict]: @pytest.fixture(scope="module") -def avd_facts(all_inputs: dict): - """ - Test get_avd_facts - """ +def avd_facts(all_inputs: dict) -> dict: + """Test get_avd_facts.""" return get_avd_facts(all_inputs) @pytest.fixture(scope="module", params=get_hostnames()) -def hostname(request) -> dict: - hostname = request.param - return hostname +def hostname(request: pytest.FixtureRequest) -> dict: + return request.param @pytest.fixture(scope="module") def structured_configs() -> dict: """ - Return dict with all structured_configs like + Return dict with all structured_configs. + { "hostname1": dict "hostname2": dict @@ -72,7 +65,7 @@ def structured_configs() -> dict: assert Path(STRUCTURED_CONFIGS_PATH).is_dir() result = {} - for filename in iglob(f"{STRUCTURED_CONFIGS_PATH}/*"): + for filename in Path(STRUCTURED_CONFIGS_PATH).glob("*"): hostname = Path(filename).name.removesuffix(".yaml").removesuffix(".yml").removesuffix(".json") result[hostname] = read_vars(filename) @@ -82,7 +75,8 @@ def structured_configs() -> dict: @pytest.fixture(scope="module") def configs() -> dict: """ - Return dict with all configs like + Return dict with all configs. + { "hostname1": str "hostname2": str @@ -92,7 +86,7 @@ def configs() -> dict: assert Path(CONFIGS_PATH).is_dir() result = {} - for filename in iglob(f"{CONFIGS_PATH}/*"): + for filename in Path(CONFIGS_PATH).glob("*"): hostname = Path(filename).name.removesuffix(".cfg") result[hostname] = read_file(filename) diff --git a/python-avd/tests/pyavd/eos_designs/test_get_avd_facts.py b/python-avd/tests/pyavd/eos_designs/test_get_avd_facts.py index a0dddef8f95..faf545c9c20 100644 --- a/python-avd/tests/pyavd/eos_designs/test_get_avd_facts.py +++ b/python-avd/tests/pyavd/eos_designs/test_get_avd_facts.py @@ -4,10 +4,8 @@ from pyavd import get_avd_facts -def test_get_avd_facts(all_inputs: dict): - """ - Test get_avd_facts - """ +def test_get_avd_facts(all_inputs: dict) -> None: + """Test get_avd_facts.""" avd_facts = get_avd_facts(all_inputs) assert isinstance(avd_facts, dict) diff --git a/python-avd/tests/pyavd/eos_designs/test_get_device_config.py b/python-avd/tests/pyavd/eos_designs/test_get_device_config.py index a15e756ca12..5bbc5f18d97 100644 --- a/python-avd/tests/pyavd/eos_designs/test_get_device_config.py +++ b/python-avd/tests/pyavd/eos_designs/test_get_device_config.py @@ -4,11 +4,8 @@ from pyavd import get_device_config, validate_structured_config -def test_get_device_config(hostname: str, all_inputs: dict, structured_configs: dict, configs: dict): - """ - Test get_device_config - """ - +def test_get_device_config(hostname: str, all_inputs: dict, structured_configs: dict, configs: dict) -> None: + """Test get_device_config.""" # Loading inputs first and then updating structured config on top. # This is how Ansible behaves, so we need this to generate the same configs. # The underlying cause is eos_cli_config_gen inputs being set in eos_designs molecule vars, diff --git a/python-avd/tests/pyavd/eos_designs/test_get_device_structured_config.py b/python-avd/tests/pyavd/eos_designs/test_get_device_structured_config.py index c78d7cb35a8..7d78598aaf4 100644 --- a/python-avd/tests/pyavd/eos_designs/test_get_device_structured_config.py +++ b/python-avd/tests/pyavd/eos_designs/test_get_device_structured_config.py @@ -4,10 +4,8 @@ from pyavd import get_device_structured_config, validate_inputs -def test_get_device_structured_config(hostname: str, all_inputs: dict, avd_facts: dict, structured_configs: dict): - """ - Test get_device_structured_config - """ +def test_get_device_structured_config(hostname: str, all_inputs: dict, avd_facts: dict, structured_configs: dict) -> None: + """Test get_device_structured_config.""" inputs = all_inputs[hostname] # run validation on inputs to ensure it is converted diff --git a/python-avd/tests/pyavd/eos_designs/test_validate_inputs.py b/python-avd/tests/pyavd/eos_designs/test_validate_inputs.py index e82230d83be..9d9578f2f12 100644 --- a/python-avd/tests/pyavd/eos_designs/test_validate_inputs.py +++ b/python-avd/tests/pyavd/eos_designs/test_validate_inputs.py @@ -4,11 +4,11 @@ from pyavd import validate_inputs -def test_validate_inputs_with_valid_inputs(hostname: str, all_inputs: dict): - """ - Test validate_inputs - """ +def test_validate_inputs_with_valid_inputs(hostname: str, all_inputs: dict) -> None: + """Test validate_inputs.""" inputs = all_inputs[hostname] validation_result = validate_inputs(inputs) - assert hostname and validation_result.validation_errors == [] - assert hostname and validation_result.failed is False + assert hostname + assert validation_result.validation_errors == [] + assert hostname + assert validation_result.failed is False diff --git a/python-avd/tests/pyavd/eos_designs/test_validate_structured_config.py b/python-avd/tests/pyavd/eos_designs/test_validate_structured_config.py index 09caae8e5b8..f8930080e40 100644 --- a/python-avd/tests/pyavd/eos_designs/test_validate_structured_config.py +++ b/python-avd/tests/pyavd/eos_designs/test_validate_structured_config.py @@ -8,20 +8,18 @@ SCHEMA = AvdSchemaTools(schema_id="eos_cli_config_gen").avdschema._schema -def test_validate_structured_config_with_valid_data(hostname: str, structured_configs: dict): - """ - Test validate_structured_config - """ +def test_validate_structured_config_with_valid_data(hostname: str, structured_configs: dict) -> None: + """Test validate_structured_config.""" structured_config = structured_configs[hostname] validation_result = validate_structured_config(structured_config) - assert hostname and validation_result.validation_errors == [] - assert hostname and validation_result.failed is False + assert hostname + assert validation_result.validation_errors == [] + assert hostname + assert validation_result.failed is False -def test_validate_structured_config_with_invalid_data(hostname: str, structured_configs: dict): - """ - Test validate_structured_config - """ +def test_validate_structured_config_with_invalid_data(hostname: str, structured_configs: dict) -> None: + """Test validate_structured_config.""" structured_config = structured_configs[hostname] updated = False diff --git a/python-avd/tests/pyavd/j2filters/test_add_md_toc.py b/python-avd/tests/pyavd/j2filters/test_add_md_toc.py index 2495efe55b5..bb8af659f94 100644 --- a/python-avd/tests/pyavd/j2filters/test_add_md_toc.py +++ b/python-avd/tests/pyavd/j2filters/test_add_md_toc.py @@ -1,9 +1,7 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from __future__ import absolute_import, division, print_function -__metaclass__ = type from pathlib import Path @@ -25,46 +23,51 @@ class TestAddMdTocFilter: """Class to test add_md_toc filter.""" @pytest.mark.parametrize("skip_lines", SKIP_LINES_LIST) - def test_add_md_toc(self, skip_lines): + def test_add_md_toc(self, skip_lines: int) -> None: """Test add_md_toc success scenarii.""" with Path(MD_INPUT_VALID).open("r", encoding="UTF-8") as input_file: resp = add_md_toc(input_file.read(), skip_lines=skip_lines, toc_levels=VALID_TOC_LEVEL, toc_marker=TOC_MARKER) - with open(EXPECTED_TOC, "r", encoding="UTF-8") as input_file: + with Path(EXPECTED_TOC).open(encoding="UTF-8") as input_file: expected_toc = input_file.read() assert resp.strip() != expected_toc.strip() - def test_add_md_toc_invalid_skip_lines(self): + def test_add_md_toc_invalid_skip_lines(self) -> None: """Test add_md_toc with invalid skip_lines.""" - with Path(MD_INPUT_VALID).open("r", encoding="UTF-8") as input_file: - with pytest.raises(TypeError, match="add_md_toc 'skip_lines' argument must be an integer."): - add_md_toc(input_file.read(), skip_lines="Not an int") + with ( + Path(MD_INPUT_VALID).open("r", encoding="UTF-8") as input_file, + pytest.raises(TypeError, match="add_md_toc 'skip_lines' argument must be an integer."), + ): + add_md_toc(input_file.read(), skip_lines="Not an int") - def test_add_md_toc_invalid_toc_level(self): + def test_add_md_toc_invalid_toc_level(self) -> None: """Test add_md_toc with invalid toc level.""" - with Path(MD_INPUT_VALID).open("r", encoding="UTF-8") as input_file: - with pytest.raises(TypeError): - add_md_toc(input_file.read(), toc_levels=INVALID_TOC_LEVEL) + with Path(MD_INPUT_VALID).open("r", encoding="UTF-8") as input_file, pytest.raises(TypeError): + add_md_toc(input_file.read(), toc_levels=INVALID_TOC_LEVEL) - def test_add_md_toc_invalid_toc_marker(self): + def test_add_md_toc_invalid_toc_marker(self) -> None: """Test add_md_toc with invalid toc_marker.""" - with Path(MD_INPUT_VALID).open("r", encoding="UTF-8") as input_file: - with pytest.raises(TypeError, match="add_md_toc 'toc_marker' argument must be a non-empty string."): - add_md_toc(input_file.read(), toc_marker=["Not_as_string"]) + with ( + Path(MD_INPUT_VALID).open("r", encoding="UTF-8") as input_file, + pytest.raises(TypeError, match="add_md_toc 'toc_marker' argument must be a non-empty string."), + ): + add_md_toc(input_file.read(), toc_marker=["Not_as_string"]) - def test_add_md_toc_invalid_md_input_type(self): + def test_add_md_toc_invalid_md_input_type(self) -> None: """Test add_md_toc with invalid md_inpuT_type.""" with pytest.raises(TypeError, match="add_md_toc expects a string."): add_md_toc(["not_as_string"]) - def test_add_md_toc_invalid(self): + def test_add_md_toc_invalid(self) -> None: """Test add_md_toc with invalid input file.""" - with Path(MD_INPUT_INVALID).open("r", encoding="UTF-8") as md_input_toc_invalid: - with pytest.raises(ValueError, match="add_md_toc expects exactly two occurrences of the toc marker"): - add_md_toc(md_input_toc_invalid.read()) + with ( + Path(MD_INPUT_INVALID).open("r", encoding="UTF-8") as md_input_toc_invalid, + pytest.raises(ValueError, match="add_md_toc expects exactly two occurrences of the toc marker"), + ): + add_md_toc(md_input_toc_invalid.read()) - def test_add_md_toc_btw_specific_markers(self): + def test_add_md_toc_btw_specific_markers(self) -> None: """Test to add the TOC at the end of the file using the specific markers features.""" with DIR_PATH.joinpath("markers_at_bottom.md").open("r", encoding="UTF-8") as input_file: resp = add_md_toc(input_file.read(), skip_lines=0, toc_levels=2, toc_marker=TOC_MARKER) diff --git a/python-avd/tests/pyavd/j2filters/test_decrypt.py b/python-avd/tests/pyavd/j2filters/test_decrypt.py index 0d4b0d735f6..ef7ecfe7bbc 100644 --- a/python-avd/tests/pyavd/j2filters/test_decrypt.py +++ b/python-avd/tests/pyavd/j2filters/test_decrypt.py @@ -11,7 +11,7 @@ @pytest.mark.parametrize( - "password, passwd_type, key, kwargs, expected_raise", + ("password", "passwd_type", "key", "kwargs", "expected_raise"), [ pytest.param("dummy", None, "dummy", {}, pytest.raises(TypeError), id="Missing Type"), pytest.param("dummy", "eigrp", "dummy", {}, pytest.raises(KeyError), id="Wrong Type"), @@ -27,7 +27,7 @@ ), ], ) -def test_decrypt(password, passwd_type, key, kwargs, expected_raise): +def test_decrypt(password: str, passwd_type: str | None, key: str, kwargs: dict, expected_raise: object) -> None: """Test decrypt method for non existing and existing type.""" with expected_raise: decrypt(password, passwd_type=passwd_type, key=key, **kwargs) diff --git a/python-avd/tests/pyavd/j2filters/test_default.py b/python-avd/tests/pyavd/j2filters/test_default.py index 984251aa2b4..56abad50161 100644 --- a/python-avd/tests/pyavd/j2filters/test_default.py +++ b/python-avd/tests/pyavd/j2filters/test_default.py @@ -1,6 +1,8 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. +from typing import Any + import pytest from jinja2.runtime import Undefined @@ -13,7 +15,7 @@ class TestDefaultFilter: @pytest.mark.parametrize("primary_value", PRIMARY_VALUE_LIST) @pytest.mark.parametrize("default_value", DEFAULT_VALUE_LIST) - def test_default(self, primary_value, default_value): + def test_default(self, primary_value: Any, default_value: Any) -> None: resp = default(primary_value, *default_value) if isinstance(primary_value, Undefined) or primary_value is None and len(DEFAULT_VALUE_LIST) >= 1: for i in default_value: diff --git a/python-avd/tests/pyavd/j2filters/test_encrypt.py b/python-avd/tests/pyavd/j2filters/test_encrypt.py index 9a9ab91cab1..05858e32521 100644 --- a/python-avd/tests/pyavd/j2filters/test_encrypt.py +++ b/python-avd/tests/pyavd/j2filters/test_encrypt.py @@ -11,7 +11,7 @@ @pytest.mark.parametrize( - "password, passwd_type, key, kwargs, expected_raise", + ("password", "passwd_type", "key", "kwargs", "expected_raise"), [ pytest.param("dummy", None, "dummy", {}, pytest.raises(TypeError), id="Missing Type"), pytest.param("dummy", "eigrp", "dummy", {}, pytest.raises(KeyError), id="Wrong Type"), @@ -21,9 +21,7 @@ pytest.param("arista", "ospf_message_digest", "Ethernet1", {"hash_algorithm": "sha512", "key_id": 66}, does_not_raise(), id="Implemented Type OSPF MD"), ], ) -def test_encrypt(password, passwd_type, key, kwargs, expected_raise): - """ - Test encrypt method for non-existing and existing type. - """ +def test_encrypt(password: str | int, passwd_type: str | None, key: str, kwargs: dict, expected_raise: object) -> None: + """Test encrypt method for non-existing and existing type.""" with expected_raise: encrypt(password, passwd_type=passwd_type, key=key, **kwargs) diff --git a/python-avd/tests/pyavd/j2filters/test_hide_passwords.py b/python-avd/tests/pyavd/j2filters/test_hide_passwords.py index 531537c751c..a13f522f331 100644 --- a/python-avd/tests/pyavd/j2filters/test_hide_passwords.py +++ b/python-avd/tests/pyavd/j2filters/test_hide_passwords.py @@ -1,9 +1,7 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from __future__ import absolute_import, division, print_function -__metaclass__ = type import pytest @@ -19,15 +17,13 @@ class TestHidePasswordsFilter: - @pytest.mark.parametrize("value, hide_passwords_flag, hidden_password", VALID_INPUT_HIDE_PASSWORDS) - def test_hide_passwords_valid(self, value, hide_passwords_flag, hidden_password): - """ - Test hide_passwords - """ + @pytest.mark.parametrize(("value", "hide_passwords_flag", "hidden_password"), VALID_INPUT_HIDE_PASSWORDS) + def test_hide_passwords_valid(self, value: str | None, hide_passwords_flag: bool, hidden_password: str) -> None: + """Test hide_passwords.""" assert hide_passwords(value, hide_passwords_flag) == hidden_password - @pytest.mark.parametrize("value, hide_passwords_flag, error_msg", INVALID_INPUT_HIDE_PASSWORDS) - def test_hide_passwords_invalid(self, value, hide_passwords_flag, error_msg): + @pytest.mark.parametrize(("value", "hide_passwords_flag", "error_msg"), INVALID_INPUT_HIDE_PASSWORDS) + def test_hide_passwords_invalid(self, value: str | None, hide_passwords_flag: bool, error_msg: str) -> None: with pytest.raises(TypeError) as exc_info: hide_passwords(value, hide_passwords_flag) assert str(exc_info.value) == error_msg diff --git a/python-avd/tests/pyavd/j2filters/test_is_in_filter.py b/python-avd/tests/pyavd/j2filters/test_is_in_filter.py index bc3e6845fbd..2267399418f 100644 --- a/python-avd/tests/pyavd/j2filters/test_is_in_filter.py +++ b/python-avd/tests/pyavd/j2filters/test_is_in_filter.py @@ -1,7 +1,6 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from __future__ import absolute_import, division, print_function import pytest @@ -23,7 +22,7 @@ class TestIsInFilter: - @pytest.mark.parametrize("hostname, hostname_filter, res_is_in_filter", IS_IN_FILTER_TEST_CASES) - def test_is_in_filter(self, hostname, hostname_filter, res_is_in_filter): + @pytest.mark.parametrize(("hostname", "hostname_filter", "res_is_in_filter"), IS_IN_FILTER_TEST_CASES) + def test_is_in_filter(self, hostname: str, hostname_filter: list | str, res_is_in_filter: bool) -> None: res = is_in_filter(hostname=hostname, hostname_filter=hostname_filter) assert res == res_is_in_filter diff --git a/python-avd/tests/pyavd/j2filters/test_list_compress.py b/python-avd/tests/pyavd/j2filters/test_list_compress.py index 1c9a6837ee3..1a70740ab87 100644 --- a/python-avd/tests/pyavd/j2filters/test_list_compress.py +++ b/python-avd/tests/pyavd/j2filters/test_list_compress.py @@ -1,9 +1,9 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from __future__ import absolute_import, division, print_function -__metaclass__ = type + +from typing import Any import pytest @@ -25,13 +25,13 @@ class TestListCompressFilter: - @pytest.mark.parametrize("invalid_list_to_compress, error_msg", LIST_COMPRESS_INVALID_TESTS) - def test_list_compress_invalid(self, invalid_list_to_compress, error_msg): + @pytest.mark.parametrize(("invalid_list_to_compress", "error_msg"), LIST_COMPRESS_INVALID_TESTS) + def test_list_compress_invalid(self, invalid_list_to_compress: Any, error_msg: str) -> None: with pytest.raises(TypeError) as exc_info: list_compress(invalid_list_to_compress) assert str(exc_info.value) == error_msg - @pytest.mark.parametrize("valid_list_to_compress, compressed_string", LIST_COMPRESS_VALID_TESTS) - def test_list_compress_valid(self, valid_list_to_compress, compressed_string): + @pytest.mark.parametrize(("valid_list_to_compress", "compressed_string"), LIST_COMPRESS_VALID_TESTS) + def test_list_compress_valid(self, valid_list_to_compress: list, compressed_string: str) -> None: resp = list_compress(valid_list_to_compress) assert resp in compressed_string diff --git a/python-avd/tests/pyavd/j2filters/test_natural_sort.py b/python-avd/tests/pyavd/j2filters/test_natural_sort.py index da9d05a6e94..a05b27123c9 100644 --- a/python-avd/tests/pyavd/j2filters/test_natural_sort.py +++ b/python-avd/tests/pyavd/j2filters/test_natural_sort.py @@ -3,6 +3,7 @@ # that can be found in the LICENSE file. from contextlib import nullcontext as does_not_raise +from typing import Any import pytest @@ -11,7 +12,7 @@ class TestNaturalSortFilter: @pytest.mark.parametrize( - ("item_to_convert, converted_item, ignore_case"), + ("item_to_convert", "converted_item", "ignore_case"), [ ("100", 100, True), ("200", 200, True), @@ -19,12 +20,12 @@ class TestNaturalSortFilter: ("ABC", "ABC", False), ], ) - def test_convert(self, item_to_convert, converted_item, ignore_case): + def test_convert(self, item_to_convert: str, converted_item: int | str, ignore_case: bool) -> None: resp = convert(item_to_convert, ignore_case) assert resp == converted_item @pytest.mark.parametrize( - ("item_to_natural_sort, sort_key, strict, ignore_case, sorted_list, expected_raise"), + ("item_to_natural_sort", "sort_key", "strict", "ignore_case", "sorted_list", "expected_raise"), [ pytest.param(None, None, False, True, [], does_not_raise(), id="None"), pytest.param([], None, False, True, [], does_not_raise(), id="empty-list"), @@ -135,7 +136,9 @@ def test_convert(self, item_to_convert, converted_item, ignore_case): ), ], ) - def test_natural_sort(self, item_to_natural_sort, sort_key, strict, ignore_case, sorted_list, expected_raise): + def test_natural_sort( + self, item_to_natural_sort: Any, sort_key: str | None, strict: bool | None, ignore_case: bool | None, sorted_list: list | None, expected_raise: object + ) -> None: with expected_raise: resp = natural_sort(item_to_natural_sort, sort_key, strict=strict, ignore_case=ignore_case) assert resp == sorted_list diff --git a/python-avd/tests/pyavd/j2filters/test_range_expand.py b/python-avd/tests/pyavd/j2filters/test_range_expand.py index 405bf532c1b..399adf705b1 100644 --- a/python-avd/tests/pyavd/j2filters/test_range_expand.py +++ b/python-avd/tests/pyavd/j2filters/test_range_expand.py @@ -4,6 +4,8 @@ from __future__ import annotations +from typing import Any + import pytest from pyavd.j2filters import range_expand @@ -80,12 +82,12 @@ class TestRangeExpandFilter: - @pytest.mark.parametrize("input_value, expected_raise, expected_raise_message", RANGE_TO_EXPAND_INVALID_VALUES) - def test_range_expand_invalid(self, input_value, expected_raise, expected_raise_message): + @pytest.mark.parametrize(("input_value", "expected_raise", "expected_raise_message"), RANGE_TO_EXPAND_INVALID_VALUES) + def test_range_expand_invalid(self, input_value: Any, expected_raise: Exception, expected_raise_message: str) -> None: with pytest.raises(expected_raise, match=expected_raise_message): range_expand(input_value) - @pytest.mark.parametrize("RANGE_TO_EXPAND_VALID", RANGE_TO_EXPAND_VALID_VALUES) - def test_range_expand_valid(self, RANGE_TO_EXPAND_VALID): - resp = range_expand(RANGE_TO_EXPAND_VALID) + @pytest.mark.parametrize("range_to_expand_valid", RANGE_TO_EXPAND_VALID_VALUES) + def test_range_expand_valid(self, range_to_expand_valid: list | str) -> None: + resp = range_expand(range_to_expand_valid) assert resp in EXPECTED_RESULT_VALID_VALUES diff --git a/python-avd/tests/pyavd/j2filters/test_snmp_hash.py b/python-avd/tests/pyavd/j2filters/test_snmp_hash.py index 6714e018030..fc9a768a777 100644 --- a/python-avd/tests/pyavd/j2filters/test_snmp_hash.py +++ b/python-avd/tests/pyavd/j2filters/test_snmp_hash.py @@ -1,9 +1,7 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from __future__ import absolute_import, division, print_function -__metaclass__ = type from contextlib import nullcontext as does_not_raise @@ -18,7 +16,7 @@ ("sha256", "sha256", does_not_raise()), ("sha384", "sha384", does_not_raise()), ("sha512", "sha512", does_not_raise()), - ("toto", None, pytest.raises(ValueError)), + ("toto", None, pytest.raises(ValueError)), # noqa: PT011 ] KEY_FROM_PASSPHRASE_TEST_CASES = [ @@ -34,7 +32,7 @@ "c005b901a9a29e140e2749bb5d51789bba97f614c3c8060a1c080d775f12494ba6d968dc22526bb06532e96e2e4d3bd0e746f2696439a4a034d040e1c7de0aaa", does_not_raise(), ), - ("testauth", "toto", None, pytest.raises(ValueError)), + ("testauth", "toto", None, pytest.raises(ValueError)), # noqa: PT011 ] LOCALIZE_PASSPHRASE_TEST_CASES = [ @@ -59,31 +57,31 @@ "bccbd436115c60540422ad8e98b8373dee507fd9e77730372f03dcf8e8a074a43d9f04bd6b7be64eb806bdbaeff43ccd1ca93c4606ab46eb797720e4c59abcc7", does_not_raise(), ), - ("testauth", "toto", "424242424242424242", None, None, pytest.raises(ValueError)), + ("testauth", "toto", "424242424242424242", None, None, pytest.raises(ValueError)), # noqa: PT011 # only testing priv with one auth algorithm, the longest, to verify key length ("testpriv", "sha512", "424242424242424242", "des", "ca5e54b5c49e7addba0046c591f8f541", does_not_raise()), ("testpriv", "sha512", "424242424242424242", "aes", "ca5e54b5c49e7addba0046c591f8f541", does_not_raise()), ("testpriv", "sha512", "424242424242424242", "aes192", "ca5e54b5c49e7addba0046c591f8f5417338cdc1043068ab", does_not_raise()), ("testpriv", "sha512", "424242424242424242", "aes256", "ca5e54b5c49e7addba0046c591f8f5417338cdc1043068abf8c2a7ab751f13dc", does_not_raise()), - ("testpriv", "sha512", "424242424242424242", "toto", None, pytest.raises(ValueError)), + ("testpriv", "sha512", "424242424242424242", "toto", None, pytest.raises(ValueError)), # noqa: PT011 # non hex engine_id - ("testpriv", "sha512", "zzzzzzzzzzzz", "toto", None, pytest.raises(ValueError)), + ("testpriv", "sha512", "zzzzzzzzzzzz", "toto", None, pytest.raises(ValueError)), # noqa: PT011 ] class TestSNMPHashFilter: - @pytest.mark.parametrize("auth_type, result, expectation", GET_HASH_OBJECT_TEST_CASES) - def test_get_hash_object(self, auth_type, result, expectation): + @pytest.mark.parametrize(("auth_type", "result", "expectation"), GET_HASH_OBJECT_TEST_CASES) + def test_get_hash_object(self, auth_type: str, result: str, expectation: object) -> None: with expectation: assert _get_hash_object(auth_type).name == result - @pytest.mark.parametrize("passphrase, auth_type, result, expectation", KEY_FROM_PASSPHRASE_TEST_CASES) - def test_key_from_passphrase(self, passphrase, auth_type, result, expectation): + @pytest.mark.parametrize(("passphrase", "auth_type", "result", "expectation"), KEY_FROM_PASSPHRASE_TEST_CASES) + def test_key_from_passphrase(self, passphrase: str, auth_type: str, result: str, expectation: object) -> None: with expectation: assert _key_from_passphrase(passphrase, auth_type) == result - @pytest.mark.parametrize("passphrase, auth_type, engine_id, priv_type, result, expectation", LOCALIZE_PASSPHRASE_TEST_CASES) - def test_localize_passphrase(self, passphrase, auth_type, engine_id, priv_type, result, expectation): + @pytest.mark.parametrize(("passphrase", "auth_type", "engine_id", "priv_type", "result", "expectation"), LOCALIZE_PASSPHRASE_TEST_CASES) + def test_localize_passphrase(self, passphrase: str, auth_type: str, engine_id: str, priv_type: str, result: str, expectation: object) -> None: with expectation: localized_passphrase = _localize_passphrase(passphrase, auth_type, engine_id, priv_type=priv_type) assert localized_passphrase == result diff --git a/python-avd/tests/pyavd/j2filters/test_status_render.py b/python-avd/tests/pyavd/j2filters/test_status_render.py index 66f30f03aa2..3e113ffb553 100644 --- a/python-avd/tests/pyavd/j2filters/test_status_render.py +++ b/python-avd/tests/pyavd/j2filters/test_status_render.py @@ -11,7 +11,7 @@ class TestMarkdownRenderingFilter: - @pytest.mark.parametrize("state_string, rendering, markdown_code", STATE_STRINGS) - def test_status_render_valid(self, state_string, rendering, markdown_code): + @pytest.mark.parametrize(("state_string", "rendering", "markdown_code"), STATE_STRINGS) + def test_status_render_valid(self, state_string: str, rendering: str, markdown_code: str) -> None: resp = status_render(state_string, rendering) assert resp == markdown_code diff --git a/python-avd/tests/pyavd/j2tests/test_contains.py b/python-avd/tests/pyavd/j2tests/test_contains.py index 6b68e60a433..96fa63ecfdd 100644 --- a/python-avd/tests/pyavd/j2tests/test_contains.py +++ b/python-avd/tests/pyavd/j2tests/test_contains.py @@ -5,6 +5,8 @@ from __future__ import annotations +from typing import Any + import pytest from jinja2.runtime import Undefined @@ -27,7 +29,7 @@ class TestContainsTest: """Test Contains.""" - @pytest.mark.parametrize(("value, test_value, expected_result"), TEST_DATA) - def test_contains(self, value, test_value, expected_result): + @pytest.mark.parametrize(("value", "test_value", "expected_result"), TEST_DATA) + def test_contains(self, value: Any, test_value: Any, expected_result: bool) -> None: """Test the contains function.""" assert contains(value, test_value) == expected_result diff --git a/python-avd/tests/pyavd/j2tests/test_defined_plugin.py b/python-avd/tests/pyavd/j2tests/test_defined_plugin.py index aaac0eaf3d1..20b748909de 100644 --- a/python-avd/tests/pyavd/j2tests/test_defined_plugin.py +++ b/python-avd/tests/pyavd/j2tests/test_defined_plugin.py @@ -4,6 +4,7 @@ from __future__ import annotations import warnings +from typing import Any import pytest from jinja2.runtime import Undefined @@ -19,7 +20,16 @@ class TestDefinedPlugin: - def defined_function(self, value, test_value=None, var_type=None, fail_action=None, var_name=None, err_msg=None, warn_msg=None): + def defined_function( + self, + value: Any, + test_value: Any = None, + var_type: str | None = None, + fail_action: str | None = None, + var_name: str | None = None, + err_msg: str | None = None, + warn_msg: str | None = None, + ) -> None: if str(fail_action).lower() == "warning": with warnings.catch_warnings(record=True) as w: resp, warning = defined(value, test_value=test_value, var_type=var_type, fail_action=fail_action, var_name=var_name, run_tests=True) @@ -27,101 +37,97 @@ def defined_function(self, value, test_value=None, var_type=None, fail_action=No assert isinstance(w[0].message, UserWarning) if warn_msg: assert warning is not None - warn = str(list(warning.keys())[0]).replace("[WARNING]: ", "").strip().replace("\n", " ") + warn = str(next(iter(warning.keys()))).replace("[WARNING]: ", "").strip().replace("\n", " ") assert warn == warn_msg assert str(w[0].message) == warn_msg assert resp is False elif str(fail_action).lower() == "error": - with pytest.raises(ValueError) as e: + with pytest.raises(ValueError) as e: # noqa: PT011 resp, warning = defined(value, test_value=test_value, var_type=var_type, fail_action=fail_action, var_name=var_name, run_tests=True) assert str(e.value) == err_msg - @pytest.mark.parametrize("VALUE", VALUE_LIST) - @pytest.mark.parametrize("FAIL_ACTION", FAIL_ACTION_LIST) - @pytest.mark.parametrize("VAR_NAME", VAR_NAME_LIST) - def test_defined_plugin_value_undefined_or_none(self, VALUE, FAIL_ACTION, VAR_NAME): - if isinstance(VALUE, Undefined) or VALUE is None: - if str(FAIL_ACTION).lower() == "warning": - if VAR_NAME is not None: - warn_msg = f"{VAR_NAME} was expected but not set. Output may be incorrect or incomplete!" + @pytest.mark.parametrize("value", VALUE_LIST) + @pytest.mark.parametrize("fail_action", FAIL_ACTION_LIST) + @pytest.mark.parametrize("var_name", VAR_NAME_LIST) + def test_defined_plugin_value_undefined_or_none(self, value: Any, fail_action: str | None, var_name: str | None) -> None: + if isinstance(value, Undefined) or value is None: + if str(fail_action).lower() == "warning": + if var_name is not None: + warn_msg = f"{var_name} was expected but not set. Output may be incorrect or incomplete!" else: warn_msg = "A variable was expected but not set. Output may be incorrect or incomplete!" - self.defined_function(VALUE, fail_action=FAIL_ACTION, var_name=VAR_NAME, warn_msg=warn_msg) - elif str(FAIL_ACTION).lower() == "error": - if VAR_NAME is not None: - err_msg = f"{VAR_NAME} was expected but not set!" - else: - err_msg = "A variable was expected but not set!" + self.defined_function(value, fail_action=fail_action, var_name=var_name, warn_msg=warn_msg) + elif str(fail_action).lower() == "error": + err_msg = f"{var_name} was expected but not set!" if var_name is not None else "A variable was expected but not set!" - self.defined_function(VALUE, fail_action=FAIL_ACTION, var_name=VAR_NAME, err_msg=err_msg) + self.defined_function(value, fail_action=fail_action, var_name=var_name, err_msg=err_msg) - @pytest.mark.parametrize("VALUE", VALUE_LIST) - def test_defined_plugin_value_not_none_and_define(self, VALUE): - if not isinstance(VALUE, Undefined) and VALUE is not None: - resp = defined(VALUE) + @pytest.mark.parametrize("value", VALUE_LIST) + def test_defined_plugin_value_not_none_and_define(self, value: Any) -> None: + if not isinstance(value, Undefined) and value is not None: + resp = defined(value) assert resp is True - @pytest.mark.parametrize("VALUE", VALUE_LIST) - @pytest.mark.parametrize("FAIL_ACTION", FAIL_ACTION_LIST) - @pytest.mark.parametrize("VAR_NAME", VAR_NAME_LIST) - @pytest.mark.parametrize("TEST_VALUE", TEST_VALUE_LIST) - def test_defined_plugin_test_value_not_none(self, VALUE, FAIL_ACTION, VAR_NAME, TEST_VALUE): - if (not isinstance(VALUE, Undefined) and VALUE is not None) and TEST_VALUE is not None and VALUE != TEST_VALUE: - if str(FAIL_ACTION).lower() == "warning": - if VAR_NAME is not None: - warn_msg = f"{VAR_NAME} was set to {VALUE} but we expected {TEST_VALUE}. Output may be incorrect or incomplete!" + @pytest.mark.parametrize("value", VALUE_LIST) + @pytest.mark.parametrize("fail_action", FAIL_ACTION_LIST) + @pytest.mark.parametrize("var_name", VAR_NAME_LIST) + @pytest.mark.parametrize("test_value", TEST_VALUE_LIST) + def test_defined_plugin_test_value_not_none(self, value: Any, fail_action: str | None, var_name: str | None, test_value: Any) -> None: + if (not isinstance(value, Undefined) and value is not None) and test_value is not None and value != test_value: + if str(fail_action).lower() == "warning": + if var_name is not None: + warn_msg = f"{var_name} was set to {value} but we expected {test_value}. Output may be incorrect or incomplete!" else: - warn_msg = f"A variable was set to {VALUE} but we expected {TEST_VALUE}. Output may be incorrect or incomplete!" - self.defined_function(VALUE, fail_action=FAIL_ACTION, var_name=VAR_NAME, test_value=TEST_VALUE, warn_msg=warn_msg) - elif str(FAIL_ACTION).lower() == "error": - if VAR_NAME is not None: - err_msg = f"{VAR_NAME} was set to {VALUE} but we expected {TEST_VALUE}!" + warn_msg = f"A variable was set to {value} but we expected {test_value}. Output may be incorrect or incomplete!" + self.defined_function(value, fail_action=fail_action, var_name=var_name, test_value=test_value, warn_msg=warn_msg) + elif str(fail_action).lower() == "error": + if var_name is not None: + err_msg = f"{var_name} was set to {value} but we expected {test_value}!" else: - err_msg = f"A variable was set to {VALUE} but we expected {TEST_VALUE}!" - self.defined_function(VALUE, fail_action=FAIL_ACTION, var_name=VAR_NAME, test_value=TEST_VALUE, err_msg=err_msg) + err_msg = f"A variable was set to {value} but we expected {test_value}!" + self.defined_function(value, fail_action=fail_action, var_name=var_name, test_value=test_value, err_msg=err_msg) - @pytest.mark.parametrize("VALUE", VALUE_LIST) - @pytest.mark.parametrize("FAIL_ACTION", FAIL_ACTION_LIST) - @pytest.mark.parametrize("VAR_NAME", VAR_NAME_LIST) - @pytest.mark.parametrize("VAR_TYPE", VAR_TYPE_LIST) - def test_defined_plugin_var_type(self, VALUE, FAIL_ACTION, VAR_NAME, VAR_TYPE): + @pytest.mark.parametrize("value", VALUE_LIST) + @pytest.mark.parametrize("fail_action", FAIL_ACTION_LIST) + @pytest.mark.parametrize("var_name", VAR_NAME_LIST) + @pytest.mark.parametrize("var_type", VAR_TYPE_LIST) + def test_defined_plugin_var_type(self, value: Any, fail_action: str | None, var_name: str | None, var_type: Any) -> None: type_list = ["float", "int", "str", "list", "dict", "tuple", "bool"] - if not isinstance(VALUE, Undefined) and VALUE is not None: - if str(VAR_TYPE).lower() in type_list and str(VAR_TYPE).lower() != type(VALUE).__name__: - if str(FAIL_ACTION).lower() == "warning": - if VAR_NAME is not None: - warn_msg = f"{VAR_NAME} was a {type(VALUE).__name__} but we expected a {str(VAR_TYPE).lower()}. Output may be incorrect or incomplete!" - else: - warn_msg = f"A variable was a {type(VALUE).__name__} but we expected a {str(VAR_TYPE).lower()}. Output may be incorrect or incomplete!" - self.defined_function(VALUE, fail_action=FAIL_ACTION, var_name=VAR_NAME, var_type=VAR_TYPE, warn_msg=warn_msg) - elif str(FAIL_ACTION).lower() == "error": - if VAR_NAME is not None: - err_msg = f"{VAR_NAME} was a {type(VALUE).__name__} but we expected a {str(VAR_TYPE).lower()}!" - else: - err_msg = f"A variable was a {type(VALUE).__name__} but we expected a {str(VAR_TYPE).lower()}!" - self.defined_function(VALUE, fail_action=FAIL_ACTION, var_name=VAR_NAME, var_type=VAR_TYPE, err_msg=err_msg) + if not isinstance(value, Undefined) and value is not None and str(var_type).lower() in type_list and str(var_type).lower() != type(value).__name__: + if str(fail_action).lower() == "warning": + if var_name is not None: + warn_msg = f"{var_name} was a {type(value).__name__} but we expected a {str(var_type).lower()}. Output may be incorrect or incomplete!" + else: + warn_msg = f"A variable was a {type(value).__name__} but we expected a {str(var_type).lower()}. Output may be incorrect or incomplete!" + self.defined_function(value, fail_action=fail_action, var_name=var_name, var_type=var_type, warn_msg=warn_msg) + elif str(fail_action).lower() == "error": + if var_name is not None: + err_msg = f"{var_name} was a {type(value).__name__} but we expected a {str(var_type).lower()}!" + else: + err_msg = f"A variable was a {type(value).__name__} but we expected a {str(var_type).lower()}!" + self.defined_function(value, fail_action=fail_action, var_name=var_name, var_type=var_type, err_msg=err_msg) - @pytest.mark.parametrize("VALUE", VALUE_LIST) - @pytest.mark.parametrize("TEST_VALUE", TEST_VALUE_LIST) - @pytest.mark.parametrize("INVALID_FAIL_ACTION", INVALID_FAIL_ACTION_LIST) - def test_defined_plugin_fail_action_None(self, VALUE, TEST_VALUE, INVALID_FAIL_ACTION): - resp = defined(VALUE, test_value=TEST_VALUE, fail_action=INVALID_FAIL_ACTION) - if not isinstance(VALUE, Undefined) and VALUE is not None: - if TEST_VALUE is not None and VALUE != TEST_VALUE: + @pytest.mark.parametrize("value", VALUE_LIST) + @pytest.mark.parametrize("test_value", TEST_VALUE_LIST) + @pytest.mark.parametrize("invalid_fail_action", INVALID_FAIL_ACTION_LIST) + def test_defined_plugin_fail_action_none(self, value: Any, test_value: Any, invalid_fail_action: str | None) -> None: + resp = defined(value, test_value=test_value, fail_action=invalid_fail_action) + if not isinstance(value, Undefined) and value is not None: + if test_value is not None and value != test_value: assert resp is False else: assert resp is True else: assert resp is False - @pytest.mark.parametrize("VALUE", VALUE_LIST) - @pytest.mark.parametrize("VAR_TYPE", VAR_TYPE_LIST) - def test_defined_plugin_var_type_fail_action_None(self, VALUE, VAR_TYPE): + @pytest.mark.parametrize("value", VALUE_LIST) + @pytest.mark.parametrize("var_type", VAR_TYPE_LIST) + def test_defined_plugin_var_type_fail_action_none(self, value: Any, var_type: str | None) -> None: type_list = ["float", "int", "str", "list", "dict", "tuple", "bool"] - if not isinstance(VALUE, Undefined) and VALUE is not None: - resp = defined(VALUE, var_type=VAR_TYPE) - if str(VAR_TYPE).lower() in type_list and type(VALUE).__name__ != str(VAR_TYPE).lower(): + if not isinstance(value, Undefined) and value is not None: + resp = defined(value, var_type=var_type) + if str(var_type).lower() in type_list and type(value).__name__ != str(var_type).lower(): assert resp is False else: assert resp is True diff --git a/python-avd/tests/pyavd/schema/test_avdschema.py b/python-avd/tests/pyavd/schema/test_avdschema.py index 5806317ea64..907b2fa17c4 100644 --- a/python-avd/tests/pyavd/schema/test_avdschema.py +++ b/python-avd/tests/pyavd/schema/test_avdschema.py @@ -1,7 +1,8 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -import os +from pathlib import Path +from typing import Any import pytest import yaml @@ -10,16 +11,16 @@ from pyavd._errors import AvdValidationError from pyavd._schema.avdschema import DEFAULT_SCHEMA, AvdSchema -script_dir = os.path.dirname(__file__) -with open(f"{script_dir}/access_lists.schema.yml", "r", encoding="utf-8") as schema_file: +script_dir = Path(__file__).parent +with Path(script_dir, "access_lists.schema.yml").open(encoding="utf-8") as schema_file: acl_schema = yaml.load(schema_file, Loader=yaml.SafeLoader) -with open(f"{script_dir}/ipv6_standard_access_lists.schema.yml", "r", encoding="utf-8") as schema_file: +with Path(script_dir, "ipv6_standard_access_lists.schema.yml").open(encoding="utf-8") as schema_file: ipv6_acl_schema = yaml.load(schema_file, Loader=yaml.SafeLoader) -with open(f"{script_dir}/combined.schema.yml", "r", encoding="utf-8") as schema_file: +with Path(script_dir, "combined.schema.yml").open(encoding="utf-8") as schema_file: combined_schema = yaml.load(schema_file, Loader=yaml.SafeLoader) -with open(f"{script_dir}/acl.yml", "r", encoding="utf-8") as data_file: +with Path(script_dir, "acl.yml").open(encoding="utf-8") as data_file: acl_test_data = yaml.load(data_file, Loader=yaml.SafeLoader) -with open(f"{script_dir}/ipv6-access-lists.yml", "r", encoding="utf-8") as data_file: +with Path(script_dir, "ipv6-access-lists.yml").open(encoding="utf-8") as data_file: ipv6_acl_test_data = yaml.load(data_file, Loader=yaml.SafeLoader) INVALID_SCHEMA = {"type": "something_invalid"} @@ -110,98 +111,79 @@ class TestAvdSchema: - def test_avd_schema_init_without_schema(self): + def test_avd_schema_init_without_schema(self) -> None: avdschema = AvdSchema() assert isinstance(avdschema, AvdSchema) assert avdschema._schema == DEFAULT_SCHEMA @pytest.mark.parametrize("test_schema", VALID_TEST_SCHEMAS) - def test_avd_schema_init_with_schema(self, test_schema): + def test_avd_schema_init_with_schema(self, test_schema: dict) -> None: avdschema = AvdSchema(test_schema) assert isinstance(avdschema, AvdSchema) assert avdschema._schema == test_schema - def test_avd_schema_init_with_invalid_schema(self): + def test_avd_schema_init_with_invalid_schema(self) -> None: with pytest.raises(AvdValidationError): AvdSchema(INVALID_SCHEMA) @pytest.mark.parametrize("test_schema", VALID_TEST_SCHEMAS) - def test_avd_schema_validate_schema(self, test_schema): - try: - for validation_error in AvdSchema().validate_schema(test_schema): - assert False, f"Validation Error '{validation_error.message}' returned" - except Exception as e: # pylint: disable=broad-exception-caught - assert False, f"AvdSchema().validate_schema(TEST_SCHEMA) raised an exception: {e}" - - def test_avd_schema_validate_invalid_schema(self): - try: - for validation_error in AvdSchema().validate_schema(INVALID_SCHEMA): - assert isinstance(validation_error, AvdValidationError) - except Exception as e: # pylint: disable=broad-exception-caught - assert False, f"AvdSchema().validate_schema(INVALID_SCHEMA) raised an exception: {e}" + def test_avd_schema_validate_schema(self, test_schema: dict) -> None: + validation_errors = list(AvdSchema().validate_schema(test_schema)) + assert not validation_errors + + def test_avd_schema_validate_invalid_schema(self) -> None: + validation_errors = list(AvdSchema().validate_schema(INVALID_SCHEMA)) + assert len(validation_errors) > 0 + for validation_error in validation_errors: + assert isinstance(validation_error, AvdValidationError) @pytest.mark.parametrize("test_data", TEST_DATA_SETS) - def test_avd_schema_validate_without_schema(self, test_data): - try: - list(AvdSchema().validate(test_data)) - except Exception as e: # pylint: disable=broad-exception-caught - assert False, f"AvdSchema().validate(TEST_DATA) raised an exception: {e}" + def test_avd_schema_validate_without_schema(self, test_data: Any) -> None: + validation_errors = list(AvdSchema().validate(test_data)) + assert not validation_errors @pytest.mark.parametrize("test_schema", VALID_TEST_SCHEMAS) @pytest.mark.parametrize("test_data", TEST_DATA_SETS) - def test_avd_schema_validate_with_loaded_schema(self, test_schema, test_data): - try: - for validation_error in AvdSchema(test_schema).validate(test_data): - assert False, f"Validation Error '{validation_error.message}' returned" - except Exception as e: # pylint: disable=broad-exception-caught - assert False, f"AvdSchema(TEST_SCHEMA).validate(TEST_DATA) raised an exception: {e}" + def test_avd_schema_validate_with_loaded_schema(self, test_schema: dict, test_data: Any) -> None: + validation_errors = list(AvdSchema(test_schema).validate(test_data)) + assert not validation_errors @pytest.mark.parametrize("invalid_data", INVALID_ACL_DATA) - def test_avd_schema_validate_with_invalid_data(self, invalid_data): - try: - for validation_error in AvdSchema(combined_schema).validate(invalid_data): - assert isinstance(validation_error, AvdValidationError) - except Exception as e: # pylint: disable=broad-exception-caught - assert False, f"AvdSchema(combined_schema).validate(INVALID_DATA) raised an exception: {e}" + def test_avd_schema_validate_with_invalid_data(self, invalid_data: Any) -> None: + validation_errors = list(AvdSchema(combined_schema).validate(invalid_data)) + assert len(validation_errors) > 0 + for validation_error in validation_errors: + assert isinstance(validation_error, AvdValidationError) @pytest.mark.parametrize("test_schema", VALID_TEST_SCHEMAS) - def test_avd_schema_load_valid_schema(self, test_schema): - try: - avdschema = AvdSchema() - avdschema.load_schema(test_schema) - except Exception as e: # pylint: disable=broad-exception-caught - assert False, f"load_schema(TEST_SCHEMA) raised an exception: {e}" + def test_avd_schema_load_valid_schema(self, test_schema: dict) -> None: + avdschema = AvdSchema() + avdschema.load_schema(test_schema) assert avdschema._schema == test_schema - def test_avd_schema_load_invalid_schema(self): + def test_avd_schema_load_invalid_schema(self) -> None: with pytest.raises(AvdValidationError): - avdschema = AvdSchema() - avdschema.load_schema(INVALID_SCHEMA) + AvdSchema().load_schema(INVALID_SCHEMA) @pytest.mark.parametrize("test_schema", VALID_TEST_SCHEMAS) - def test_avd_schema_extend_valid_schema(self, test_schema): + def test_avd_schema_extend_valid_schema(self, test_schema: dict) -> None: expected_schema = {} expected_schema = always_merger.merge(expected_schema, DEFAULT_SCHEMA) expected_schema = always_merger.merge(expected_schema, test_schema) - try: - avdschema = AvdSchema() - avdschema.extend_schema(test_schema) - except Exception as e: # pylint: disable=broad-exception-caught - assert False, f"extend_schema(TEST_SCHEMA) raised an exception: {e}" + + avdschema = AvdSchema() + avdschema.extend_schema(test_schema) assert avdschema._schema == expected_schema - def test_avd_schema_extend_invalid_schema(self): + def test_avd_schema_extend_invalid_schema(self) -> None: with pytest.raises(AvdValidationError): - avdschema = AvdSchema() - avdschema.extend_schema(INVALID_SCHEMA) + AvdSchema().extend_schema(INVALID_SCHEMA) @pytest.mark.parametrize("test_path", TEST_DATA_PATHS) - def test_avd_schema_subschema_with_loaded_schema(self, test_path): - try: - avdschema = AvdSchema(combined_schema) - subschema = avdschema.subschema(test_path) - except Exception as e: # pylint: disable=broad-exception-caught - assert False, f"subschema(TEST_PATH) raised an exception: {e}" + def test_avd_schema_subschema_with_loaded_schema(self, test_path: list) -> None: + avdschema = AvdSchema(combined_schema) + subschema = avdschema.subschema(test_path) + if len(test_path) == 0: assert subschema == EXPECTED_SUBSCHEMAS["_empty"] else: @@ -209,23 +191,14 @@ def test_avd_schema_subschema_with_loaded_schema(self, test_path): @pytest.mark.parametrize("test_schema", UNIQUE_KEYS_SCHEMAS) @pytest.mark.parametrize("test_data", UNIQUE_KEYS_VALID_DATA) - def test_avd_schema_validate_unique_keys_valid_data(self, test_schema, test_data): - try: - for validation_error in AvdSchema(test_schema).validate(test_data): - assert False, f"Validation Error '{validation_error.message}' returned" - except Exception as e: # pylint: disable=broad-exception-caught - assert False, f"AvdSchema(UNIQUE_KEYS_SCHEMAS).validate(UNIQUE_KEYS_VALID_DATA) raised an exception: {e}" + def test_avd_schema_validate_unique_keys_valid_data(self, test_schema: dict, test_data: Any) -> None: # NOSONAR + validation_errors = list(AvdSchema(test_schema).validate(test_data)) + assert not validation_errors @pytest.mark.parametrize("test_schema", UNIQUE_KEYS_SCHEMAS) @pytest.mark.parametrize("invalid_data", UNIQUE_KEYS_INVALID_DATA) - def test_avd_schema_validate_unique_keys_invalid_data(self, test_schema, invalid_data): - try: - validation_errors = tuple(AvdSchema(test_schema).validate(invalid_data)) - if not validation_errors: - assert False, "did NOT fail validation" - for validation_error in validation_errors: - assert isinstance(validation_error, AvdValidationError) - assert validation_error.path.endswith((".key", ".nested_list_key")) - - except Exception as e: # pylint: disable=broad-exception-caught - assert False, f"AvdSchema(UNIQUE_KEYS_SCHEMAS).validate(UNIQUE_KEYS_INVALID_DATA) raised an exception: {e}" + def test_avd_schema_validate_unique_keys_invalid_data(self, test_schema: dict, invalid_data: Any) -> None: # NOSONAR + validation_errors = list(AvdSchema(test_schema).validate(invalid_data)) + assert len(validation_errors) > 0 + for validation_error in validation_errors: + assert isinstance(validation_error, AvdValidationError) diff --git a/python-avd/tests/pyavd/utils/merge/test_merge.py b/python-avd/tests/pyavd/utils/merge/test_merge.py index 8ac457da051..908cefda47d 100644 --- a/python-avd/tests/pyavd/utils/merge/test_merge.py +++ b/python-avd/tests/pyavd/utils/merge/test_merge.py @@ -1,11 +1,7 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -import os +from pathlib import Path import pytest import yaml @@ -13,32 +9,31 @@ from pyavd._schema.avdschema import AvdSchema from pyavd._utils import merge -script_dir = os.path.dirname(__file__) -with open(f"{script_dir}/access_lists.schema.yml", "r", encoding="utf-8") as schema_file: +script_dir = Path(__file__).parent +with Path(script_dir, "access_lists.schema.yml").open(encoding="utf-8") as schema_file: acl_schema = yaml.load(schema_file, Loader=yaml.SafeLoader) -with open(f"{script_dir}/acl1.yml", "r", encoding="utf-8") as data_file: +with Path(script_dir, "acl1.yml").open(encoding="utf-8") as data_file: acl1 = yaml.load(data_file, Loader=yaml.SafeLoader) -with open(f"{script_dir}/acl2.yml", "r", encoding="utf-8") as data_file: +with Path(script_dir, "acl2.yml").open(encoding="utf-8") as data_file: acl2 = yaml.load(data_file, Loader=yaml.SafeLoader) -with open(f"{script_dir}/acl_merged.yml", "r", encoding="utf-8") as data_file: +with Path(script_dir, "acl_merged.yml").open(encoding="utf-8") as data_file: acl_merged = yaml.load(data_file, Loader=yaml.SafeLoader) class TestMerge: - def test_merge_of_lists_with_primary_keys(self): + def test_merge_of_lists_with_primary_keys(self) -> None: merge_result = {} schema = AvdSchema(acl_schema) merge(merge_result, acl1, acl2, schema=schema) - # print(yaml.dump(merge_result, indent=2)) assert merge_result == acl_merged @pytest.mark.parametrize("schema", [None, AvdSchema(acl_schema)]) - def test_list_merge_replace(self, schema): + def test_list_merge_replace(self, schema: dict) -> None: """ Testing with list_merge="replace" with or without schema. + Expecting acl2 as result since we only have lists in the input. """ merge_result = {} merge(merge_result, acl1, acl2, list_merge="replace", schema=schema) - # print(yaml.dump(merge_result, indent=2)) assert merge_result == acl2 diff --git a/python-avd/tests/pyavd/utils/password/test_password.py b/python-avd/tests/pyavd/utils/password/test_password.py index de785336093..a717f2afdfb 100644 --- a/python-avd/tests/pyavd/utils/password/test_password.py +++ b/python-avd/tests/pyavd/utils/password/test_password.py @@ -59,36 +59,28 @@ OSPF_MESSAGE_DIGEST_HASH_ALGORITHMS = ["md5", "sha1", "sha256", "sha384", "sha512"] -@pytest.mark.parametrize("key, password, expected", BGP_INPUT_DICT_ENCRYPT_EXPECTED) -def test_bgp_encrypt(key, password, expected): - """ - Test bgp_encrypt - """ +@pytest.mark.parametrize(("key", "password", "expected"), BGP_INPUT_DICT_ENCRYPT_EXPECTED) +def test_bgp_encrypt(key: str, password: str, expected: str) -> None: + """Test bgp_encrypt.""" assert bgp_encrypt(password, key=key) == expected -@pytest.mark.parametrize("key, password,, expected", BGP_VALID_INPUT_DICT_DECRYPT_EXPECTED) -def test_bgp_decrypt_success(key, password, expected): - """ - Test bgp_decrypt successful cases - """ +@pytest.mark.parametrize(("key", "password", "expected"), BGP_VALID_INPUT_DICT_DECRYPT_EXPECTED) +def test_bgp_decrypt_success(key: str, password: str, expected: str) -> None: + """Test bgp_decrypt successful cases.""" assert bgp_decrypt(password, key=key) == expected -@pytest.mark.parametrize("key, password", BGP_INVALID_INPUT_DICT_DECRYPT) -def test_bgp_decrypt_failure(key, password): - """ - Test bgp_decrypt failure cases - """ - with pytest.raises(ValueError): +@pytest.mark.parametrize(("key", "password"), BGP_INVALID_INPUT_DICT_DECRYPT) +def test_bgp_decrypt_failure(key: str, password: str) -> None: + """Test bgp_decrypt failure cases.""" + with pytest.raises(ValueError): # noqa: PT011 bgp_decrypt(password, key=key) -@pytest.mark.parametrize("key, password, expected", BGP_MOLECULE_PASSWORDS_TEST) -def test_molecule_bgp_encrypt(key, password, expected): - """ - Test bgp_encrypt - """ +@pytest.mark.parametrize(("key", "password", "expected"), BGP_MOLECULE_PASSWORDS_TEST) +def test_molecule_bgp_encrypt(key: str, password: str, expected: str) -> None: + """Test bgp_encrypt.""" assert bgp_encrypt(password, key=key) == expected @@ -123,57 +115,43 @@ def test_molecule_bgp_encrypt(key, password, expected): # The following list uses all the molecule OSPF passwords available # and the expected encryption # The password is always arista123 -# TODO -# OSPF_MOLECULE_PASSWORDS_TEST = [] +# TODO: OSPF_MOLECULE_PASSWORDS_TEST = [] -@pytest.mark.parametrize("key, password, expected", OSPF_INPUT_SIMPLE_DICT_ENCRYPT_EXPECTED) -def test_ospf_simple_encrypt(key, password, expected): - """ - Test ospf_simple_encrypt - """ +@pytest.mark.parametrize(("key", "password", "expected"), OSPF_INPUT_SIMPLE_DICT_ENCRYPT_EXPECTED) +def test_ospf_simple_encrypt(key: str, password: str, expected: str) -> None: + """Test ospf_simple_encrypt.""" assert ospf_simple_encrypt(password, key=key) == expected -@pytest.mark.parametrize("key, password, expected", OSPF_VALID_INPUT_SIMPLE_DICT_DECRYPT_EXPECTED) -def test_ospf_simple_decrypt_success(key, password, expected): - """ - Test ospf_simple_decrypt successful cases - """ +@pytest.mark.parametrize(("key", "password", "expected"), OSPF_VALID_INPUT_SIMPLE_DICT_DECRYPT_EXPECTED) +def test_ospf_simple_decrypt_success(key: str, password: str, expected: str) -> None: + """Test ospf_simple_decrypt successful cases.""" assert ospf_simple_decrypt(password, key=key) == expected -@pytest.mark.parametrize("key, password", OSPF_INVALID_INPUT_SIMPLE_DICT_DECRYPT) -def test_ospf_simple_decrypt_failure(key, password): - """ - Test ospf_simple_decrypt failure cases - """ - with pytest.raises(ValueError): +@pytest.mark.parametrize(("key", "password"), OSPF_INVALID_INPUT_SIMPLE_DICT_DECRYPT) +def test_ospf_simple_decrypt_failure(key: str, password: str) -> None: + """Test ospf_simple_decrypt failure cases.""" + with pytest.raises(ValueError): # noqa: PT011 ospf_simple_decrypt(password, key=key) -@pytest.mark.parametrize("key, password, hash_algorithm, key_id, expected", OSPF_INPUT_MD_DICT_ENCRYPT_EXPECTED) -def test_ospf_message_digest_encrypt(key, password, expected, hash_algorithm, key_id): - """ - Test ospf_message_digest_encrypt - """ +@pytest.mark.parametrize(("key", "password", "hash_algorithm", "key_id", "expected"), OSPF_INPUT_MD_DICT_ENCRYPT_EXPECTED) +def test_ospf_message_digest_encrypt(key: str, password: str, expected: str, hash_algorithm: str, key_id: str) -> None: + """Test ospf_message_digest_encrypt.""" assert ospf_message_digest_encrypt(password, key=key, hash_algorithm=hash_algorithm, key_id=key_id) == expected -@pytest.mark.parametrize("key, password, hash_algorithm, key_id, expected", OSPF_VALID_INPUT_MD_DICT_DECRYPT_EXPECTED) -def test_ospf_message_digest_decrypt_success(key, password, hash_algorithm, key_id, expected): - """ - Test ospf_message_digest_decrypt successful cases - """ +@pytest.mark.parametrize(("key", "password", "hash_algorithm", "key_id", "expected"), OSPF_VALID_INPUT_MD_DICT_DECRYPT_EXPECTED) +def test_ospf_message_digest_decrypt_success(key: str, password: str, hash_algorithm: str, key_id: str, expected: str) -> None: + """Test ospf_message_digest_decrypt successful cases.""" assert ospf_message_digest_decrypt(password, key=key, hash_algorithm=hash_algorithm, key_id=key_id) == expected -@pytest.mark.parametrize("key, password, hash_algorithm, key_id", OSPF_INVALID_INPUT_MD_DICT_DECRYPT) -def test_ospf_message_digest_decrypt_failure(key, password, hash_algorithm, key_id): - """ - Test ospf_message_digest_decrypt failure cases - """ - +@pytest.mark.parametrize(("key", "password", "hash_algorithm", "key_id"), OSPF_INVALID_INPUT_MD_DICT_DECRYPT) +def test_ospf_message_digest_decrypt_failure(key: str, password: str, hash_algorithm: str, key_id: str) -> None: + """Test ospf_message_digest_decrypt failure cases.""" if hash_algorithm is None or key_id is None: with pytest.raises(ValueError, match="For OSPF message digest keys, both hash_algorithm and key_id are required"): ospf_message_digest_encrypt(password, key=key, hash_algorithm=hash_algorithm, key_id=key_id) @@ -213,26 +191,20 @@ def test_ospf_message_digest_decrypt_failure(key, password, hash_algorithm, key_ ] -@pytest.mark.parametrize("key, mode, password, encrypted_password", ISIS_INPUT_EXPECTED) -def test_isis_encrypt(key, mode, password, encrypted_password): - """ - Test isis_encrypt - """ +@pytest.mark.parametrize(("key", "mode", "password", "encrypted_password"), ISIS_INPUT_EXPECTED) +def test_isis_encrypt(key: str, mode: str, password: str, encrypted_password: str) -> None: + """Test isis_encrypt.""" assert isis_encrypt(password, key=key, mode=mode) == encrypted_password -@pytest.mark.parametrize("key, mode, password, encrypted_password", ISIS_INPUT_EXPECTED) -def test_isis_decrypt_success(key, mode, password, encrypted_password): - """ - Test isis_decrypt successful cases - """ +@pytest.mark.parametrize(("key", "mode", "password", "encrypted_password"), ISIS_INPUT_EXPECTED) +def test_isis_decrypt_success(key: str, mode: str, password: str, encrypted_password: str) -> None: + """Test isis_decrypt successful cases.""" assert isis_decrypt(encrypted_password, key=key, mode=mode) == password -@pytest.mark.parametrize("key, mode, password", ISIS_INVALID_INPUT_DECRYPT) -def test_isis_decrypt_failure(key, mode, password): - """ - Test isis_decrypt failure cases - """ - with pytest.raises(ValueError): +@pytest.mark.parametrize(("key", "mode", "password"), ISIS_INVALID_INPUT_DECRYPT) +def test_isis_decrypt_failure(key: str, mode: str, password: str) -> None: + """Test isis_decrypt failure cases.""" + with pytest.raises(ValueError): # noqa: PT011 isis_decrypt(password, key=key, mode=mode) diff --git a/python-avd/tests/pyavd/utils/password/test_password_utils.py b/python-avd/tests/pyavd/utils/password/test_password_utils.py index d891f06d2b4..1e14da92a07 100644 --- a/python-avd/tests/pyavd/utils/password/test_password_utils.py +++ b/python-avd/tests/pyavd/utils/password/test_password_utils.py @@ -12,52 +12,42 @@ INVALID_PASSWORD_KEY_PAIRS = [("10.42.42.43", b"3QGcqpU2YTwKh2jVQ4Vj/A=="), ("AVD-TEST-DUMMY", b"bM7t58t04qSqLHAfZR/Szg==")] -@pytest.mark.parametrize("key, expected", VALID_PASSWORD_KEY_PAIRS) -def test_cbc_encrypt(key, expected): - """ - Valid cases for both neighbor IP and peer group name - """ +@pytest.mark.parametrize(("key", "expected"), VALID_PASSWORD_KEY_PAIRS) +def test_cbc_encrypt(key: str, expected: str) -> None: + """Valid cases for both neighbor IP and peer group name.""" augmented_key = bytes(f"{key}_passwd", encoding="utf-8") assert cbc_encrypt(augmented_key, b"arista") == expected -@pytest.mark.parametrize("key, password", VALID_PASSWORD_KEY_PAIRS) -def test_cbc_decrypt(key, password): - """ - Valid cases for both neighbor IP and peer group name - """ +@pytest.mark.parametrize(("key", "password"), VALID_PASSWORD_KEY_PAIRS) +def test_cbc_decrypt(key: str, password: str) -> None: + """Valid cases for both neighbor IP and peer group name.""" augmented_key = bytes(f"{key}_passwd", encoding="utf-8") assert cbc_decrypt(augmented_key, password) == b"arista" @pytest.mark.parametrize( - "key, password, expected_raise", + ("key", "password", "expected_raise"), [ pytest.param("TOTO", b"3QGcqpU2YTwKh2jVQ4Vj/A==", ValueError, id="ValueError"), ], ) -def test_cbc_decrypt_failure(key, password, expected_raise): - """ - Valid cases for both neighbor IP and peer group name - """ +def test_cbc_decrypt_failure(key: str, password: str, expected_raise: Exception) -> None: + """Valid cases for both neighbor IP and peer group name.""" augmented_key = bytes(f"{key}_passwd", encoding="utf-8") with pytest.raises(expected_raise): cbc_decrypt(augmented_key, password) -@pytest.mark.parametrize("key, password", VALID_PASSWORD_KEY_PAIRS) -def test_cbc_check_password_success(key, password): - """ - Valid cases for both neighbor IP and peer group name - """ +@pytest.mark.parametrize(("key", "password"), VALID_PASSWORD_KEY_PAIRS) +def test_cbc_check_password_success(key: str, password: str) -> None: + """Valid cases for both neighbor IP and peer group name.""" augmented_key = bytes(f"{key}_passwd", encoding="utf-8") assert cbc_check_password(augmented_key, password) is True -@pytest.mark.parametrize("key, password", INVALID_PASSWORD_KEY_PAIRS) -def test_cbc_check_password_invalid_values(key, password): - """ - Invalid cases for both neighbor IP and peer group name - """ +@pytest.mark.parametrize(("key", "password"), INVALID_PASSWORD_KEY_PAIRS) +def test_cbc_check_password_invalid_values(key: str, password: str) -> None: + """Invalid cases for both neighbor IP and peer group name.""" augmented_key = bytes(f"{key}_passwd", encoding="utf-8") assert cbc_check_password(augmented_key, password) is False diff --git a/python-avd/tests/pyavd/utils/test_get.py b/python-avd/tests/pyavd/utils/test_get.py index 75740f90008..4d70533ed0b 100644 --- a/python-avd/tests/pyavd/utils/test_get.py +++ b/python-avd/tests/pyavd/utils/test_get.py @@ -1,11 +1,10 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from __future__ import absolute_import, division, print_function -__metaclass__ = type import re +from collections.abc import Generator from contextlib import contextmanager import pytest @@ -15,7 +14,7 @@ @contextmanager -def does_not_raise(): +def does_not_raise() -> Generator: yield @@ -94,8 +93,8 @@ def does_not_raise(): class TestUtils: - @pytest.mark.parametrize("DATA", GET_DATA) - def test_get(self, DATA): - with DATA["expected_exception"]: - res = get(DATA["dictionary"], DATA["key"], DATA["default"], DATA["required"], DATA["org_key"], DATA["separator"]) - assert res == DATA["expected_result"] + @pytest.mark.parametrize("data", GET_DATA) + def test_get(self, data: dict) -> None: + with data["expected_exception"]: + res = get(data["dictionary"], data["key"], data["default"], data["required"], data["org_key"], data["separator"]) + assert res == data["expected_result"] diff --git a/python-avd/tests/pyavd/utils/test_get_ip_from_pool.py b/python-avd/tests/pyavd/utils/test_get_ip_from_pool.py index a2e477e3a3e..fc572a80673 100644 --- a/python-avd/tests/pyavd/utils/test_get_ip_from_pool.py +++ b/python-avd/tests/pyavd/utils/test_get_ip_from_pool.py @@ -12,7 +12,7 @@ @pytest.mark.parametrize( - "pool, prefixlen, subnet_offset, ip_offset, expected", + ("pool", "prefixlen", "subnet_offset", "ip_offset", "expected"), [ ("1.2.3.0/32", PREFIXLEN, SUBNET_OFFSET, IP_OFFSET, f"Unable to get {SUBNET_OFFSET + 1} /{PREFIXLEN} subnets from pool 1.2.3.0/32"), (POOL, 8, SUBNET_OFFSET, IP_OFFSET, "Prefix length 8 is smaller than pool network prefix length"), @@ -20,17 +20,15 @@ (POOL, PREFIXLEN, SUBNET_OFFSET, 1, "Unable to get 2 hosts in subnet"), ], ) -def test_get_ip_from_pool_invalid(pool, prefixlen, subnet_offset, ip_offset, expected): - """ - Invalid cases for get_ip_from_pool. - """ +def test_get_ip_from_pool_invalid(pool: str, prefixlen: int, subnet_offset: int, ip_offset: int, expected: str) -> None: + """Invalid cases for get_ip_from_pool.""" with pytest.raises(AristaAvdError) as exc_info: get_ip_from_pool(pool, prefixlen, subnet_offset, ip_offset) assert expected in str(exc_info.value) @pytest.mark.parametrize( - "pool, prefixlen, subnet_offset, ip_offset, expected", + ("pool", "prefixlen", "subnet_offset", "ip_offset", "expected"), [ ("1.2.3.0/31", PREFIXLEN, SUBNET_OFFSET, IP_OFFSET, "1.2.3.1"), (POOL, 25, SUBNET_OFFSET, IP_OFFSET, "1.2.3.129"), @@ -39,10 +37,7 @@ def test_get_ip_from_pool_invalid(pool, prefixlen, subnet_offset, ip_offset, exp (POOL, 31, SUBNET_OFFSET, 1, "1.2.3.3"), ], ) -def test_get_ip_from_pool_valid(pool, prefixlen, subnet_offset, ip_offset, expected): - """ - Valid cases for get_ip_from_pool with default values. - """ - +def test_get_ip_from_pool_valid(pool: str, prefixlen: int, subnet_offset: int, ip_offset: int, expected: str) -> None: + """Valid cases for get_ip_from_pool with default values.""" resp = get_ip_from_pool(pool, prefixlen, subnet_offset, ip_offset) assert resp == expected diff --git a/python-avd/tests/pyavd/utils/test_short_esi_to_route_target.py b/python-avd/tests/pyavd/utils/test_short_esi_to_route_target.py index 1c1d6726270..eef59a56cb9 100644 --- a/python-avd/tests/pyavd/utils/test_short_esi_to_route_target.py +++ b/python-avd/tests/pyavd/utils/test_short_esi_to_route_target.py @@ -1,9 +1,7 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from __future__ import absolute_import, division, print_function -__metaclass__ = type import pytest @@ -20,7 +18,7 @@ class TestGenerateRouteTargetFilter: - @pytest.mark.parametrize("short_esi, route_target", ESI_TO_RT_TEST_CASES) - def test_short_esi_to_route_target(self, short_esi, route_target): + @pytest.mark.parametrize(("short_esi", "route_target"), ESI_TO_RT_TEST_CASES) + def test_short_esi_to_route_target(self, short_esi: str, route_target: str) -> None: resp = short_esi_to_route_target(short_esi) assert resp == route_target diff --git a/python-avd/tests/pyavd/utils/test_strip_empties.py b/python-avd/tests/pyavd/utils/test_strip_empties.py index 86716ce3407..0ab7cfaaccb 100644 --- a/python-avd/tests/pyavd/utils/test_strip_empties.py +++ b/python-avd/tests/pyavd/utils/test_strip_empties.py @@ -1,9 +1,9 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -from __future__ import absolute_import, division, print_function -__metaclass__ = type + +from typing import Any import pytest @@ -31,7 +31,7 @@ class TestStripEmpties: - def strip_empties_checks(self, output): + def strip_empties_checks(self, output: Any) -> None: assert None not in output assert "" not in output assert [] not in output @@ -42,15 +42,15 @@ def strip_empties_checks(self, output): if isinstance(entry, dict): self.strip_empties_checks(entry.values()) - @pytest.mark.parametrize("DATA", STRIP_EMPTIES.values(), ids=STRIP_EMPTIES.keys()) - def test_strip_empties_string(self, DATA): - output = strip_null_from_data(DATA) - assert output == DATA + @pytest.mark.parametrize("data", STRIP_EMPTIES.values(), ids=STRIP_EMPTIES.keys()) + def test_strip_empties_string(self, data: Any) -> None: + output = strip_null_from_data(data) + assert output == data - @pytest.mark.parametrize("DATA", STRIP_EMPTIES_LIST.values(), ids=STRIP_EMPTIES_LIST.keys()) - def test_strip_empties_list(self, DATA): + @pytest.mark.parametrize("data", STRIP_EMPTIES_LIST.values(), ids=STRIP_EMPTIES_LIST.keys()) + def test_strip_empties_list(self, data: Any) -> None: output = strip_null_from_data( - DATA, + data, strip_values_tuple=( None, "", @@ -60,10 +60,10 @@ def test_strip_empties_list(self, DATA): ) self.strip_empties_checks(output) - @pytest.mark.parametrize("DATA", STRIP_EMPTIES_DICT.values(), ids=STRIP_EMPTIES_DICT.keys()) - def test_strip_empties_dict(self, DATA): + @pytest.mark.parametrize("data", STRIP_EMPTIES_DICT.values(), ids=STRIP_EMPTIES_DICT.keys()) + def test_strip_empties_dict(self, data: Any) -> None: output = strip_null_from_data( - DATA, + data, strip_values_tuple=( None, "", diff --git a/python-avd/tests/schema_tools/conftest.py b/python-avd/tests/schema_tools/conftest.py index 4bcef50c469..3170037e3c4 100644 --- a/python-avd/tests/schema_tools/conftest.py +++ b/python-avd/tests/schema_tools/conftest.py @@ -12,9 +12,9 @@ # The schemas are read of test artifacts which are frozen copies of the regular schemas. # We keep a frozen copy of the schemas here, so the expected outputs don't change as the schemas evolve. -with open(EOS_CLI_CONFIG_GEN_SCHEMA_PATH, encoding="UTF-8") as file: +with Path(EOS_CLI_CONFIG_GEN_SCHEMA_PATH).open(encoding="UTF-8") as file: test_eos_cli_config_gen_schema = safe_load(file) -with open(EOS_DESIGNS_SCHEMA_PATH, encoding="UTF-8") as file: +with Path(EOS_DESIGNS_SCHEMA_PATH).open(encoding="UTF-8") as file: test_eos_designs_schema = safe_load(file) @@ -31,11 +31,12 @@ def output_path() -> Path: @pytest.fixture(scope="module") def schema_store() -> dict[str, dict]: """ - Return dict with schemas + Return dict with schemas. + { "eos_cli_config_gen": dict "eos_designs": dict - } + }. """ return { "eos_cli_config_gen": test_eos_cli_config_gen_schema, @@ -46,11 +47,12 @@ def schema_store() -> dict[str, dict]: @pytest.fixture(scope="module") def schema_paths() -> dict[str, dict]: """ - Return dict with schema paths + Return dict with schema paths. + { "eos_cli_config_gen": Path "eos_designs": Path - } + }. """ return { "eos_cli_config_gen": EOS_CLI_CONFIG_GEN_SCHEMA_PATH, diff --git a/python-avd/tests/schema_tools/generate_docs/test_mdtabsgen.py b/python-avd/tests/schema_tools/generate_docs/test_mdtabsgen.py index 2691a46901f..ba5ede5eb4b 100644 --- a/python-avd/tests/schema_tools/generate_docs/test_mdtabsgen.py +++ b/python-avd/tests/schema_tools/generate_docs/test_mdtabsgen.py @@ -16,9 +16,10 @@ @pytest.mark.parametrize("table_name", ["network-services-multicast-settings"]) -def test_get_md_tabs(table_name: str, schema_store, artifacts_path, output_path): +def test_get_md_tabs(table_name: str, schema_store: dict, artifacts_path: Path, output_path: Path) -> None: """ Loads the schema with the resolved $refs and generated md_tabs. + Write the resulting md_tabs to a file. Compare the output with the expected file. """ @@ -27,14 +28,14 @@ def test_get_md_tabs(table_name: str, schema_store, artifacts_path, output_path) output_file = output_path.joinpath(f"{table_name}.md") expected_file = artifacts_path.joinpath(f"expected-{table_name}.md") - def mocked_create_store(): + def mocked_create_store() -> dict: return schema_store with patch("schema_tools.metaschema.resolvemodel.create_store", new=mocked_create_store): schema = AristaAvdSchema(resolve_schema=True, **raw_schema) md_tabs = get_md_tabs(schema, table_name) - with open(output_file, mode="w", encoding="UTF-8") as file: + with Path(output_file).open(mode="w", encoding="UTF-8") as file: file.write(md_tabs) - with open(expected_file, mode="r", encoding="UTF-8") as file: + with Path(expected_file).open(encoding="UTF-8") as file: assert md_tabs == file.read() diff --git a/python-avd/tests/schema_tools/metaschema/test_meta_schema_model.py b/python-avd/tests/schema_tools/metaschema/test_meta_schema_model.py index 4e6d9de2e1d..9d74b868a63 100644 --- a/python-avd/tests/schema_tools/metaschema/test_meta_schema_model.py +++ b/python-avd/tests/schema_tools/metaschema/test_meta_schema_model.py @@ -4,6 +4,7 @@ import json from pathlib import Path from sys import path +from typing import Any import yaml @@ -18,15 +19,16 @@ class NoAliasDumper(yaml.Dumper): - """Dump YAML without generating aliases and anchors for reused ids""" + """Dump YAML without generating aliases and anchors for reused ids.""" - def ignore_aliases(self, _): + def ignore_aliases(self, _: Any) -> bool: return True -def test_pydantic_dump_matches_original_yaml(): +def test_pydantic_dump_matches_original_yaml() -> None: """ Loads the schema _without_ resolving the $ref and then dumps the schema again as json. + Then compares the input schema with the dumped schema. """ pydantic_schema = AristaAvdSchema(resolve_schema=False, **raw_schema) diff --git a/python-avd/tests/utils.py b/python-avd/tests/utils.py index 92c302a9bbf..ff768263bd7 100644 --- a/python-avd/tests/utils.py +++ b/python-avd/tests/utils.py @@ -1,9 +1,9 @@ # Copyright (c) 2023-2024 Arista Networks, Inc. # Use of this source code is governed by the Apache License 2.0 # that can be found in the LICENSE file. -import os from json import JSONDecodeError from json import loads as json_loads +from pathlib import Path from sys import stdin from yaml import unsafe_load as yaml_load @@ -11,12 +11,12 @@ def read_file(filename: str) -> str: if filename == "/dev/stdin" and stdin.isatty(): - print("Write variables in YAML or JSON format and end with ctrl+d to exit") - with open(filename, "r", encoding="UTF-8") as file: + print("Write variables in YAML or JSON format and end with ctrl+d to exit") # noqa: T201 accepting print for stdin inputs + with Path.open(filename, encoding="UTF-8") as file: return file.read() -def read_vars(filename: str): +def read_vars(filename: str) -> dict: data = read_file(filename) try: @@ -27,25 +27,28 @@ def read_vars(filename: str): return yaml_load(data) or {} -def write_result(filename, result): +def write_result(filename: str, result: str) -> None: mode = "w+" if filename == "/dev/stdout": mode = "w" - with open(filename, mode, encoding="UTF-8") as file: + with Path.open(filename, mode, encoding="UTF-8") as file: file.write(result) -def create_common_vars(common_varfiles): +def create_common_vars(common_varfiles: list) -> dict: common_vars = {} for file in common_varfiles: common_vars.update(read_vars(file)) return common_vars -def get_files_in_folder(folder_path): - files = [] - for root, _, filenames in os.walk(folder_path): - for filename in filenames: - files.append(os.path.join(root, filename)) - return files +def get_files_in_folder(folder_path: str) -> list: + return [ + Path( + root, + filename, + ) + for root, _, filenames in Path(folder_path).walk() + for filename in filenames + ]