summaryrefslogtreecommitdiffstats
path: root/test/test_rules_collection.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/test_rules_collection.py')
-rw-r--r--test/test_rules_collection.py175
1 files changed, 175 insertions, 0 deletions
diff --git a/test/test_rules_collection.py b/test/test_rules_collection.py
new file mode 100644
index 0000000..66c69ec
--- /dev/null
+++ b/test/test_rules_collection.py
@@ -0,0 +1,175 @@
+"""Tests for rule collection class."""
+# Copyright (c) 2013-2014 Will Thames <will@thames.id.au>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+from __future__ import annotations
+
+import collections
+import re
+from pathlib import Path
+
+import pytest
+
+from ansiblelint.config import options
+from ansiblelint.file_utils import Lintable
+from ansiblelint.rules import RulesCollection
+from ansiblelint.testing import run_ansible_lint
+
+
+@pytest.fixture(name="test_rules_collection")
+def fixture_test_rules_collection() -> RulesCollection:
+ """Create a shared rules collection test instance."""
+ return RulesCollection([Path("./test/rules/fixtures").resolve()])
+
+
+@pytest.fixture(name="ematchtestfile")
+def fixture_ematchtestfile() -> Lintable:
+ """Produce a test lintable with an id violation."""
+ return Lintable("examples/playbooks/ematcher-rule.yml", kind="playbook")
+
+
+@pytest.fixture(name="bracketsmatchtestfile")
+def fixture_bracketsmatchtestfile() -> Lintable:
+ """Produce a test lintable with matching brackets."""
+ return Lintable("examples/playbooks/bracketsmatchtest.yml", kind="playbook")
+
+
+def test_load_collection_from_directory(test_rules_collection: RulesCollection) -> None:
+ """Test that custom rules extend the default ones."""
+ # two detected rules plus the internal ones
+ assert len(test_rules_collection) == 7
+
+
+def test_run_collection(
+ test_rules_collection: RulesCollection,
+ ematchtestfile: Lintable,
+) -> None:
+ """Test that default rules match pre-meditated violations."""
+ matches = test_rules_collection.run(ematchtestfile)
+ assert len(matches) == 4 # 3 occurrences of BANNED using TEST0001 + 1 for raw-task
+ assert matches[0].lineno == 3
+
+
+def test_tags(
+ test_rules_collection: RulesCollection,
+ ematchtestfile: Lintable,
+ bracketsmatchtestfile: Lintable,
+) -> None:
+ """Test that tags are treated as skip markers."""
+ matches = test_rules_collection.run(ematchtestfile, tags={"test1"})
+ assert len(matches) == 3
+ matches = test_rules_collection.run(ematchtestfile, tags={"test2"})
+ assert len(matches) == 0
+ matches = test_rules_collection.run(bracketsmatchtestfile, tags={"test1"})
+ assert len(matches) == 0
+ matches = test_rules_collection.run(bracketsmatchtestfile, tags={"test2"})
+ assert len(matches) == 2
+
+
+def test_skip_tags(
+ test_rules_collection: RulesCollection,
+ ematchtestfile: Lintable,
+ bracketsmatchtestfile: Lintable,
+) -> None:
+ """Test that tags can be skipped."""
+ matches = test_rules_collection.run(ematchtestfile, skip_list=["test1", "test3"])
+ assert len(matches) == 0
+ matches = test_rules_collection.run(ematchtestfile, skip_list=["test2", "test3"])
+ assert len(matches) == 3
+ matches = test_rules_collection.run(bracketsmatchtestfile, skip_list=["test1"])
+ assert len(matches) == 2
+ matches = test_rules_collection.run(bracketsmatchtestfile, skip_list=["test2"])
+ assert len(matches) == 0
+
+
+def test_skip_id(
+ test_rules_collection: RulesCollection,
+ ematchtestfile: Lintable,
+ bracketsmatchtestfile: Lintable,
+) -> None:
+ """Check that skipping valid IDs excludes their violations."""
+ matches = test_rules_collection.run(
+ ematchtestfile,
+ skip_list=["TEST0001", "raw-task"],
+ )
+ assert len(matches) == 0
+ matches = test_rules_collection.run(
+ ematchtestfile,
+ skip_list=["TEST0002", "raw-task"],
+ )
+ assert len(matches) == 3
+ matches = test_rules_collection.run(bracketsmatchtestfile, skip_list=["TEST0001"])
+ assert len(matches) == 2
+ matches = test_rules_collection.run(bracketsmatchtestfile, skip_list=["TEST0002"])
+ assert len(matches) == 0
+
+
+def test_skip_non_existent_id(
+ test_rules_collection: RulesCollection,
+ ematchtestfile: Lintable,
+) -> None:
+ """Check that skipping invalid IDs changes nothing."""
+ matches = test_rules_collection.run(ematchtestfile, skip_list=["DOESNOTEXIST"])
+ assert len(matches) == 4
+
+
+def test_no_duplicate_rule_ids() -> None:
+ """Check that rules of the collection don't have duplicate IDs."""
+ real_rules = RulesCollection([Path("./src/ansiblelint/rules").resolve()])
+ rule_ids = [rule.id for rule in real_rules]
+ assert not any(y > 1 for y in collections.Counter(rule_ids).values())
+
+
+def test_rich_rule_listing() -> None:
+ """Test that rich list format output is rendered as a table.
+
+ This check also offers the contract of having rule id, short and long
+ descriptions in the console output.
+ """
+ rules_path = Path("./test/rules/fixtures").resolve()
+ result = run_ansible_lint("-r", str(rules_path), "-f", "full", "-L")
+ assert result.returncode == 0
+
+ for rule in RulesCollection([rules_path]):
+ assert rule.id in result.stdout
+ assert rule.shortdesc in result.stdout
+ # description could wrap inside table, so we do not check full length
+ assert rule.description[:30] in result.stdout
+
+
+def test_rules_id_format() -> None:
+ """Assure all our rules have consistent format."""
+ rule_id_re = re.compile("^[a-z-]{4,30}$")
+ rules = RulesCollection(
+ [Path("./src/ansiblelint/rules").resolve()],
+ options=options,
+ conditional=False,
+ )
+ keys: set[str] = set()
+ for rule in rules:
+ assert rule_id_re.match(
+ rule.id,
+ ), f"Rule id {rule.id} did not match our required format."
+ keys.add(rule.id)
+ assert (
+ rule.help or rule.description or rule.__doc__
+ ), f"Rule {rule.id} must have at least one of: .help, .description, .__doc__"
+ assert "yaml" in keys, "yaml rule is missing"
+ assert len(rules) == 49 # update this number when adding new rules!
+ assert len(keys) == len(rules), "Duplicate rule ids?"