summaryrefslogtreecommitdiffstats
path: root/tests/units/cli
diff options
context:
space:
mode:
Diffstat (limited to 'tests/units/cli')
-rw-r--r--tests/units/cli/conftest.py1
-rw-r--r--tests/units/cli/get/local_module/__init__.py4
-rw-r--r--tests/units/cli/get/test_commands.py183
-rw-r--r--tests/units/cli/get/test_utils.py93
-rw-r--r--tests/units/cli/nrfu/test_commands.py15
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"])