diff options
Diffstat (limited to 'tests/units/cli')
-rw-r--r-- | tests/units/cli/conftest.py | 1 | ||||
-rw-r--r-- | tests/units/cli/get/local_module/__init__.py | 4 | ||||
-rw-r--r-- | tests/units/cli/get/test_commands.py | 183 | ||||
-rw-r--r-- | tests/units/cli/get/test_utils.py | 93 | ||||
-rw-r--r-- | tests/units/cli/nrfu/test_commands.py | 15 |
5 files changed, 291 insertions, 5 deletions
diff --git a/tests/units/cli/conftest.py b/tests/units/cli/conftest.py index e63e60e..71c23e9 100644 --- a/tests/units/cli/conftest.py +++ b/tests/units/cli/conftest.py @@ -39,6 +39,7 @@ MOCK_CLI_JSON: dict[str, asynceapi.EapiCommandError | dict[str, Any]] = { errmsg="Invalid command", not_exec=[], ), + "show interfaces": {}, } MOCK_CLI_TEXT: dict[str, asynceapi.EapiCommandError | str] = { diff --git a/tests/units/cli/get/local_module/__init__.py b/tests/units/cli/get/local_module/__init__.py new file mode 100644 index 0000000..f93ff2b --- /dev/null +++ b/tests/units/cli/get/local_module/__init__.py @@ -0,0 +1,4 @@ +# Copyright (c) 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. +"""Module used for test purposes.""" diff --git a/tests/units/cli/get/test_commands.py b/tests/units/cli/get/test_commands.py index ff3d922..0e263f7 100644 --- a/tests/units/cli/get/test_commands.py +++ b/tests/units/cli/get/test_commands.py @@ -114,6 +114,27 @@ def test_from_cvp( assert result.exit_code == ExitCode.OK +def test_from_cvp_os_error(tmp_path: Path, click_runner: CliRunner, caplog: pytest.LogCaptureFixture) -> None: + """Test from_cvp when an OSError occurs.""" + output: Path = tmp_path / "output.yml" + cli_args = ["get", "from-cvp", "--output", str(output), "--host", "42.42.42.42", "--username", "anta", "--password", "anta"] + + with ( + patch("anta.cli.get.commands.get_cv_token", autospec=True, side_effect=None), + patch("cvprac.cvp_client.CvpClient.connect", autospec=True, side_effect=None) as mocked_cvp_connect, + patch("cvprac.cvp_client.CvpApi.get_inventory", autospec=True, return_value=[]) as mocked_get_inventory, + patch("cvprac.cvp_client.CvpApi.get_devices_in_container", autospec=True, return_value=[]), + patch("anta.cli.get.utils.Path.open", side_effect=OSError("Permission denied")), + ): + result = click_runner.invoke(anta, cli_args) + + mocked_cvp_connect.assert_called_once() + mocked_get_inventory.assert_called_once() + assert not output.exists() + assert "Could not write inventory to path" in caplog.text + assert result.exit_code == ExitCode.USAGE_ERROR + + @pytest.mark.parametrize( ("ansible_inventory", "ansible_group", "expected_exit", "expected_log"), [ @@ -257,8 +278,7 @@ def test_from_ansible_overwrite( else: temp_env["ANTA_INVENTORY"] = None tmp_inv = tmp_output - cli_args.extend(["--output", str(tmp_output)]) - + cli_args.extend(["--output", str(tmp_inv)]) if overwrite: cli_args.append("--overwrite") @@ -275,3 +295,162 @@ def test_from_ansible_overwrite( elif expected_exit == ExitCode.INTERNAL_ERROR: assert expected_log assert expected_log in result.output + + +@pytest.mark.parametrize( + ("module", "test_name", "short", "count", "expected_output", "expected_exit_code"), + [ + pytest.param( + None, + None, + False, + False, + "VerifyAcctConsoleMethods", + ExitCode.OK, + id="Get all tests", + ), + pytest.param( + "anta.tests.aaa", + None, + False, + False, + "VerifyAcctConsoleMethods", + ExitCode.OK, + id="Get tests, filter on module", + ), + pytest.param( + None, + "VerifyNTPAssociations", + False, + False, + "VerifyNTPAssociations", + ExitCode.OK, + id="Get tests, filter on exact test name", + ), + pytest.param( + None, + "VerifyNTP", + False, + False, + "anta.tests.system", + ExitCode.OK, + id="Get tests, filter on included test name", + ), + pytest.param( + None, + "VerifyNTP", + True, + False, + "VerifyNTPAssociations", + ExitCode.OK, + id="Get tests --short", + ), + pytest.param( + "unknown_module", + None, + True, + False, + "Module `unknown_module` was not found!", + ExitCode.USAGE_ERROR, + id="Get tests wrong module", + ), + pytest.param( + "unknown_module.unknown", + None, + True, + False, + "Module `unknown_module.unknown` was not found!", + ExitCode.USAGE_ERROR, + id="Get tests wrong submodule", + ), + pytest.param( + ".unknown_module", + None, + True, + False, + "`anta get tests --module <module>` does not support relative imports", + ExitCode.USAGE_ERROR, + id="Use relative module name", + ), + pytest.param( + None, + "VerifySomething", + True, + False, + "No test 'VerifySomething' found in 'anta.tests'", + ExitCode.OK, + id="Get tests wrong test name", + ), + pytest.param( + "anta.tests.aaa", + "VerifyNTP", + True, + False, + "No test 'VerifyNTP' found in 'anta.tests.aaa'", + ExitCode.OK, + id="Get tests test exists but not in module", + ), + pytest.param( + "anta.tests.system", + "VerifyNTPAssociations", + False, + True, + "There is 1 test available in 'anta.tests.system'.", + ExitCode.OK, + id="Get single test count", + ), + pytest.param( + "anta.tests.stun", + None, + False, + True, + "There are 3 tests available in 'anta.tests.stun'", + ExitCode.OK, + id="Get multiple test count", + ), + ], +) +def test_get_tests( + click_runner: CliRunner, module: str | None, test_name: str | None, *, short: bool, count: bool, expected_output: str, expected_exit_code: str +) -> None: + """Test `anta get tests`.""" + cli_args = [ + "get", + "tests", + ] + if module is not None: + cli_args.extend(["--module", module]) + + if test_name is not None: + cli_args.extend(["--test", test_name]) + + if short: + cli_args.append("--short") + + if count: + cli_args.append("--count") + + result = click_runner.invoke(anta, cli_args) + + assert result.exit_code == expected_exit_code + assert expected_output in result.output + + +def test_get_tests_local_module(click_runner: CliRunner) -> None: + """Test injecting CWD in sys. + + The test overwrite CWD to return this file parents and local_module is located there. + """ + cli_args = ["get", "tests", "--module", "local_module"] + + cwd = Path.cwd() + local_module_parent_path = Path(__file__).parent + with patch("anta.cli.get.utils.Path.cwd", return_value=local_module_parent_path): + result = click_runner.invoke(anta, cli_args) + + assert result.exit_code == ExitCode.OK + + # In the rare case where people would be running `pytest .` in this directory + if cwd != local_module_parent_path: + assert "injecting CWD in PYTHONPATH and retrying..." in result.output + assert "No test found in 'local_module'" in result.output diff --git a/tests/units/cli/get/test_utils.py b/tests/units/cli/get/test_utils.py index 46ce14f..9cff4ce 100644 --- a/tests/units/cli/get/test_utils.py +++ b/tests/units/cli/get/test_utils.py @@ -7,14 +7,15 @@ from __future__ import annotations from contextlib import AbstractContextManager, nullcontext from pathlib import Path -from typing import Any +from typing import Any, ClassVar from unittest.mock import MagicMock, patch import pytest import requests -from anta.cli.get.utils import create_inventory_from_ansible, create_inventory_from_cvp, get_cv_token +from anta.cli.get.utils import create_inventory_from_ansible, create_inventory_from_cvp, extract_examples, find_tests_examples, get_cv_token, print_test from anta.inventory import AntaInventory +from anta.models import AntaCommand, AntaTemplate, AntaTest DATA_DIR: Path = Path(__file__).parents[3].resolve() / "data" @@ -160,3 +161,91 @@ def test_create_inventory_from_ansible( assert not target_file.exists() if expected_log: assert expected_log in caplog.text + + +class MissingExampleTest(AntaTest): + """ANTA test that always succeed but has no Examples section.""" + + categories: ClassVar[list[str]] = [] + commands: ClassVar[list[AntaCommand | AntaTemplate]] = [] + + @AntaTest.anta_test + def test(self) -> None: + """Test function.""" + self.result.is_success() + + +class EmptyExampleTest(AntaTest): + """ANTA test that always succeed but has an empty Examples section. + + Examples + -------- + """ + + # For the test purpose we want am empty section as custom tests could not be using ruff. + # ruff: noqa: D414 + + categories: ClassVar[list[str]] = [] + commands: ClassVar[list[AntaCommand | AntaTemplate]] = [] + + @AntaTest.anta_test + def test(self) -> None: + """Test function.""" + self.result.is_success() + + +class TypoExampleTest(AntaTest): + """ANTA test that always succeed but has a Typo in the test name in the example. + + Notice capital P in TyPo below. + + Examples + -------- + ```yaml + tests.units.cli.get.test_utils: + - TyPoExampleTest: + ``` + """ + + # For the test purpose we want am empty section as custom tests could not be using ruff. + # ruff: noqa: D414 + + categories: ClassVar[list[str]] = [] + commands: ClassVar[list[AntaCommand | AntaTemplate]] = [] + + @AntaTest.anta_test + def test(self) -> None: + """Test function.""" + self.result.is_success() + + +def test_find_tests_examples() -> None: + """Test find_tests_examples. + + Only testing the failure scenarii not tested through test_commands. + TODO: expand + """ + with pytest.raises(ValueError, match="Error when importing"): + find_tests_examples("blah", "UnusedTestName") + + +def test_print_test() -> None: + """Test print_test.""" + with pytest.raises(ValueError, match="Could not find the name of the test"): + print_test(TypoExampleTest) + with pytest.raises(LookupError, match="is missing an Example"): + print_test(MissingExampleTest) + with pytest.raises(LookupError, match="is missing an Example"): + print_test(EmptyExampleTest) + + +def test_extract_examples() -> None: + """Test extract_examples. + + Only testing the case where the 'Examples' is missing as everything else + is covered already in test_commands.py. + """ + assert MissingExampleTest.__doc__ is not None + assert EmptyExampleTest.__doc__ is not None + assert extract_examples(MissingExampleTest.__doc__) is None + assert extract_examples(EmptyExampleTest.__doc__) is None diff --git a/tests/units/cli/nrfu/test_commands.py b/tests/units/cli/nrfu/test_commands.py index 6a2624c..372c86a 100644 --- a/tests/units/cli/nrfu/test_commands.py +++ b/tests/units/cli/nrfu/test_commands.py @@ -17,7 +17,7 @@ from anta.cli.utils import ExitCode if TYPE_CHECKING: from click.testing import CliRunner -DATA_DIR: Path = Path(__file__).parent.parent.parent.parent.resolve() / "data" +DATA_DIR: Path = Path(__file__).parents[3].resolve() / "data" def test_anta_nrfu_table_help(click_runner: CliRunner) -> None: @@ -76,6 +76,19 @@ def test_anta_nrfu_text(click_runner: CliRunner) -> None: assert "leaf1 :: VerifyEOSVersion :: SUCCESS" in result.output +def test_anta_nrfu_text_multiple_failures(click_runner: CliRunner) -> None: + """Test anta nrfu text with multiple failures, catalog is given via env.""" + result = click_runner.invoke(anta, ["nrfu", "text"], env={"ANTA_CATALOG": str(DATA_DIR / "test_catalog_double_failure.yml")}) + assert result.exit_code == ExitCode.TESTS_FAILED + assert ( + """spine1 :: VerifyInterfacesSpeed :: FAILURE + Interface `Ethernet2` is not found. + Interface `Ethernet3` is not found. + Interface `Ethernet4` is not found.""" + in result.output + ) + + def test_anta_nrfu_json(click_runner: CliRunner) -> None: """Test anta nrfu, catalog is given via env.""" result = click_runner.invoke(anta, ["nrfu", "json"]) |