Skip to content

Commit

Permalink
Apply var-spacing tests to vars files
Browse files Browse the repository at this point in the history
  • Loading branch information
notok committed Apr 10, 2022
1 parent c058c6d commit 46d5494
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ jobs:
WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:TOX_PARALLEL_NO_SPINNER
# Number of expected test passes, safety measure for accidental skip of
# tests. Update value if you add/remove tests.
PYTEST_REQPASS: 605
PYTEST_REQPASS: 606

steps:
- name: Activate WSL1
Expand Down
36 changes: 36 additions & 0 deletions examples/playbooks/vars/var-spacing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
# Should raise var-spacing at line [12, 13, 14, 27, 33], at following variables.
# ".bad_var_1", ".bad_var_2", ".bad_var_3", ".invalid_multiline_nested_json", ".invalid_nested_json"
good_var_1: "{{ good_format }}"
good_var_2: "Value: {{ good_format }}"
jinja_escape_1: "{{ '{{' }}"
jinja_escape_2: docker info --format '{{ '{{' }}json .Swarm.LocalNodeState{{ '}}' }}' | tr -d '"'
jinja_whitespace_control: |
{{ good_format }}/
{{- good_format }}
{{- good_format -}}
bad_var_1: "{{bad_format}}"
bad_var_2: "Value: {{ bad_format}}"
bad_var_3: "{{bad_format }}"
# spell-checker: disable-next-line
non_jinja_var: "data = ${lookup{$local_part}lsearch{/etc/aliases}}" # noqa: var-spacing
json_inside_jinja: "{{ {'test': {'subtest': variable}} }}"
multiline_vars: # Assert that no false positive on multiline exists
cases:
case1: >-
http://example.com/{{
case1 }}
case2: >-
http://example.com/{{
case2 }}
valid_nested_json: "{{ {'dummy_2': {'nested_dummy_1': 'value_1', 'nested_dummy_2': value_2}} | combine(dummy_1) }}"
invalid_nested_json: "{{ {'dummy_2': {'nested_dummy_1': 'value_1', 'nested_dummy_2': value_2}} | combine(dummy_1)}}"

valid_multiline_nested_json: >-
{{ {'dummy_2': {'nested_dummy_1': value_1,
'nested_dummy_2': value_2}} |
combine(dummy_1) }}
invalid_multiline_nested_json: >-
{{ {'dummy_2': {'nested_dummy_1': value_1,
'nested_dummy_2': value_2}} |
combine(dummy_1)}}
98 changes: 96 additions & 2 deletions src/ansiblelint/rules/var_spacing.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@

import re
import sys
from typing import Any, Dict, List, Optional, Union
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union

from ansible.parsing.yaml.objects import AnsibleUnicode

from ansiblelint.file_utils import Lintable
from ansiblelint.rules import AnsibleLintRule
from ansiblelint.skip_utils import get_rule_skips_from_line
from ansiblelint.utils import parse_yaml_from_file
from ansiblelint.yaml_utils import nested_items_path

if TYPE_CHECKING:
from ansiblelint.errors import MatchError


class VariableHasSpacesRule(AnsibleLintRule):
"""Variables should have spaces before and after: {{ var_name }}."""
Expand All @@ -33,8 +40,42 @@ def matchtask(
return self.shortdesc.format(var_name=v)
return False

def matchyaml(self, file: Lintable) -> List["MatchError"]:
"""Return matches for variables defined in vars files."""
data: Dict[str, Any] = {}
raw_results: List["MatchError"] = []
results: List["MatchError"] = []

if str(file.kind) == "vars":
data = parse_yaml_from_file(str(file.path))
for k, v, path in nested_items_path(data):
if isinstance(v, AnsibleUnicode):
cleaned = self.exclude_json_re.sub("", v)
if bool(self.bracket_regex.search(cleaned)):
path_elem = [
f"[{i}]" if isinstance(i, int) else i for i in path + [k]
]
raw_results.append(
self.create_matcherror(
filename=file,
linenumber=v.ansible_pos[1],
message=self.shortdesc.format(var_name=v),
details=f".{'.'.join(path_elem)}",
)
)
if raw_results:
lines = file.content.splitlines()
for match in raw_results:
# linenumber starts with 1, not zero
skip_list = get_rule_skips_from_line(lines[match.linenumber - 1])
if match.rule.id not in skip_list and match.tag not in skip_list:
results.append(match)
else:
results.extend(super().matchyaml(file))
return results

if "pytest" in sys.modules:

if "pytest" in sys.modules: # noqa: C901

import pytest

Expand Down Expand Up @@ -69,3 +110,56 @@ def test_var_spacing(
set(error_expected_lines).symmetric_difference(set(lint_error_lines))
)
assert len(error_lines_difference) == 0

# Test for vars file
@pytest.fixture(name="error_expected_details_varsfile")
def fixture_error_expected_details_varsfile() -> List[str]:
"""Return list of expected error details."""
return [
".bad_var_1",
".bad_var_2",
".bad_var_3",
".invalid_multiline_nested_json",
".invalid_nested_json",
]

@pytest.fixture(name="error_expected_lines_varsfile")
def fixture_error_expected_lines_varsfile() -> List[int]:
"""Return list of expected error lines."""
return [12, 13, 14, 27, 33]

@pytest.fixture(name="test_varsfile_path")
def fixture_test_varsfile_path() -> str:
"""Return test cases vars file path."""
return "examples/playbooks/vars/var-spacing.yml"

@pytest.fixture(name="lint_error_results_varsfile")
def fixture_lint_error_results_varsfile(
test_varsfile_path: str,
) -> List["MatchError"]:
"""Get VarHasSpacesRules linting results on test_vars."""
collection = RulesCollection()
collection.register(VariableHasSpacesRule())
lintable = Lintable(test_varsfile_path)
results = Runner(lintable, rules=collection).run()
return results

def test_var_spacing_vars(
error_expected_details_varsfile: List[str],
error_expected_lines_varsfile: List[int],
lint_error_results_varsfile: List["MatchError"],
) -> None:
"""Ensure that expected error details are matching found linting error details."""
details = list(map(lambda item: item.details, lint_error_results_varsfile))
# list unexpected error details or non-matching error details
error_details_difference = list(
set(error_expected_details_varsfile).symmetric_difference(set(details))
)
assert len(error_details_difference) == 0

lines = list(map(lambda item: item.linenumber, lint_error_results_varsfile))
# list unexpected error lines or non-matching error lines
error_lines_difference = list(
set(error_expected_lines_varsfile).symmetric_difference(set(lines))
)
assert len(error_lines_difference) == 0

0 comments on commit 46d5494

Please sign in to comment.