diff options
Diffstat (limited to '')
-rw-r--r-- | tests/units/test_tools.py | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/tests/units/test_tools.py b/tests/units/test_tools.py new file mode 100644 index 0000000..a846fd6 --- /dev/null +++ b/tests/units/test_tools.py @@ -0,0 +1,490 @@ +# 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. +"""Tests for `anta.tools`.""" + +from __future__ import annotations + +from contextlib import AbstractContextManager +from contextlib import nullcontext as does_not_raise +from typing import Any + +import pytest + +from anta.tools import get_dict_superset, get_failed_logs, get_item, get_value + +TEST_GET_FAILED_LOGS_DATA = [ + {"id": 1, "name": "Alice", "age": 30, "email": "alice@example.com"}, + {"id": 2, "name": "Bob", "age": 35, "email": "bob@example.com"}, + {"id": 3, "name": "Charlie", "age": 40, "email": "charlie@example.com"}, + {"id": 4, "name": "Jon", "age": 25, "email": "Jon@example.com"}, + {"id": 4, "name": "Rob", "age": 25, "email": "Jon@example.com"}, +] +TEST_GET_DICT_SUPERSET_DATA = [ + ("id", 0), + { + "id": 1, + "name": "Alice", + "age": 30, + "email": "alice@example.com", + }, + { + "id": 2, + "name": "Bob", + "age": 35, + "email": "bob@example.com", + }, + { + "id": 3, + "name": "Charlie", + "age": 40, + "email": "charlie@example.com", + }, +] +TEST_GET_VALUE_DATA = {"test_value": 42, "nested_test": {"nested_value": 43}} +TEST_GET_ITEM_DATA = [ + ("id", 0), + { + "id": 1, + "name": "Alice", + "age": 30, + "email": "alice@example.com", + }, + { + "id": 2, + "name": "Bob", + "age": 35, + "email": "bob@example.com", + }, + { + "id": 3, + "name": "Charlie", + "age": 40, + "email": "charlie@example.com", + }, +] + + +@pytest.mark.parametrize( + ("expected_output", "actual_output", "expected_result"), + [ + pytest.param( + TEST_GET_FAILED_LOGS_DATA[0], + TEST_GET_FAILED_LOGS_DATA[0], + "", + id="no difference", + ), + pytest.param( + TEST_GET_FAILED_LOGS_DATA[0], + TEST_GET_FAILED_LOGS_DATA[1], + "\nExpected `1` as the id, but found `2` instead.\nExpected `Alice` as the name, but found `Bob` instead.\n" + "Expected `30` as the age, but found `35` instead.\nExpected `alice@example.com` as the email, but found `bob@example.com` instead.", + id="different data", + ), + pytest.param( + TEST_GET_FAILED_LOGS_DATA[0], + {}, + "\nExpected `1` as the id, but it was not found in the actual output.\nExpected `Alice` as the name, but it was not found in the actual output.\n" + "Expected `30` as the age, but it was not found in the actual output.\nExpected `alice@example.com` as the email, but it was not found in " + "the actual output.", + id="empty actual output", + ), + pytest.param( + TEST_GET_FAILED_LOGS_DATA[3], + TEST_GET_FAILED_LOGS_DATA[4], + "\nExpected `Jon` as the name, but found `Rob` instead.", + id="different name", + ), + ], +) +def test_get_failed_logs( + expected_output: dict[Any, Any], + actual_output: dict[Any, Any], + expected_result: str, +) -> None: + """Test get_failed_logs.""" + assert get_failed_logs(expected_output, actual_output) == expected_result + + +@pytest.mark.parametrize( + ( + "list_of_dicts", + "input_dict", + "default", + "required", + "var_name", + "custom_error_msg", + "expected_result", + "expected_raise", + ), + [ + pytest.param( + [], + {"id": 1, "name": "Alice"}, + None, + False, + None, + None, + None, + does_not_raise(), + id="empty list", + ), + pytest.param( + [], + {"id": 1, "name": "Alice"}, + None, + True, + None, + None, + None, + pytest.raises(ValueError, match="not found in the provided list."), + id="empty list and required", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {"id": 10, "name": "Jack"}, + None, + False, + None, + None, + None, + does_not_raise(), + id="missing item", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {"id": 1, "name": "Alice"}, + None, + False, + None, + None, + TEST_GET_DICT_SUPERSET_DATA[1], + does_not_raise(), + id="found item", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {"id": 10, "name": "Jack"}, + "default_value", + False, + None, + None, + "default_value", + does_not_raise(), + id="default value", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {"id": 10, "name": "Jack"}, + None, + True, + None, + None, + None, + pytest.raises(ValueError, match="not found in the provided list."), + id="required", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {"id": 10, "name": "Jack"}, + None, + True, + "custom_var_name", + None, + None, + pytest.raises(ValueError, match="custom_var_name not found in the provided list."), + id="custom var_name", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {"id": 1, "name": "Alice"}, + None, + True, + "custom_var_name", + "Custom error message", + TEST_GET_DICT_SUPERSET_DATA[1], + does_not_raise(), + id="custom error message", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {"id": 10, "name": "Jack"}, + None, + True, + "custom_var_name", + "Custom error message", + None, + pytest.raises(ValueError, match="Custom error message"), + id="custom error message and required", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {"id": 1, "name": "Jack"}, + None, + False, + None, + None, + None, + does_not_raise(), + id="id ok but name not ok", + ), + pytest.param( + "not a list", + {"id": 1, "name": "Alice"}, + None, + True, + None, + None, + None, + pytest.raises(ValueError, match="not found in the provided list."), + id="non-list input for list_of_dicts", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + "not a dict", + None, + True, + None, + None, + None, + pytest.raises(ValueError, match="not found in the provided list."), + id="non-dictionary input", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {}, + None, + False, + None, + None, + None, + does_not_raise(), + id="empty dictionary input", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {"id": 1, "name": "Alice", "extra_key": "extra_value"}, + None, + True, + None, + None, + None, + pytest.raises(ValueError, match="not found in the provided list."), + id="input dictionary with extra keys", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + {"id": 1}, + None, + False, + None, + None, + TEST_GET_DICT_SUPERSET_DATA[1], + does_not_raise(), + id="input dictionary is a subset of more than one dictionary in list_of_dicts", + ), + pytest.param( + TEST_GET_DICT_SUPERSET_DATA, + { + "id": 1, + "name": "Alice", + "age": 30, + "email": "alice@example.com", + "extra_key": "extra_value", + }, + None, + True, + None, + None, + None, + pytest.raises(ValueError, match="not found in the provided list."), + id="input dictionary is a superset of a dictionary in list_of_dicts", + ), + ], +) +def test_get_dict_superset( + list_of_dicts: list[dict[Any, Any]], + input_dict: dict[Any, Any], + default: str | None, + required: bool, + var_name: str | None, + custom_error_msg: str | None, + expected_result: str, + expected_raise: AbstractContextManager[Exception], +) -> None: + """Test get_dict_superset.""" + # pylint: disable=too-many-arguments + with expected_raise: + assert get_dict_superset(list_of_dicts, input_dict, default, var_name, custom_error_msg, required=required) == expected_result + + +@pytest.mark.parametrize( + ( + "input_dict", + "key", + "default", + "required", + "org_key", + "separator", + "expected_result", + "expected_raise", + ), + [ + pytest.param({}, "test", None, False, None, None, None, does_not_raise(), id="empty dict"), + pytest.param( + TEST_GET_VALUE_DATA, + "test_value", + None, + False, + None, + None, + 42, + does_not_raise(), + id="simple key", + ), + pytest.param( + TEST_GET_VALUE_DATA, + "nested_test.nested_value", + None, + False, + None, + None, + 43, + does_not_raise(), + id="nested_key", + ), + pytest.param( + TEST_GET_VALUE_DATA, + "missing_value", + None, + False, + None, + None, + None, + does_not_raise(), + id="missing_value", + ), + pytest.param( + TEST_GET_VALUE_DATA, + "missing_value_with_default", + "default_value", + False, + None, + None, + "default_value", + does_not_raise(), + id="default", + ), + pytest.param( + TEST_GET_VALUE_DATA, + "missing_required", + None, + True, + None, + None, + None, + pytest.raises(ValueError, match="missing_required"), + id="required", + ), + pytest.param( + TEST_GET_VALUE_DATA, + "missing_required", + None, + True, + "custom_org_key", + None, + None, + pytest.raises(ValueError, match="custom_org_key"), + id="custom org_key", + ), + pytest.param( + TEST_GET_VALUE_DATA, + "nested_test||nested_value", + None, + None, + None, + "||", + 43, + does_not_raise(), + id="custom separator", + ), + ], +) +def test_get_value( + input_dict: dict[Any, Any], + key: str, + default: str | None, + required: bool, + org_key: str | None, + separator: str | None, + expected_result: int | str | None, + expected_raise: AbstractContextManager[Exception], +) -> None: + """Test get_value.""" + # pylint: disable=too-many-arguments + kwargs = { + "default": default, + "required": required, + "org_key": org_key, + "separator": separator, + } + kwargs = {k: v for k, v in kwargs.items() if v is not None} + with expected_raise: + assert get_value(input_dict, key, **kwargs) == expected_result # type: ignore[arg-type] + + +@pytest.mark.parametrize( + ("list_of_dicts", "key", "value", "default", "required", "case_sensitive", "var_name", "custom_error_msg", "expected_result", "expected_raise"), + [ + pytest.param([], "name", "Bob", None, False, False, None, None, None, does_not_raise(), id="empty list"), + pytest.param([], "name", "Bob", None, True, False, None, None, None, pytest.raises(ValueError, match="name"), id="empty list and required"), + pytest.param(TEST_GET_ITEM_DATA, "name", "Jack", None, False, False, None, None, None, does_not_raise(), id="missing item"), + pytest.param(TEST_GET_ITEM_DATA, "name", "Alice", None, False, False, None, None, TEST_GET_ITEM_DATA[1], does_not_raise(), id="found item"), + pytest.param(TEST_GET_ITEM_DATA, "name", "Jack", "default_value", False, False, None, None, "default_value", does_not_raise(), id="default value"), + pytest.param(TEST_GET_ITEM_DATA, "name", "Jack", None, True, False, None, None, None, pytest.raises(ValueError, match="name"), id="required"), + pytest.param(TEST_GET_ITEM_DATA, "name", "Bob", None, False, True, None, None, TEST_GET_ITEM_DATA[2], does_not_raise(), id="case sensitive"), + pytest.param(TEST_GET_ITEM_DATA, "name", "charlie", None, False, False, None, None, TEST_GET_ITEM_DATA[3], does_not_raise(), id="case insensitive"), + pytest.param( + TEST_GET_ITEM_DATA, + "name", + "Jack", + None, + True, + False, + "custom_var_name", + None, + None, + pytest.raises(ValueError, match="custom_var_name"), + id="custom var_name", + ), + pytest.param( + TEST_GET_ITEM_DATA, + "name", + "Jack", + None, + True, + False, + None, + "custom_error_msg", + None, + pytest.raises(ValueError, match="custom_error_msg"), + id="custom error msg", + ), + ], +) +def test_get_item( + list_of_dicts: list[dict[Any, Any]], + key: str, + value: str | None, + default: str | None, + required: bool, + case_sensitive: bool, + var_name: str | None, + custom_error_msg: str | None, + expected_result: str, + expected_raise: AbstractContextManager[Exception], +) -> None: + """Test get_item.""" + # pylint: disable=too-many-arguments + with expected_raise: + assert get_item(list_of_dicts, key, value, default, var_name, custom_error_msg, required=required, case_sensitive=case_sensitive) == expected_result |