summaryrefslogtreecommitdiffstats
path: root/test/rules
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--test/rules/__init__.py1
-rw-r--r--test/rules/fixtures/__init__.py3
-rw-r--r--test/rules/fixtures/ematcher.py15
-rw-r--r--test/rules/fixtures/raw_task.md3
-rw-r--r--test/rules/fixtures/raw_task.py30
-rw-r--r--test/rules/fixtures/unset_variable_matcher.py15
-rw-r--r--test/rules/test_deprecated_module.py27
-rw-r--r--test/rules/test_inline_env_var.py90
-rw-r--r--test/rules/test_no_changed_when.py23
-rw-r--r--test/rules/test_package_latest.py23
-rw-r--r--test/rules/test_role_names.py91
-rw-r--r--test/rules/test_syntax_check.py70
12 files changed, 391 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..1b04b6b
--- /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..0d5b023
--- /dev/null
+++ b/test/rules/fixtures/raw_task.py
@@ -0,0 +1,30 @@
+"""Test Rule that needs_raw_task."""
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from ansiblelint.rules import AnsibleLintRule
+
+if TYPE_CHECKING:
+ from ansiblelint.file_utils import Lintable
+ from ansiblelint.utils import Task
+
+
+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: Task,
+ 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..8486009
--- /dev/null
+++ b/test/rules/fixtures/unset_variable_matcher.py
@@ -0,0 +1,15 @@
+"""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..a57d8db
--- /dev/null
+++ b/test/rules/test_deprecated_module.py
@@ -0,0 +1,27 @@
+"""Tests for deprecated-module rule."""
+from pathlib import Path
+
+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(tmp_path: Path) -> None:
+ """Test for deprecated-module."""
+ collection = RulesCollection()
+ collection.register(DeprecatedModuleRule())
+ runner = RunFromText(collection)
+ results = runner.run_role_tasks_main(MODULE_DEPRECATED, tmp_path=tmp_path)
+ 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_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_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_role_names.py b/test/rules/test_role_names.py
new file mode 100644
index 0000000..491cf14
--- /dev/null
+++ b/test/rules/test_role_names.py
@@ -0,0 +1,91 @@
+"""Test the RoleNames rule."""
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+import pytest
+
+from ansiblelint.rules import RulesCollection
+from ansiblelint.rules.role_name import RoleNames
+from ansiblelint.runner import Runner
+
+if TYPE_CHECKING:
+ from pathlib import Path
+
+ from _pytest.fixtures import SubRequest
+
+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_syntax_check.py b/test/rules/test_syntax_check.py
new file mode 100644
index 0000000..2fe36a3
--- /dev/null
+++ b/test/rules/test_syntax_check.py
@@ -0,0 +1,70 @@
+"""Tests for syntax-check rule."""
+from typing import Any
+
+from ansiblelint.file_utils import Lintable
+from ansiblelint.rules import RulesCollection
+from ansiblelint.runner import Runner
+
+
+def test_get_ansible_syntax_check_matches(
+ default_rules_collection: RulesCollection,
+) -> None:
+ """Validate parsing of ansible output."""
+ lintable = Lintable(
+ "examples/playbooks/conflicting_action.yml",
+ kind="playbook",
+ )
+
+ result = Runner(lintable, rules=default_rules_collection).run()
+
+ assert result[0].lineno == 4
+ assert result[0].column == 7
+ assert (
+ result[0].message
+ == "conflicting action statements: ansible.builtin.debug, ansible.builtin.command"
+ )
+ # We internally convert absolute paths returned by ansible into paths
+ # relative to current directory.
+ assert result[0].filename.endswith("/conflicting_action.yml")
+ assert len(result) == 1
+
+
+def test_empty_playbook(default_rules_collection: RulesCollection) -> None:
+ """Validate detection of empty-playbook."""
+ lintable = Lintable("examples/playbooks/empty_playbook.yml", kind="playbook")
+ result = Runner(lintable, rules=default_rules_collection).run()
+ assert result[0].lineno == 1
+ # We internally convert absolute paths returned by ansible into paths
+ # relative to current directory.
+ assert result[0].filename.endswith("/empty_playbook.yml")
+ assert result[0].tag == "syntax-check[empty-playbook]"
+ assert result[0].message == "Empty playbook, nothing to do"
+ assert len(result) == 1
+
+
+def test_extra_vars_passed_to_command(
+ default_rules_collection: RulesCollection,
+ config_options: Any,
+) -> None:
+ """Validate `extra-vars` are passed to syntax check command."""
+ config_options.extra_vars = {
+ "foo": "bar",
+ "complex_variable": ":{;\t$()",
+ }
+ lintable = Lintable("examples/playbooks/extra_vars.yml", kind="playbook")
+
+ result = Runner(lintable, rules=default_rules_collection).run()
+
+ assert not result
+
+
+def test_syntax_check_role() -> None:
+ """Validate syntax check of a broken role."""
+ lintable = Lintable("examples/playbooks/roles/invalid_due_syntax", kind="role")
+ rules = RulesCollection()
+ result = Runner(lintable, rules=rules).run()
+ assert len(result) == 1, result
+ assert result[0].lineno == 2
+ assert result[0].filename == "examples/roles/invalid_due_syntax/tasks/main.yml"
+ assert result[0].tag == "syntax-check[specific]"
+ assert result[0].message == "no module/action detected in task."