# 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