diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:04:56 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:04:56 +0000 |
commit | d964cec5e6aa807b75c7a4e7cdc5f11e54b2eda2 (patch) | |
tree | 794bc3738a00b5e599f06d1f2f6d79048d87ff8e /test/rules | |
parent | Initial commit. (diff) | |
download | ansible-lint-upstream.tar.xz ansible-lint-upstream.zip |
Adding upstream version 6.13.1.upstream/6.13.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | test/rules/__init__.py | 1 | ||||
-rw-r--r-- | test/rules/fixtures/__init__.py | 3 | ||||
-rw-r--r-- | test/rules/fixtures/ematcher.py | 15 | ||||
-rw-r--r-- | test/rules/fixtures/raw_task.md | 3 | ||||
-rw-r--r-- | test/rules/fixtures/raw_task.py | 25 | ||||
-rw-r--r-- | test/rules/fixtures/unset_variable_matcher.py | 16 | ||||
-rw-r--r-- | test/rules/test_deprecated_module.py | 25 | ||||
-rw-r--r-- | test/rules/test_inline_env_var.py | 90 | ||||
-rw-r--r-- | test/rules/test_line_too_long.py | 20 | ||||
-rw-r--r-- | test/rules/test_literal_compare.py | 93 | ||||
-rw-r--r-- | test/rules/test_meta_change_from_default.py | 42 | ||||
-rw-r--r-- | test/rules/test_meta_no_info.py | 98 | ||||
-rw-r--r-- | test/rules/test_meta_video_links.py | 46 | ||||
-rw-r--r-- | test/rules/test_no_changed_when.py | 23 | ||||
-rw-r--r-- | test/rules/test_no_relative_paths.py | 53 | ||||
-rw-r--r-- | test/rules/test_package_latest.py | 23 | ||||
-rw-r--r-- | test/rules/test_risky_shell_pipe.py | 94 | ||||
-rw-r--r-- | test/rules/test_role_names.py | 86 | ||||
-rw-r--r-- | test/rules/test_use_handler_rather_than_when_changed.py | 86 |
19 files changed, 842 insertions, 0 deletions
diff --git a/test/rules/__init__.py b/test/rules/__init__.py new file mode 100644 index 0000000..28b581d --- /dev/null +++ b/test/rules/__init__.py @@ -0,0 +1 @@ +"""Tests for specific rules.""" diff --git a/test/rules/fixtures/__init__.py b/test/rules/fixtures/__init__.py new file mode 100644 index 0000000..d049bf0 --- /dev/null +++ b/test/rules/fixtures/__init__.py @@ -0,0 +1,3 @@ +"""Test rules resources.""" + +__all__ = ["ematcher", "raw_task", "unset_variable_matcher"] diff --git a/test/rules/fixtures/ematcher.py b/test/rules/fixtures/ematcher.py new file mode 100644 index 0000000..cbae696 --- /dev/null +++ b/test/rules/fixtures/ematcher.py @@ -0,0 +1,15 @@ +"""Custom rule used as fixture.""" +from ansiblelint.rules import AnsibleLintRule + + +class EMatcherRule(AnsibleLintRule): + """BANNED string found.""" + + id = "TEST0001" + description = ( + "This is a test custom rule that looks for lines " + "containing BANNED string" + ) + tags = ["fake", "dummy", "test1"] + + def match(self, line: str) -> bool: + return "BANNED" in line diff --git a/test/rules/fixtures/raw_task.md b/test/rules/fixtures/raw_task.md new file mode 100644 index 0000000..2aa6d22 --- /dev/null +++ b/test/rules/fixtures/raw_task.md @@ -0,0 +1,3 @@ +# raw-task + +This is a test rule that looks in a raw task to flag raw action params. diff --git a/test/rules/fixtures/raw_task.py b/test/rules/fixtures/raw_task.py new file mode 100644 index 0000000..672789d --- /dev/null +++ b/test/rules/fixtures/raw_task.py @@ -0,0 +1,25 @@ +"""Test Rule that needs_raw_task.""" +from __future__ import annotations + +from typing import Any + +from ansiblelint.file_utils import Lintable +from ansiblelint.rules import AnsibleLintRule + + +class RawTaskRule(AnsibleLintRule): + """Test rule that inspects the raw task.""" + + id = "raw-task" + shortdesc = "Test rule that inspects the raw task" + tags = ["fake", "dummy", "test3"] + needs_raw_task = True + + def matchtask( + self, task: dict[str, Any], file: Lintable | None = None + ) -> bool | str: + """Match a task using __raw_task__ to inspect the module params type.""" + raw_task = task["__raw_task__"] + module = task["action"]["__ansible_module_original__"] + found_raw_task_params = not isinstance(raw_task[module], dict) + return found_raw_task_params diff --git a/test/rules/fixtures/unset_variable_matcher.py b/test/rules/fixtures/unset_variable_matcher.py new file mode 100644 index 0000000..c2b1744 --- /dev/null +++ b/test/rules/fixtures/unset_variable_matcher.py @@ -0,0 +1,16 @@ +"""Custom linting rule used as test fixture.""" +from ansiblelint.rules import AnsibleLintRule + + +class UnsetVariableMatcherRule(AnsibleLintRule): + """Line contains untemplated variable.""" + + id = "TEST0002" + description = ( + "This is a test rule that looks for lines " + + "post templating that still contain {{" + ) + tags = ["fake", "dummy", "test2"] + + def match(self, line: str) -> bool: + return "{{" in line diff --git a/test/rules/test_deprecated_module.py b/test/rules/test_deprecated_module.py new file mode 100644 index 0000000..506d91f --- /dev/null +++ b/test/rules/test_deprecated_module.py @@ -0,0 +1,25 @@ +"""Tests for deprecated-module rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.deprecated_module import DeprecatedModuleRule +from ansiblelint.testing import RunFromText + +MODULE_DEPRECATED = """ +- name: Task example + docker: + debug: test +""" + + +def test_module_deprecated() -> None: + """Test for deprecated-module.""" + collection = RulesCollection() + collection.register(DeprecatedModuleRule()) + runner = RunFromText(collection) + results = runner.run_role_tasks_main(MODULE_DEPRECATED) + assert len(results) == 1 + # based on version and blend of ansible being used, we may + # get a missing module, so we future proof the test + assert ( + "couldn't resolve module" not in results[0].message + or "Deprecated module" not in results[0].message + ) diff --git a/test/rules/test_inline_env_var.py b/test/rules/test_inline_env_var.py new file mode 100644 index 0000000..98f337e --- /dev/null +++ b/test/rules/test_inline_env_var.py @@ -0,0 +1,90 @@ +"""Tests for inline-env-var rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.inline_env_var import EnvVarsInCommandRule +from ansiblelint.testing import RunFromText + +SUCCESS_PLAY_TASKS = """ +- hosts: localhost + + tasks: + - name: Actual use of environment + shell: echo $HELLO + environment: + HELLO: hello + + - name: Use some key-value pairs + command: chdir=/tmp creates=/tmp/bobbins warn=no touch bobbins + + - name: Commands can have flags + command: abc --xyz=def blah + + - name: Commands can have equals in them + command: echo "===========" + + - name: Commands with cmd + command: + cmd: + echo "-------" + + - name: Command with stdin (ansible > 2.4) + command: /bin/cat + args: + stdin: "Hello, world!" + + - name: Use argv to send the command as a list + command: + argv: + - /bin/echo + - Hello + - World + + - name: Another use of argv + command: + args: + argv: + - echo + - testing + + - name: Environment variable with shell + shell: HELLO=hello echo $HELLO + + - name: Command with stdin_add_newline (ansible > 2.8) + command: /bin/cat + args: + stdin: "Hello, world!" + stdin_add_newline: false + + - name: Command with strip_empty_ends (ansible > 2.8) + command: echo + args: + strip_empty_ends: false +""" + +FAIL_PLAY_TASKS = """ +- hosts: localhost + + tasks: + - name: Environment variable with command + command: HELLO=hello echo $HELLO + + - name: Typo some stuff + command: cerates=/tmp/blah warn=no touch /tmp/blah +""" + + +def test_success() -> None: + """Positive test for inline-env-var.""" + collection = RulesCollection() + collection.register(EnvVarsInCommandRule()) + runner = RunFromText(collection) + results = runner.run_playbook(SUCCESS_PLAY_TASKS) + assert len(results) == 0 + + +def test_fail() -> None: + """Negative test for inline-env-var.""" + collection = RulesCollection() + collection.register(EnvVarsInCommandRule()) + runner = RunFromText(collection) + results = runner.run_playbook(FAIL_PLAY_TASKS) + assert len(results) == 2 diff --git a/test/rules/test_line_too_long.py b/test/rules/test_line_too_long.py new file mode 100644 index 0000000..3c7517b --- /dev/null +++ b/test/rules/test_line_too_long.py @@ -0,0 +1,20 @@ +"""Tests for line-too-long rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.yaml_rule import YamllintRule +from ansiblelint.testing import RunFromText + +LONG_LINE = """\ +--- +- name: Task example + debug: + msg: 'This is a very long text that is used in order to verify the rule that checks for very long lines. We do hope it was long enough to go over the line limit.' +""" # noqa: E501 + + +def test_long_line() -> None: + """Negative test for long-line.""" + collection = RulesCollection() + collection.register(YamllintRule()) + runner = RunFromText(collection) + results = runner.run_role_tasks_main(LONG_LINE) + assert len(results) == 1 diff --git a/test/rules/test_literal_compare.py b/test/rules/test_literal_compare.py new file mode 100644 index 0000000..6953cb1 --- /dev/null +++ b/test/rules/test_literal_compare.py @@ -0,0 +1,93 @@ +"""Tests for literal-compare rule.""" +import pytest + +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.literal_compare import ComparisonToLiteralBoolRule +from ansiblelint.testing import RunFromText + +PASS_WHEN = """ +- name: Example task + debug: + msg: test + when: my_var + +- name: Another example task + debug: + msg: test + when: + - 1 + 1 == 2 + - true +""" + +PASS_WHEN_NOT_FALSE = """ +- name: Example task + debug: + msg: test + when: not my_var +""" + +PASS_WHEN_NOT_NULL = """ +- name: Example task + debug: + msg: test + when: my_var not None +""" + +FAIL_LITERAL_TRUE = """ +- name: Example task + debug: + msg: test + when: my_var == True +""" + +FAIL_LITERAL_FALSE = """ +- name: Example task + debug: + msg: test + when: my_var == false + +- name: Another example task + debug: + msg: test + when: + - my_var == false +""" + + +@pytest.mark.parametrize( + ("input_str", "found_errors"), + ( + pytest.param( + PASS_WHEN, + 0, + id="pass_when", + ), + pytest.param( + PASS_WHEN_NOT_FALSE, + 0, + id="when_not_false", + ), + pytest.param( + PASS_WHEN_NOT_NULL, + 0, + id="when_not_null", + ), + pytest.param( + FAIL_LITERAL_TRUE, + 1, + id="literal_true", + ), + pytest.param( + FAIL_LITERAL_FALSE, + 2, + id="literal_false", + ), + ), +) +def test_literal_compare(input_str: str, found_errors: int) -> None: + """Test literal-compare.""" + collection = RulesCollection() + collection.register(ComparisonToLiteralBoolRule()) + runner = RunFromText(collection) + results = runner.run_role_tasks_main(input_str) + assert len(results) == found_errors diff --git a/test/rules/test_meta_change_from_default.py b/test/rules/test_meta_change_from_default.py new file mode 100644 index 0000000..31125d3 --- /dev/null +++ b/test/rules/test_meta_change_from_default.py @@ -0,0 +1,42 @@ +"""Tests for meta-incorrect rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.meta_incorrect import MetaChangeFromDefaultRule +from ansiblelint.testing import RunFromText + +DEFAULT_GALAXY_INFO = """ +galaxy_info: + author: your name + description: your description + company: your company (optional) + license: license (GPLv2, CC-BY, etc) +""" + +NO_GALAXY_INFO = """ +galaxy_information: + author: your name + description: your description + company: your company (optional) + license: license (GPLv2, CC-BY, etc) +""" + + +def test_default_galaxy_info() -> None: + """Test for meta-incorrect.""" + collection = RulesCollection() + collection.register(MetaChangeFromDefaultRule()) + runner = RunFromText(collection) + results = runner.run_role_meta_main(DEFAULT_GALAXY_INFO) + # Disabled check because default value is not passing schema validation + # assert "Should change default metadata: author" in str(results) + assert "Should change default metadata: description" in str(results) + assert "Should change default metadata: company" in str(results) + assert "Should change default metadata: license" in str(results) + + +def test_no_galaxy_info() -> None: + """Test for no galaxy info passed to meta-incorrect.""" + collection = RulesCollection() + collection.register(MetaChangeFromDefaultRule()) + runner = RunFromText(collection) + results = runner.run_role_meta_main(NO_GALAXY_INFO) + assert results == [] diff --git a/test/rules/test_meta_no_info.py b/test/rules/test_meta_no_info.py new file mode 100644 index 0000000..bbadec6 --- /dev/null +++ b/test/rules/test_meta_no_info.py @@ -0,0 +1,98 @@ +"""Tests for meta-no-info rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.meta_no_info import MetaMainHasInfoRule +from ansiblelint.testing import RunFromText + +NO_GALAXY_INFO = """ +author: the author +description: this meta/main.yml has no galaxy_info +""" + +MISSING_INFO = """ +galaxy_info: + # author: the author + description: Testing if meta contains values + company: Not applicable + + license: MIT + + # min_ansible_version: 2.5 + + platforms: + - name: Fedora + versions: + - 25 + - missing_name: No name + versions: + - 25 +""" + +BAD_TYPES = """ +galaxy_info: + author: 007 + description: ['Testing meta'] + company: Not applicable + + license: MIT + + min_ansible_version: 2.5 + + platforms: Fedora +""" + +PLATFORMS_LIST_OF_STR = """ +galaxy_info: + author: '007' + description: 'Testing meta' + company: Not applicable + + license: MIT + + min_ansible_version: 2.5 + + platforms: ['Fedora', 'EL'] +""" + + +def test_no_galaxy_info() -> None: + """Test meta-no-info with missing galaxy_info.""" + collection = RulesCollection() + collection.register(MetaMainHasInfoRule()) + runner = RunFromText(collection) + results = runner.run_role_meta_main(NO_GALAXY_INFO) + assert len(results) == 1 + assert "No 'galaxy_info' found" in str(results) + + +def test_missing_info() -> None: + """Test meta-no-info.""" + collection = RulesCollection() + collection.register(MetaMainHasInfoRule()) + runner = RunFromText(collection) + results = runner.run_role_meta_main(MISSING_INFO) + assert len(results) == 3 + assert "Role info should contain author" in str(results) + assert "Role info should contain min_ansible_version" in str(results) + assert "Platform should contain name" in str(results) + + +def test_bad_types() -> None: + """Test meta-no-info with bad types.""" + collection = RulesCollection() + collection.register(MetaMainHasInfoRule()) + runner = RunFromText(collection) + results = runner.run_role_meta_main(BAD_TYPES) + assert len(results) == 3 + assert "author should be a string" in str(results) + assert "description should be a string" in str(results) + assert "Platforms should be a list of dictionaries" in str(results) + + +def test_platform_list_of_str() -> None: + """Test meta-no-info with platforms.""" + collection = RulesCollection() + collection.register(MetaMainHasInfoRule()) + runner = RunFromText(collection) + results = runner.run_role_meta_main(PLATFORMS_LIST_OF_STR) + assert len(results) == 1 + assert "Platforms should be a list of dictionaries" in str(results) diff --git a/test/rules/test_meta_video_links.py b/test/rules/test_meta_video_links.py new file mode 100644 index 0000000..3772ea2 --- /dev/null +++ b/test/rules/test_meta_video_links.py @@ -0,0 +1,46 @@ +"""Tests for meta-video-links rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.meta_video_links import MetaVideoLinksRule +from ansiblelint.testing import RunFromText + +META_VIDEO_LINKS = """ +galaxy_info: + video_links: + - url: https://www.youtube.com/watch?v=aWmRepTSFKs&feature=youtu.be + title: Proper format + - url: https://drive.google.com/file/d/1spYR51l8SqQqvAhSdZE7/view + title: Check for VIDEO_REGEXP validity and break + - https://www.youtube.com/watch?v=aWmRepTSFKs&feature=youtu.be + - my_bad_key: https://www.youtube.com/watch?v=aWmRepTSFKs&feature=youtu.be + title: This has a bad key + - url: www.acme.com/vid + title: Bad format of url +""" + +META_NO_GALAXY_INFO = """ +galaxy_information: + video_links: + - url: https://www.youtube.com/watch?v=aWmRepTSFKs&feature=youtu.be +""" + + +def test_video_links() -> None: + """Test meta_video_links.""" + collection = RulesCollection() + collection.register(MetaVideoLinksRule()) + runner = RunFromText(collection) + + results = runner.run_role_meta_main(META_VIDEO_LINKS) + assert "Expected item in 'video_links' to be a dictionary" in str(results) + assert "'video_links' to contain only keys 'url' and 'title'" in str(results) + assert "URL format 'www.acme.com/vid' is not recognized" in str(results) + + +def test_meta_video_links_no_galaxy_info() -> None: + """Test meta_video_links.""" + collection = RulesCollection() + collection.register(MetaVideoLinksRule()) + runner = RunFromText(collection) + + results = runner.run_role_meta_main(META_NO_GALAXY_INFO) + assert len(results) == 0 diff --git a/test/rules/test_no_changed_when.py b/test/rules/test_no_changed_when.py new file mode 100644 index 0000000..c89d8f4 --- /dev/null +++ b/test/rules/test_no_changed_when.py @@ -0,0 +1,23 @@ +"""Tests for no-change-when rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.no_changed_when import CommandHasChangesCheckRule +from ansiblelint.runner import Runner + + +def test_command_changes_positive() -> None: + """Positive test for no-changed-when.""" + collection = RulesCollection() + collection.register(CommandHasChangesCheckRule()) + success = "examples/playbooks/command-check-success.yml" + good_runner = Runner(success, rules=collection) + assert [] == good_runner.run() + + +def test_command_changes_negative() -> None: + """Negative test for no-changed-when.""" + collection = RulesCollection() + collection.register(CommandHasChangesCheckRule()) + failure = "examples/playbooks/command-check-failure.yml" + bad_runner = Runner(failure, rules=collection) + errs = bad_runner.run() + assert len(errs) == 2 diff --git a/test/rules/test_no_relative_paths.py b/test/rules/test_no_relative_paths.py new file mode 100644 index 0000000..6999cbb --- /dev/null +++ b/test/rules/test_no_relative_paths.py @@ -0,0 +1,53 @@ +"""Tests for no-relative-paths rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.no_relative_paths import RoleRelativePath +from ansiblelint.testing import RunFromText + +FAIL_TASKS = """ +- name: Template example + template: + src: ../templates/foo.j2 + dest: /etc/file.conf +- name: Copy example + copy: + src: ../files/foo.conf + dest: /etc/foo.conf +# Removed from test suite as module is no longer part of core +# - name: Some win_template example +# win_template: +# src: ../win_templates/file.conf.j2 +# dest: file.conf +# - name: Some win_copy example +# win_copy: +# src: ../files/foo.conf +# dest: renamed-foo.conf +""" + +SUCCESS_TASKS = """ +- name: Content example with no src + copy: + content: '# This file was moved to /etc/other.conf' + dest: /etc/mine.conf +- name: Copy example + copy: + src: /home/example/files/foo.conf + dest: /etc/foo.conf +""" + + +def test_no_relative_paths_fail() -> None: + """Negative test for no-relative-paths.""" + collection = RulesCollection() + collection.register(RoleRelativePath()) + runner = RunFromText(collection) + results = runner.run_role_tasks_main(FAIL_TASKS) + assert len(results) == 2 + + +def test_no_relative_paths_success() -> None: + """Positive test for no-relative-paths.""" + collection = RulesCollection() + collection.register(RoleRelativePath()) + runner = RunFromText(collection) + results = runner.run_role_tasks_main(SUCCESS_TASKS) + assert len(results) == 0 diff --git a/test/rules/test_package_latest.py b/test/rules/test_package_latest.py new file mode 100644 index 0000000..5631f02 --- /dev/null +++ b/test/rules/test_package_latest.py @@ -0,0 +1,23 @@ +"""Tests for package-latest rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.package_latest import PackageIsNotLatestRule +from ansiblelint.runner import Runner + + +def test_package_not_latest_positive() -> None: + """Positive test for package-latest.""" + collection = RulesCollection() + collection.register(PackageIsNotLatestRule()) + success = "examples/playbooks/package-check-success.yml" + good_runner = Runner(success, rules=collection) + assert [] == good_runner.run() + + +def test_package_not_latest_negative() -> None: + """Negative test for package-latest.""" + collection = RulesCollection() + collection.register(PackageIsNotLatestRule()) + failure = "examples/playbooks/package-check-failure.yml" + bad_runner = Runner(failure, rules=collection) + errs = bad_runner.run() + assert len(errs) == 4 diff --git a/test/rules/test_risky_shell_pipe.py b/test/rules/test_risky_shell_pipe.py new file mode 100644 index 0000000..b2ecb67 --- /dev/null +++ b/test/rules/test_risky_shell_pipe.py @@ -0,0 +1,94 @@ +"""Tests for risky-shell-pile rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.risky_shell_pipe import ShellWithoutPipefail +from ansiblelint.testing import RunFromText + +FAIL_TASKS = """ +--- +- hosts: localhost + become: no + tasks: + - name: Pipeline without pipefail + shell: false | cat + + - name: Pipeline with or and pipe, no pipefail + shell: false || true | cat + + - shell: | + df | grep '/dev' +""" + +SUCCESS_TASKS = """ +--- +- hosts: localhost + become: no + tasks: + - name: Pipeline with pipefail + shell: set -o pipefail && false | cat + + - name: Pipeline with pipefail, multi-line + shell: | + set -o pipefail + false | cat + + - name: Pipeline with pipefail, complex set + shell: | + set -e -x -o pipefail + false | cat + + - name: Pipeline with pipefail, complex set + shell: | + set -e -x -o pipefail + false | cat + + - name: Pipeline with pipefail, complex set + shell: | + set -eo pipefail + false | cat + + - name: Pipeline with pipefail not at first line + shell: | + echo foo + set -eo pipefail + false | cat + + - name: Pipeline without pipefail, ignoring errors + shell: false | cat + ignore_errors: true + + - name: Non-pipeline without pipefail + shell: "true" + + - name: Command without pipefail + command: "true" + + - name: Shell with or + shell: + false || true + + - shell: | + set -o pipefail + df | grep '/dev' + + - name: Should not fail due to ignore_errors being true + shell: false | cat + ignore_errors: true +""" + + +def test_fail() -> None: + """Negative test for risky-shell-pipe.""" + collection = RulesCollection() + collection.register(ShellWithoutPipefail()) + runner = RunFromText(collection) + results = runner.run_playbook(FAIL_TASKS) + assert len(results) == 3 + + +def test_success() -> None: + """Positive test for risky-shell-pipe.""" + collection = RulesCollection() + collection.register(ShellWithoutPipefail()) + runner = RunFromText(collection) + results = runner.run_playbook(SUCCESS_TASKS) + assert len(results) == 0 diff --git a/test/rules/test_role_names.py b/test/rules/test_role_names.py new file mode 100644 index 0000000..4d55a61 --- /dev/null +++ b/test/rules/test_role_names.py @@ -0,0 +1,86 @@ +"""Test the RoleNames rule.""" +from __future__ import annotations + +from pathlib import Path +from typing import Any + +import pytest +from _pytest.fixtures import SubRequest + +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.role_name import RoleNames +from ansiblelint.runner import Runner + +ROLE_NAME_VALID = "test_role" + +TASK_MINIMAL = """ +- name: Some task + ping: +""" + +ROLE_MINIMAL = {"tasks": {"main.yml": TASK_MINIMAL}} +ROLE_META_EMPTY = {"meta": {"main.yml": ""}} + +ROLE_WITH_EMPTY_META = {**ROLE_MINIMAL, **ROLE_META_EMPTY} + +PLAY_INCLUDE_ROLE = f""" +- hosts: all + roles: + - {ROLE_NAME_VALID} +""" + + +@pytest.fixture(name="test_rules_collection") +def fixture_test_rules_collection() -> RulesCollection: + """Instantiate a roles collection for tests.""" + collection = RulesCollection() + collection.register(RoleNames()) + return collection + + +def dict_to_files(parent_dir: Path, file_dict: dict[str, Any]) -> None: + """Write a nested dict to a file and directory structure below parent_dir.""" + for file, content in file_dict.items(): + if isinstance(content, dict): + directory = parent_dir / file + directory.mkdir() + dict_to_files(directory, content) + else: + (parent_dir / file).write_text(content) + + +@pytest.fixture(name="playbook_path") +def fixture_playbook_path(request: SubRequest, tmp_path: Path) -> str: + """Create a playbook with a role in a temporary directory.""" + playbook_text = request.param[0] + role_name = request.param[1] + role_layout = request.param[2] + role_path = tmp_path / role_name + role_path.mkdir() + dict_to_files(role_path, role_layout) + play_path = tmp_path / "playbook.yml" + play_path.write_text(playbook_text) + return str(play_path) + + +@pytest.mark.parametrize( + ("playbook_path", "messages"), + ( + pytest.param( + (PLAY_INCLUDE_ROLE, ROLE_NAME_VALID, ROLE_WITH_EMPTY_META), + [], + id="ROLE_EMPTY_META", + ), + ), + indirect=("playbook_path",), +) +def test_role_name( + test_rules_collection: RulesCollection, playbook_path: str, messages: list[str] +) -> None: + """Lint a playbook and compare the expected messages with the actual messages.""" + runner = Runner(playbook_path, rules=test_rules_collection) + results = runner.run() + assert len(results) == len(messages) + results_text = str(results) + for message in messages: + assert message in results_text diff --git a/test/rules/test_use_handler_rather_than_when_changed.py b/test/rules/test_use_handler_rather_than_when_changed.py new file mode 100644 index 0000000..2b70bf6 --- /dev/null +++ b/test/rules/test_use_handler_rather_than_when_changed.py @@ -0,0 +1,86 @@ +"""Tests for no-handler rule.""" +from ansiblelint.rules import RulesCollection +from ansiblelint.rules.no_handler import UseHandlerRatherThanWhenChangedRule +from ansiblelint.testing import RunFromText + +SUCCESS_TASKS = """ +- name: Print helpful error message + debug: + var: result + when: result.failed + +- name: Do something when hello is output + debug: + msg: why isn't this a handler + when: result.stdout == "hello" + +- name: Never actually debug + debug: + var: result + when: False + +- name: "Don't execute this step" + debug: + msg: "debug message" + when: + - false + +- name: Check when with a list + debug: + var: result + when: + - conditionA + - conditionB +""" + + +FAIL_TASKS = """ +- name: Execute command + command: echo hello + register: result + +- name: This should be a handler + debug: + msg: why isn't this a handler + when: result.changed + +- name: This should be a handler 2 + debug: + msg: why isn't this a handler + when: result|changed + +- name: This should be a handler 3 + debug: + msg: why isn't this a handler + when: result.changed == true + +- name: This should be a handler 4 + debug: + msg: why isn't this a handler + when: result['changed'] == true + +- name: This should be a handler 5 + debug: + msg: why isn't this a handler + when: + - result['changed'] == true + - another_condition +""" + + +def test_no_handler_success() -> None: + """Positive test for no-handler.""" + collection = RulesCollection() + collection.register(UseHandlerRatherThanWhenChangedRule()) + runner = RunFromText(collection) + results = runner.run_role_tasks_main(SUCCESS_TASKS) + assert len(results) == 0 + + +def test_no_handler_fail() -> None: + """Negative test for no-handler.""" + collection = RulesCollection() + collection.register(UseHandlerRatherThanWhenChangedRule()) + runner = RunFromText(collection) + results = runner.run_role_tasks_main(FAIL_TASKS) + assert len(results) == 5 |