summaryrefslogtreecommitdiffstats
path: root/tests/units/test_tools.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/units/test_tools.py')
-rw-r--r--tests/units/test_tools.py490
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