summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/conftest.py3
-rw-r--r--test/fixtures/broken-ansible.cfg/ansible.cfg2
-rw-r--r--test/fixtures/formatting-after/fmt-2.yml7
-rw-r--r--test/fixtures/formatting-after/fmt-4.yml22
-rw-r--r--test/fixtures/formatting-after/fmt-5.yml25
-rw-r--r--test/fixtures/formatting-after/fmt-hex.yml3
-rw-r--r--test/fixtures/formatting-before/fmt-2.yml7
-rw-r--r--test/fixtures/formatting-before/fmt-4.yml25
-rw-r--r--test/fixtures/formatting-before/fmt-5.yml28
-rw-r--r--test/fixtures/formatting-before/fmt-hex.yml3
-rw-r--r--test/fixtures/formatting-prettier/fmt-2.yml7
-rw-r--r--test/fixtures/formatting-prettier/fmt-4.yml22
-rw-r--r--test/fixtures/formatting-prettier/fmt-5.yml25
-rw-r--r--test/fixtures/formatting-prettier/fmt-hex.yml3
-rw-r--r--test/local-content/collections/ansible_collections/testns/test_collection/plugins/filter/test_filter.py1
-rw-r--r--test/local-content/test-roles-failed-complete/roles/role2/test_plugins/b_failed_complete.py1
-rw-r--r--test/rules/fixtures/ematcher.py1
-rw-r--r--test/rules/fixtures/raw_task.py1
-rw-r--r--test/rules/fixtures/unset_variable_matcher.py1
-rw-r--r--test/rules/test_args.py19
-rw-r--r--test/rules/test_deprecated_module.py1
-rw-r--r--test/rules/test_inline_env_var.py5
-rw-r--r--test/rules/test_no_changed_when.py1
-rw-r--r--test/rules/test_package_latest.py3
-rw-r--r--test/rules/test_role_names.py1
-rw-r--r--test/rules/test_syntax_check.py68
-rw-r--r--test/schemas/.mocharc.json6
-rw-r--r--test/schemas/negative_test/.ansible-lint.md1
-rw-r--r--test/schemas/negative_test/.config/ansible-lint.yml.md1
-rw-r--r--test/schemas/negative_test/changelogs/invalid-date/changelogs/changelog.yaml.md1
-rw-r--r--test/schemas/negative_test/changelogs/invalid-plugin-namespace/changelogs/changelog.yaml.md1
-rw-r--r--test/schemas/negative_test/changelogs/list/changelogs/changelog.yaml.md1
-rw-r--r--test/schemas/negative_test/changelogs/no-semver/changelogs/changelog.yaml.md1
-rw-r--r--test/schemas/negative_test/changelogs/unknown-keys/changelogs/changelog.yaml.md1
-rw-r--r--test/schemas/negative_test/galaxy_1/galaxy.yml.md1
-rw-r--r--test/schemas/negative_test/inventory/broken_dev_inventory.yml.md1
-rw-r--r--test/schemas/negative_test/meta/runtime.yml.md1
-rw-r--r--test/schemas/negative_test/molecule/platforms_children/molecule.yml.md1
-rw-r--r--test/schemas/negative_test/molecule/platforms_networks/molecule.yml.md1
-rw-r--r--test/schemas/negative_test/playbooks/environment.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/failed_when.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/gather_facts.yml.md34
-rw-r--r--test/schemas/negative_test/playbooks/gather_subset.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/gather_subset2.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/gather_subset3.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/gather_subset4.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/ignore-unreachable.yml13
-rw-r--r--test/schemas/negative_test/playbooks/ignore-unreachable.yml.md329
-rw-r--r--test/schemas/negative_test/playbooks/ignore_errors.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/import_playbook.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/import_playbook_exclusive.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/include.yml3
-rw-r--r--test/schemas/negative_test/playbooks/include.yml.md142
-rw-r--r--test/schemas/negative_test/playbooks/invalid-failed-when.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/invalid-serial.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/invalid.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/invalid_become.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/local_action.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/loop.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/loop2.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/no_log_partial_template.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/no_log_string.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/roles.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/run_once_list.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tags-mapping.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tags-number.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/args_integer.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/args_string.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/become_method_invalid.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/ignore_errors.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/invalid_block.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/local_action.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/loop.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/loop2.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/no_log_number.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/no_log_string.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/tags-mapping.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/tags-string.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/when_integer.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/when_object.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/with_items_boolean.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/tasks/with_items_untemplated_string.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/var_files_list_number.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/var_files_list_of_list_number.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/var_files_number.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/vars/asterisk.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/vars/dash-in-var-name.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/vars/list.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/vars/numeric-var-name.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/vars/play-keyword.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/vars/python-keyword.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/vars/varname-numeric-prefix.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/vas_prompt.yml.md6
-rw-r--r--test/schemas/negative_test/playbooks/when.yml.md6
-rw-r--r--test/schemas/negative_test/reqs3/meta/requirements.yml.md6
-rw-r--r--test/schemas/negative_test/roles/meta/argument_specs.yml.md1
-rw-r--r--test/schemas/negative_test/roles/meta/main.yml.md1
-rw-r--r--test/schemas/negative_test/roles/meta_invalid_collection/meta/main.yml.md1
-rw-r--r--test/schemas/negative_test/roles/meta_invalid_collections/meta/main.yml.md1
-rw-r--r--test/schemas/negative_test/roles/meta_invalid_role_namespace/meta/main.yml.md16
-rw-r--r--test/schemas/negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml1
-rw-r--r--test/schemas/negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml.md82
-rw-r--r--test/schemas/package-lock.json578
-rw-r--r--test/schemas/package.json27
-rw-r--r--test/schemas/src/rebuild.py16
-rw-r--r--test/schemas/src/schema.spec.ts40
-rw-r--r--test/schemas/test/playbooks/gather_facts.yml6
-rw-r--r--test/schemas/test/playbooks/ignore-unreachable.yml13
-rw-r--r--test/schemas/test/playbooks/order.yml15
-rw-r--r--test/schemas/test/roles/foo/meta/argument_specs.yml49
-rw-r--r--test/schemas/test/roles/foo/meta/main.yml1
-rw-r--r--test/schemas/tsconfig.json10
-rw-r--r--test/test_adjacent_plugins.py25
-rw-r--r--test/test_ansiblelintrule.py26
-rw-r--r--test/test_ansiblesyntax.py1
-rw-r--r--test/test_app.py1
-rw-r--r--test/test_cli.py82
-rw-r--r--test/test_cli_role_paths.py44
-rw-r--r--test/test_config.py1
-rw-r--r--test/test_constants.py1
-rw-r--r--test/test_dependencies_in_meta.py1
-rw-r--r--test/test_errors.py25
-rw-r--r--test/test_examples.py35
-rw-r--r--test/test_file_path_evaluation.py13
-rw-r--r--test/test_file_utils.py11
-rw-r--r--test/test_formatter.py5
-rw-r--r--test/test_formatter_base.py44
-rw-r--r--test/test_formatter_json.py11
-rw-r--r--test/test_formatter_sarif.py120
-rw-r--r--test/test_import_include_role.py1
-rw-r--r--test/test_import_playbook.py27
-rw-r--r--test/test_import_tasks.py15
-rw-r--r--test/test_include_miss_file_with_role.py1
-rw-r--r--test/test_internal_rules.py29
-rw-r--r--test/test_lint_rule.py6
-rw-r--r--test/test_list_rules.py7
-rw-r--r--test/test_load_failure.py1
-rw-r--r--test/test_loaders.py3
-rw-r--r--test/test_local_content.py1
-rw-r--r--test/test_main.py53
-rw-r--r--test/test_matcherrror.py48
-rw-r--r--test/test_mockings.py12
-rw-r--r--test/test_profiles.py3
-rw-r--r--test/test_requirements.py18
-rw-r--r--test/test_rule_properties.py1
-rw-r--r--test/test_rules_collection.py12
-rw-r--r--test/test_runner.py72
-rw-r--r--test/test_schemas.py61
-rw-r--r--test/test_skip_import_playbook.py1
-rw-r--r--test/test_skip_inside_yaml.py1
-rw-r--r--test/test_skip_playbook_items.py1
-rw-r--r--test/test_skiputils.py5
-rw-r--r--test/test_strict.py1
-rw-r--r--test/test_task_includes.py8
-rw-r--r--test/test_text.py1
-rw-r--r--test/test_transform_mixin.py82
-rw-r--r--test/test_transformer.py562
-rw-r--r--test/test_utils.py107
-rw-r--r--test/test_verbosity.py5
-rw-r--r--test/test_with_skip_tagid.py10
-rw-r--r--test/test_yaml_utils.py130
162 files changed, 2937 insertions, 788 deletions
diff --git a/test/conftest.py b/test/conftest.py
index 8ffa3bd..35115b9 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -1,4 +1,5 @@
"""PyTest fixtures for testing the project."""
+
from __future__ import annotations
import shutil
@@ -83,7 +84,7 @@ def regenerate_formatting_fixtures() -> None:
# Writing fixtures with ansiblelint.yaml_utils.FormattedYAML()
for fixture in fixtures_dir_after.glob("fmt-[0-9].yml"):
- data = yaml.loads(fixture.read_text())
+ data = yaml.load(fixture.read_text())
output = yaml.dumps(data)
fixture.write_text(output)
diff --git a/test/fixtures/broken-ansible.cfg/ansible.cfg b/test/fixtures/broken-ansible.cfg/ansible.cfg
new file mode 100644
index 0000000..06c1b92
--- /dev/null
+++ b/test/fixtures/broken-ansible.cfg/ansible.cfg
@@ -0,0 +1,2 @@
+[defaults]
+fact_caching_timeout=invalid-value
diff --git a/test/fixtures/formatting-after/fmt-2.yml b/test/fixtures/formatting-after/fmt-2.yml
index a162721..3601acc 100644
--- a/test/fixtures/formatting-after/fmt-2.yml
+++ b/test/fixtures/formatting-after/fmt-2.yml
@@ -22,3 +22,10 @@
- 10
- 9999
zero: 0 # Not an octal. See #2071
+
+- string:
+ - 0steps
+ - 9steps
+ - 0.0.0.0
+ - "0"
+ - "01234"
diff --git a/test/fixtures/formatting-after/fmt-4.yml b/test/fixtures/formatting-after/fmt-4.yml
new file mode 100644
index 0000000..5ded596
--- /dev/null
+++ b/test/fixtures/formatting-after/fmt-4.yml
@@ -0,0 +1,22 @@
+---
+- name: Gather all legacy facts
+ cisco.ios.ios_facts:
+
+- name: Update modification and access time of given file
+ ansible.builtin.file:
+ path: /etc/some_file
+ state: file
+ modification_time: now
+ access_time: now
+
+- name: Disable ufw service
+ ansible.builtin.service:
+ name: ufw
+ enabled: false
+ state: stopped
+ when: '"ufw" in services'
+
+- name: Remove file (delete file)
+ ansible.builtin.file:
+ path: /etc/foo.txt
+ state: absent
diff --git a/test/fixtures/formatting-after/fmt-5.yml b/test/fixtures/formatting-after/fmt-5.yml
new file mode 100644
index 0000000..b259e0e
--- /dev/null
+++ b/test/fixtures/formatting-after/fmt-5.yml
@@ -0,0 +1,25 @@
+---
+- name: Test this playbook
+ hosts: all
+ tasks:
+ - name: Gather all legacy facts
+ cisco.ios.ios_facts:
+
+ - name: Update modification and access time of given file
+ ansible.builtin.file:
+ path: /etc/some_file
+ state: file
+ modification_time: now
+ access_time: now
+
+ - name: Disable ufw service
+ ansible.builtin.service:
+ name: ufw
+ enabled: false
+ state: stopped
+ when: '"ufw" in services'
+
+ - name: Remove file (delete file)
+ ansible.builtin.file:
+ path: /etc/foo.txt
+ state: absent
diff --git a/test/fixtures/formatting-after/fmt-hex.yml b/test/fixtures/formatting-after/fmt-hex.yml
new file mode 100644
index 0000000..7f09cb9
--- /dev/null
+++ b/test/fixtures/formatting-after/fmt-hex.yml
@@ -0,0 +1,3 @@
+---
+d: 0x123 # <-- hex
+e: 0x0123
diff --git a/test/fixtures/formatting-before/fmt-2.yml b/test/fixtures/formatting-before/fmt-2.yml
index 2941663..dbfb777 100644
--- a/test/fixtures/formatting-before/fmt-2.yml
+++ b/test/fixtures/formatting-before/fmt-2.yml
@@ -22,3 +22,10 @@
- 10
- 9999
zero: 0 # Not an octal. See #2071
+
+ - string:
+ - 0steps
+ - 9steps
+ - 0.0.0.0
+ - "0"
+ - "01234"
diff --git a/test/fixtures/formatting-before/fmt-4.yml b/test/fixtures/formatting-before/fmt-4.yml
new file mode 100644
index 0000000..579231f
--- /dev/null
+++ b/test/fixtures/formatting-before/fmt-4.yml
@@ -0,0 +1,25 @@
+---
+- name: Gather all legacy facts
+ cisco.ios.ios_facts:
+
+- name: Update modification and access time of given file
+ ansible.builtin.file:
+ path: /etc/some_file
+ state: file
+ modification_time: now
+ access_time: now
+
+
+- name: Disable ufw service
+ ansible.builtin.service:
+ name: ufw
+ enabled: false
+ state: stopped
+ when: '"ufw" in services'
+
+
+
+- name: Remove file (delete file)
+ ansible.builtin.file:
+ path: /etc/foo.txt
+ state: absent
diff --git a/test/fixtures/formatting-before/fmt-5.yml b/test/fixtures/formatting-before/fmt-5.yml
new file mode 100644
index 0000000..a9145e6
--- /dev/null
+++ b/test/fixtures/formatting-before/fmt-5.yml
@@ -0,0 +1,28 @@
+---
+- name: Test this playbook
+ hosts: all
+ tasks:
+ - name: Gather all legacy facts
+ cisco.ios.ios_facts:
+
+ - name: Update modification and access time of given file
+ ansible.builtin.file:
+ path: /etc/some_file
+ state: file
+ modification_time: now
+ access_time: now
+
+
+ - name: Disable ufw service
+ ansible.builtin.service:
+ name: ufw
+ enabled: false
+ state: stopped
+ when: '"ufw" in services'
+
+
+
+ - name: Remove file (delete file)
+ ansible.builtin.file:
+ path: /etc/foo.txt
+ state: absent
diff --git a/test/fixtures/formatting-before/fmt-hex.yml b/test/fixtures/formatting-before/fmt-hex.yml
new file mode 100644
index 0000000..3bc15a7
--- /dev/null
+++ b/test/fixtures/formatting-before/fmt-hex.yml
@@ -0,0 +1,3 @@
+---
+d: 0x123 # <-- hex
+e: 0x0123
diff --git a/test/fixtures/formatting-prettier/fmt-2.yml b/test/fixtures/formatting-prettier/fmt-2.yml
index 90ac484..037c9dd 100644
--- a/test/fixtures/formatting-prettier/fmt-2.yml
+++ b/test/fixtures/formatting-prettier/fmt-2.yml
@@ -22,3 +22,10 @@
- 10
- 9999
zero: 0 # Not an octal. See #2071
+
+- string:
+ - 0steps
+ - 9steps
+ - 0.0.0.0
+ - "0"
+ - "01234"
diff --git a/test/fixtures/formatting-prettier/fmt-4.yml b/test/fixtures/formatting-prettier/fmt-4.yml
new file mode 100644
index 0000000..5ded596
--- /dev/null
+++ b/test/fixtures/formatting-prettier/fmt-4.yml
@@ -0,0 +1,22 @@
+---
+- name: Gather all legacy facts
+ cisco.ios.ios_facts:
+
+- name: Update modification and access time of given file
+ ansible.builtin.file:
+ path: /etc/some_file
+ state: file
+ modification_time: now
+ access_time: now
+
+- name: Disable ufw service
+ ansible.builtin.service:
+ name: ufw
+ enabled: false
+ state: stopped
+ when: '"ufw" in services'
+
+- name: Remove file (delete file)
+ ansible.builtin.file:
+ path: /etc/foo.txt
+ state: absent
diff --git a/test/fixtures/formatting-prettier/fmt-5.yml b/test/fixtures/formatting-prettier/fmt-5.yml
new file mode 100644
index 0000000..b259e0e
--- /dev/null
+++ b/test/fixtures/formatting-prettier/fmt-5.yml
@@ -0,0 +1,25 @@
+---
+- name: Test this playbook
+ hosts: all
+ tasks:
+ - name: Gather all legacy facts
+ cisco.ios.ios_facts:
+
+ - name: Update modification and access time of given file
+ ansible.builtin.file:
+ path: /etc/some_file
+ state: file
+ modification_time: now
+ access_time: now
+
+ - name: Disable ufw service
+ ansible.builtin.service:
+ name: ufw
+ enabled: false
+ state: stopped
+ when: '"ufw" in services'
+
+ - name: Remove file (delete file)
+ ansible.builtin.file:
+ path: /etc/foo.txt
+ state: absent
diff --git a/test/fixtures/formatting-prettier/fmt-hex.yml b/test/fixtures/formatting-prettier/fmt-hex.yml
new file mode 100644
index 0000000..7f09cb9
--- /dev/null
+++ b/test/fixtures/formatting-prettier/fmt-hex.yml
@@ -0,0 +1,3 @@
+---
+d: 0x123 # <-- hex
+e: 0x0123
diff --git a/test/local-content/collections/ansible_collections/testns/test_collection/plugins/filter/test_filter.py b/test/local-content/collections/ansible_collections/testns/test_collection/plugins/filter/test_filter.py
index 58bc269..6244329 100644
--- a/test/local-content/collections/ansible_collections/testns/test_collection/plugins/filter/test_filter.py
+++ b/test/local-content/collections/ansible_collections/testns/test_collection/plugins/filter/test_filter.py
@@ -1,5 +1,4 @@
"""A filter plugin."""
-# pylint: disable=invalid-name
def a_test_filter(a, b):
diff --git a/test/local-content/test-roles-failed-complete/roles/role2/test_plugins/b_failed_complete.py b/test/local-content/test-roles-failed-complete/roles/role2/test_plugins/b_failed_complete.py
index 92bd6e7..63f4532 100644
--- a/test/local-content/test-roles-failed-complete/roles/role2/test_plugins/b_failed_complete.py
+++ b/test/local-content/test-roles-failed-complete/roles/role2/test_plugins/b_failed_complete.py
@@ -1,5 +1,4 @@
"""A test plugin."""
-# pylint: disable=invalid-name
def compatibility_in_test(a, b):
diff --git a/test/rules/fixtures/ematcher.py b/test/rules/fixtures/ematcher.py
index 1b04b6b..b034064 100644
--- a/test/rules/fixtures/ematcher.py
+++ b/test/rules/fixtures/ematcher.py
@@ -1,4 +1,5 @@
"""Custom rule used as fixture."""
+
from ansiblelint.rules import AnsibleLintRule
diff --git a/test/rules/fixtures/raw_task.py b/test/rules/fixtures/raw_task.py
index 0d5b023..6dfd7d9 100644
--- a/test/rules/fixtures/raw_task.py
+++ b/test/rules/fixtures/raw_task.py
@@ -1,4 +1,5 @@
"""Test Rule that needs_raw_task."""
+
from __future__ import annotations
from typing import TYPE_CHECKING
diff --git a/test/rules/fixtures/unset_variable_matcher.py b/test/rules/fixtures/unset_variable_matcher.py
index 8486009..ea8b0c0 100644
--- a/test/rules/fixtures/unset_variable_matcher.py
+++ b/test/rules/fixtures/unset_variable_matcher.py
@@ -1,4 +1,5 @@
"""Custom linting rule used as test fixture."""
+
from ansiblelint.rules import AnsibleLintRule
diff --git a/test/rules/test_args.py b/test/rules/test_args.py
new file mode 100644
index 0000000..30d83f1
--- /dev/null
+++ b/test/rules/test_args.py
@@ -0,0 +1,19 @@
+"""Tests for args rule."""
+
+from ansiblelint.file_utils import Lintable
+from ansiblelint.rules import RulesCollection
+from ansiblelint.runner import Runner
+
+
+def test_args_module_relative_import(default_rules_collection: RulesCollection) -> None:
+ """Validate args check of a module with a relative import."""
+ lintable = Lintable(
+ "examples/playbooks/module_relative_import.yml",
+ kind="playbook",
+ )
+ result = Runner(lintable, rules=default_rules_collection).run()
+ assert len(result) == 1, result
+ assert result[0].lineno == 5
+ assert result[0].filename == "examples/playbooks/module_relative_import.yml"
+ assert result[0].tag == "args[module]"
+ assert result[0].message == "missing required arguments: name"
diff --git a/test/rules/test_deprecated_module.py b/test/rules/test_deprecated_module.py
index a57d8db..6346b80 100644
--- a/test/rules/test_deprecated_module.py
+++ b/test/rules/test_deprecated_module.py
@@ -1,4 +1,5 @@
"""Tests for deprecated-module rule."""
+
from pathlib import Path
from ansiblelint.rules import RulesCollection
diff --git a/test/rules/test_inline_env_var.py b/test/rules/test_inline_env_var.py
index 98f337e..aa833ec 100644
--- a/test/rules/test_inline_env_var.py
+++ b/test/rules/test_inline_env_var.py
@@ -1,4 +1,5 @@
"""Tests for inline-env-var rule."""
+
from ansiblelint.rules import RulesCollection
from ansiblelint.rules.inline_env_var import EnvVarsInCommandRule
from ansiblelint.testing import RunFromText
@@ -13,7 +14,7 @@ SUCCESS_PLAY_TASKS = """
HELLO: hello
- name: Use some key-value pairs
- command: chdir=/tmp creates=/tmp/bobbins warn=no touch bobbins
+ command: chdir=/tmp creates=/tmp/bobbins touch bobbins
- name: Commands can have flags
command: abc --xyz=def blah
@@ -68,7 +69,7 @@ FAIL_PLAY_TASKS = """
command: HELLO=hello echo $HELLO
- name: Typo some stuff
- command: cerates=/tmp/blah warn=no touch /tmp/blah
+ command: crates=/tmp/blah touch /tmp/blah
"""
diff --git a/test/rules/test_no_changed_when.py b/test/rules/test_no_changed_when.py
index c89d8f4..3316e12 100644
--- a/test/rules/test_no_changed_when.py
+++ b/test/rules/test_no_changed_when.py
@@ -1,4 +1,5 @@
"""Tests for no-change-when rule."""
+
from ansiblelint.rules import RulesCollection
from ansiblelint.rules.no_changed_when import CommandHasChangesCheckRule
from ansiblelint.runner import Runner
diff --git a/test/rules/test_package_latest.py b/test/rules/test_package_latest.py
index 5631f02..972fced 100644
--- a/test/rules/test_package_latest.py
+++ b/test/rules/test_package_latest.py
@@ -1,4 +1,5 @@
"""Tests for package-latest rule."""
+
from ansiblelint.rules import RulesCollection
from ansiblelint.rules.package_latest import PackageIsNotLatestRule
from ansiblelint.runner import Runner
@@ -20,4 +21,4 @@ def test_package_not_latest_negative() -> None:
failure = "examples/playbooks/package-check-failure.yml"
bad_runner = Runner(failure, rules=collection)
errs = bad_runner.run()
- assert len(errs) == 4
+ assert len(errs) == 5
diff --git a/test/rules/test_role_names.py b/test/rules/test_role_names.py
index 491cf14..e13e56a 100644
--- a/test/rules/test_role_names.py
+++ b/test/rules/test_role_names.py
@@ -1,4 +1,5 @@
"""Test the RoleNames rule."""
+
from __future__ import annotations
from typing import TYPE_CHECKING, Any
diff --git a/test/rules/test_syntax_check.py b/test/rules/test_syntax_check.py
index 2fe36a3..6ec111d 100644
--- a/test/rules/test_syntax_check.py
+++ b/test/rules/test_syntax_check.py
@@ -1,32 +1,71 @@
"""Tests for syntax-check rule."""
+
from typing import Any
+import pytest
+
from ansiblelint.file_utils import Lintable
from ansiblelint.rules import RulesCollection
from ansiblelint.runner import Runner
+@pytest.mark.parametrize(
+ ("filename", "expected_results"),
+ (
+ pytest.param(
+ "examples/playbooks/conflicting_action.yml",
+ [
+ (
+ "syntax-check[specific]",
+ 4,
+ 7,
+ "conflicting action statements: ansible.builtin.debug, ansible.builtin.command",
+ ),
+ ],
+ id="0",
+ ),
+ pytest.param(
+ "examples/playbooks/conflicting_action2.yml",
+ [
+ (
+ "parser-error",
+ 1,
+ None,
+ "conflicting action statements: block, include_role",
+ ),
+ (
+ "syntax-check[specific]",
+ 5,
+ 7,
+ "'include_role' is not a valid attribute for a Block",
+ ),
+ ],
+ id="1",
+ ),
+ ),
+)
def test_get_ansible_syntax_check_matches(
default_rules_collection: RulesCollection,
+ filename: str,
+ expected_results: list[tuple[str, int, int, str]],
) -> None:
"""Validate parsing of ansible output."""
lintable = Lintable(
- "examples/playbooks/conflicting_action.yml",
+ filename,
kind="playbook",
)
- result = Runner(lintable, rules=default_rules_collection).run()
+ result = sorted(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
+ assert len(result) == len(expected_results)
+ for index, expected in enumerate(expected_results):
+ assert result[index].tag == expected[0]
+ assert result[index].lineno == expected[1]
+ assert result[index].column == expected[2]
+ assert str(expected[3]) in result[index].message
+ # We internally convert absolute paths returned by ansible into paths
+ # relative to current directory.
+ # assert result[index].filename.endswith("/conflicting_action.yml")
def test_empty_playbook(default_rules_collection: RulesCollection) -> None:
@@ -58,11 +97,10 @@ def test_extra_vars_passed_to_command(
assert not result
-def test_syntax_check_role() -> None:
+def test_syntax_check_role(default_rules_collection: RulesCollection) -> 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()
+ result = Runner(lintable, rules=default_rules_collection).run()
assert len(result) == 1, result
assert result[0].lineno == 2
assert result[0].filename == "examples/roles/invalid_due_syntax/tasks/main.yml"
diff --git a/test/schemas/.mocharc.json b/test/schemas/.mocharc.json
index 0148197..c3b1d46 100644
--- a/test/schemas/.mocharc.json
+++ b/test/schemas/.mocharc.json
@@ -1,7 +1,11 @@
{
"colors": true,
- "extension": ["ts"],
+ "extensions": ["ts"],
"require": "ts-node/register",
+ "node-option": [
+ "experimental-specifier-resolution=node",
+ "loader=ts-node/esm"
+ ],
"slow": "500",
"spec": "src/**/*.spec.ts"
}
diff --git a/test/schemas/negative_test/.ansible-lint.md b/test/schemas/negative_test/.ansible-lint.md
index f1f2308..7746f3c 100644
--- a/test/schemas/negative_test/.ansible-lint.md
+++ b/test/schemas/negative_test/.ansible-lint.md
@@ -128,6 +128,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [],
"parse_errors": [
{
diff --git a/test/schemas/negative_test/.config/ansible-lint.yml.md b/test/schemas/negative_test/.config/ansible-lint.yml.md
index 4fe331e..8d055af 100644
--- a/test/schemas/negative_test/.config/ansible-lint.yml.md
+++ b/test/schemas/negative_test/.config/ansible-lint.yml.md
@@ -29,6 +29,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/.config/ansible-lint.yml",
diff --git a/test/schemas/negative_test/changelogs/invalid-date/changelogs/changelog.yaml.md b/test/schemas/negative_test/changelogs/invalid-date/changelogs/changelog.yaml.md
index 72b4f96..82b2601 100644
--- a/test/schemas/negative_test/changelogs/invalid-date/changelogs/changelog.yaml.md
+++ b/test/schemas/negative_test/changelogs/invalid-date/changelogs/changelog.yaml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/changelogs/invalid-date/changelogs/changelog.yaml",
diff --git a/test/schemas/negative_test/changelogs/invalid-plugin-namespace/changelogs/changelog.yaml.md b/test/schemas/negative_test/changelogs/invalid-plugin-namespace/changelogs/changelog.yaml.md
index ef847c3..f9cbc03 100644
--- a/test/schemas/negative_test/changelogs/invalid-plugin-namespace/changelogs/changelog.yaml.md
+++ b/test/schemas/negative_test/changelogs/invalid-plugin-namespace/changelogs/changelog.yaml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/changelogs/invalid-plugin-namespace/changelogs/changelog.yaml",
diff --git a/test/schemas/negative_test/changelogs/list/changelogs/changelog.yaml.md b/test/schemas/negative_test/changelogs/list/changelogs/changelog.yaml.md
index 5938944..c21eca7 100644
--- a/test/schemas/negative_test/changelogs/list/changelogs/changelog.yaml.md
+++ b/test/schemas/negative_test/changelogs/list/changelogs/changelog.yaml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/changelogs/list/changelogs/changelog.yaml",
diff --git a/test/schemas/negative_test/changelogs/no-semver/changelogs/changelog.yaml.md b/test/schemas/negative_test/changelogs/no-semver/changelogs/changelog.yaml.md
index 64c4665..9acc793 100644
--- a/test/schemas/negative_test/changelogs/no-semver/changelogs/changelog.yaml.md
+++ b/test/schemas/negative_test/changelogs/no-semver/changelogs/changelog.yaml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/changelogs/no-semver/changelogs/changelog.yaml",
diff --git a/test/schemas/negative_test/changelogs/unknown-keys/changelogs/changelog.yaml.md b/test/schemas/negative_test/changelogs/unknown-keys/changelogs/changelog.yaml.md
index 490bdbe..35cf572 100644
--- a/test/schemas/negative_test/changelogs/unknown-keys/changelogs/changelog.yaml.md
+++ b/test/schemas/negative_test/changelogs/unknown-keys/changelogs/changelog.yaml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/changelogs/unknown-keys/changelogs/changelog.yaml",
diff --git a/test/schemas/negative_test/galaxy_1/galaxy.yml.md b/test/schemas/negative_test/galaxy_1/galaxy.yml.md
index bbb79ec..0119fbe 100644
--- a/test/schemas/negative_test/galaxy_1/galaxy.yml.md
+++ b/test/schemas/negative_test/galaxy_1/galaxy.yml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/galaxy_1/galaxy.yml",
diff --git a/test/schemas/negative_test/inventory/broken_dev_inventory.yml.md b/test/schemas/negative_test/inventory/broken_dev_inventory.yml.md
index d4fefaf..1979297 100644
--- a/test/schemas/negative_test/inventory/broken_dev_inventory.yml.md
+++ b/test/schemas/negative_test/inventory/broken_dev_inventory.yml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/inventory/broken_dev_inventory.yml",
diff --git a/test/schemas/negative_test/meta/runtime.yml.md b/test/schemas/negative_test/meta/runtime.yml.md
index 761fa6f..45dfc74 100644
--- a/test/schemas/negative_test/meta/runtime.yml.md
+++ b/test/schemas/negative_test/meta/runtime.yml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/meta/runtime.yml",
diff --git a/test/schemas/negative_test/molecule/platforms_children/molecule.yml.md b/test/schemas/negative_test/molecule/platforms_children/molecule.yml.md
index 68e09eb..5c0320e 100644
--- a/test/schemas/negative_test/molecule/platforms_children/molecule.yml.md
+++ b/test/schemas/negative_test/molecule/platforms_children/molecule.yml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/molecule/platforms_children/molecule.yml",
diff --git a/test/schemas/negative_test/molecule/platforms_networks/molecule.yml.md b/test/schemas/negative_test/molecule/platforms_networks/molecule.yml.md
index 74b8de7..8ecbddf 100644
--- a/test/schemas/negative_test/molecule/platforms_networks/molecule.yml.md
+++ b/test/schemas/negative_test/molecule/platforms_networks/molecule.yml.md
@@ -30,6 +30,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/molecule/platforms_networks/molecule.yml",
diff --git a/test/schemas/negative_test/playbooks/environment.yml.md b/test/schemas/negative_test/playbooks/environment.yml.md
index 8923cb3..b34d039 100644
--- a/test/schemas/negative_test/playbooks/environment.yml.md
+++ b/test/schemas/negative_test/playbooks/environment.yml.md
@@ -91,6 +91,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/environment.yml",
@@ -101,6 +102,11 @@ stdout:
"path": "$[0]",
"message": "'environment', 'hosts' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].environment",
+ "message": "'{{ foo }}-123' is not of type 'object'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/failed_when.yml.md b/test/schemas/negative_test/playbooks/failed_when.yml.md
index e843e1f..c1c6e6c 100644
--- a/test/schemas/negative_test/playbooks/failed_when.yml.md
+++ b/test/schemas/negative_test/playbooks/failed_when.yml.md
@@ -118,6 +118,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/failed_when.yml",
@@ -128,6 +129,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tasks[0].failed_when",
+ "message": "123 is not of type 'boolean'"
+ },
+ "num_sub_errors": 9,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/gather_facts.yml.md b/test/schemas/negative_test/playbooks/gather_facts.yml.md
index 0eb3a4b..6b8d90a 100644
--- a/test/schemas/negative_test/playbooks/gather_facts.yml.md
+++ b/test/schemas/negative_test/playbooks/gather_facts.yml.md
@@ -63,7 +63,25 @@
"params": {
"type": "boolean"
},
- "schemaPath": "#/properties/gather_facts/type"
+ "schemaPath": "#/oneOf/0/type"
+ },
+ {
+ "instancePath": "/0/gather_facts",
+ "keyword": "pattern",
+ "message": "must match pattern \"^\\{[\\{%](.|[\r\n])*[\\}%]\\}$\"",
+ "params": {
+ "pattern": "^\\{[\\{%](.|[\r\n])*[\\}%]\\}$"
+ },
+ "schemaPath": "#/$defs/full-jinja/pattern"
+ },
+ {
+ "instancePath": "/0/gather_facts",
+ "keyword": "oneOf",
+ "message": "must match exactly one schema in oneOf",
+ "params": {
+ "passingSchemas": null
+ },
+ "schemaPath": "#/oneOf"
},
{
"instancePath": "/0",
@@ -84,6 +102,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/gather_facts.yml",
@@ -94,6 +113,11 @@ stdout:
"path": "$[0]",
"message": "'gather_facts', 'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].gather_facts",
+ "message": "'non' is not of type 'boolean'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0]",
@@ -113,7 +137,15 @@ stdout:
},
{
"path": "$[0].gather_facts",
+ "message": "'non' is not valid under any of the given schemas"
+ },
+ {
+ "path": "$[0].gather_facts",
"message": "'non' is not of type 'boolean'"
+ },
+ {
+ "path": "$[0].gather_facts",
+ "message": "'non' does not match '^\\\\{[\\\\{%](.|[\\r\\n])*[\\\\}%]\\\\}$'"
}
]
}
diff --git a/test/schemas/negative_test/playbooks/gather_subset.yml.md b/test/schemas/negative_test/playbooks/gather_subset.yml.md
index b426a23..5ee372b 100644
--- a/test/schemas/negative_test/playbooks/gather_subset.yml.md
+++ b/test/schemas/negative_test/playbooks/gather_subset.yml.md
@@ -84,6 +84,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/gather_subset.yml",
@@ -94,6 +95,11 @@ stdout:
"path": "$[0]",
"message": "'gather_subset', 'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].gather_subset",
+ "message": "'all' is not of type 'array'"
+ },
+ "num_sub_errors": 4,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/gather_subset2.yml.md b/test/schemas/negative_test/playbooks/gather_subset2.yml.md
index 8d6be68..d5ec667 100644
--- a/test/schemas/negative_test/playbooks/gather_subset2.yml.md
+++ b/test/schemas/negative_test/playbooks/gather_subset2.yml.md
@@ -230,6 +230,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/gather_subset2.yml",
@@ -240,6 +241,11 @@ stdout:
"path": "$[0]",
"message": "'gather_subset', 'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].gather_subset[0]",
+ "message": "'invalid' is not one of ['all', 'min', 'all_ipv4_addresses', 'all_ipv6_addresses', 'apparmor', 'architecture', 'caps', 'chroot,cmdline', 'date_time', 'default_ipv4', 'default_ipv6', 'devices', 'distribution', 'distribution_major_version', 'distribution_release', 'distribution_version', 'dns', 'effective_group_ids', 'effective_user_id', 'env', 'facter', 'fips', 'hardware', 'interfaces', 'is_chroot', 'iscsi', 'kernel', 'local', 'lsb', 'machine', 'machine_id', 'mounts', 'network', 'ohai', 'os_family', 'pkg_mgr', 'platform', 'processor', 'processor_cores', 'processor_count', 'python', 'python_version', 'real_user_id', 'selinux', 'service_mgr', 'ssh_host_key_dsa_public', 'ssh_host_key_ecdsa_public', 'ssh_host_key_ed25519_public', 'ssh_host_key_rsa_public', 'ssh_host_pub_keys', 'ssh_pub_keys', 'system', 'system_capabilities', 'system_capabilities_enforced', 'user', 'user_dir', 'user_gecos', 'user_gid', 'user_id', 'user_shell', 'user_uid', 'virtual', 'virtualization_role', 'virtualization_type']"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/gather_subset3.yml.md b/test/schemas/negative_test/playbooks/gather_subset3.yml.md
index 7dc1b13..c2ed681 100644
--- a/test/schemas/negative_test/playbooks/gather_subset3.yml.md
+++ b/test/schemas/negative_test/playbooks/gather_subset3.yml.md
@@ -248,6 +248,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/gather_subset3.yml",
@@ -258,6 +259,11 @@ stdout:
"path": "$[0]",
"message": "'gather_subset', 'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].gather_subset[0]",
+ "message": "1 is not one of ['all', 'min', 'all_ipv4_addresses', 'all_ipv6_addresses', 'apparmor', 'architecture', 'caps', 'chroot,cmdline', 'date_time', 'default_ipv4', 'default_ipv6', 'devices', 'distribution', 'distribution_major_version', 'distribution_release', 'distribution_version', 'dns', 'effective_group_ids', 'effective_user_id', 'env', 'facter', 'fips', 'hardware', 'interfaces', 'is_chroot', 'iscsi', 'kernel', 'local', 'lsb', 'machine', 'machine_id', 'mounts', 'network', 'ohai', 'os_family', 'pkg_mgr', 'platform', 'processor', 'processor_cores', 'processor_count', 'python', 'python_version', 'real_user_id', 'selinux', 'service_mgr', 'ssh_host_key_dsa_public', 'ssh_host_key_ecdsa_public', 'ssh_host_key_ed25519_public', 'ssh_host_key_rsa_public', 'ssh_host_pub_keys', 'ssh_pub_keys', 'system', 'system_capabilities', 'system_capabilities_enforced', 'user', 'user_dir', 'user_gecos', 'user_gid', 'user_id', 'user_shell', 'user_uid', 'virtual', 'virtualization_role', 'virtualization_type']"
+ },
+ "num_sub_errors": 8,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/gather_subset4.yml.md b/test/schemas/negative_test/playbooks/gather_subset4.yml.md
index ada01cb..6372c84 100644
--- a/test/schemas/negative_test/playbooks/gather_subset4.yml.md
+++ b/test/schemas/negative_test/playbooks/gather_subset4.yml.md
@@ -84,6 +84,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/gather_subset4.yml",
@@ -94,6 +95,11 @@ stdout:
"path": "$[0]",
"message": "'gather_subset', 'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].gather_subset",
+ "message": "1 is not of type 'array'"
+ },
+ "num_sub_errors": 4,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/ignore-unreachable.yml b/test/schemas/negative_test/playbooks/ignore-unreachable.yml
new file mode 100644
index 0000000..0934936
--- /dev/null
+++ b/test/schemas/negative_test/playbooks/ignore-unreachable.yml
@@ -0,0 +1,13 @@
+---
+- name: Test
+ hosts: localhost
+ tasks:
+ - name: Debug
+ ansible.builtin.debug:
+ msg: ignore_unreachable should not be a string
+ ignore_unreachable: "yes"
+
+ - name: Debug
+ ansible.builtin.debug:
+ msg: jinja evaluation should not be a string
+ ignore_unreachable: 123
diff --git a/test/schemas/negative_test/playbooks/ignore-unreachable.yml.md b/test/schemas/negative_test/playbooks/ignore-unreachable.yml.md
new file mode 100644
index 0000000..b12403d
--- /dev/null
+++ b/test/schemas/negative_test/playbooks/ignore-unreachable.yml.md
@@ -0,0 +1,329 @@
+# ajv errors
+
+```json
+[
+ {
+ "instancePath": "/0",
+ "keyword": "required",
+ "message": "must have required property 'ansible.builtin.import_playbook'",
+ "params": {
+ "missingProperty": "ansible.builtin.import_playbook"
+ },
+ "schemaPath": "#/oneOf/0/required"
+ },
+ {
+ "instancePath": "/0",
+ "keyword": "required",
+ "message": "must have required property 'import_playbook'",
+ "params": {
+ "missingProperty": "import_playbook"
+ },
+ "schemaPath": "#/oneOf/1/required"
+ },
+ {
+ "instancePath": "/0",
+ "keyword": "oneOf",
+ "message": "must match exactly one schema in oneOf",
+ "params": {
+ "passingSchemas": null
+ },
+ "schemaPath": "#/oneOf"
+ },
+ {
+ "instancePath": "/0",
+ "keyword": "additionalProperties",
+ "message": "must NOT have additional properties",
+ "params": {
+ "additionalProperty": "hosts"
+ },
+ "schemaPath": "#/additionalProperties"
+ },
+ {
+ "instancePath": "/0",
+ "keyword": "additionalProperties",
+ "message": "must NOT have additional properties",
+ "params": {
+ "additionalProperty": "tasks"
+ },
+ "schemaPath": "#/additionalProperties"
+ },
+ {
+ "instancePath": "/0/tasks/0",
+ "keyword": "required",
+ "message": "must have required property 'block'",
+ "params": {
+ "missingProperty": "block"
+ },
+ "schemaPath": "#/required"
+ },
+ {
+ "instancePath": "/0/tasks/0/ignore_unreachable",
+ "keyword": "type",
+ "message": "must be boolean",
+ "params": {
+ "type": "boolean"
+ },
+ "schemaPath": "#/oneOf/0/type"
+ },
+ {
+ "instancePath": "/0/tasks/0/ignore_unreachable",
+ "keyword": "pattern",
+ "message": "must match pattern \"^\\{[\\{%](.|[\r\n])*[\\}%]\\}$\"",
+ "params": {
+ "pattern": "^\\{[\\{%](.|[\r\n])*[\\}%]\\}$"
+ },
+ "schemaPath": "#/$defs/full-jinja/pattern"
+ },
+ {
+ "instancePath": "/0/tasks/0/ignore_unreachable",
+ "keyword": "oneOf",
+ "message": "must match exactly one schema in oneOf",
+ "params": {
+ "passingSchemas": null
+ },
+ "schemaPath": "#/oneOf"
+ },
+ {
+ "instancePath": "/0/tasks/0/ignore_unreachable",
+ "keyword": "type",
+ "message": "must be boolean",
+ "params": {
+ "type": "boolean"
+ },
+ "schemaPath": "#/oneOf/0/type"
+ },
+ {
+ "instancePath": "/0/tasks/0/ignore_unreachable",
+ "keyword": "pattern",
+ "message": "must match pattern \"^\\{[\\{%](.|[\r\n])*[\\}%]\\}$\"",
+ "params": {
+ "pattern": "^\\{[\\{%](.|[\r\n])*[\\}%]\\}$"
+ },
+ "schemaPath": "#/$defs/full-jinja/pattern"
+ },
+ {
+ "instancePath": "/0/tasks/0/ignore_unreachable",
+ "keyword": "oneOf",
+ "message": "must match exactly one schema in oneOf",
+ "params": {
+ "passingSchemas": null
+ },
+ "schemaPath": "#/oneOf"
+ },
+ {
+ "instancePath": "/0/tasks/0",
+ "keyword": "anyOf",
+ "message": "must match a schema in anyOf",
+ "params": {},
+ "schemaPath": "#/items/anyOf"
+ },
+ {
+ "instancePath": "/0/tasks/1",
+ "keyword": "required",
+ "message": "must have required property 'block'",
+ "params": {
+ "missingProperty": "block"
+ },
+ "schemaPath": "#/required"
+ },
+ {
+ "instancePath": "/0/tasks/1/ignore_unreachable",
+ "keyword": "type",
+ "message": "must be boolean",
+ "params": {
+ "type": "boolean"
+ },
+ "schemaPath": "#/oneOf/0/type"
+ },
+ {
+ "instancePath": "/0/tasks/1/ignore_unreachable",
+ "keyword": "type",
+ "message": "must be string",
+ "params": {
+ "type": "string"
+ },
+ "schemaPath": "#/oneOf/1/type"
+ },
+ {
+ "instancePath": "/0/tasks/1/ignore_unreachable",
+ "keyword": "type",
+ "message": "must be string",
+ "params": {
+ "type": "string"
+ },
+ "schemaPath": "#/$defs/full-jinja/type"
+ },
+ {
+ "instancePath": "/0/tasks/1/ignore_unreachable",
+ "keyword": "oneOf",
+ "message": "must match exactly one schema in oneOf",
+ "params": {
+ "passingSchemas": null
+ },
+ "schemaPath": "#/oneOf"
+ },
+ {
+ "instancePath": "/0/tasks/1/ignore_unreachable",
+ "keyword": "type",
+ "message": "must be boolean",
+ "params": {
+ "type": "boolean"
+ },
+ "schemaPath": "#/oneOf/0/type"
+ },
+ {
+ "instancePath": "/0/tasks/1/ignore_unreachable",
+ "keyword": "type",
+ "message": "must be string",
+ "params": {
+ "type": "string"
+ },
+ "schemaPath": "#/oneOf/1/type"
+ },
+ {
+ "instancePath": "/0/tasks/1/ignore_unreachable",
+ "keyword": "type",
+ "message": "must be string",
+ "params": {
+ "type": "string"
+ },
+ "schemaPath": "#/$defs/full-jinja/type"
+ },
+ {
+ "instancePath": "/0/tasks/1/ignore_unreachable",
+ "keyword": "oneOf",
+ "message": "must match exactly one schema in oneOf",
+ "params": {
+ "passingSchemas": null
+ },
+ "schemaPath": "#/oneOf"
+ },
+ {
+ "instancePath": "/0/tasks/1",
+ "keyword": "anyOf",
+ "message": "must match a schema in anyOf",
+ "params": {},
+ "schemaPath": "#/items/anyOf"
+ },
+ {
+ "instancePath": "/0",
+ "keyword": "oneOf",
+ "message": "must match exactly one schema in oneOf",
+ "params": {
+ "passingSchemas": null
+ },
+ "schemaPath": "#/items/oneOf"
+ }
+]
+```
+
+# check-jsonschema
+
+stdout:
+
+```json
+{
+ "status": "fail",
+ "successes": [],
+ "errors": [
+ {
+ "filename": "negative_test/playbooks/ignore-unreachable.yml",
+ "path": "$[0]",
+ "message": "{'name': 'Test', 'hosts': 'localhost', 'tasks': [{'name': 'Debug', 'ansible.builtin.debug': {'msg': 'ignore_unreachable should not be a string'}, 'ignore_unreachable': 'yes'}, {'name': 'Debug', 'ansible.builtin.debug': {'msg': 'jinja evaluation should not be a string'}, 'ignore_unreachable': 123}]} is not valid under any of the given schemas",
+ "has_sub_errors": true,
+ "best_match": {
+ "path": "$[0]",
+ "message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
+ },
+ "best_deep_match": {
+ "path": "$[0].tasks[0].ignore_unreachable",
+ "message": "'yes' is not of type 'boolean'"
+ },
+ "num_sub_errors": 19,
+ "sub_errors": [
+ {
+ "path": "$[0]",
+ "message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
+ },
+ {
+ "path": "$[0]",
+ "message": "{'name': 'Test', 'hosts': 'localhost', 'tasks': [{'name': 'Debug', 'ansible.builtin.debug': {'msg': 'ignore_unreachable should not be a string'}, 'ignore_unreachable': 'yes'}, {'name': 'Debug', 'ansible.builtin.debug': {'msg': 'jinja evaluation should not be a string'}, 'ignore_unreachable': 123}]} is not valid under any of the given schemas"
+ },
+ {
+ "path": "$[0]",
+ "message": "'ansible.builtin.import_playbook' is a required property"
+ },
+ {
+ "path": "$[0]",
+ "message": "'import_playbook' is a required property"
+ },
+ {
+ "path": "$[0].tasks[0]",
+ "message": "{'name': 'Debug', 'ansible.builtin.debug': {'msg': 'ignore_unreachable should not be a string'}, 'ignore_unreachable': 'yes'} is not valid under any of the given schemas"
+ },
+ {
+ "path": "$[0].tasks[0].ignore_unreachable",
+ "message": "'yes' is not valid under any of the given schemas"
+ },
+ {
+ "path": "$[0].tasks[0].ignore_unreachable",
+ "message": "'yes' is not of type 'boolean'"
+ },
+ {
+ "path": "$[0].tasks[0].ignore_unreachable",
+ "message": "'yes' does not match '^\\\\{[\\\\{%](.|[\\r\\n])*[\\\\}%]\\\\}$'"
+ },
+ {
+ "path": "$[0].tasks[0]",
+ "message": "'block' is a required property"
+ },
+ {
+ "path": "$[0].tasks[0].ignore_unreachable",
+ "message": "'yes' is not valid under any of the given schemas"
+ },
+ {
+ "path": "$[0].tasks[0].ignore_unreachable",
+ "message": "'yes' is not of type 'boolean'"
+ },
+ {
+ "path": "$[0].tasks[0].ignore_unreachable",
+ "message": "'yes' does not match '^\\\\{[\\\\{%](.|[\\r\\n])*[\\\\}%]\\\\}$'"
+ },
+ {
+ "path": "$[0].tasks[1]",
+ "message": "{'name': 'Debug', 'ansible.builtin.debug': {'msg': 'jinja evaluation should not be a string'}, 'ignore_unreachable': 123} is not valid under any of the given schemas"
+ },
+ {
+ "path": "$[0].tasks[1].ignore_unreachable",
+ "message": "123 is not valid under any of the given schemas"
+ },
+ {
+ "path": "$[0].tasks[1].ignore_unreachable",
+ "message": "123 is not of type 'boolean'"
+ },
+ {
+ "path": "$[0].tasks[1].ignore_unreachable",
+ "message": "123 is not of type 'string'"
+ },
+ {
+ "path": "$[0].tasks[1]",
+ "message": "'block' is a required property"
+ },
+ {
+ "path": "$[0].tasks[1].ignore_unreachable",
+ "message": "123 is not valid under any of the given schemas"
+ },
+ {
+ "path": "$[0].tasks[1].ignore_unreachable",
+ "message": "123 is not of type 'boolean'"
+ },
+ {
+ "path": "$[0].tasks[1].ignore_unreachable",
+ "message": "123 is not of type 'string'"
+ }
+ ]
+ }
+ ],
+ "parse_errors": []
+}
+```
diff --git a/test/schemas/negative_test/playbooks/ignore_errors.yml.md b/test/schemas/negative_test/playbooks/ignore_errors.yml.md
index 61c3116..c76c098 100644
--- a/test/schemas/negative_test/playbooks/ignore_errors.yml.md
+++ b/test/schemas/negative_test/playbooks/ignore_errors.yml.md
@@ -136,6 +136,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/ignore_errors.yml",
@@ -146,6 +147,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tasks[0].ignore_errors",
+ "message": "'should_ignore_errors' is not of type 'boolean'"
+ },
+ "num_sub_errors": 11,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/import_playbook.yml.md b/test/schemas/negative_test/playbooks/import_playbook.yml.md
index def3dce..a04a1b8 100644
--- a/test/schemas/negative_test/playbooks/import_playbook.yml.md
+++ b/test/schemas/negative_test/playbooks/import_playbook.yml.md
@@ -55,6 +55,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/import_playbook.yml",
@@ -65,6 +66,11 @@ stdout:
"path": "$[0]",
"message": "{'ansible.builtin.import_playbook': {}} should not be valid under {'required': ['ansible.builtin.import_playbook']}"
},
+ "best_deep_match": {
+ "path": "$[0].ansible.builtin.import_playbook",
+ "message": "{} is not of type 'string'"
+ },
+ "num_sub_errors": 3,
"sub_errors": [
{
"path": "$[0].ansible.builtin.import_playbook",
diff --git a/test/schemas/negative_test/playbooks/import_playbook_exclusive.yml.md b/test/schemas/negative_test/playbooks/import_playbook_exclusive.yml.md
index 184a434..143165f 100644
--- a/test/schemas/negative_test/playbooks/import_playbook_exclusive.yml.md
+++ b/test/schemas/negative_test/playbooks/import_playbook_exclusive.yml.md
@@ -85,6 +85,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/import_playbook_exclusive.yml",
@@ -95,6 +96,11 @@ stdout:
"path": "$[0]",
"message": "{'ansible.builtin.import_playbook': 'foo.yml', 'import_playbook': 'other.yml'} should not be valid under {'required': ['ansible.builtin.import_playbook']}"
},
+ "best_deep_match": {
+ "path": "$[0]",
+ "message": "{'ansible.builtin.import_playbook': 'foo.yml', 'import_playbook': 'other.yml'} should not be valid under {'required': ['import_playbook']}"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/include.yml b/test/schemas/negative_test/playbooks/include.yml
new file mode 100644
index 0000000..5504e13
--- /dev/null
+++ b/test/schemas/negative_test/playbooks/include.yml
@@ -0,0 +1,3 @@
+- hosts: localhost
+ tasks:
+ - include: foo.yml # <-- removed in Ansible 2.16
diff --git a/test/schemas/negative_test/playbooks/include.yml.md b/test/schemas/negative_test/playbooks/include.yml.md
new file mode 100644
index 0000000..c577d84
--- /dev/null
+++ b/test/schemas/negative_test/playbooks/include.yml.md
@@ -0,0 +1,142 @@
+# ajv errors
+
+```json
+[
+ {
+ "instancePath": "/0",
+ "keyword": "required",
+ "message": "must have required property 'ansible.builtin.import_playbook'",
+ "params": {
+ "missingProperty": "ansible.builtin.import_playbook"
+ },
+ "schemaPath": "#/oneOf/0/required"
+ },
+ {
+ "instancePath": "/0",
+ "keyword": "required",
+ "message": "must have required property 'import_playbook'",
+ "params": {
+ "missingProperty": "import_playbook"
+ },
+ "schemaPath": "#/oneOf/1/required"
+ },
+ {
+ "instancePath": "/0",
+ "keyword": "oneOf",
+ "message": "must match exactly one schema in oneOf",
+ "params": {
+ "passingSchemas": null
+ },
+ "schemaPath": "#/oneOf"
+ },
+ {
+ "instancePath": "/0",
+ "keyword": "additionalProperties",
+ "message": "must NOT have additional properties",
+ "params": {
+ "additionalProperty": "hosts"
+ },
+ "schemaPath": "#/additionalProperties"
+ },
+ {
+ "instancePath": "/0",
+ "keyword": "additionalProperties",
+ "message": "must NOT have additional properties",
+ "params": {
+ "additionalProperty": "tasks"
+ },
+ "schemaPath": "#/additionalProperties"
+ },
+ {
+ "instancePath": "/0/tasks/0",
+ "keyword": "required",
+ "message": "must have required property 'block'",
+ "params": {
+ "missingProperty": "block"
+ },
+ "schemaPath": "#/required"
+ },
+ {
+ "instancePath": "/0/tasks/0/include",
+ "keyword": "not",
+ "message": "must NOT be valid",
+ "params": {},
+ "schemaPath": "#/$defs/removed-include-module/not"
+ },
+ {
+ "instancePath": "/0/tasks/0",
+ "keyword": "anyOf",
+ "message": "must match a schema in anyOf",
+ "params": {},
+ "schemaPath": "#/items/anyOf"
+ },
+ {
+ "instancePath": "/0",
+ "keyword": "oneOf",
+ "message": "must match exactly one schema in oneOf",
+ "params": {
+ "passingSchemas": null
+ },
+ "schemaPath": "#/items/oneOf"
+ }
+]
+```
+
+# check-jsonschema
+
+stdout:
+
+```json
+{
+ "status": "fail",
+ "successes": [],
+ "errors": [
+ {
+ "filename": "negative_test/playbooks/include.yml",
+ "path": "$[0]",
+ "message": "{'hosts': 'localhost', 'tasks': [{'include': 'foo.yml'}]} is not valid under any of the given schemas",
+ "has_sub_errors": true,
+ "best_match": {
+ "path": "$[0]",
+ "message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
+ },
+ "best_deep_match": {
+ "path": "$[0].tasks[0].include",
+ "message": "'foo.yml' should not be valid under {}"
+ },
+ "num_sub_errors": 6,
+ "sub_errors": [
+ {
+ "path": "$[0]",
+ "message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
+ },
+ {
+ "path": "$[0]",
+ "message": "{'hosts': 'localhost', 'tasks': [{'include': 'foo.yml'}]} is not valid under any of the given schemas"
+ },
+ {
+ "path": "$[0]",
+ "message": "'ansible.builtin.import_playbook' is a required property"
+ },
+ {
+ "path": "$[0]",
+ "message": "'import_playbook' is a required property"
+ },
+ {
+ "path": "$[0].tasks[0]",
+ "message": "{'include': 'foo.yml'} is not valid under any of the given schemas"
+ },
+ {
+ "path": "$[0].tasks[0]",
+ "message": "'block' is a required property"
+ },
+ {
+ "path": "$[0].tasks[0].include",
+ "message": "'foo.yml' should not be valid under {}"
+ }
+ ]
+ }
+ ],
+ "parse_errors": []
+}
+```
diff --git a/test/schemas/negative_test/playbooks/invalid-failed-when.yml.md b/test/schemas/negative_test/playbooks/invalid-failed-when.yml.md
index 3a41059..e2d1a0d 100644
--- a/test/schemas/negative_test/playbooks/invalid-failed-when.yml.md
+++ b/test/schemas/negative_test/playbooks/invalid-failed-when.yml.md
@@ -170,6 +170,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/invalid-failed-when.yml",
@@ -180,6 +181,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tasks[0].failed_when",
+ "message": "123 is not of type 'boolean'"
+ },
+ "num_sub_errors": 15,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/invalid-serial.yml.md b/test/schemas/negative_test/playbooks/invalid-serial.yml.md
index 5c48b21..80785f0 100644
--- a/test/schemas/negative_test/playbooks/invalid-serial.yml.md
+++ b/test/schemas/negative_test/playbooks/invalid-serial.yml.md
@@ -118,6 +118,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/invalid-serial.yml",
@@ -128,6 +129,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'serial' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].serial",
+ "message": "'10%BAD' is not of type 'integer'"
+ },
+ "num_sub_errors": 9,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/invalid.yml.md b/test/schemas/negative_test/playbooks/invalid.yml.md
index c3435dd..6a48a92 100644
--- a/test/schemas/negative_test/playbooks/invalid.yml.md
+++ b/test/schemas/negative_test/playbooks/invalid.yml.md
@@ -46,6 +46,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/invalid.yml",
@@ -56,6 +57,11 @@ stdout:
"path": "$[0]",
"message": "{'name': 'foo', 'hosts': 'localhost', 'import_playbook': 'included.yml'} should not be valid under {'required': ['import_playbook']}"
},
+ "best_deep_match": {
+ "path": "$[0]",
+ "message": "'hosts' does not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
+ },
+ "num_sub_errors": 2,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/invalid_become.yml.md b/test/schemas/negative_test/playbooks/invalid_become.yml.md
index 37d730d..e4fd6d5 100644
--- a/test/schemas/negative_test/playbooks/invalid_become.yml.md
+++ b/test/schemas/negative_test/playbooks/invalid_become.yml.md
@@ -93,6 +93,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/invalid_become.yml",
@@ -103,6 +104,11 @@ stdout:
"path": "$[0]",
"message": "'become', 'hosts' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].become",
+ "message": "'yes' is not of type 'boolean'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/local_action.yml.md b/test/schemas/negative_test/playbooks/local_action.yml.md
index 17f6244..d41de95 100644
--- a/test/schemas/negative_test/playbooks/local_action.yml.md
+++ b/test/schemas/negative_test/playbooks/local_action.yml.md
@@ -94,6 +94,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/local_action.yml",
@@ -104,6 +105,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tasks[0].local_action",
+ "message": "[] is not of type 'string', 'object'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/loop.yml.md b/test/schemas/negative_test/playbooks/loop.yml.md
index 88df838..c7b3e45 100644
--- a/test/schemas/negative_test/playbooks/loop.yml.md
+++ b/test/schemas/negative_test/playbooks/loop.yml.md
@@ -94,6 +94,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/loop.yml",
@@ -104,6 +105,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tasks[0].loop",
+ "message": "123 is not of type 'string', 'array'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/loop2.yml.md b/test/schemas/negative_test/playbooks/loop2.yml.md
index df60a41..cf77f85 100644
--- a/test/schemas/negative_test/playbooks/loop2.yml.md
+++ b/test/schemas/negative_test/playbooks/loop2.yml.md
@@ -94,6 +94,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/loop2.yml",
@@ -104,6 +105,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tasks[0].loop",
+ "message": "{} is not of type 'string', 'array'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/no_log_partial_template.yml.md b/test/schemas/negative_test/playbooks/no_log_partial_template.yml.md
index ee73686..c7fd4fd 100644
--- a/test/schemas/negative_test/playbooks/no_log_partial_template.yml.md
+++ b/test/schemas/negative_test/playbooks/no_log_partial_template.yml.md
@@ -136,6 +136,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/no_log_partial_template.yml",
@@ -146,6 +147,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tasks[0].no_log",
+ "message": "'foo-{{ some_var }}' is not of type 'boolean'"
+ },
+ "num_sub_errors": 11,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/no_log_string.yml.md b/test/schemas/negative_test/playbooks/no_log_string.yml.md
index c8213c0..98b4bc2 100644
--- a/test/schemas/negative_test/playbooks/no_log_string.yml.md
+++ b/test/schemas/negative_test/playbooks/no_log_string.yml.md
@@ -136,6 +136,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/no_log_string.yml",
@@ -146,6 +147,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tasks[0].no_log",
+ "message": "'some_var' is not of type 'boolean'"
+ },
+ "num_sub_errors": 11,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/roles.yml.md b/test/schemas/negative_test/playbooks/roles.yml.md
index 9b4e25a..72d7b85 100644
--- a/test/schemas/negative_test/playbooks/roles.yml.md
+++ b/test/schemas/negative_test/playbooks/roles.yml.md
@@ -75,6 +75,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/roles.yml",
@@ -85,6 +86,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'roles' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].roles",
+ "message": "'xxx' is not of type 'array'"
+ },
+ "num_sub_errors": 4,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/run_once_list.yml.md b/test/schemas/negative_test/playbooks/run_once_list.yml.md
index 84b7dc1..63424ff 100644
--- a/test/schemas/negative_test/playbooks/run_once_list.yml.md
+++ b/test/schemas/negative_test/playbooks/run_once_list.yml.md
@@ -154,6 +154,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/run_once_list.yml",
@@ -164,6 +165,11 @@ stdout:
"path": "$[0]",
"message": "'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tasks[0].run_once",
+ "message": "['{{ true }}', 'xxx'] is not of type 'boolean'"
+ },
+ "num_sub_errors": 11,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/tags-mapping.yml.md b/test/schemas/negative_test/playbooks/tags-mapping.yml.md
index aada0c6..10cdb9b 100644
--- a/test/schemas/negative_test/playbooks/tags-mapping.yml.md
+++ b/test/schemas/negative_test/playbooks/tags-mapping.yml.md
@@ -107,6 +107,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tags-mapping.yml",
@@ -117,6 +118,11 @@ stdout:
"path": "$[0]",
"message": "'hosts' does not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tags",
+ "message": "{} is not of type 'string'"
+ },
+ "num_sub_errors": 9,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/tags-number.yml.md b/test/schemas/negative_test/playbooks/tags-number.yml.md
index 3d32737..48a264a 100644
--- a/test/schemas/negative_test/playbooks/tags-number.yml.md
+++ b/test/schemas/negative_test/playbooks/tags-number.yml.md
@@ -107,6 +107,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tags-number.yml",
@@ -117,6 +118,11 @@ stdout:
"path": "$[0]",
"message": "'hosts' does not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tags",
+ "message": "123 is not of type 'string'"
+ },
+ "num_sub_errors": 9,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/tasks.yml.md b/test/schemas/negative_test/playbooks/tasks.yml.md
index 309912b..08d7fe4 100644
--- a/test/schemas/negative_test/playbooks/tasks.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks.yml.md
@@ -141,6 +141,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks.yml",
@@ -151,6 +152,11 @@ stdout:
"path": "$[0]",
"message": "'handlers', 'hosts', 'post_tasks', 'pre_tasks', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].handlers",
+ "message": "1.0 is not of type 'array', 'null'"
+ },
+ "num_sub_errors": 7,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/tasks/args_integer.yml.md b/test/schemas/negative_test/playbooks/tasks/args_integer.yml.md
index 8820251..25000f8 100644
--- a/test/schemas/negative_test/playbooks/tasks/args_integer.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/args_integer.yml.md
@@ -64,6 +64,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/args_integer.yml",
@@ -74,6 +75,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].args",
+ "message": "123 is not of type 'object'"
+ },
+ "num_sub_errors": 3,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/tasks/args_string.yml.md b/test/schemas/negative_test/playbooks/tasks/args_string.yml.md
index 6359a14..b1bf502 100644
--- a/test/schemas/negative_test/playbooks/tasks/args_string.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/args_string.yml.md
@@ -55,6 +55,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/args_string.yml",
@@ -65,6 +66,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].args",
+ "message": "'{{ }}123' is not of type 'object'"
+ },
+ "num_sub_errors": 3,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/tasks/become_method_invalid.yml.md b/test/schemas/negative_test/playbooks/tasks/become_method_invalid.yml.md
index fc1e692..b94527a 100644
--- a/test/schemas/negative_test/playbooks/tasks/become_method_invalid.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/become_method_invalid.yml.md
@@ -140,6 +140,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/become_method_invalid.yml",
@@ -150,6 +151,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].become_method",
+ "message": "True is not one of ['ansible.builtin.sudo', 'ansible.builtin.su', 'community.general.pbrun', 'community.general.pfexec', 'ansible.builtin.runas', 'community.general.dzdo', 'community.general.ksu', 'community.general.doas', 'community.general.machinectl', 'community.general.pmrun', 'community.general.sesu', 'community.general.sudosu']"
+ },
+ "num_sub_errors": 10,
"sub_errors": [
{
"path": "$[0].become_method",
diff --git a/test/schemas/negative_test/playbooks/tasks/ignore_errors.yml.md b/test/schemas/negative_test/playbooks/tasks/ignore_errors.yml.md
index 559a200..abd8968 100644
--- a/test/schemas/negative_test/playbooks/tasks/ignore_errors.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/ignore_errors.yml.md
@@ -82,6 +82,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/ignore_errors.yml",
@@ -92,6 +93,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].ignore_errors",
+ "message": "'should_ignore_errors' is not of type 'boolean'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0].ignore_errors",
diff --git a/test/schemas/negative_test/playbooks/tasks/invalid_block.yml.md b/test/schemas/negative_test/playbooks/tasks/invalid_block.yml.md
index bf4b30e..f952161 100644
--- a/test/schemas/negative_test/playbooks/tasks/invalid_block.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/invalid_block.yml.md
@@ -35,6 +35,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/invalid_block.yml",
@@ -45,6 +46,11 @@ stdout:
"path": "$[0]",
"message": "{'block': {}} should not be valid under {'required': ['block']}"
},
+ "best_deep_match": {
+ "path": "$[0].block",
+ "message": "{} is not of type 'array'"
+ },
+ "num_sub_errors": 1,
"sub_errors": [
{
"path": "$[0].block",
diff --git a/test/schemas/negative_test/playbooks/tasks/local_action.yml.md b/test/schemas/negative_test/playbooks/tasks/local_action.yml.md
index cf67e7b..0b1151b 100644
--- a/test/schemas/negative_test/playbooks/tasks/local_action.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/local_action.yml.md
@@ -40,6 +40,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/local_action.yml",
@@ -50,6 +51,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].local_action",
+ "message": "[] is not of type 'string', 'object'"
+ },
+ "num_sub_errors": 1,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/tasks/loop.yml.md b/test/schemas/negative_test/playbooks/tasks/loop.yml.md
index de8277f..45a1908 100644
--- a/test/schemas/negative_test/playbooks/tasks/loop.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/loop.yml.md
@@ -40,6 +40,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/loop.yml",
@@ -50,6 +51,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].loop",
+ "message": "{} is not of type 'string', 'array'"
+ },
+ "num_sub_errors": 1,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/tasks/loop2.yml.md b/test/schemas/negative_test/playbooks/tasks/loop2.yml.md
index c36d7c9..e29af19 100644
--- a/test/schemas/negative_test/playbooks/tasks/loop2.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/loop2.yml.md
@@ -40,6 +40,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/loop2.yml",
@@ -50,6 +51,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].loop",
+ "message": "123 is not of type 'string', 'array'"
+ },
+ "num_sub_errors": 1,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/tasks/no_log_number.yml.md b/test/schemas/negative_test/playbooks/tasks/no_log_number.yml.md
index 4b9516c..bc85fd7 100644
--- a/test/schemas/negative_test/playbooks/tasks/no_log_number.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/no_log_number.yml.md
@@ -100,6 +100,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/no_log_number.yml",
@@ -110,6 +111,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].no_log",
+ "message": "123 is not of type 'boolean'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0].no_log",
diff --git a/test/schemas/negative_test/playbooks/tasks/no_log_string.yml.md b/test/schemas/negative_test/playbooks/tasks/no_log_string.yml.md
index 6742175..ec88c66 100644
--- a/test/schemas/negative_test/playbooks/tasks/no_log_string.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/no_log_string.yml.md
@@ -82,6 +82,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/no_log_string.yml",
@@ -92,6 +93,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].no_log",
+ "message": "'some_var' is not of type 'boolean'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0].no_log",
diff --git a/test/schemas/negative_test/playbooks/tasks/tags-mapping.yml.md b/test/schemas/negative_test/playbooks/tasks/tags-mapping.yml.md
index d860605..998d783 100644
--- a/test/schemas/negative_test/playbooks/tasks/tags-mapping.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/tags-mapping.yml.md
@@ -78,6 +78,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/tags-mapping.yml",
@@ -88,6 +89,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].tags",
+ "message": "{} is not of type 'string'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0].tags",
diff --git a/test/schemas/negative_test/playbooks/tasks/tags-string.yml.md b/test/schemas/negative_test/playbooks/tasks/tags-string.yml.md
index 0bb7ed0..cdf421d 100644
--- a/test/schemas/negative_test/playbooks/tasks/tags-string.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/tags-string.yml.md
@@ -78,6 +78,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/tags-string.yml",
@@ -88,6 +89,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].tags",
+ "message": "123 is not of type 'string'"
+ },
+ "num_sub_errors": 6,
"sub_errors": [
{
"path": "$[0].tags",
diff --git a/test/schemas/negative_test/playbooks/tasks/when_integer.yml.md b/test/schemas/negative_test/playbooks/tasks/when_integer.yml.md
index bc59cc4..8acb890 100644
--- a/test/schemas/negative_test/playbooks/tasks/when_integer.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/when_integer.yml.md
@@ -100,6 +100,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/when_integer.yml",
@@ -110,6 +111,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].when",
+ "message": "123 is not of type 'boolean'"
+ },
+ "num_sub_errors": 8,
"sub_errors": [
{
"path": "$[0].when",
diff --git a/test/schemas/negative_test/playbooks/tasks/when_object.yml.md b/test/schemas/negative_test/playbooks/tasks/when_object.yml.md
index 6c28d0c..4ea653b 100644
--- a/test/schemas/negative_test/playbooks/tasks/when_object.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/when_object.yml.md
@@ -100,6 +100,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/when_object.yml",
@@ -110,6 +111,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].when",
+ "message": "{} is not of type 'boolean'"
+ },
+ "num_sub_errors": 8,
"sub_errors": [
{
"path": "$[0].when",
diff --git a/test/schemas/negative_test/playbooks/tasks/with_items_boolean.yml.md b/test/schemas/negative_test/playbooks/tasks/with_items_boolean.yml.md
index ffc8ef8..92340d2 100644
--- a/test/schemas/negative_test/playbooks/tasks/with_items_boolean.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/with_items_boolean.yml.md
@@ -53,6 +53,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/with_items_boolean.yml",
@@ -63,6 +64,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].with_items",
+ "message": "True is not of type 'string'"
+ },
+ "num_sub_errors": 3,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/tasks/with_items_untemplated_string.yml.md b/test/schemas/negative_test/playbooks/tasks/with_items_untemplated_string.yml.md
index 158b0ee..8ecd7bf 100644
--- a/test/schemas/negative_test/playbooks/tasks/with_items_untemplated_string.yml.md
+++ b/test/schemas/negative_test/playbooks/tasks/with_items_untemplated_string.yml.md
@@ -53,6 +53,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/tasks/with_items_untemplated_string.yml",
@@ -63,6 +64,11 @@ stdout:
"path": "$[0]",
"message": "'block' is a required property"
},
+ "best_deep_match": {
+ "path": "$[0].with_items",
+ "message": "'foobar' does not match '^\\\\{[\\\\{%](.|[\\r\\n])*[\\\\}%]\\\\}$'"
+ },
+ "num_sub_errors": 3,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/var_files_list_number.yml.md b/test/schemas/negative_test/playbooks/var_files_list_number.yml.md
index e915593..c47cc1b 100644
--- a/test/schemas/negative_test/playbooks/var_files_list_number.yml.md
+++ b/test/schemas/negative_test/playbooks/var_files_list_number.yml.md
@@ -93,6 +93,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/var_files_list_number.yml",
@@ -103,6 +104,11 @@ stdout:
"path": "$[0]",
"message": "'hosts' does not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].vars_files[0]",
+ "message": "0 is not of type 'string'"
+ },
+ "num_sub_errors": 7,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/var_files_list_of_list_number.yml.md b/test/schemas/negative_test/playbooks/var_files_list_of_list_number.yml.md
index 3494498..2f9b9cb 100644
--- a/test/schemas/negative_test/playbooks/var_files_list_of_list_number.yml.md
+++ b/test/schemas/negative_test/playbooks/var_files_list_of_list_number.yml.md
@@ -102,6 +102,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/var_files_list_of_list_number.yml",
@@ -112,6 +113,11 @@ stdout:
"path": "$[0]",
"message": "'hosts' does not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].vars_files[0][0]",
+ "message": "0 is not of type 'string'"
+ },
+ "num_sub_errors": 8,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/var_files_number.yml.md b/test/schemas/negative_test/playbooks/var_files_number.yml.md
index fa97e7e..f121b09 100644
--- a/test/schemas/negative_test/playbooks/var_files_number.yml.md
+++ b/test/schemas/negative_test/playbooks/var_files_number.yml.md
@@ -79,6 +79,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/var_files_number.yml",
@@ -89,6 +90,11 @@ stdout:
"path": "$[0]",
"message": "'hosts' does not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].vars_files",
+ "message": "0 is not of type 'object'"
+ },
+ "num_sub_errors": 5,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/vars/asterisk.yml.md b/test/schemas/negative_test/playbooks/vars/asterisk.yml.md
index 1ea9a98..9204de1 100644
--- a/test/schemas/negative_test/playbooks/vars/asterisk.yml.md
+++ b/test/schemas/negative_test/playbooks/vars/asterisk.yml.md
@@ -46,6 +46,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/vars/asterisk.yml",
@@ -56,6 +57,11 @@ stdout:
"path": "$",
"message": "{'*foo': '...'} is not of type 'string'"
},
+ "best_deep_match": {
+ "path": "$",
+ "message": "'*foo' does not match any of the regexes: '^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[a-zA-Z_][\\\\w]*$'"
+ },
+ "num_sub_errors": 2,
"sub_errors": [
{
"path": "$",
diff --git a/test/schemas/negative_test/playbooks/vars/dash-in-var-name.yml.md b/test/schemas/negative_test/playbooks/vars/dash-in-var-name.yml.md
index b862e69..6e0a83b 100644
--- a/test/schemas/negative_test/playbooks/vars/dash-in-var-name.yml.md
+++ b/test/schemas/negative_test/playbooks/vars/dash-in-var-name.yml.md
@@ -46,6 +46,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/vars/dash-in-var-name.yml",
@@ -56,6 +57,11 @@ stdout:
"path": "$",
"message": "{'foo-bar': '...'} is not of type 'string'"
},
+ "best_deep_match": {
+ "path": "$",
+ "message": "'foo-bar' does not match any of the regexes: '^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[a-zA-Z_][\\\\w]*$'"
+ },
+ "num_sub_errors": 2,
"sub_errors": [
{
"path": "$",
diff --git a/test/schemas/negative_test/playbooks/vars/list.yml.md b/test/schemas/negative_test/playbooks/vars/list.yml.md
index e2c9bf5..82f599a 100644
--- a/test/schemas/negative_test/playbooks/vars/list.yml.md
+++ b/test/schemas/negative_test/playbooks/vars/list.yml.md
@@ -46,6 +46,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/vars/list.yml",
@@ -56,6 +57,11 @@ stdout:
"path": "$",
"message": "['foo', 'bar'] is not of type 'object'"
},
+ "best_deep_match": {
+ "path": "$",
+ "message": "['foo', 'bar'] is not of type 'object'"
+ },
+ "num_sub_errors": 2,
"sub_errors": [
{
"path": "$",
diff --git a/test/schemas/negative_test/playbooks/vars/numeric-var-name.yml.md b/test/schemas/negative_test/playbooks/vars/numeric-var-name.yml.md
index 7ddcff6..9f15015 100644
--- a/test/schemas/negative_test/playbooks/vars/numeric-var-name.yml.md
+++ b/test/schemas/negative_test/playbooks/vars/numeric-var-name.yml.md
@@ -46,6 +46,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/vars/numeric-var-name.yml",
@@ -56,6 +57,11 @@ stdout:
"path": "$",
"message": "{'12': '...'} is not of type 'string'"
},
+ "best_deep_match": {
+ "path": "$",
+ "message": "'12' does not match any of the regexes: '^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[a-zA-Z_][\\\\w]*$'"
+ },
+ "num_sub_errors": 2,
"sub_errors": [
{
"path": "$",
diff --git a/test/schemas/negative_test/playbooks/vars/play-keyword.yml.md b/test/schemas/negative_test/playbooks/vars/play-keyword.yml.md
index 6b88b2a..d463c1c 100644
--- a/test/schemas/negative_test/playbooks/vars/play-keyword.yml.md
+++ b/test/schemas/negative_test/playbooks/vars/play-keyword.yml.md
@@ -46,6 +46,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/vars/play-keyword.yml",
@@ -56,6 +57,11 @@ stdout:
"path": "$",
"message": "{'environment': '...'} is not of type 'string'"
},
+ "best_deep_match": {
+ "path": "$",
+ "message": "'environment' does not match any of the regexes: '^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[a-zA-Z_][\\\\w]*$'"
+ },
+ "num_sub_errors": 2,
"sub_errors": [
{
"path": "$",
diff --git a/test/schemas/negative_test/playbooks/vars/python-keyword.yml.md b/test/schemas/negative_test/playbooks/vars/python-keyword.yml.md
index ca42f74..667364c 100644
--- a/test/schemas/negative_test/playbooks/vars/python-keyword.yml.md
+++ b/test/schemas/negative_test/playbooks/vars/python-keyword.yml.md
@@ -55,6 +55,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/vars/python-keyword.yml",
@@ -65,6 +66,11 @@ stdout:
"path": "$",
"message": "{'async': '...', 'lambda': '...'} is not of type 'string'"
},
+ "best_deep_match": {
+ "path": "$",
+ "message": "'async', 'lambda' do not match any of the regexes: '^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[a-zA-Z_][\\\\w]*$'"
+ },
+ "num_sub_errors": 2,
"sub_errors": [
{
"path": "$",
diff --git a/test/schemas/negative_test/playbooks/vars/varname-numeric-prefix.yml.md b/test/schemas/negative_test/playbooks/vars/varname-numeric-prefix.yml.md
index 8b73b0a..620a03c 100644
--- a/test/schemas/negative_test/playbooks/vars/varname-numeric-prefix.yml.md
+++ b/test/schemas/negative_test/playbooks/vars/varname-numeric-prefix.yml.md
@@ -46,6 +46,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/vars/varname-numeric-prefix.yml",
@@ -56,6 +57,11 @@ stdout:
"path": "$",
"message": "{'5foo': '...'} is not of type 'string'"
},
+ "best_deep_match": {
+ "path": "$",
+ "message": "'5foo' does not match any of the regexes: '^(?!(False|None|True|and|any_errors_fatal|as|assert|async|await|become|become_exe|become_flags|become_method|become_user|break|check_mode|class|collections|connection|continue|debugger|def|del|diff|elif|else|environment|except|fact_path|finally|for|force_handlers|from|gather_facts|gather_subset|gather_timeout|global|handlers|hosts|if|ignore_errors|ignore_unreachable|import|in|is|lambda|max_fail_percentage|module_defaults|name|no_log|nonlocal|not|or|order|pass|port|post_tasks|pre_tasks|raise|remote_user|return|roles|run_once|serial|strategy|tags|tasks|throttle|timeout|try|vars|vars_files|vars_prompt|while|with|yield)$)[a-zA-Z_][\\\\w]*$'"
+ },
+ "num_sub_errors": 2,
"sub_errors": [
{
"path": "$",
diff --git a/test/schemas/negative_test/playbooks/vas_prompt.yml.md b/test/schemas/negative_test/playbooks/vas_prompt.yml.md
index d2d809d..ca1863f 100644
--- a/test/schemas/negative_test/playbooks/vas_prompt.yml.md
+++ b/test/schemas/negative_test/playbooks/vas_prompt.yml.md
@@ -75,6 +75,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/vas_prompt.yml",
@@ -85,6 +86,11 @@ stdout:
"path": "$[0]",
"message": "'hosts' does not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].vars_prompt[0]",
+ "message": "Additional properties are not allowed ('tags' was unexpected)"
+ },
+ "num_sub_errors": 5,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/playbooks/when.yml.md b/test/schemas/negative_test/playbooks/when.yml.md
index 4c23dcb..125e9d6 100644
--- a/test/schemas/negative_test/playbooks/when.yml.md
+++ b/test/schemas/negative_test/playbooks/when.yml.md
@@ -195,6 +195,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/playbooks/when.yml",
@@ -205,6 +206,11 @@ stdout:
"path": "$[0]",
"message": "'gather_facts', 'hosts', 'tasks' do not match any of the regexes: '^(ansible\\\\.builtin\\\\.)?import_playbook$', 'name', 'tags', 'vars', 'when'"
},
+ "best_deep_match": {
+ "path": "$[0].tasks[0].when[1]",
+ "message": "123 is not of type 'boolean'"
+ },
+ "num_sub_errors": 17,
"sub_errors": [
{
"path": "$[0]",
diff --git a/test/schemas/negative_test/reqs3/meta/requirements.yml.md b/test/schemas/negative_test/reqs3/meta/requirements.yml.md
index 5de6643..d0d7623 100644
--- a/test/schemas/negative_test/reqs3/meta/requirements.yml.md
+++ b/test/schemas/negative_test/reqs3/meta/requirements.yml.md
@@ -62,6 +62,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/reqs3/meta/requirements.yml",
@@ -72,6 +73,11 @@ stdout:
"path": "$",
"message": "{'foo': 'bar'} is not of type 'array'"
},
+ "best_deep_match": {
+ "path": "$",
+ "message": "{'foo': 'bar'} is not of type 'array'"
+ },
+ "num_sub_errors": 4,
"sub_errors": [
{
"path": "$",
diff --git a/test/schemas/negative_test/roles/meta/argument_specs.yml.md b/test/schemas/negative_test/roles/meta/argument_specs.yml.md
index 34da932..e06b00d 100644
--- a/test/schemas/negative_test/roles/meta/argument_specs.yml.md
+++ b/test/schemas/negative_test/roles/meta/argument_specs.yml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/roles/meta/argument_specs.yml",
diff --git a/test/schemas/negative_test/roles/meta/main.yml.md b/test/schemas/negative_test/roles/meta/main.yml.md
index 2c9e99b..5ed52bf 100644
--- a/test/schemas/negative_test/roles/meta/main.yml.md
+++ b/test/schemas/negative_test/roles/meta/main.yml.md
@@ -39,6 +39,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/roles/meta/main.yml",
diff --git a/test/schemas/negative_test/roles/meta_invalid_collection/meta/main.yml.md b/test/schemas/negative_test/roles/meta_invalid_collection/meta/main.yml.md
index 1b8dcd0..bea80be 100644
--- a/test/schemas/negative_test/roles/meta_invalid_collection/meta/main.yml.md
+++ b/test/schemas/negative_test/roles/meta_invalid_collection/meta/main.yml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/roles/meta_invalid_collection/meta/main.yml",
diff --git a/test/schemas/negative_test/roles/meta_invalid_collections/meta/main.yml.md b/test/schemas/negative_test/roles/meta_invalid_collections/meta/main.yml.md
index 5d775f0..722b549 100644
--- a/test/schemas/negative_test/roles/meta_invalid_collections/meta/main.yml.md
+++ b/test/schemas/negative_test/roles/meta_invalid_collections/meta/main.yml.md
@@ -21,6 +21,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/roles/meta_invalid_collections/meta/main.yml",
diff --git a/test/schemas/negative_test/roles/meta_invalid_role_namespace/meta/main.yml.md b/test/schemas/negative_test/roles/meta_invalid_role_namespace/meta/main.yml.md
index ad7e9d3..73369a2 100644
--- a/test/schemas/negative_test/roles/meta_invalid_role_namespace/meta/main.yml.md
+++ b/test/schemas/negative_test/roles/meta_invalid_role_namespace/meta/main.yml.md
@@ -19,15 +19,6 @@
"failingKeyword": "then"
},
"schemaPath": "#/allOf/0/if"
- },
- {
- "instancePath": "/galaxy_info/namespace",
- "keyword": "pattern",
- "message": "must match pattern \"^[a-z][a-z0-9_]+$\"",
- "params": {
- "pattern": "^[a-z][a-z0-9_]+$"
- },
- "schemaPath": "#/properties/namespace/pattern"
}
]
```
@@ -39,18 +30,13 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/roles/meta_invalid_role_namespace/meta/main.yml",
"path": "$.galaxy_info",
"message": "'author' is a required property",
"has_sub_errors": false
- },
- {
- "filename": "negative_test/roles/meta_invalid_role_namespace/meta/main.yml",
- "path": "$.galaxy_info.namespace",
- "message": "'foo-bar' does not match '^[a-z][a-z0-9_]+$'",
- "has_sub_errors": false
}
],
"parse_errors": []
diff --git a/test/schemas/negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml b/test/schemas/negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml
index 81d4d3d..0e94325 100644
--- a/test/schemas/negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml
+++ b/test/schemas/negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml
@@ -11,3 +11,4 @@ galaxy_info:
dependencies:
- version: foo # invalid, should have at least name, role or src properties
+ - 1234 # invalid, needs to be a string
diff --git a/test/schemas/negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml.md b/test/schemas/negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml.md
index f09b1ac..a518b18 100644
--- a/test/schemas/negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml.md
+++ b/test/schemas/negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml.md
@@ -4,6 +4,15 @@
[
{
"instancePath": "/dependencies/0",
+ "keyword": "type",
+ "message": "must be string",
+ "params": {
+ "type": "string"
+ },
+ "schemaPath": "#/properties/dependencies/items/anyOf/0/type"
+ },
+ {
+ "instancePath": "/dependencies/0",
"keyword": "required",
"message": "must have required property 'role'",
"params": {
@@ -37,6 +46,38 @@
"schemaPath": "#/anyOf"
},
{
+ "instancePath": "/dependencies/0",
+ "keyword": "anyOf",
+ "message": "must match a schema in anyOf",
+ "params": {},
+ "schemaPath": "#/properties/dependencies/items/anyOf"
+ },
+ {
+ "instancePath": "/dependencies/1",
+ "keyword": "type",
+ "message": "must be string",
+ "params": {
+ "type": "string"
+ },
+ "schemaPath": "#/properties/dependencies/items/anyOf/0/type"
+ },
+ {
+ "instancePath": "/dependencies/1",
+ "keyword": "type",
+ "message": "must be object",
+ "params": {
+ "type": "object"
+ },
+ "schemaPath": "#/type"
+ },
+ {
+ "instancePath": "/dependencies/1",
+ "keyword": "anyOf",
+ "message": "must match a schema in anyOf",
+ "params": {},
+ "schemaPath": "#/properties/dependencies/items/anyOf"
+ },
+ {
"instancePath": "/galaxy_info",
"keyword": "required",
"message": "must have required property 'author'",
@@ -64,6 +105,7 @@ stdout:
```json
{
"status": "fail",
+ "successes": [],
"errors": [
{
"filename": "negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml",
@@ -72,11 +114,24 @@ stdout:
"has_sub_errors": true,
"best_match": {
"path": "$.dependencies[0]",
- "message": "'role' is a required property"
+ "message": "{'version': 'foo'} is not of type 'string'"
+ },
+ "best_deep_match": {
+ "path": "$.dependencies[0]",
+ "message": "{'version': 'foo'} is not of type 'string'"
},
+ "num_sub_errors": 4,
"sub_errors": [
{
"path": "$.dependencies[0]",
+ "message": "{'version': 'foo'} is not of type 'string'"
+ },
+ {
+ "path": "$.dependencies[0]",
+ "message": "{'version': 'foo'} is not valid under any of the given schemas"
+ },
+ {
+ "path": "$.dependencies[0]",
"message": "'role' is a required property"
},
{
@@ -91,6 +146,31 @@ stdout:
},
{
"filename": "negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml",
+ "path": "$.dependencies[1]",
+ "message": "1234 is not valid under any of the given schemas",
+ "has_sub_errors": true,
+ "best_match": {
+ "path": "$.dependencies[1]",
+ "message": "1234 is not of type 'string'"
+ },
+ "best_deep_match": {
+ "path": "$.dependencies[1]",
+ "message": "1234 is not of type 'string'"
+ },
+ "num_sub_errors": 1,
+ "sub_errors": [
+ {
+ "path": "$.dependencies[1]",
+ "message": "1234 is not of type 'string'"
+ },
+ {
+ "path": "$.dependencies[1]",
+ "message": "1234 is not of type 'object'"
+ }
+ ]
+ },
+ {
+ "filename": "negative_test/roles/role_with_bad_deps_in_meta/meta/main.yml",
"path": "$.galaxy_info",
"message": "'author' is a required property",
"has_sub_errors": false
diff --git a/test/schemas/package-lock.json b/test/schemas/package-lock.json
index 3745a97..52bee86 100644
--- a/test/schemas/package-lock.json
+++ b/test/schemas/package-lock.json
@@ -5,22 +5,22 @@
"packages": {
"": {
"dependencies": {
- "ajv-formats": "^2.1.1",
+ "ajv-formats": "^3.0.1",
"js-yaml": "^4.1.0",
"safe-stable-stringify": "^2.4.3",
- "ts-node": "^10.9.1",
- "vscode-json-languageservice": "^5.3.5"
+ "ts-node": "^10.9.2",
+ "vscode-json-languageservice": "^5.3.11"
},
"devDependencies": {
- "@types/chai": "^4.3.5",
- "@types/js-yaml": "^4.0.5",
+ "@types/chai": "^4.3.16",
+ "@types/js-yaml": "^4.0.9",
"@types/minimatch": "^5.1.2",
- "@types/mocha": "^10.0.1",
- "@types/node": "^20.3.1",
- "chai": "^4.3.7",
- "minimatch": "^9.0.1",
- "mocha": "^10.2.0",
- "typescript": "^5.1.3"
+ "@types/mocha": "^10.0.6",
+ "@types/node": "^20.12.13",
+ "chai": "^5.1.1",
+ "minimatch": "^9.0.4",
+ "mocha": "^10.4.0",
+ "typescript": "^5.4.5"
}
},
"node_modules/@cspotcode/source-map-support": {
@@ -77,15 +77,15 @@
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA=="
},
"node_modules/@types/chai": {
- "version": "4.3.5",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz",
- "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==",
+ "version": "4.3.16",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz",
+ "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==",
"dev": true
},
"node_modules/@types/js-yaml": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz",
- "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==",
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
+ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
"dev": true
},
"node_modules/@types/minimatch": {
@@ -95,20 +95,23 @@
"dev": true
},
"node_modules/@types/mocha": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz",
- "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==",
+ "version": "10.0.6",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz",
+ "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==",
"dev": true
},
"node_modules/@types/node": {
- "version": "20.3.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
- "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg=="
+ "version": "20.12.13",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.13.tgz",
+ "integrity": "sha512-gBGeanV41c1L171rR7wjbMiEpEI/l5XFQdLLfhr/REwpgDy/4U8y89+i8kRiLzDyZdOkXh+cRaTetUnCYutoXA==",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
},
"node_modules/@vscode/l10n": {
- "version": "0.0.13",
- "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.13.tgz",
- "integrity": "sha512-A3uY356uOU9nGa+TQIT/i3ziWUgJjVMUrGGXSrtRiTwklyCFjGVWIOHoEIHbJpiyhDkJd9kvIWUOfXK1IkK8XQ=="
+ "version": "0.0.18",
+ "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz",
+ "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="
},
"node_modules/acorn": {
"version": "8.6.0",
@@ -145,9 +148,9 @@
}
},
"node_modules/ajv-formats": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
- "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
"dependencies": {
"ajv": "^8.0.0"
},
@@ -217,12 +220,12 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/assertion-error": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
- "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
"engines": {
- "node": "*"
+ "node": ">=12"
}
},
"node_modules/balanced-match": {
@@ -241,22 +244,21 @@
}
},
"node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "balanced-match": "^1.0.0"
}
},
"node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"dependencies": {
- "fill-range": "^7.0.1"
+ "fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -281,21 +283,19 @@
}
},
"node_modules/chai": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
- "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz",
+ "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==",
"dev": true,
"dependencies": {
- "assertion-error": "^1.1.0",
- "check-error": "^1.0.2",
- "deep-eql": "^4.1.2",
- "get-func-name": "^2.0.0",
- "loupe": "^2.3.1",
- "pathval": "^1.1.1",
- "type-detect": "^4.0.5"
+ "assertion-error": "^2.0.1",
+ "check-error": "^2.1.1",
+ "deep-eql": "^5.0.1",
+ "loupe": "^3.1.0",
+ "pathval": "^2.0.0"
},
"engines": {
- "node": ">=4"
+ "node": ">=12"
}
},
"node_modules/chalk": {
@@ -327,12 +327,12 @@
}
},
"node_modules/check-error": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
- "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+ "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
"dev": true,
"engines": {
- "node": "*"
+ "node": ">= 16"
}
},
"node_modules/chokidar": {
@@ -391,12 +391,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
- },
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
@@ -438,13 +432,10 @@
}
},
"node_modules/deep-eql": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
- "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.1.tgz",
+ "integrity": "sha512-nwQCf6ne2gez3o1MxWifqkciwt0zhl0LO1/UwVu4uMBuPmflWM4oQ70XMqHqnBJA+nhzncaqL9HVL6KkHJ28lw==",
"dev": true,
- "dependencies": {
- "type-detect": "^4.0.0"
- },
"engines": {
"node": ">=6"
}
@@ -491,9 +482,9 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -530,7 +521,7 @@
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
"node_modules/fsevents": {
@@ -557,29 +548,28 @@
}
},
"node_modules/get-func-name": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
- "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/glob": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
- "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
},
"engines": {
- "node": "*"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -598,15 +588,15 @@
}
},
"node_modules/glob/node_modules/minimatch": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.1.tgz",
- "integrity": "sha512-reLxBcKUPNBnc/sVtAbxgRVFSegoGeLaSjmphNhcwcolhYLRgtJscn5mRl6YRZNQv40Y7P6JM2YhSIsbL9OB5A==",
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "brace-expansion": "^2.0.1"
},
"engines": {
- "node": "*"
+ "node": ">=10"
}
},
"node_modules/has-flag": {
@@ -630,7 +620,7 @@
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"dependencies": {
"once": "^1.3.0",
@@ -732,9 +722,9 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/jsonc-parser": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
- "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w=="
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
+ "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA=="
},
"node_modules/locate-path": {
"version": "6.0.0",
@@ -768,12 +758,12 @@
}
},
"node_modules/loupe": {
- "version": "2.3.4",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
- "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.0.tgz",
+ "integrity": "sha512-qKl+FrLXUhFuHUoDJG7f8P8gEMHq9NFS0c6ghXG1J0rldmZFQZoNVv/vyirE9qwCIhWZDsvEFd1sbFu3GvRQFg==",
"dev": true,
"dependencies": {
- "get-func-name": "^2.0.0"
+ "get-func-name": "^2.0.1"
}
},
"node_modules/make-error": {
@@ -782,9 +772,9 @@
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
"node_modules/minimatch": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz",
- "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==",
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -796,19 +786,10 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/minimatch/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
"node_modules/mocha": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
- "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==",
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz",
+ "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==",
"dev": true,
"dependencies": {
"ansi-colors": "4.1.1",
@@ -818,13 +799,12 @@
"diff": "5.0.0",
"escape-string-regexp": "4.0.0",
"find-up": "5.0.0",
- "glob": "7.2.0",
+ "glob": "8.1.0",
"he": "1.2.0",
"js-yaml": "4.1.0",
"log-symbols": "4.1.0",
"minimatch": "5.0.1",
"ms": "2.1.3",
- "nanoid": "3.3.3",
"serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1",
"supports-color": "8.1.1",
@@ -839,19 +819,6 @@
},
"engines": {
"node": ">= 14.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mochajs"
- }
- },
- "node_modules/mocha/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0"
}
},
"node_modules/mocha/node_modules/minimatch": {
@@ -872,18 +839,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
- "node_modules/nanoid": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
- "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
- "dev": true,
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -896,7 +851,7 @@
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"dependencies": {
"wrappy": "1"
@@ -941,22 +896,13 @@
"node": ">=8"
}
},
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/pathval": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
- "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
+ "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
"dev": true,
"engines": {
- "node": "*"
+ "node": ">= 14.16"
}
},
"node_modules/picomatch": {
@@ -1120,9 +1066,9 @@
}
},
"node_modules/ts-node": {
- "version": "10.9.1",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
- "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
@@ -1169,19 +1115,10 @@
"node": ">=0.3.1"
}
},
- "node_modules/type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/typescript": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz",
- "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==",
+ "version": "5.4.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -1190,6 +1127,11 @@
"node": ">=14.17"
}
},
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
+ },
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -1204,31 +1146,31 @@
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
},
"node_modules/vscode-json-languageservice": {
- "version": "5.3.5",
- "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.3.5.tgz",
- "integrity": "sha512-DasT+bKtpaS2rTPEB4VMROnvO1WES2KD8RZZxXbumnk9sk5wco10VdB6sJgTlsKQN14tHQLZDXuHnSoSAlE8LQ==",
+ "version": "5.3.11",
+ "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.3.11.tgz",
+ "integrity": "sha512-WYS72Ymria3dn8ZbjtBbt5K71m05wY1Q6hpXV5JxUT0q75Ts0ljLmnZJAVpx8DjPgYbFD+Z8KHpWh2laKLUCtQ==",
"dependencies": {
- "@vscode/l10n": "^0.0.13",
- "jsonc-parser": "^3.2.0",
- "vscode-languageserver-textdocument": "^1.0.8",
- "vscode-languageserver-types": "^3.17.3",
- "vscode-uri": "^3.0.7"
+ "@vscode/l10n": "^0.0.18",
+ "jsonc-parser": "^3.2.1",
+ "vscode-languageserver-textdocument": "^1.0.11",
+ "vscode-languageserver-types": "^3.17.5",
+ "vscode-uri": "^3.0.8"
}
},
"node_modules/vscode-languageserver-textdocument": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz",
- "integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q=="
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz",
+ "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA=="
},
"node_modules/vscode-languageserver-types": {
- "version": "3.17.3",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
- "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
+ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="
},
"node_modules/vscode-uri": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz",
- "integrity": "sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA=="
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz",
+ "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="
},
"node_modules/workerpool": {
"version": "6.2.1",
@@ -1256,7 +1198,7 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
"node_modules/y18n": {
@@ -1380,15 +1322,15 @@
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA=="
},
"@types/chai": {
- "version": "4.3.5",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz",
- "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==",
+ "version": "4.3.16",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.16.tgz",
+ "integrity": "sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==",
"dev": true
},
"@types/js-yaml": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz",
- "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==",
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz",
+ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==",
"dev": true
},
"@types/minimatch": {
@@ -1398,20 +1340,23 @@
"dev": true
},
"@types/mocha": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz",
- "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==",
+ "version": "10.0.6",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz",
+ "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==",
"dev": true
},
"@types/node": {
- "version": "20.3.1",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz",
- "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg=="
+ "version": "20.12.13",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.13.tgz",
+ "integrity": "sha512-gBGeanV41c1L171rR7wjbMiEpEI/l5XFQdLLfhr/REwpgDy/4U8y89+i8kRiLzDyZdOkXh+cRaTetUnCYutoXA==",
+ "requires": {
+ "undici-types": "~5.26.4"
+ }
},
"@vscode/l10n": {
- "version": "0.0.13",
- "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.13.tgz",
- "integrity": "sha512-A3uY356uOU9nGa+TQIT/i3ziWUgJjVMUrGGXSrtRiTwklyCFjGVWIOHoEIHbJpiyhDkJd9kvIWUOfXK1IkK8XQ=="
+ "version": "0.0.18",
+ "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz",
+ "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="
},
"acorn": {
"version": "8.6.0",
@@ -1435,9 +1380,9 @@
}
},
"ajv-formats": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
- "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
"requires": {
"ajv": "^8.0.0"
}
@@ -1484,9 +1429,9 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"assertion-error": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
- "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true
},
"balanced-match": {
@@ -1502,22 +1447,21 @@
"dev": true
},
"brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "balanced-match": "^1.0.0"
}
},
"braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"requires": {
- "fill-range": "^7.0.1"
+ "fill-range": "^7.1.1"
}
},
"browser-stdout": {
@@ -1533,18 +1477,16 @@
"dev": true
},
"chai": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz",
- "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==",
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz",
+ "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==",
"dev": true,
"requires": {
- "assertion-error": "^1.1.0",
- "check-error": "^1.0.2",
- "deep-eql": "^4.1.2",
- "get-func-name": "^2.0.0",
- "loupe": "^2.3.1",
- "pathval": "^1.1.1",
- "type-detect": "^4.0.5"
+ "assertion-error": "^2.0.1",
+ "check-error": "^2.1.1",
+ "deep-eql": "^5.0.1",
+ "loupe": "^3.1.0",
+ "pathval": "^2.0.0"
}
},
"chalk": {
@@ -1569,9 +1511,9 @@
}
},
"check-error": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
- "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+ "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
"dev": true
},
"chokidar": {
@@ -1616,12 +1558,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
- },
"create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
@@ -1651,13 +1587,10 @@
"dev": true
},
"deep-eql": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
- "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
- "dev": true,
- "requires": {
- "type-detect": "^4.0.0"
- }
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.1.tgz",
+ "integrity": "sha512-nwQCf6ne2gez3o1MxWifqkciwt0zhl0LO1/UwVu4uMBuPmflWM4oQ70XMqHqnBJA+nhzncaqL9HVL6KkHJ28lw==",
+ "dev": true
},
"diff": {
"version": "5.0.0",
@@ -1689,9 +1622,9 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
@@ -1716,7 +1649,7 @@
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
},
"fsevents": {
@@ -1733,32 +1666,31 @@
"dev": true
},
"get-func-name": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
- "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
"dev": true
},
"glob": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
- "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
},
"dependencies": {
"minimatch": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.1.tgz",
- "integrity": "sha512-reLxBcKUPNBnc/sVtAbxgRVFSegoGeLaSjmphNhcwcolhYLRgtJscn5mRl6YRZNQv40Y7P6JM2YhSIsbL9OB5A==",
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
"requires": {
- "brace-expansion": "^1.1.7"
+ "brace-expansion": "^2.0.1"
}
}
}
@@ -1787,7 +1719,7 @@
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"requires": {
"once": "^1.3.0",
@@ -1862,9 +1794,9 @@
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"jsonc-parser": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
- "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w=="
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
+ "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA=="
},
"locate-path": {
"version": "6.0.0",
@@ -1886,12 +1818,12 @@
}
},
"loupe": {
- "version": "2.3.4",
- "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
- "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.0.tgz",
+ "integrity": "sha512-qKl+FrLXUhFuHUoDJG7f8P8gEMHq9NFS0c6ghXG1J0rldmZFQZoNVv/vyirE9qwCIhWZDsvEFd1sbFu3GvRQFg==",
"dev": true,
"requires": {
- "get-func-name": "^2.0.0"
+ "get-func-name": "^2.0.1"
}
},
"make-error": {
@@ -1900,29 +1832,18 @@
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
},
"minimatch": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz",
- "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==",
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
"dev": true,
"requires": {
"brace-expansion": "^2.0.1"
- },
- "dependencies": {
- "brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "requires": {
- "balanced-match": "^1.0.0"
- }
- }
}
},
"mocha": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
- "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==",
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz",
+ "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==",
"dev": true,
"requires": {
"ansi-colors": "4.1.1",
@@ -1932,13 +1853,12 @@
"diff": "5.0.0",
"escape-string-regexp": "4.0.0",
"find-up": "5.0.0",
- "glob": "7.2.0",
+ "glob": "8.1.0",
"he": "1.2.0",
"js-yaml": "4.1.0",
"log-symbols": "4.1.0",
"minimatch": "5.0.1",
"ms": "2.1.3",
- "nanoid": "3.3.3",
"serialize-javascript": "6.0.0",
"strip-json-comments": "3.1.1",
"supports-color": "8.1.1",
@@ -1948,15 +1868,6 @@
"yargs-unparser": "2.0.0"
},
"dependencies": {
- "brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "requires": {
- "balanced-match": "^1.0.0"
- }
- },
"minimatch": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
@@ -1974,12 +1885,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
- "nanoid": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
- "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
- "dev": true
- },
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -1989,7 +1894,7 @@
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true,
"requires": {
"wrappy": "1"
@@ -2019,16 +1924,10 @@
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true
},
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
- },
"pathval": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
- "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
+ "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
"dev": true
},
"picomatch": {
@@ -2136,9 +2035,9 @@
}
},
"ts-node": {
- "version": "10.9.1",
- "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
- "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+ "version": "10.9.2",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"requires": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
@@ -2162,16 +2061,15 @@
}
}
},
- "type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true
- },
"typescript": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz",
- "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw=="
+ "version": "5.4.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ=="
+ },
+ "undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"uri-js": {
"version": "4.4.1",
@@ -2187,31 +2085,31 @@
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
},
"vscode-json-languageservice": {
- "version": "5.3.5",
- "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.3.5.tgz",
- "integrity": "sha512-DasT+bKtpaS2rTPEB4VMROnvO1WES2KD8RZZxXbumnk9sk5wco10VdB6sJgTlsKQN14tHQLZDXuHnSoSAlE8LQ==",
+ "version": "5.3.11",
+ "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.3.11.tgz",
+ "integrity": "sha512-WYS72Ymria3dn8ZbjtBbt5K71m05wY1Q6hpXV5JxUT0q75Ts0ljLmnZJAVpx8DjPgYbFD+Z8KHpWh2laKLUCtQ==",
"requires": {
- "@vscode/l10n": "^0.0.13",
- "jsonc-parser": "^3.2.0",
- "vscode-languageserver-textdocument": "^1.0.8",
- "vscode-languageserver-types": "^3.17.3",
- "vscode-uri": "^3.0.7"
+ "@vscode/l10n": "^0.0.18",
+ "jsonc-parser": "^3.2.1",
+ "vscode-languageserver-textdocument": "^1.0.11",
+ "vscode-languageserver-types": "^3.17.5",
+ "vscode-uri": "^3.0.8"
}
},
"vscode-languageserver-textdocument": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz",
- "integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q=="
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz",
+ "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA=="
},
"vscode-languageserver-types": {
- "version": "3.17.3",
- "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
- "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
+ "version": "3.17.5",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
+ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="
},
"vscode-uri": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz",
- "integrity": "sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA=="
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz",
+ "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="
},
"workerpool": {
"version": "6.2.1",
@@ -2233,7 +2131,7 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true
},
"y18n": {
diff --git a/test/schemas/package.json b/test/schemas/package.json
index c318ca0..bc7d264 100644
--- a/test/schemas/package.json
+++ b/test/schemas/package.json
@@ -1,28 +1,29 @@
{
"dependencies": {
- "ajv-formats": "^2.1.1",
+ "ajv-formats": "^3.0.1",
"js-yaml": "^4.1.0",
"safe-stable-stringify": "^2.4.3",
- "ts-node": "^10.9.1",
- "vscode-json-languageservice": "^5.3.5"
+ "ts-node": "^10.9.2",
+ "vscode-json-languageservice": "^5.3.11"
},
"scripts": {
- "compile": "tsc -p ./src",
+ "compile": "tsc",
"deps": "npx --yes npm-check-updates -u && npm install --ignore-scripts",
"test": "python3 src/rebuild.py && mocha"
},
"devDependencies": {
- "@types/chai": "^4.3.5",
- "@types/js-yaml": "^4.0.5",
+ "@types/chai": "^4.3.16",
+ "@types/js-yaml": "^4.0.9",
"@types/minimatch": "^5.1.2",
- "@types/mocha": "^10.0.1",
- "@types/node": "^20.3.1",
- "chai": "^4.3.7",
- "minimatch": "^9.0.1",
- "mocha": "^10.2.0",
- "typescript": "^5.1.3"
+ "@types/mocha": "^10.0.6",
+ "@types/node": "^20.12.13",
+ "chai": "^5.1.1",
+ "minimatch": "^9.0.4",
+ "mocha": "^10.4.0",
+ "typescript": "^5.4.5"
},
"directories": {
"test": "./src"
- }
+ },
+ "type": "module"
}
diff --git a/test/schemas/src/rebuild.py b/test/schemas/src/rebuild.py
index 2fab8c0..5eb4807 100644
--- a/test/schemas/src/rebuild.py
+++ b/test/schemas/src/rebuild.py
@@ -1,4 +1,5 @@
"""Utility to generate some complex patterns."""
+
import copy
import json
import keyword
@@ -63,11 +64,11 @@ def is_ref_used(obj: Any, ref: str) -> bool:
if obj.get("$ref", None) == ref_use:
return True
for _ in obj.values():
- if isinstance(_, (dict, list)) and is_ref_used(_, ref):
+ if isinstance(_, dict | list) and is_ref_used(_, ref):
return True
elif isinstance(obj, list):
for _ in obj:
- if isinstance(_, (dict, list)) and is_ref_used(_, ref):
+ if isinstance(_, dict | list) and is_ref_used(_, ref):
return True
return False
@@ -119,16 +120,13 @@ if __name__ == "__main__":
for key, value in combined_json["$defs"][subschema].items():
sub_json[key] = value
sub_json["$comment"] = "Generated from ansible.json, do not edit."
- sub_json[
- "$id"
- ] = f"https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/{subschema}.json"
+ sub_json["$id"] = (
+ f"https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/{subschema}.json"
+ )
# Remove all unreferenced ($ref) definitions ($defs) recursively
while True:
- spare = []
- for k in sub_json["$defs"]:
- if not is_ref_used(sub_json, k):
- spare.append(k)
+ spare = [k for k in sub_json["$defs"] if not is_ref_used(sub_json, k)]
for k in spare:
print(f"{subschema}: deleting unused '{k}' definition") # noqa: T201
del sub_json["$defs"][k]
diff --git a/test/schemas/src/schema.spec.ts b/test/schemas/src/schema.spec.ts
index b826461..beb6ee2 100644
--- a/test/schemas/src/schema.spec.ts
+++ b/test/schemas/src/schema.spec.ts
@@ -5,9 +5,7 @@ import { minimatch } from "minimatch";
import yaml from "js-yaml";
import { assert } from "chai";
import stringify from "safe-stable-stringify";
-import { integer } from "vscode-languageserver-types";
-import { exec } from "child_process";
-const spawnSync = require("child_process").spawnSync;
+import { spawnSync } from "child_process";
function ansiRegex({ onlyFirst = false } = {}) {
const pattern = [
@@ -21,7 +19,7 @@ function ansiRegex({ onlyFirst = false } = {}) {
function stripAnsi(data: string) {
if (typeof data !== "string") {
throw new TypeError(
- `Expected a \`string\`, got \`${typeof data}\ = ${data}`
+ `Expected a \`string\`, got \`${typeof data}\ = ${data}`,
);
}
return data.replace(ansiRegex(), "");
@@ -57,7 +55,7 @@ describe("schemas under f/", function () {
const validator = ajv.compile(schema_json);
if (schema_json.examples == undefined) {
console.error(
- `Schema file ${schema_file} is missing an examples key that we need for documenting file matching patterns.`
+ `Schema file ${schema_file} is missing an examples key that we need for documenting file matching patterns.`,
);
return process.exit(1);
}
@@ -67,7 +65,7 @@ describe("schemas under f/", function () {
it(`linting ${test_file} using ${schema_file}`, function () {
var errors_md = "";
const result = validator(
- yaml.load(fs.readFileSync(test_file, "utf8"))
+ yaml.load(fs.readFileSync(test_file, "utf8")),
);
if (validator.errors) {
errors_md += "# ajv errors\n\n```json\n";
@@ -76,17 +74,17 @@ describe("schemas under f/", function () {
}
// validate using check-jsonschema (python-jsonschema):
// const py = exec();
- // Do not use python -m ... calling notation because for some
+ // Do not use python3 -m ... calling notation because for some
// reason, nodejs environment lacks some env variables needed
// and breaks usage from inside virtualenvs.
const proc = spawnSync(
`${process.env.VIRTUAL_ENV}/bin/check-jsonschema -v -o json --schemafile f/${schema_file} ${test_file}`,
- { shell: true, encoding: "utf-8", stdio: "pipe" }
+ { shell: true, encoding: "utf-8", stdio: "pipe" },
);
if (proc.status != 0) {
// real errors are sent to stderr due to https://github.com/python-jsonschema/check-jsonschema/issues/88
errors_md += "# check-jsonschema\n\nstdout:\n\n```json\n";
- errors_md += stripAnsi(proc.output[1]);
+ errors_md += stripAnsi(proc.output[1] || "");
errors_md += "```\n";
if (proc.output[2]) {
errors_md += "\nstderr:\n\n```\n";
@@ -110,10 +108,10 @@ describe("schemas under f/", function () {
assert.equal(
result,
!expect_fail,
- `${JSON.stringify(validator.errors)}`
+ `${JSON.stringify(validator.errors)}`,
);
});
- }
+ },
);
// All /$defs/ that have examples property are assumed to be
// subschemas, "tasks" being the primary such case, which is also used
@@ -130,15 +128,15 @@ describe("schemas under f/", function () {
({ file: test_file, expect_fail }) => {
it(`linting ${test_file} using ${subschema_uri}`, function () {
const result = subschema_validator(
- yaml.load(fs.readFileSync(test_file, "utf8"))
+ yaml.load(fs.readFileSync(test_file, "utf8")),
);
assert.equal(
result,
!expect_fail,
- `${JSON.stringify(validator.errors)}`
+ `${JSON.stringify(validator.errors)}`,
);
});
- }
+ },
);
}
}
@@ -148,29 +146,29 @@ describe("schemas under f/", function () {
// find all tests for each schema file
function getTestFiles(
- globs: string[]
+ globs: string[],
): { file: string; expect_fail: boolean }[] {
const files = Array.from(
new Set(
globs
.map((glob: any) => minimatch.match(test_files, path.join("**", glob)))
- .flat()
- )
+ .flat(),
+ ),
);
const negative_files = Array.from(
new Set(
globs
.map((glob: any) =>
- minimatch.match(negative_test_files, path.join("**", glob))
+ minimatch.match(negative_test_files, path.join("**", glob)),
)
- .flat()
- )
+ .flat(),
+ ),
);
// All fails ending with fail, like `foo.fail.yml` are expected to fail validation
let result = files.map((f) => ({ file: f, expect_fail: false }));
result = result.concat(
- negative_files.map((f) => ({ file: f, expect_fail: true }))
+ negative_files.map((f) => ({ file: f, expect_fail: true })),
);
return result;
}
diff --git a/test/schemas/test/playbooks/gather_facts.yml b/test/schemas/test/playbooks/gather_facts.yml
index 598188d..bdba790 100644
--- a/test/schemas/test/playbooks/gather_facts.yml
+++ b/test/schemas/test/playbooks/gather_facts.yml
@@ -4,3 +4,9 @@
tasks:
- ansible.builtin.debug:
msg: foo
+
+- hosts: localhost
+ gather_facts: "{{ facts_var_bool | default(false) }}"
+ tasks:
+ - ansible.builtin.debug:
+ msg: bar
diff --git a/test/schemas/test/playbooks/ignore-unreachable.yml b/test/schemas/test/playbooks/ignore-unreachable.yml
new file mode 100644
index 0000000..8dfdc21
--- /dev/null
+++ b/test/schemas/test/playbooks/ignore-unreachable.yml
@@ -0,0 +1,13 @@
+---
+- name: Test
+ hosts: localhost
+ tasks:
+ - name: Debug
+ ansible.builtin.debug:
+ msg: ignore_unreachable should be a boolean
+ ignore_unreachable: true
+
+ - name: Debug
+ ansible.builtin.debug:
+ msg: "foo"
+ ignore_unreachable: '{{ "yes" | bool }}'
diff --git a/test/schemas/test/playbooks/order.yml b/test/schemas/test/playbooks/order.yml
new file mode 100644
index 0000000..08534de
--- /dev/null
+++ b/test/schemas/test/playbooks/order.yml
@@ -0,0 +1,15 @@
+---
+- name: Test
+ hosts: localhost
+ order: "{{ host_order | default('shuffle') }}"
+ gather_facts: false
+ serial: 1
+ tasks:
+ - name: ABC
+ ansible.builtin.debug:
+ msg: "hello"
+- name: Test 2
+ hosts: localhost
+ order: inventory
+ gather_facts: false
+ tasks: []
diff --git a/test/schemas/test/roles/foo/meta/argument_specs.yml b/test/schemas/test/roles/foo/meta/argument_specs.yml
index c8d8c68..a83b82c 100644
--- a/test/schemas/test/roles/foo/meta/argument_specs.yml
+++ b/test/schemas/test/roles/foo/meta/argument_specs.yml
@@ -40,6 +40,35 @@ argument_specs:
- 3
- 123
+ complex_required_options:
+ type: dict
+ description: Contains sub-options with interacting requirements
+ options:
+ foo:
+ type: str
+ bar:
+ type: str
+ baz:
+ type: str
+
+ mutually_exclusive:
+ - ["foo", "bar"]
+
+ required_together:
+ - ["bar", "baz"]
+
+ required_one_of:
+ - ["foo", "bar", "baz"]
+
+ required_if:
+ - ["foo", "must_have_bar_and_baz_default", ["bar", "baz"]]
+ - ["foo", "must_have_bar_and_baz_explicit", ["bar", "baz"], false]
+ - ["foo", "must_have_one_of_bar_or_baz", ["bar", "baz"], true]
+
+ required_by:
+ foo: "bar"
+ bar: ["foo", "baz"]
+
seealso:
- module: community.foo.bar
- module: community.foo.baz
@@ -55,11 +84,31 @@ argument_specs:
name: The Ansible documentation.
description: A link to the Ansible documentation.
+ examples: |-
+ - name: Use role
+ include_role: foo.bar.baz
+
alternate:
short_description: The alternate entry point for the my_app role.
author:
- Foobar Baz
- Bert Foo
+ attributes:
+ idempotent:
+ description: Whether the role is idempotent.
+ support: full
+ check_mode:
+ description:
+ - Whether the role supports check mode.
+ support: partial
+ details:
+ - Does not work if O(my_app_int=5).
+ version_added: 1.2.0
+ action_group:
+ description:
+ - Use C(group/foo.bar.baz) in C(module_defaults) to set authentication options for the C(foo.bar) modules used by this role.
+ support: full
+ membership: foo.bar.baz
options:
my_app_int:
type: "int"
diff --git a/test/schemas/test/roles/foo/meta/main.yml b/test/schemas/test/roles/foo/meta/main.yml
index b84b10c..2536c22 100644
--- a/test/schemas/test/roles/foo/meta/main.yml
+++ b/test/schemas/test/roles/foo/meta/main.yml
@@ -5,6 +5,7 @@ dependencies:
version: "1.0"
- name: ansible-role-bar
version: "1.0"
+ - ansible-role-baz
# from Bitbucket
- src: git+http://bitbucket.org/willthames/git-ansible-galaxy
version: v1.4
diff --git a/test/schemas/tsconfig.json b/test/schemas/tsconfig.json
index fe51c68..b2291af 100644
--- a/test/schemas/tsconfig.json
+++ b/test/schemas/tsconfig.json
@@ -2,16 +2,16 @@
"compilerOptions": {
"declaration": true,
"esModuleInterop": true,
- "lib": ["es5", "es2015.promise"],
- "module": "commonjs",
+ "lib": ["ESNext"],
+ "module": "esnext",
"moduleResolution": "node",
- "outDir": "../lib/umd",
+ "outDir": "../../.tox/out",
"resolveJsonModule": true,
"sourceMap": true,
"strict": true,
"stripInternal": true,
- "target": "es5"
+ "target": "ESNext",
},
"exclude": ["node_modules"],
- "include": ["src/**/*"]
+ "include": ["src/**/*"],
}
diff --git a/test/test_adjacent_plugins.py b/test/test_adjacent_plugins.py
new file mode 100644
index 0000000..3e642ce
--- /dev/null
+++ b/test/test_adjacent_plugins.py
@@ -0,0 +1,25 @@
+"""Test ability to recognize adjacent modules/plugins."""
+
+import logging
+
+import pytest
+
+from ansiblelint.rules import RulesCollection
+from ansiblelint.runner import Runner
+
+
+def test_adj_action(
+ default_rules_collection: RulesCollection,
+ caplog: pytest.LogCaptureFixture,
+) -> None:
+ """Assures local collections are found."""
+ playbook_path = "examples/playbooks/adj_action.yml"
+
+ with caplog.at_level(logging.DEBUG):
+ runner = Runner(playbook_path, rules=default_rules_collection, verbosity=1)
+ results = runner.run()
+ assert "Unable to load module" not in caplog.text
+ assert "Unable to resolve FQCN" not in caplog.text
+
+ assert len(runner.lintables) == 1
+ assert len(results) == 0
diff --git a/test/test_ansiblelintrule.py b/test/test_ansiblelintrule.py
index c576e0f..dcce2ea 100644
--- a/test/test_ansiblelintrule.py
+++ b/test/test_ansiblelintrule.py
@@ -1,15 +1,15 @@
"""Generic tests for AnsibleLintRule class."""
+
from __future__ import annotations
from typing import TYPE_CHECKING, Any
import pytest
-from ansiblelint.config import options
-from ansiblelint.rules import AnsibleLintRule
+from ansiblelint.rules import AnsibleLintRule, RulesCollection
if TYPE_CHECKING:
- from _pytest.monkeypatch import MonkeyPatch
+ from ansiblelint.config import Options
def test_unjinja() -> None:
@@ -20,12 +20,14 @@ def test_unjinja() -> None:
@pytest.mark.parametrize("rule_config", ({}, {"foo": True, "bar": 1}))
-def test_rule_config(rule_config: dict[str, Any], monkeypatch: MonkeyPatch) -> None:
- """Check that a rule config is inherited from options."""
- rule_id = "rule-0"
- monkeypatch.setattr(AnsibleLintRule, "id", rule_id)
- monkeypatch.setitem(options.rules, rule_id, rule_config)
-
- rule = AnsibleLintRule()
- assert set(rule.rule_config.items()) == set(rule_config.items())
- assert all(rule.get_config(k) == v for k, v in rule_config.items())
+def test_rule_config(
+ rule_config: dict[str, Any],
+ config_options: Options,
+) -> None:
+ """Check that a rule config can be accessed."""
+ config_options.rules["load-failure"] = rule_config
+ rules = RulesCollection(options=config_options)
+ for rule in rules:
+ if rule.id == "load-failure":
+ assert rule._collection # noqa: SLF001
+ assert rule.rule_config == rule_config
diff --git a/test/test_ansiblesyntax.py b/test/test_ansiblesyntax.py
index f71a525..649833e 100644
--- a/test/test_ansiblesyntax.py
+++ b/test/test_ansiblesyntax.py
@@ -3,6 +3,7 @@
This module contains tests that validate that linter does not produce errors
when encountering what counts as valid Ansible syntax.
"""
+
from ansiblelint.testing import RunFromText
PB_WITH_NULL_TASKS = """\
diff --git a/test/test_app.py b/test/test_app.py
index 140f5f6..cbeae3d 100644
--- a/test/test_app.py
+++ b/test/test_app.py
@@ -1,4 +1,5 @@
"""Test for app module."""
+
from pathlib import Path
from ansiblelint.constants import RC
diff --git a/test/test_cli.py b/test/test_cli.py
index a37a43d..b2c3320 100644
--- a/test/test_cli.py
+++ b/test/test_cli.py
@@ -1,4 +1,5 @@
"""Test cli arguments and config."""
+
from __future__ import annotations
import os
@@ -66,37 +67,77 @@ def test_ensure_config_are_equal(
@pytest.mark.parametrize(
- ("with_base", "args", "config"),
+ ("with_base", "args", "config", "expected"),
(
- (True, ["--write"], "test/fixtures/config-with-write-all.yml"),
- (True, ["--write=all"], "test/fixtures/config-with-write-all.yml"),
- (True, ["--write", "all"], "test/fixtures/config-with-write-all.yml"),
- (True, ["--write=none"], "test/fixtures/config-with-write-none.yml"),
- (True, ["--write", "none"], "test/fixtures/config-with-write-none.yml"),
- (
+ pytest.param(
+ True,
+ ["--fix"],
+ "test/fixtures/config-with-write-all.yml",
+ ["all"],
+ id="1",
+ ),
+ pytest.param(
+ True,
+ ["--fix=all"],
+ "test/fixtures/config-with-write-all.yml",
+ ["all"],
+ id="2",
+ ),
+ pytest.param(
+ True,
+ ["--fix", "all"],
+ "test/fixtures/config-with-write-all.yml",
+ ["all"],
+ id="3",
+ ),
+ pytest.param(
True,
- ["--write=rule-tag,rule-id"],
+ ["--fix=none"],
+ "test/fixtures/config-with-write-none.yml",
+ [],
+ id="4",
+ ),
+ pytest.param(
+ True,
+ ["--fix", "none"],
+ "test/fixtures/config-with-write-none.yml",
+ [],
+ id="5",
+ ),
+ pytest.param(
+ True,
+ ["--fix=rule-tag,rule-id"],
"test/fixtures/config-with-write-subset.yml",
+ ["rule-tag", "rule-id"],
+ id="6",
),
- (
+ pytest.param(
True,
- ["--write", "rule-tag,rule-id"],
+ ["--fix", "rule-tag,rule-id"],
"test/fixtures/config-with-write-subset.yml",
+ ["rule-tag", "rule-id"],
+ id="7",
),
- (
+ pytest.param(
True,
- ["--write", "rule-tag", "--write", "rule-id"],
+ ["--fix", "rule-tag", "--fix", "rule-id"],
"test/fixtures/config-with-write-subset.yml",
+ ["rule-tag", "rule-id"],
+ id="8",
),
- (
+ pytest.param(
False,
- ["--write", "examples/playbooks/example.yml"],
+ ["--fix", "examples/playbooks/example.yml"],
"test/fixtures/config-with-write-all.yml",
+ ["all"],
+ id="9",
),
- (
+ pytest.param(
False,
- ["--write", "examples/playbooks/example.yml", "non-existent.yml"],
+ ["--fix", "examples/playbooks/example.yml", "non-existent.yml"],
"test/fixtures/config-with-write-all.yml",
+ ["all"],
+ id="10",
),
),
)
@@ -105,21 +146,22 @@ def test_ensure_write_cli_does_not_consume_lintables(
with_base: bool,
args: list[str],
config: str,
+ expected: list[str],
) -> None:
- """Check equality of the CLI --write options to config files."""
+ """Check equality of the CLI --fix options to config files."""
cli_parser = cli.get_cli_parser()
command = base_arguments + args if with_base else args
options = cli_parser.parse_args(command)
file_config = cli.load_config(config)[0]
- file_value = file_config.get("write_list")
+ file_config.get("write_list")
orig_cli_value = options.write_list
- cli_value = cli.WriteArgAction.merge_write_list_config(
+ cli_value = cli.WriteArgAction.merge_fix_list_config(
from_file=[],
from_cli=orig_cli_value,
)
- assert file_value == cli_value
+ assert cli_value == expected
def test_config_can_be_overridden(base_arguments: list[str]) -> None:
diff --git a/test/test_cli_role_paths.py b/test/test_cli_role_paths.py
index 148e1ed..131c3b5 100644
--- a/test/test_cli_role_paths.py
+++ b/test/test_cli_role_paths.py
@@ -1,4 +1,5 @@
"""Tests related to role paths."""
+
from __future__ import annotations
import os
@@ -174,7 +175,10 @@ def test_run_single_role_path_with_roles_path_env(local_test_dir: Path) -> None:
@pytest.mark.parametrize(
("result", "env"),
- ((True, {"GITHUB_ACTIONS": "true", "GITHUB_WORKFLOW": "foo"}), (False, None)),
+ (
+ (True, {"GITHUB_ACTIONS": "true", "GITHUB_WORKFLOW": "foo", "NO_COLOR": "1"}),
+ (False, None),
+ ),
ids=("on", "off"),
)
def test_run_playbook_github(result: bool, env: dict[str, str]) -> None:
@@ -192,3 +196,41 @@ def test_run_playbook_github(result: bool, env: dict[str, str]) -> None:
"Package installs should not use latest"
)
assert (expected in result_gh.stderr) is result
+
+
+def test_run_role_identified(local_test_dir: Path) -> None:
+ """Test that role name is identified correctly."""
+ cwd = local_test_dir
+
+ env = os.environ.copy()
+ env["ANSIBLE_ROLES_PATH"] = os.path.realpath(
+ (cwd / "../examples/roles/role_detection").resolve(),
+ )
+ result = run_ansible_lint(
+ Path("roles/role_detection/foo/defaults/main.yml"),
+ cwd=cwd,
+ env=env,
+ )
+ assert result.returncode == RC.SUCCESS
+
+
+def test_run_role_identified_prefix_missing(local_test_dir: Path) -> None:
+ """Test that role name is identified correctly, with prefix violations."""
+ cwd = local_test_dir
+
+ env = os.environ.copy()
+ env["ANSIBLE_ROLES_PATH"] = os.path.realpath(
+ (cwd / "../examples/roles/role_detection/base").resolve(),
+ )
+ result = run_ansible_lint(
+ Path("roles/role_detection/base/bar/defaults/main.yml"),
+ cwd=cwd,
+ env=env,
+ )
+ assert result.returncode == RC.VIOLATIONS_FOUND
+ assert (
+ "Variables names from within roles should use bar_ as a prefix" in result.stdout
+ )
+ assert (
+ "Variables names from within roles should use bar_ as a prefix" in result.stdout
+ )
diff --git a/test/test_config.py b/test/test_config.py
index 51a09b0..4c4ff5a 100644
--- a/test/test_config.py
+++ b/test/test_config.py
@@ -1,4 +1,5 @@
"""Tests for config module."""
+
from ansiblelint.config import PROFILES
from ansiblelint.rules import RulesCollection
diff --git a/test/test_constants.py b/test/test_constants.py
index 52b297a..ad957e2 100644
--- a/test/test_constants.py
+++ b/test/test_constants.py
@@ -1,4 +1,5 @@
"""Tests for constants module."""
+
from ansiblelint.constants import States
diff --git a/test/test_dependencies_in_meta.py b/test/test_dependencies_in_meta.py
index 44007b7..0206164 100644
--- a/test/test_dependencies_in_meta.py
+++ b/test/test_dependencies_in_meta.py
@@ -1,4 +1,5 @@
"""Tests about dependencies in meta."""
+
from ansiblelint.rules import RulesCollection
from ansiblelint.runner import Runner
diff --git a/test/test_errors.py b/test/test_errors.py
new file mode 100644
index 0000000..69b7fe8
--- /dev/null
+++ b/test/test_errors.py
@@ -0,0 +1,25 @@
+"""Test ansiblelint.errors."""
+
+import pytest
+
+from ansiblelint.errors import MatchError
+
+
+def test_matcherror() -> None:
+ """."""
+ match = MatchError("foo", lineno=1, column=2)
+ with pytest.raises(TypeError):
+ assert match <= 0
+
+ assert match != 0
+
+ assert match.position == "1:2"
+
+ match2 = MatchError("foo", lineno=1)
+ assert match2.position == "1"
+
+ # str and repr are for the moment the same
+ assert str(match) == repr(match)
+
+ # tests implicit level
+ assert match.level == "warning"
diff --git a/test/test_examples.py b/test/test_examples.py
index 2842930..7840360 100644
--- a/test/test_examples.py
+++ b/test/test_examples.py
@@ -1,4 +1,5 @@
"""Assure samples produced desire outcomes."""
+
import pytest
from ansiblelint.app import get_app
@@ -17,31 +18,37 @@ def test_example(default_rules_collection: RulesCollection) -> None:
@pytest.mark.parametrize(
- ("filename", "line", "column"),
+ ("filename", "expected_results"),
(
pytest.param(
"examples/playbooks/syntax-error-string.yml",
- 6,
- 7,
- id="syntax-error",
+ [("syntax-check[unknown-module]", 6, 7)],
+ id="0",
+ ),
+ pytest.param(
+ "examples/playbooks/syntax-error.yml",
+ [("syntax-check[specific]", 2, 3)],
+ id="1",
),
- pytest.param("examples/playbooks/syntax-error.yml", 2, 3, id="syntax-error"),
),
)
def test_example_syntax_error(
default_rules_collection: RulesCollection,
filename: str,
- line: int,
- column: int,
+ expected_results: list[tuple[str, int | None, int | None]],
) -> None:
"""Validates that loading valid YAML string produce error."""
result = Runner(filename, rules=default_rules_collection).run()
- assert len(result) == 1
- assert result[0].rule.id == "syntax-check"
- # This also ensures that line and column numbers start at 1, so they
- # match what editors will show (or output from other linters)
- assert result[0].lineno == line
- assert result[0].column == column
+ assert len(result) == len(expected_results)
+ for i, expected in enumerate(expected_results):
+ if expected[0] is not None:
+ assert result[i].tag == expected[0]
+ # This also ensures that line and column numbers start at 1, so they
+ # match what editors will show (or output from other linters)
+ if expected[1] is not None:
+ assert result[i].lineno == expected[1]
+ if expected[2] is not None:
+ assert result[i].column == expected[2]
def test_example_custom_module(default_rules_collection: RulesCollection) -> None:
@@ -67,7 +74,7 @@ def test_vault_partial(
default_rules_collection: RulesCollection,
caplog: pytest.LogCaptureFixture,
) -> None:
- """Check ability to precess files that container !vault inside."""
+ """Check ability to process files that container !vault inside."""
result = Runner(
"examples/playbooks/vars/vault_partial.yml",
rules=default_rules_collection,
diff --git a/test/test_file_path_evaluation.py b/test/test_file_path_evaluation.py
index b31f923..69f02bb 100644
--- a/test/test_file_path_evaluation.py
+++ b/test/test_file_path_evaluation.py
@@ -1,4 +1,5 @@
"""Testing file path evaluation when using import_tasks / include_tasks."""
+
from __future__ import annotations
import textwrap
@@ -42,14 +43,14 @@ LAYOUT_IMPORTS: dict[str, str] = {
"tasks/subtasks/subtask_1.yml": textwrap.dedent(
"""\
---
- - name: subtask_1 | From subtask 1 import subtask 2
+ - name: subtasks | subtask_1 | From subtask 1 import subtask 2
ansible.builtin.import_tasks: tasks/subtasks/subtask_2.yml
""",
),
"tasks/subtasks/subtask_2.yml": textwrap.dedent(
"""\
---
- - name: subtask_2 | From subtask 2 do something
+ - name: subtasks | subtask_2 | From subtask 2 do something
debug: # <-- expected to raise fqcn[action-core]
msg: |
Something...
@@ -86,14 +87,14 @@ LAYOUT_INCLUDES: dict[str, str] = {
"tasks/subtasks/subtask_1.yml": textwrap.dedent(
"""\
---
- - name: subtask_1 | From subtask 1 import subtask 2
+ - name: subtasks | subtask_1 | From subtask 1 import subtask 2
ansible.builtin.include_tasks: tasks/subtasks/subtask_2.yml
""",
),
"tasks/subtasks/subtask_2.yml": textwrap.dedent(
"""\
---
- - name: subtask_2 | From subtask 2 do something
+ - name: subtasks | subtask_2 | From subtask 2 do something
debug: # <-- expected to raise fqcn[action-core]
msg: |
Something...
@@ -105,8 +106,8 @@ LAYOUT_INCLUDES: dict[str, str] = {
@pytest.mark.parametrize(
"ansible_project_layout",
(
- pytest.param(LAYOUT_IMPORTS, id="using only import_tasks"),
- pytest.param(LAYOUT_INCLUDES, id="using only include_tasks"),
+ pytest.param(LAYOUT_IMPORTS, id="using-only-import_tasks"),
+ pytest.param(LAYOUT_INCLUDES, id="using-only-include_tasks"),
),
)
def test_file_path_evaluation(
diff --git a/test/test_file_utils.py b/test/test_file_utils.py
index b7b9115..74f1934 100644
--- a/test/test_file_utils.py
+++ b/test/test_file_utils.py
@@ -1,4 +1,5 @@
"""Tests for file utility functions."""
+
from __future__ import annotations
import copy
@@ -59,7 +60,7 @@ def test_expand_path_vars(monkeypatch: MonkeyPatch) -> None:
pytest.param(Path("$TEST_PATH"), "/test/path", id="pathlib.Path"),
pytest.param("$TEST_PATH", "/test/path", id="str"),
pytest.param(" $TEST_PATH ", "/test/path", id="stripped-str"),
- pytest.param("~", os.path.expanduser("~"), id="home"), # noqa: PTH:111
+ pytest.param("~", os.path.expanduser("~"), id="home"), # noqa: PTH111
),
)
def test_expand_paths_vars(
@@ -236,7 +237,7 @@ def test_discover_lintables_umlaut(monkeypatch: MonkeyPatch) -> None:
"tasks",
id="33",
), # content should determine is tasks
- pytest.param("examples/collection/galaxy.yml", "galaxy", id="34"),
+ pytest.param("examples/.collection/galaxy.yml", "galaxy", id="34"),
pytest.param("examples/meta/runtime.yml", "meta-runtime", id="35"),
pytest.param("examples/meta/changelogs/changelog.yaml", "changelog", id="36"),
pytest.param("examples/inventory/inventory.yml", "inventory", id="37"),
@@ -258,6 +259,12 @@ def test_discover_lintables_umlaut(monkeypatch: MonkeyPatch) -> None:
"playbook",
id="43",
), # content should determine it as a play
+ pytest.param(
+ "plugins/modules/fake_module.py",
+ "plugin",
+ id="44",
+ ),
+ pytest.param("examples/meta/changelogs/changelog.yml", "changelog", id="45"),
),
)
def test_kinds(path: str, kind: FileType) -> None:
diff --git a/test/test_formatter.py b/test/test_formatter.py
index 68f0508..c41f673 100644
--- a/test/test_formatter.py
+++ b/test/test_formatter.py
@@ -1,4 +1,5 @@
"""Test for output formatter."""
+
# Copyright (c) 2016 Will Thames <will@thames.id.au>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -23,10 +24,12 @@ import pathlib
from ansiblelint.errors import MatchError
from ansiblelint.file_utils import Lintable
from ansiblelint.formatters import Formatter
-from ansiblelint.rules import AnsibleLintRule
+from ansiblelint.rules import AnsibleLintRule, RulesCollection
+collection = RulesCollection()
rule = AnsibleLintRule()
rule.id = "TCF0001"
+collection.register(rule)
formatter = Formatter(pathlib.Path.cwd(), display_relative_path=True)
# These details would generate a rich rendering error if not escaped:
DETAILS = "Some [/tmp/foo] details."
diff --git a/test/test_formatter_base.py b/test/test_formatter_base.py
index 5cc86b8..462fc62 100644
--- a/test/test_formatter_base.py
+++ b/test/test_formatter_base.py
@@ -1,4 +1,5 @@
"""Tests related to base formatter."""
+
from __future__ import annotations
from pathlib import Path
@@ -12,12 +13,18 @@ from ansiblelint.formatters import BaseFormatter
@pytest.mark.parametrize(
("base_dir", "relative_path"),
(
- (None, True),
- ("/whatever", False),
- (Path("/whatever"), False),
+ pytest.param(None, True, id="0"),
+ pytest.param("/whatever", False, id="1"),
+ pytest.param(Path("/whatever"), False, id="2"),
+ ),
+)
+@pytest.mark.parametrize(
+ "path",
+ (
+ pytest.param("/whatever/string", id="a"),
+ pytest.param(Path("/whatever/string"), id="b"),
),
)
-@pytest.mark.parametrize("path", ("/whatever/string", Path("/whatever/string")))
def test_base_formatter_when_base_dir(
base_dir: Any,
relative_path: bool,
@@ -28,18 +35,15 @@ def test_base_formatter_when_base_dir(
base_formatter = BaseFormatter(base_dir, relative_path) # type: ignore[var-annotated]
# When
- output_path = (
- base_formatter._format_path( # pylint: disable=protected-access # noqa: SLF001
- path,
- )
+ output_path = base_formatter._format_path( # noqa: SLF001
+ path,
)
# Then
- assert isinstance(output_path, (str, Path))
- # pylint: disable=protected-access
+ assert isinstance(output_path, str | Path)
assert base_formatter.base_dir is None or isinstance(
base_formatter.base_dir,
- (str, Path),
+ str | Path,
)
assert output_path == path
@@ -47,11 +51,17 @@ def test_base_formatter_when_base_dir(
@pytest.mark.parametrize(
"base_dir",
(
- Path("/whatever"),
- "/whatever",
+ pytest.param(Path("/whatever"), id="0"),
+ pytest.param("/whatever", id="1"),
+ ),
+)
+@pytest.mark.parametrize(
+ "path",
+ (
+ pytest.param("/whatever/string", id="a"),
+ pytest.param(Path("/whatever/string"), id="b"),
),
)
-@pytest.mark.parametrize("path", ("/whatever/string", Path("/whatever/string")))
def test_base_formatter_when_base_dir_is_given_and_relative_is_true(
path: str | Path,
base_dir: str | Path,
@@ -61,13 +71,11 @@ def test_base_formatter_when_base_dir_is_given_and_relative_is_true(
base_formatter = BaseFormatter(base_dir, True) # type: ignore[var-annotated]
# When
- # pylint: disable=protected-access
output_path = base_formatter._format_path(path) # noqa: SLF001
# Then
- assert isinstance(output_path, (str, Path))
- # pylint: disable=protected-access
- assert isinstance(base_formatter.base_dir, (str, Path))
+ assert isinstance(output_path, str | Path)
+ assert isinstance(base_formatter.base_dir, str | Path)
assert output_path == Path(path).name
diff --git a/test/test_formatter_json.py b/test/test_formatter_json.py
index 25aa5f5..763c843 100644
--- a/test/test_formatter_json.py
+++ b/test/test_formatter_json.py
@@ -1,4 +1,5 @@
"""Test the codeclimate JSON formatter."""
+
from __future__ import annotations
import json
@@ -11,7 +12,7 @@ import pytest
from ansiblelint.errors import MatchError
from ansiblelint.file_utils import Lintable
from ansiblelint.formatters import CodeclimateJSONFormatter
-from ansiblelint.rules import AnsibleLintRule
+from ansiblelint.rules import AnsibleLintRule, RulesCollection
class TestCodeclimateJSONFormatter:
@@ -20,12 +21,14 @@ class TestCodeclimateJSONFormatter:
rule = AnsibleLintRule()
matches: list[MatchError] = []
formatter: CodeclimateJSONFormatter | None = None
+ collection = RulesCollection()
def setup_class(self) -> None:
"""Set up few MatchError objects."""
self.rule = AnsibleLintRule()
self.rule.id = "TCF0001"
self.rule.severity = "VERY_HIGH"
+ self.collection.register(self.rule)
self.matches = []
self.matches.append(
MatchError(
@@ -51,7 +54,7 @@ class TestCodeclimateJSONFormatter:
display_relative_path=True,
)
- def test_format_list(self) -> None:
+ def test_json_format_list(self) -> None:
"""Test if the return value is a string."""
assert isinstance(self.formatter, CodeclimateJSONFormatter)
assert isinstance(self.formatter.format_result(self.matches), str)
@@ -64,10 +67,10 @@ class TestCodeclimateJSONFormatter:
# https://github.com/ansible/ansible-navigator/issues/1490
assert "\n" not in output
- def test_single_match(self) -> None:
+ def test_json_single_match(self) -> None:
"""Test negative case. Only lists are allowed. Otherwise a RuntimeError will be raised."""
assert isinstance(self.formatter, CodeclimateJSONFormatter)
- with pytest.raises(RuntimeError):
+ with pytest.raises(TypeError):
self.formatter.format_result(self.matches[0]) # type: ignore[arg-type]
def test_result_is_list(self) -> None:
diff --git a/test/test_formatter_sarif.py b/test/test_formatter_sarif.py
index 026d336..982bb6e 100644
--- a/test/test_formatter_sarif.py
+++ b/test/test_formatter_sarif.py
@@ -1,4 +1,5 @@
"""Test the codeclimate JSON formatter."""
+
from __future__ import annotations
import json
@@ -13,54 +14,75 @@ import pytest
from ansiblelint.errors import MatchError
from ansiblelint.file_utils import Lintable
from ansiblelint.formatters import SarifFormatter
-from ansiblelint.rules import AnsibleLintRule
+from ansiblelint.rules import AnsibleLintRule, RulesCollection
class TestSarifFormatter:
"""Unit test for SarifFormatter."""
- rule = AnsibleLintRule()
+ rule1 = AnsibleLintRule()
+ rule2 = AnsibleLintRule()
matches: list[MatchError] = []
formatter: SarifFormatter | None = None
+ collection = RulesCollection()
+ collection.register(rule1)
+ collection.register(rule2)
def setup_class(self) -> None:
"""Set up few MatchError objects."""
- self.rule = AnsibleLintRule()
- self.rule.id = "TCF0001"
- self.rule.severity = "VERY_HIGH"
- self.rule.description = "This is the rule description."
- self.rule.link = "https://rules/help#TCF0001"
- self.rule.tags = ["tag1", "tag2"]
- self.matches = []
- self.matches.append(
- MatchError(
- message="message",
- lineno=1,
- column=10,
- details="details",
- lintable=Lintable("filename.yml", content=""),
- rule=self.rule,
- tag="yaml[test]",
- ),
- )
- self.matches.append(
- MatchError(
- message="message",
- lineno=2,
- details="",
- lintable=Lintable("filename.yml", content=""),
- rule=self.rule,
- tag="yaml[test]",
- ),
+ self.rule1.id = "TCF0001"
+ self.rule1.severity = "VERY_HIGH"
+ self.rule1.description = "This is the rule description."
+ self.rule1.link = "https://rules/help#TCF0001"
+ self.rule1.tags = ["tag1", "tag2"]
+
+ self.rule2.id = "TCF0002"
+ self.rule2.severity = "MEDIUM"
+ self.rule2.link = "https://rules/help#TCF0002"
+ self.rule2.tags = ["tag3", "tag4"]
+
+ self.matches.extend(
+ [
+ MatchError(
+ message="message1",
+ lineno=1,
+ column=10,
+ details="details1",
+ lintable=Lintable("filename1.yml", content=""),
+ rule=self.rule1,
+ tag="yaml[test1]",
+ ignored=False,
+ ),
+ MatchError(
+ message="message2",
+ lineno=2,
+ details="",
+ lintable=Lintable("filename2.yml", content=""),
+ rule=self.rule1,
+ tag="yaml[test2]",
+ ignored=True,
+ ),
+ MatchError(
+ message="message3",
+ lineno=666,
+ column=667,
+ details="details3",
+ lintable=Lintable("filename3.yml", content=""),
+ rule=self.rule2,
+ tag="yaml[test3]",
+ ignored=False,
+ ),
+ ],
)
+
self.formatter = SarifFormatter(pathlib.Path.cwd(), display_relative_path=True)
- def test_format_list(self) -> None:
+ def test_sarif_format_list(self) -> None:
"""Test if the return value is a string."""
assert isinstance(self.formatter, SarifFormatter)
assert isinstance(self.formatter.format_result(self.matches), str)
- def test_result_is_json(self) -> None:
+ def test_sarif_result_is_json(self) -> None:
"""Test if returned string value is a JSON."""
assert isinstance(self.formatter, SarifFormatter)
output = self.formatter.format_result(self.matches)
@@ -68,17 +90,22 @@ class TestSarifFormatter:
# https://github.com/ansible/ansible-navigator/issues/1490
assert "\n" not in output
- def test_single_match(self) -> None:
+ def test_sarif_single_match(self) -> None:
"""Test negative case. Only lists are allowed. Otherwise, a RuntimeError will be raised."""
assert isinstance(self.formatter, SarifFormatter)
- with pytest.raises(RuntimeError):
+ with pytest.raises(TypeError):
self.formatter.format_result(self.matches[0]) # type: ignore[arg-type]
- def test_result_is_list(self) -> None:
- """Test if the return SARIF object contains the results with length of 2."""
+ def test_sarif_format(self) -> None:
+ """Test if the return SARIF object contains the expected results."""
assert isinstance(self.formatter, SarifFormatter)
sarif = json.loads(self.formatter.format_result(self.matches))
- assert len(sarif["runs"][0]["results"]) == 2
+ assert len(sarif["runs"][0]["results"]) == 3
+ for result in sarif["runs"][0]["results"]:
+ # Ensure all reported entries have a level
+ assert "level" in result
+ # Ensure reported levels are either error or warning
+ assert result["level"] in ("error", "warning")
def test_validate_sarif_schema(self) -> None:
"""Test if the returned JSON is a valid SARIF report."""
@@ -90,16 +117,18 @@ class TestSarifFormatter:
assert driver["name"] == SarifFormatter.TOOL_NAME
assert driver["informationUri"] == SarifFormatter.TOOL_URL
rules = driver["rules"]
- assert len(rules) == 1
+ assert len(rules) == 3
assert rules[0]["id"] == self.matches[0].tag
assert rules[0]["name"] == self.matches[0].tag
assert rules[0]["shortDescription"]["text"] == self.matches[0].message
- assert rules[0]["defaultConfiguration"]["level"] == "error"
+ assert rules[0]["defaultConfiguration"][
+ "level"
+ ] == SarifFormatter.get_sarif_rule_severity_level(self.matches[0].rule)
assert rules[0]["help"]["text"] == self.matches[0].rule.description
assert rules[0]["properties"]["tags"] == self.matches[0].rule.tags
assert rules[0]["helpUri"] == self.matches[0].rule.url
results = sarif["runs"][0]["results"]
- assert len(results) == 2
+ assert len(results) == 3
for i, result in enumerate(results):
assert result["ruleId"] == self.matches[i].tag
assert (
@@ -126,6 +155,9 @@ class TestSarifFormatter:
"startColumn"
not in result["locations"][0]["physicalLocation"]["region"]
)
+ assert result["level"] == SarifFormatter.get_sarif_result_severity_level(
+ self.matches[i],
+ )
assert sarif["runs"][0]["originalUriBaseIds"][SarifFormatter.BASE_URI_ID]["uri"]
assert results[0]["message"]["text"] == self.matches[0].details
assert results[1]["message"]["text"] == self.matches[1].message
@@ -151,8 +183,8 @@ def test_sarif_parsable_ignored() -> None:
@pytest.mark.parametrize(
("file", "return_code"),
(
- pytest.param("examples/playbooks/valid.yml", 0),
- pytest.param("playbook.yml", 2),
+ pytest.param("examples/playbooks/valid.yml", 0, id="0"),
+ pytest.param("playbook.yml", 2, id="1"),
),
)
def test_sarif_file(file: str, return_code: int) -> None:
@@ -168,12 +200,12 @@ def test_sarif_file(file: str, return_code: int) -> None:
result = subprocess.run([*cmd, file], check=False, capture_output=True)
assert result.returncode == return_code
assert os.path.exists(output_file.name) # noqa: PTH110
- assert os.path.getsize(output_file.name) > 0
+ assert pathlib.Path(output_file.name).stat().st_size > 0
@pytest.mark.parametrize(
("file", "return_code"),
- (pytest.param("examples/playbooks/valid.yml", 0),),
+ (pytest.param("examples/playbooks/valid.yml", 0, id="0"),),
)
def test_sarif_file_creates_it_if_none_exists(file: str, return_code: int) -> None:
"""Test ability to create sarif file if none exists and dump output to it (--sarif-file)."""
@@ -188,5 +220,5 @@ def test_sarif_file_creates_it_if_none_exists(file: str, return_code: int) -> No
result = subprocess.run([*cmd, file], check=False, capture_output=True)
assert result.returncode == return_code
assert os.path.exists(sarif_file_name) # noqa: PTH110
- assert os.path.getsize(sarif_file_name) > 0
+ assert pathlib.Path(sarif_file_name).stat().st_size > 0
pathlib.Path.unlink(pathlib.Path(sarif_file_name))
diff --git a/test/test_import_include_role.py b/test/test_import_include_role.py
index bc3fdbe..b221646 100644
--- a/test/test_import_include_role.py
+++ b/test/test_import_include_role.py
@@ -1,4 +1,5 @@
"""Tests related to role imports."""
+
from __future__ import annotations
from typing import TYPE_CHECKING
diff --git a/test/test_import_playbook.py b/test/test_import_playbook.py
index 66d8763..63c91d2 100644
--- a/test/test_import_playbook.py
+++ b/test/test_import_playbook.py
@@ -1,4 +1,5 @@
"""Test ability to import playbooks."""
+
from ansiblelint.rules import RulesCollection
from ansiblelint.runner import Runner
@@ -16,3 +17,29 @@ def test_task_hook_import_playbook(default_rules_collection: RulesCollection) ->
assert "Commands should not change things" in results_text
assert "[name]" in results_text
assert "All tasks should be named" in results_text
+
+
+def test_import_playbook_from_collection(
+ default_rules_collection: RulesCollection,
+) -> None:
+ """Assures import_playbook from collection."""
+ playbook_path = "examples/playbooks/test_import_playbook.yml"
+ runner = Runner(playbook_path, rules=default_rules_collection)
+ results = runner.run()
+
+ assert len(runner.lintables) == 1
+ assert len(results) == 0
+
+
+def test_import_playbook_invalid(
+ default_rules_collection: RulesCollection,
+) -> None:
+ """Assures import_playbook from collection."""
+ playbook_path = "examples/playbooks/test_import_playbook_invalid.yml"
+ runner = Runner(playbook_path, rules=default_rules_collection)
+ results = runner.run()
+
+ assert len(runner.lintables) == 1
+ assert len(results) == 1
+ assert results[0].tag == "syntax-check[specific]"
+ assert results[0].lineno == 2
diff --git a/test/test_import_tasks.py b/test/test_import_tasks.py
index aec1c25..ceb5c28 100644
--- a/test/test_import_tasks.py
+++ b/test/test_import_tasks.py
@@ -1,4 +1,5 @@
"""Test related to import of invalid files."""
+
import pytest
from ansiblelint.rules import RulesCollection
@@ -6,24 +7,28 @@ from ansiblelint.runner import Runner
@pytest.mark.parametrize(
- "playbook_path",
+ ("playbook_path", "lintable_count", "match_count"),
(
pytest.param(
"examples/playbooks/test_import_with_conflicting_action_statements.yml",
+ 2,
+ 4,
id="0",
),
- pytest.param("examples/playbooks/test_import_with_malformed.yml", id="1"),
+ pytest.param("examples/playbooks/test_import_with_malformed.yml", 2, 2, id="1"),
),
)
def test_import_tasks(
default_rules_collection: RulesCollection,
playbook_path: str,
+ lintable_count: int,
+ match_count: int,
) -> None:
"""Assures import_playbook includes are recognized."""
runner = Runner(playbook_path, rules=default_rules_collection)
results = runner.run()
- assert len(runner.lintables) == 1
- assert len(results) == 1
+ assert len(runner.lintables) == lintable_count
+ assert len(results) == match_count
# Assures we detected the issues from imported file
- assert results[0].rule.id == "syntax-check"
+ assert results[0].rule.id in ("syntax-check", "load-failure")
diff --git a/test/test_include_miss_file_with_role.py b/test/test_include_miss_file_with_role.py
index 6834758..599928e 100644
--- a/test/test_include_miss_file_with_role.py
+++ b/test/test_include_miss_file_with_role.py
@@ -1,4 +1,5 @@
"""Tests related to inclusions."""
+
import pytest
from _pytest.logging import LogCaptureFixture
diff --git a/test/test_internal_rules.py b/test/test_internal_rules.py
index b949238..e1cc69e 100644
--- a/test/test_internal_rules.py
+++ b/test/test_internal_rules.py
@@ -1,8 +1,35 @@
"""Tests for internal rules."""
+
+import pytest
+
from ansiblelint._internal.rules import BaseRule
+from ansiblelint.rules import RulesCollection
+from ansiblelint.runner import Runner
def test_base_rule_url() -> None:
"""Test that rule URL is set to expected value."""
rule = BaseRule()
- assert rule.url == "https://ansible-lint.readthedocs.io/rules/"
+ assert rule.url == "https://ansible.readthedocs.io/projects/lint/rules/"
+
+
+@pytest.mark.parametrize(
+ ("path"),
+ (
+ pytest.param(
+ "examples/playbooks/incorrect_module_args.yml",
+ id="playbook",
+ ),
+ ),
+)
+def test_incorrect_module_args(
+ path: str,
+ default_rules_collection: RulesCollection,
+) -> None:
+ """Check that we fail when file encoding is wrong."""
+ runner = Runner(path, rules=default_rules_collection)
+ matches = runner.run()
+ assert len(matches) == 1, matches
+ assert matches[0].rule.id == "load-failure"
+ assert "Failed to find required 'name' key in include_role" in matches[0].message
+ assert matches[0].tag == "internal-error"
diff --git a/test/test_lint_rule.py b/test/test_lint_rule.py
index 2e13aa2..d0edbe9 100644
--- a/test/test_lint_rule.py
+++ b/test/test_lint_rule.py
@@ -1,4 +1,5 @@
"""Tests for lintable."""
+
# Copyright (c) 2013-2014 Will Thames <will@thames.id.au>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -18,13 +19,12 @@
# 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 test.rules.fixtures import ematcher, raw_task
-
import pytest
from ansiblelint.file_utils import Lintable
+from .rules.fixtures import ematcher, raw_task
+
@pytest.fixture(name="lintable")
def fixture_lintable() -> Lintable:
diff --git a/test/test_list_rules.py b/test/test_list_rules.py
index dab16e3..85ef53f 100644
--- a/test/test_list_rules.py
+++ b/test/test_list_rules.py
@@ -18,6 +18,13 @@ def test_list_rules_includes_opt_in_rules(project_path: Path) -> None:
assert ("opt-in" in result_list_rules.stdout) is True
+def test_list_rules_includes_autofix() -> None:
+ """Checks that listing rules also includes the autofix label for applicable rules."""
+ result_list_rules = run_ansible_lint("--list-rules")
+
+ assert ("autofix" in result_list_rules.stdout) is True
+
+
@pytest.mark.parametrize(
("result", "returncode", "format_string"),
(
diff --git a/test/test_load_failure.py b/test/test_load_failure.py
index 98d178f..72112d6 100644
--- a/test/test_load_failure.py
+++ b/test/test_load_failure.py
@@ -1,4 +1,5 @@
"""Tests for LoadFailureRule."""
+
import pytest
from ansiblelint.rules import RulesCollection
diff --git a/test/test_loaders.py b/test/test_loaders.py
index be12cfd..6e8d66b 100644
--- a/test/test_loaders.py
+++ b/test/test_loaders.py
@@ -1,4 +1,5 @@
"""Tests for loaders submodule."""
+
import os
import tempfile
import uuid
@@ -31,7 +32,7 @@ def test_load_ignore_txt_default_success() -> None:
_ignore_file.write(
dedent(
"""
- # See https://ansible-lint.readthedocs.io/configuring/#ignoring-rules-for-entire-files
+ # See https://ansible.readthedocs.io/projects/lint/configuring/#ignoring-rules-for-entire-files
playbook2.yml package-latest # comment
playbook2.yml foo-bar
""",
diff --git a/test/test_local_content.py b/test/test_local_content.py
index 8455aaf..63472c2 100644
--- a/test/test_local_content.py
+++ b/test/test_local_content.py
@@ -1,4 +1,5 @@
"""Test playbooks with local content."""
+
from ansiblelint.rules import RulesCollection
from ansiblelint.runner import Runner
diff --git a/test/test_main.py b/test/test_main.py
index 870926f..e7258ee 100644
--- a/test/test_main.py
+++ b/test/test_main.py
@@ -1,4 +1,5 @@
"""Tests related to ansiblelint.__main__ module."""
+
import os
import shutil
import subprocess
@@ -10,6 +11,7 @@ import pytest
from pytest_mock import MockerFixture
from ansiblelint.config import get_version_warning
+from ansiblelint.constants import RC
@pytest.mark.parametrize(
@@ -52,9 +54,9 @@ def test_call_from_outside_venv(expected_warning: bool) -> None:
@pytest.mark.parametrize(
("ver_diff", "found", "check", "outlen"),
(
- ("v1.2.2", True, "pre-release", 1),
- ("v1.2.3", False, "", 1),
- ("v1.2.4", True, "new release", 2),
+ pytest.param("v1.2.2", True, "pre-release", 1, id="0"),
+ pytest.param("v1.2.3", False, "", 1, id="1"),
+ pytest.param("v1.2.4", True, "new release", 2, id="2"),
),
)
def test_get_version_warning(
@@ -82,3 +84,48 @@ def test_get_version_warning(
else:
assert check in msg
assert len(msg.split("\n")) == outlen
+
+
+def test_get_version_warning_no_pip(mocker: MockerFixture) -> None:
+ """Test that we do not display any message if install method is not pip."""
+ mocker.patch("ansiblelint.config.guess_install_method", return_value="")
+ assert get_version_warning() == ""
+
+
+@pytest.mark.parametrize(
+ ("lintable"),
+ (
+ pytest.param("examples/playbooks/nodeps.yml", id="1"),
+ pytest.param("examples/playbooks/nodeps2.yml", id="2"),
+ ),
+)
+def test_nodeps(lintable: str) -> None:
+ """Asserts ability to be called w/ or w/o venv activation."""
+ env = os.environ.copy()
+ env["ANSIBLE_LINT_NODEPS"] = "1"
+ py_path = Path(sys.executable).parent
+ proc = subprocess.run(
+ [str(py_path / "ansible-lint"), lintable],
+ check=False,
+ capture_output=True,
+ text=True,
+ env=env,
+ )
+ assert proc.returncode == 0, proc
+
+
+def test_broken_ansible_cfg() -> None:
+ """Asserts behavior when encountering broken ansible.cfg files."""
+ py_path = Path(sys.executable).parent
+ proc = subprocess.run(
+ [str(py_path / "ansible-lint"), "--version"],
+ check=False,
+ capture_output=True,
+ text=True,
+ cwd="test/fixtures/broken-ansible.cfg",
+ )
+ assert proc.returncode == RC.INVALID_CONFIG, proc
+ assert (
+ "Invalid type for configuration option setting: CACHE_PLUGIN_TIMEOUT"
+ in proc.stderr
+ )
diff --git a/test/test_matcherrror.py b/test/test_matcherrror.py
index 03d9cbd..5b67e23 100644
--- a/test/test_matcherrror.py
+++ b/test/test_matcherrror.py
@@ -1,7 +1,8 @@
"""Tests for MatchError."""
import operator
-from typing import Any, Callable
+from collections.abc import Callable
+from typing import Any
import pytest
@@ -121,18 +122,18 @@ class TestMatchErrorCompare:
@pytest.mark.parametrize(
"other",
(
- None,
- "foo",
- 42,
- Exception("foo"),
+ pytest.param(None, id="none"),
+ pytest.param("foo", id="str"),
+ pytest.param(42, id="int"),
+ pytest.param(Exception("foo"), id="exc"),
),
ids=repr,
)
@pytest.mark.parametrize(
("operation", "operator_char"),
(
- pytest.param(operator.le, "<=", id="<="),
- pytest.param(operator.gt, ">", id=">"),
+ pytest.param(operator.le, "<=", id="le"),
+ pytest.param(operator.gt, ">", id="gt"),
),
)
def test_matcherror_compare_no_other_fallback(
@@ -143,12 +144,9 @@ def test_matcherror_compare_no_other_fallback(
"""Check that MatchError comparison with other types causes TypeError."""
expected_error = (
r"^("
- r"unsupported operand type\(s\) for {operator!s}:|"
- r"'{operator!s}' not supported between instances of"
- r") 'MatchError' and '{other_type!s}'$".format(
- other_type=type(other).__name__,
- operator=operator_char,
- )
+ rf"unsupported operand type\(s\) for {operator_char!s}:|"
+ rf"'{operator_char!s}' not supported between instances of"
+ rf") 'MatchError' and '{type(other).__name__!s}'$"
)
with pytest.raises(TypeError, match=expected_error):
operation(MatchError("foo"), other)
@@ -157,21 +155,20 @@ def test_matcherror_compare_no_other_fallback(
@pytest.mark.parametrize(
"other",
(
- None,
- "foo",
- 42,
- Exception("foo"),
- DummyTestObject(),
+ pytest.param(None, id="none"),
+ pytest.param("foo", id="str"),
+ pytest.param(42, id="int"),
+ pytest.param(Exception("foo"), id="exception"),
+ pytest.param(DummyTestObject(), id="obj"),
),
ids=repr,
)
@pytest.mark.parametrize(
("operation", "expected_value"),
(
- (operator.eq, False),
- (operator.ne, True),
+ pytest.param(operator.eq, False, id="eq"),
+ pytest.param(operator.ne, True, id="ne"),
),
- ids=("==", "!="),
)
def test_matcherror_compare_with_other_fallback(
other: object,
@@ -185,16 +182,15 @@ def test_matcherror_compare_with_other_fallback(
@pytest.mark.parametrize(
("operation", "expected_value"),
(
- (operator.eq, "EQ_SENTINEL"),
- (operator.ne, "NE_SENTINEL"),
+ pytest.param(operator.eq, "EQ_SENTINEL", id="eq"),
+ pytest.param(operator.ne, "NE_SENTINEL", id="ne"),
# NOTE: these are swapped because when we do `x < y`, and `x.__lt__(y)`
# NOTE: returns `NotImplemented`, Python will reverse the check into
# NOTE: `y > x`, and so `y.__gt__(x) is called.
# Ref: https://docs.python.org/3/reference/datamodel.html#object.__lt__
- (operator.lt, "GT_SENTINEL"),
- (operator.gt, "LT_SENTINEL"),
+ pytest.param(operator.lt, "GT_SENTINEL", id="gt"),
+ pytest.param(operator.gt, "LT_SENTINEL", id="lt"),
),
- ids=("==", "!=", "<", ">"),
)
def test_matcherror_compare_with_dummy_sentinel(
operation: Callable[..., bool],
diff --git a/test/test_mockings.py b/test/test_mockings.py
index 0e8d77a..417d5d5 100644
--- a/test/test_mockings.py
+++ b/test/test_mockings.py
@@ -1,18 +1,18 @@
"""Test mockings module."""
-from typing import Any
+
+from pathlib import Path
import pytest
from ansiblelint._mockings import _make_module_stub
-from ansiblelint.config import options
+from ansiblelint.config import Options
from ansiblelint.constants import RC
-def test_make_module_stub(mocker: Any) -> None:
+def test_make_module_stub(config_options: Options) -> None:
"""Test make module stub."""
- mocker.patch("ansiblelint.config.options.cache_dir", return_value=".")
- assert options.cache_dir is not None
+ config_options.cache_dir = Path() # current directory
with pytest.raises(SystemExit) as exc:
- _make_module_stub(module_name="", options=options)
+ _make_module_stub(module_name="", options=config_options)
assert exc.type == SystemExit
assert exc.value.code == RC.INVALID_CONFIG
diff --git a/test/test_profiles.py b/test/test_profiles.py
index a40382c..a1d9865 100644
--- a/test/test_profiles.py
+++ b/test/test_profiles.py
@@ -1,4 +1,5 @@
"""Tests for the --profile feature."""
+
import platform
import subprocess
import sys
@@ -21,7 +22,7 @@ def test_profile_min() -> None:
filter_rules_with_profile(collection.rules, "min")
assert (
- len(collection.rules) == 3
+ len(collection.rules) == 4
), "Failed to unload rule that is not part of 'min' profile."
diff --git a/test/test_requirements.py b/test/test_requirements.py
new file mode 100644
index 0000000..0703d34
--- /dev/null
+++ b/test/test_requirements.py
@@ -0,0 +1,18 @@
+"""Tests requirements module."""
+
+from ansible_compat.runtime import Runtime
+
+from ansiblelint.requirements import Reqs
+
+
+def test_reqs() -> None:
+ """Performs basic testing of Reqs class."""
+ reqs = Reqs()
+ runtime = Runtime()
+ assert "ansible-core" in reqs
+ # checks that this ansible core version is not supported:
+ assert reqs.matches("ansible-core", "0.0") is False
+ # assert that invalid package name
+ assert reqs.matches("this-package-does-not-exist", "0.0") is False
+ # check the current ansible core version is supported:
+ assert reqs.matches("ansible-core", runtime.version)
diff --git a/test/test_rule_properties.py b/test/test_rule_properties.py
index 7db3afd..3e5eb3e 100644
--- a/test/test_rule_properties.py
+++ b/test/test_rule_properties.py
@@ -1,4 +1,5 @@
"""Tests related to rule properties."""
+
from ansiblelint.rules import RulesCollection
diff --git a/test/test_rules_collection.py b/test/test_rules_collection.py
index 66c69ec..44317fe 100644
--- a/test/test_rules_collection.py
+++ b/test/test_rules_collection.py
@@ -1,4 +1,5 @@
"""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
@@ -23,14 +24,17 @@ from __future__ import annotations
import collections
import re
from pathlib import Path
+from typing import TYPE_CHECKING
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
+if TYPE_CHECKING:
+ from ansiblelint.config import Options
+
@pytest.fixture(name="test_rules_collection")
def fixture_test_rules_collection() -> RulesCollection:
@@ -153,12 +157,12 @@ def test_rich_rule_listing() -> None:
assert rule.description[:30] in result.stdout
-def test_rules_id_format() -> None:
+def test_rules_id_format(config_options: Options) -> 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,
+ options=config_options,
conditional=False,
)
keys: set[str] = set()
@@ -171,5 +175,5 @@ def test_rules_id_format() -> None:
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(rules) == 50 # update this number when adding new rules!
assert len(keys) == len(rules), "Duplicate rule ids?"
diff --git a/test/test_runner.py b/test/test_runner.py
index e89cee1..aa76b65 100644
--- a/test/test_runner.py
+++ b/test/test_runner.py
@@ -1,4 +1,5 @@
"""Tests for runner submodule."""
+
# Copyright (c) 2013-2014 Will Thames <will@thames.id.au>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -48,7 +49,7 @@ LOTS_OF_WARNINGS_PLAYBOOK = Path("examples/playbooks/lots_of_warnings.yml").reso
pytest.param(
LOTS_OF_WARNINGS_PLAYBOOK,
[LOTS_OF_WARNINGS_PLAYBOOK],
- 992,
+ 993,
id="lots_of_warnings",
),
pytest.param(Path("examples/playbooks/become.yml"), [], 0, id="become"),
@@ -86,21 +87,23 @@ def test_runner_exclude_paths(default_rules_collection: RulesCollection) -> None
assert len(matches) == 0
-@pytest.mark.parametrize(("exclude_path"), ("**/playbooks/*.yml",))
+@pytest.mark.parametrize(
+ ("exclude_path"),
+ (pytest.param("**/playbooks_globs/*b.yml", id="1"),),
+)
def test_runner_exclude_globs(
default_rules_collection: RulesCollection,
exclude_path: str,
) -> None:
"""Test that globs work."""
runner = Runner(
- "examples/playbooks",
+ "examples/playbooks_globs",
rules=default_rules_collection,
exclude_paths=[exclude_path],
)
matches = runner.run()
- # we expect to find one match from the very few .yaml file we have there (most of them have .yml extension)
- assert len(matches) == 1
+ assert len(matches) == 0
@pytest.mark.parametrize(
@@ -175,6 +178,52 @@ def test_files_not_scanned_twice(default_rules_collection: RulesCollection) -> N
assert len(run2) == 0
+@pytest.mark.parametrize(
+ ("filename", "failures", "checked_files_no"),
+ (
+ pytest.param(
+ "examples/playbooks/common-include-wrong-syntax.yml",
+ 1,
+ 1,
+ id="1",
+ ),
+ pytest.param(
+ "examples/playbooks/common-include-wrong-syntax2.yml",
+ 1,
+ 1,
+ id="2",
+ ),
+ pytest.param(
+ "examples/playbooks/common-include-wrong-syntax3.yml",
+ 0,
+ 2,
+ id="3",
+ ),
+ ),
+)
+def test_include_wrong_syntax(
+ filename: str,
+ failures: int,
+ checked_files_no: int,
+ default_rules_collection: RulesCollection,
+) -> None:
+ """Ensure that lintables aren't double-checked."""
+ checked_files: set[Lintable] = set()
+
+ path = Path(filename).resolve()
+ runner = Runner(
+ path,
+ rules=default_rules_collection,
+ verbosity=0,
+ checked_files=checked_files,
+ )
+ result = runner.run()
+ assert len(runner.checked_files) == checked_files_no
+ assert len(result) == failures, result
+ for item in result:
+ assert item.tag == "syntax-check[no-file]"
+
+
def test_runner_not_found(default_rules_collection: RulesCollection) -> None:
"""Ensure that lintables aren't double-checked."""
checked_files: set[Lintable] = set()
@@ -208,3 +257,16 @@ def test_runner_tmp_file(
result = runner.run()
assert len(result) == 1
assert result[0].tag == "syntax-check[empty-playbook]"
+
+
+def test_with_full_path(default_rules_collection: RulesCollection) -> None:
+ """Ensure that lintables include file path starting from home directory."""
+ filename = Path("examples/playbooks/deep").absolute()
+ runner = Runner(
+ filename,
+ rules=default_rules_collection,
+ verbosity=0,
+ )
+ result = runner.run()
+ assert len(result) == 1
+ assert result[0].tag == "name[casing]"
diff --git a/test/test_schemas.py b/test/test_schemas.py
index 6392241..646a283 100644
--- a/test/test_schemas.py
+++ b/test/test_schemas.py
@@ -1,16 +1,18 @@
"""Test schemas modules."""
+
import json
import logging
+import os
import subprocess
import sys
import urllib
+import warnings
from pathlib import Path
-from time import sleep
from typing import Any
from unittest.mock import DEFAULT, MagicMock, patch
+import license_expression
import pytest
-import spdx.config
from ansiblelint.file_utils import Lintable
from ansiblelint.schemas import __file__ as schema_module
@@ -18,21 +20,9 @@ from ansiblelint.schemas.__main__ import refresh_schemas
from ansiblelint.schemas.main import validate_file_schema
schema_path = Path(schema_module).parent
-spdx_config_path = Path(spdx.config.__file__).parent
-
-
-def test_refresh_schemas() -> None:
- """Test for schema update skip."""
- # This is written as a single test in order to avoid concurrency issues,
- # which caused random issues on CI when the two tests run in parallel
- # and or in different order.
- assert refresh_schemas(min_age_seconds=3600 * 24 * 365 * 10) == 0
- sleep(1)
- # this should disable the cache and force an update
- assert refresh_schemas(min_age_seconds=0) == 1
- sleep(1)
- # should be cached now
- assert refresh_schemas(min_age_seconds=10) == 0
+spdx_config_path = (
+ Path(license_expression.__file__).parent / "data" / "scancode-licensedb-index.json"
+)
def urlopen_side_effect(*_args: Any, **kwargs: Any) -> DEFAULT:
@@ -59,7 +49,7 @@ def test_request_timeouterror_handling(
error_msg = "Simulating handshake operation time out."
mock_request.urlopen.side_effect = urllib.error.URLError(TimeoutError(error_msg))
with caplog.at_level(logging.DEBUG):
- assert refresh_schemas(min_age_seconds=0) == 1
+ assert refresh_schemas(min_age_seconds=0) == 0
mock_request.urlopen.assert_called()
assert "Skipped schema refresh due to unexpected exception: " in caplog.text
assert error_msg in caplog.text
@@ -73,7 +63,7 @@ def test_schema_refresh_cli() -> None:
capture_output=True,
text=True,
)
- assert proc.returncode == 0
+ assert proc.returncode == 0, proc
def test_validate_file_schema() -> None:
@@ -86,24 +76,33 @@ def test_validate_file_schema() -> None:
def test_spdx() -> None:
"""Test that SPDX license identifiers are in sync."""
- _licenses = spdx_config_path / "licenses.json"
-
license_ids = set()
- with _licenses.open(encoding="utf-8") as license_fh:
+ with spdx_config_path.open(encoding="utf-8") as license_fh:
licenses = json.load(license_fh)
- for lic in licenses["licenses"]:
- if lic.get("isDeprecatedLicenseId"):
+ for lic in licenses:
+ if lic.get("is_deprecated"):
+ continue
+ lic_id = lic["spdx_license_key"]
+ if lic_id.startswith("LicenseRef"):
continue
- license_ids.add(lic["licenseId"])
+ license_ids.add(lic_id)
galaxy_json = schema_path / "galaxy.json"
with galaxy_json.open(encoding="utf-8") as f:
schema = json.load(f)
spx_enum = schema["$defs"]["SPDXLicenseEnum"]["enum"]
if set(spx_enum) != license_ids:
- with galaxy_json.open("w", encoding="utf-8") as f:
- schema["$defs"]["SPDXLicenseEnum"]["enum"] = sorted(license_ids)
- json.dump(schema, f, indent=2)
- pytest.fail(
- "SPDX license list inside galaxy.json JSON Schema file was updated.",
- )
+ # In absence of a
+ if os.environ.get("PIP_CONSTRAINT", "/dev/null") == "/dev/null":
+ with galaxy_json.open("w", encoding="utf-8") as f:
+ schema["$defs"]["SPDXLicenseEnum"]["enum"] = sorted(license_ids)
+ json.dump(schema, f, indent=2)
+ pytest.fail(
+ "SPDX license list inside galaxy.json JSON Schema file was updated.",
+ )
+ else:
+ warnings.warn(
+ "test_spdx failure was ignored because constraints were not pinned (PIP_CONSTRAINTS). This is expected for py310 and py-devel jobs.",
+ category=pytest.PytestWarning,
+ stacklevel=1,
+ )
diff --git a/test/test_skip_import_playbook.py b/test/test_skip_import_playbook.py
index 777fec6..8674c16 100644
--- a/test/test_skip_import_playbook.py
+++ b/test/test_skip_import_playbook.py
@@ -1,4 +1,5 @@
"""Test related to skipping import_playbook."""
+
from pathlib import Path
import pytest
diff --git a/test/test_skip_inside_yaml.py b/test/test_skip_inside_yaml.py
index 363734e..8050f13 100644
--- a/test/test_skip_inside_yaml.py
+++ b/test/test_skip_inside_yaml.py
@@ -1,4 +1,5 @@
"""Tests related to use of inline noqa."""
+
import pytest
from ansiblelint.rules import RulesCollection
diff --git a/test/test_skip_playbook_items.py b/test/test_skip_playbook_items.py
index 2861c6a..2fc05ea 100644
--- a/test/test_skip_playbook_items.py
+++ b/test/test_skip_playbook_items.py
@@ -1,4 +1,5 @@
"""Tests related to use of noqa inside playbooks."""
+
import pytest
from ansiblelint.testing import RunFromText
diff --git a/test/test_skiputils.py b/test/test_skiputils.py
index 7e736e7..2975945 100644
--- a/test/test_skiputils.py
+++ b/test/test_skiputils.py
@@ -1,4 +1,5 @@
"""Validate ansiblelint.skip_utils."""
+
from __future__ import annotations
from pathlib import Path
@@ -39,8 +40,8 @@ PLAYBOOK_WITH_NOQA = """\
@pytest.mark.parametrize(
("line", "expected"),
(
- ("foo # noqa: bar", "bar"),
- ("foo # noqa bar", "bar"),
+ pytest.param("foo # noqa: bar", "bar", id="0"),
+ pytest.param("foo # noqa bar", "bar", id="1"),
),
)
def test_get_rule_skips_from_line(line: str, expected: str) -> None:
diff --git a/test/test_strict.py b/test/test_strict.py
index ba93d7c..5994ffd 100644
--- a/test/test_strict.py
+++ b/test/test_strict.py
@@ -1,4 +1,5 @@
"""Test strict mode."""
+
import os
import pytest
diff --git a/test/test_task_includes.py b/test/test_task_includes.py
index 3b02d00..80b5856 100644
--- a/test/test_task_includes.py
+++ b/test/test_task_includes.py
@@ -1,4 +1,5 @@
"""Tests related to task inclusions."""
+
import pytest
from ansiblelint.file_utils import Lintable
@@ -9,7 +10,12 @@ from ansiblelint.runner import Runner
@pytest.mark.parametrize(
("filename", "file_count", "match_count"),
(
- pytest.param("examples/playbooks/blockincludes.yml", 4, 3, id="blockincludes"),
+ pytest.param(
+ "examples/playbooks/blockincludes.yml",
+ 4,
+ 3,
+ id="blockincludes",
+ ),
pytest.param(
"examples/playbooks/blockincludes2.yml",
4,
diff --git a/test/test_text.py b/test/test_text.py
index fa91fee..22214c7 100644
--- a/test/test_text.py
+++ b/test/test_text.py
@@ -1,4 +1,5 @@
"""Tests for text module."""
+
from typing import Any
import pytest
diff --git a/test/test_transform_mixin.py b/test/test_transform_mixin.py
index d639bff..44b851b 100644
--- a/test/test_transform_mixin.py
+++ b/test/test_transform_mixin.py
@@ -1,4 +1,5 @@
"""Tests for TransformMixin."""
+
from __future__ import annotations
from typing import TYPE_CHECKING
@@ -55,72 +56,91 @@ def test_seek_with_bad_path(
@pytest.mark.parametrize(
("yaml_path", "data", "expected"),
(
- ([], DUMMY_MAP, DUMMY_MAP),
- (["foo"], DUMMY_MAP, DUMMY_MAP["foo"]),
- (["bar"], DUMMY_MAP, DUMMY_MAP["bar"]),
- (["bar", "some"], DUMMY_MAP, DUMMY_MAP["bar"]["some"]),
- (["fruits"], DUMMY_MAP, DUMMY_MAP["fruits"]),
- (["fruits", 0], DUMMY_MAP, DUMMY_MAP["fruits"][0]),
- (["fruits", 1], DUMMY_MAP, DUMMY_MAP["fruits"][1]),
- (["answer"], DUMMY_MAP, DUMMY_MAP["answer"]),
- (["answer", 0], DUMMY_MAP, DUMMY_MAP["answer"][0]),
- (["answer", 0, "forty-two"], DUMMY_MAP, DUMMY_MAP["answer"][0]["forty-two"]),
- (
+ pytest.param([], DUMMY_MAP, DUMMY_MAP, id="0"),
+ pytest.param(["foo"], DUMMY_MAP, DUMMY_MAP["foo"], id="1"),
+ pytest.param(["bar"], DUMMY_MAP, DUMMY_MAP["bar"], id="2"),
+ pytest.param(["bar", "some"], DUMMY_MAP, DUMMY_MAP["bar"]["some"], id="3"),
+ pytest.param(["fruits"], DUMMY_MAP, DUMMY_MAP["fruits"], id="4"),
+ pytest.param(["fruits", 0], DUMMY_MAP, DUMMY_MAP["fruits"][0], id="5"),
+ pytest.param(["fruits", 1], DUMMY_MAP, DUMMY_MAP["fruits"][1], id="6"),
+ pytest.param(["answer"], DUMMY_MAP, DUMMY_MAP["answer"], id="7"),
+ pytest.param(["answer", 0], DUMMY_MAP, DUMMY_MAP["answer"][0], id="8"),
+ pytest.param(
+ ["answer", 0, "forty-two"],
+ DUMMY_MAP,
+ DUMMY_MAP["answer"][0]["forty-two"],
+ id="9",
+ ),
+ pytest.param(
["answer", 0, "forty-two", 0],
DUMMY_MAP,
DUMMY_MAP["answer"][0]["forty-two"][0],
+ id="10",
),
- (
+ pytest.param(
["answer", 0, "forty-two", 1],
DUMMY_MAP,
DUMMY_MAP["answer"][0]["forty-two"][1],
+ id="11",
),
- (
+ pytest.param(
["answer", 0, "forty-two", 2],
DUMMY_MAP,
DUMMY_MAP["answer"][0]["forty-two"][2],
+ id="12",
+ ),
+ pytest.param([], DUMMY_LIST, DUMMY_LIST, id="13"),
+ pytest.param([0], DUMMY_LIST, DUMMY_LIST[0], id="14"),
+ pytest.param([0, "foo"], DUMMY_LIST, DUMMY_LIST[0]["foo"], id="15"),
+ pytest.param([1], DUMMY_LIST, DUMMY_LIST[1], id="16"),
+ pytest.param([1, "bar"], DUMMY_LIST, DUMMY_LIST[1]["bar"], id="17"),
+ pytest.param(
+ [1, "bar", "some"],
+ DUMMY_LIST,
+ DUMMY_LIST[1]["bar"]["some"],
+ id="18",
),
- ([], DUMMY_LIST, DUMMY_LIST),
- ([0], DUMMY_LIST, DUMMY_LIST[0]),
- ([0, "foo"], DUMMY_LIST, DUMMY_LIST[0]["foo"]),
- ([1], DUMMY_LIST, DUMMY_LIST[1]),
- ([1, "bar"], DUMMY_LIST, DUMMY_LIST[1]["bar"]),
- ([1, "bar", "some"], DUMMY_LIST, DUMMY_LIST[1]["bar"]["some"]),
- ([1, "fruits"], DUMMY_LIST, DUMMY_LIST[1]["fruits"]),
- ([1, "fruits", 0], DUMMY_LIST, DUMMY_LIST[1]["fruits"][0]),
- ([1, "fruits", 1], DUMMY_LIST, DUMMY_LIST[1]["fruits"][1]),
- ([2], DUMMY_LIST, DUMMY_LIST[2]),
- ([2, "answer"], DUMMY_LIST, DUMMY_LIST[2]["answer"]),
- ([2, "answer", 0], DUMMY_LIST, DUMMY_LIST[2]["answer"][0]),
- (
+ pytest.param([1, "fruits"], DUMMY_LIST, DUMMY_LIST[1]["fruits"], id="19"),
+ pytest.param([1, "fruits", 0], DUMMY_LIST, DUMMY_LIST[1]["fruits"][0], id="20"),
+ pytest.param([1, "fruits", 1], DUMMY_LIST, DUMMY_LIST[1]["fruits"][1], id="21"),
+ pytest.param([2], DUMMY_LIST, DUMMY_LIST[2], id="22"),
+ pytest.param([2, "answer"], DUMMY_LIST, DUMMY_LIST[2]["answer"], id="23"),
+ pytest.param([2, "answer", 0], DUMMY_LIST, DUMMY_LIST[2]["answer"][0], id="24"),
+ pytest.param(
[2, "answer", 0, "forty-two"],
DUMMY_LIST,
DUMMY_LIST[2]["answer"][0]["forty-two"],
+ id="25",
),
- (
+ pytest.param(
[2, "answer", 0, "forty-two", 0],
DUMMY_LIST,
DUMMY_LIST[2]["answer"][0]["forty-two"][0],
+ id="26",
),
- (
+ pytest.param(
[2, "answer", 0, "forty-two", 1],
DUMMY_LIST,
DUMMY_LIST[2]["answer"][0]["forty-two"][1],
+ id="27",
),
- (
+ pytest.param(
[2, "answer", 0, "forty-two", 2],
DUMMY_LIST,
DUMMY_LIST[2]["answer"][0]["forty-two"][2],
+ id="28",
),
- (
+ pytest.param(
[],
"this is a string that should be returned as is, ignoring path.",
"this is a string that should be returned as is, ignoring path.",
+ id="29",
),
- (
+ pytest.param(
[2, "answer", 0, "forty-two", 2],
"this is a string that should be returned as is, ignoring path.",
"this is a string that should be returned as is, ignoring path.",
+ id="30",
),
),
)
diff --git a/test/test_transformer.py b/test/test_transformer.py
index 78dd121..51e97d5 100644
--- a/test/test_transformer.py
+++ b/test/test_transformer.py
@@ -1,125 +1,247 @@
+# cspell:ignore classinfo
"""Tests for Transformer."""
+
from __future__ import annotations
+import builtins
import os
import shutil
from pathlib import Path
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Any
+from unittest import mock
import pytest
+import ansiblelint.__main__ as main
+from ansiblelint.app import App
+from ansiblelint.file_utils import Lintable
+from ansiblelint.rules import TransformMixin
+
# noinspection PyProtectedMember
-from ansiblelint.runner import LintResult, _get_matches
+from ansiblelint.runner import LintResult, get_matches
from ansiblelint.transformer import Transformer
if TYPE_CHECKING:
- from argparse import Namespace
- from collections.abc import Iterator
-
from ansiblelint.config import Options
+ from ansiblelint.errors import MatchError
from ansiblelint.rules import RulesCollection
-@pytest.fixture(name="copy_examples_dir")
-def fixture_copy_examples_dir(
- tmp_path: Path,
- config_options: Namespace,
-) -> Iterator[tuple[Path, Path]]:
- """Fixture that copies the examples/ dir into a tmpdir."""
- examples_dir = Path("examples")
-
- shutil.copytree(examples_dir, tmp_path / "examples")
- old_cwd = Path.cwd()
- try:
- os.chdir(tmp_path)
- config_options.cwd = tmp_path
- yield old_cwd, tmp_path
- finally:
- os.chdir(old_cwd)
-
-
@pytest.fixture(name="runner_result")
def fixture_runner_result(
config_options: Options,
default_rules_collection: RulesCollection,
- playbook: str,
+ playbook_str: str,
+ monkeypatch: pytest.MonkeyPatch,
) -> LintResult:
"""Fixture that runs the Runner to populate a LintResult for a given file."""
- config_options.lintables = [playbook]
- result = _get_matches(rules=default_rules_collection, options=config_options)
+ # needed for testing transformer when roles/modules are missing:
+ monkeypatch.setenv("ANSIBLE_LINT_NODEPS", "1")
+ config_options.lintables = [playbook_str]
+ result = get_matches(rules=default_rules_collection, options=config_options)
return result
@pytest.mark.parametrize(
- ("playbook", "matches_count", "transformed"),
+ ("playbook_str", "matches_count", "transformed", "is_owned_by_ansible"),
(
# reuse TestRunner::test_runner test cases to ensure transformer does not mangle matches
pytest.param(
"examples/playbooks/nomatchestest.yml",
0,
False,
+ True,
id="nomatchestest",
),
- pytest.param("examples/playbooks/unicode.yml", 1, False, id="unicode"),
+ pytest.param("examples/playbooks/unicode.yml", 1, False, True, id="unicode"),
pytest.param(
"examples/playbooks/lots_of_warnings.yml",
- 992,
+ 993,
False,
+ True,
id="lots_of_warnings",
),
- pytest.param("examples/playbooks/become.yml", 0, False, id="become"),
+ pytest.param("examples/playbooks/become.yml", 0, False, True, id="become"),
pytest.param(
"examples/playbooks/contains_secrets.yml",
0,
False,
+ True,
id="contains_secrets",
),
pytest.param(
"examples/playbooks/vars/empty_vars.yml",
0,
False,
+ True,
id="empty_vars",
),
- pytest.param("examples/playbooks/vars/strings.yml", 0, True, id="strings"),
- pytest.param("examples/playbooks/vars/empty.yml", 1, False, id="empty"),
- pytest.param("examples/playbooks/name-case.yml", 1, True, id="name_case"),
- pytest.param("examples/playbooks/fqcn.yml", 3, True, id="fqcn"),
+ pytest.param(
+ "examples/playbooks/vars/strings.yml",
+ 0,
+ True,
+ True,
+ id="strings",
+ ),
+ pytest.param("examples/playbooks/vars/empty.yml", 1, False, True, id="empty"),
+ pytest.param("examples/playbooks/fqcn.yml", 3, True, True, id="fqcn"),
+ pytest.param(
+ "examples/playbooks/multi_yaml_doc.yml",
+ 1,
+ False,
+ True,
+ id="multi_yaml_doc",
+ ),
+ pytest.param(
+ "examples/playbooks/transform_command_instead_of_shell.yml",
+ 3,
+ True,
+ True,
+ id="cmd_instead_of_shell",
+ ),
+ pytest.param(
+ "examples/playbooks/transform-deprecated-local-action.yml",
+ 1,
+ True,
+ True,
+ id="dep_local_action",
+ ),
+ pytest.param(
+ "examples/playbooks/transform-block-indentation-indicator.yml",
+ 0,
+ True,
+ True,
+ id="multiline_msg_with_indent_indicator",
+ ),
+ pytest.param(
+ "examples/playbooks/transform-jinja.yml",
+ 7,
+ True,
+ True,
+ id="jinja_spacing",
+ ),
+ pytest.param(
+ "examples/playbooks/transform-no-jinja-when.yml",
+ 3,
+ True,
+ True,
+ id="no_jinja_when",
+ ),
+ pytest.param(
+ "examples/playbooks/vars/transform_nested_data.yml",
+ 3,
+ True,
+ True,
+ id="nested",
+ ),
+ pytest.param(
+ "examples/playbooks/transform-key-order.yml",
+ 6,
+ True,
+ True,
+ id="key_order_transform",
+ ),
+ pytest.param(
+ "examples/playbooks/transform-no-free-form.yml",
+ 5,
+ True,
+ True,
+ id="no_free_form_transform",
+ ),
+ pytest.param(
+ "examples/playbooks/transform-partial-become.yml",
+ 4,
+ True,
+ True,
+ id="partial_become",
+ ),
+ pytest.param(
+ "examples/playbooks/transform-key-order-play.yml",
+ 1,
+ True,
+ True,
+ id="key_order_play_transform",
+ ),
+ pytest.param(
+ "examples/playbooks/transform-key-order-block.yml",
+ 1,
+ True,
+ True,
+ id="key_order_block_transform",
+ ),
+ pytest.param(
+ "examples/.github/workflows/sample.yml",
+ 0,
+ False,
+ False,
+ id="github-workflow",
+ ),
+ pytest.param(
+ "examples/playbooks/invalid-transform.yml",
+ 1,
+ False,
+ True,
+ id="invalid_transform",
+ ),
+ pytest.param(
+ "examples/roles/name_prefix/tasks/test.yml",
+ 1,
+ True,
+ True,
+ id="name_casing_prefix",
+ ),
+ pytest.param(
+ "examples/roles/name_casing/tasks/main.yml",
+ 2,
+ True,
+ True,
+ id="name_case_roles",
+ ),
+ pytest.param(
+ "examples/playbooks/4114/transform-with-missing-role-and-modules.yml",
+ 1,
+ True,
+ True,
+ id="4114",
+ ),
),
)
-def test_transformer( # pylint: disable=too-many-arguments, too-many-locals
+@mock.patch.dict(os.environ, {"ANSIBLE_LINT_WRITE_TMP": "1"}, clear=True)
+def test_transformer( # pylint: disable=too-many-arguments
config_options: Options,
- copy_examples_dir: tuple[Path, Path],
- playbook: str,
+ playbook_str: str,
runner_result: LintResult,
transformed: bool,
+ is_owned_by_ansible: bool,
matches_count: int,
) -> None:
"""Test that transformer can go through any corner cases.
Based on TestRunner::test_runner
"""
+ # test ability to detect is_owned_by_ansible
+ assert Lintable(playbook_str).is_owned_by_ansible() == is_owned_by_ansible
+ playbook = Path(playbook_str)
config_options.write_list = ["all"]
- transformer = Transformer(result=runner_result, options=config_options)
- transformer.run()
matches = runner_result.matches
assert len(matches) == matches_count
- orig_dir, tmp_dir = copy_examples_dir
- orig_playbook = orig_dir / playbook
- expected_playbook = orig_dir / playbook.replace(".yml", ".transformed.yml")
- transformed_playbook = tmp_dir / playbook
-
- orig_playbook_content = orig_playbook.read_text()
- expected_playbook_content = expected_playbook.read_text()
- transformed_playbook_content = transformed_playbook.read_text()
+ transformer = Transformer(result=runner_result, options=config_options)
+ transformer.run()
+ orig_content = playbook.read_text(encoding="utf-8")
if transformed:
- assert orig_playbook_content != transformed_playbook_content
- else:
- assert orig_playbook_content == transformed_playbook_content
+ expected_content = playbook.with_suffix(
+ f".transformed{playbook.suffix}",
+ ).read_text(encoding="utf-8")
+ transformed_content = playbook.with_suffix(f".tmp{playbook.suffix}").read_text(
+ encoding="utf-8",
+ )
- assert transformed_playbook_content == expected_playbook_content
+ assert orig_content != transformed_content
+ assert expected_content == transformed_content
+ playbook.with_suffix(f".tmp{playbook.suffix}").unlink()
@pytest.mark.parametrize(
@@ -173,3 +295,341 @@ def test_effective_write_set(write_list: list[str], expected: set[str]) -> None:
"""Make sure effective_write_set handles all/none keywords correctly."""
actual = Transformer.effective_write_set(write_list)
assert actual == expected
+
+
+def test_pruned_err_after_fix(monkeypatch: pytest.MonkeyPatch, tmpdir: Path) -> None:
+ """Test that pruned errors are not reported after fixing.
+
+ :param monkeypatch: Monkeypatch
+ :param tmpdir: Temporary directory
+ """
+ file = Path("examples/playbooks/transform-jinja.yml")
+ source = Path.cwd() / file
+ dest = tmpdir / source.name
+ shutil.copyfile(source, dest)
+
+ monkeypatch.setattr("sys.argv", ["ansible-lint", str(dest), "--fix=all"])
+
+ fix_called = False
+ orig_fix = main.fix
+
+ def test_fix(
+ runtime_options: Options,
+ result: LintResult,
+ rules: RulesCollection,
+ ) -> None:
+ """Wrap main.fix to check if it was called and match count is correct.
+
+ :param runtime_options: Runtime options
+ :param result: Lint result
+ :param rules: Rules collection
+ """
+ nonlocal fix_called
+ fix_called = True
+ assert len(result.matches) == 7
+ orig_fix(runtime_options, result, rules)
+
+ report_called = False
+
+ class TestApp(App):
+ """Wrap App to check if it was called and match count is correct."""
+
+ def report_outcome(
+ self: TestApp,
+ result: LintResult,
+ *,
+ mark_as_success: bool = False,
+ ) -> int:
+ """Wrap App.report_outcome to check if it was called and match count is correct.
+
+ :param result: Lint result
+ :param mark_as_success: Mark as success
+ :returns: Exit code
+ """
+ nonlocal report_called
+ report_called = True
+ assert len(result.matches) == 1
+ return super().report_outcome(result, mark_as_success=mark_as_success)
+
+ monkeypatch.setattr("ansiblelint.__main__.fix", test_fix)
+ monkeypatch.setattr("ansiblelint.app.App", TestApp)
+
+ main.main()
+ assert fix_called
+ assert report_called
+
+
+class TransformTests:
+ """A carrier for some common test constants."""
+
+ FILE_NAME = "examples/playbooks/transform-no-free-form.yml"
+ FILE_TYPE = "playbook"
+ LINENO = 5
+ ID = "no-free-form"
+ MATCH_TYPE = "task"
+ VERSION_PART = "version=(1, 1)"
+
+ @classmethod
+ def match_id(cls) -> str:
+ """Generate a match id.
+
+ :returns: Match id string
+ """
+ return f"{cls.ID}/{cls.MATCH_TYPE} {cls.FILE_NAME}:{cls.LINENO}"
+
+ @classmethod
+ def rewrite_part(cls) -> str:
+ """Generate a rewrite part.
+
+ :returns: Rewrite part string
+ """
+ return f"{cls.FILE_NAME} ({cls.FILE_TYPE}), {cls.VERSION_PART}"
+
+
+@pytest.fixture(name="test_result")
+def fixture_test_result(
+ config_options: Options,
+ default_rules_collection: RulesCollection,
+) -> tuple[LintResult, Options]:
+ """Fixture that runs the Runner to populate a LintResult for a given file.
+
+ The results are confirmed and a limited to a single match.
+
+ :param config_options: Configuration options
+ :param default_rules_collection: Default rules collection
+ :returns: Tuple of LintResult and Options
+ """
+ config_options.write_list = [TransformTests.ID]
+ config_options.lintables = [TransformTests.FILE_NAME]
+
+ result = get_matches(rules=default_rules_collection, options=config_options)
+ match = result.matches[0]
+
+ def write(*_args: Any, **_kwargs: Any) -> None:
+ """Don't rewrite the test fixture.
+
+ :param _args: Arguments
+ :param _kwargs: Keyword arguments
+ """
+
+ setattr(match.lintable, "write", write) # noqa: B010
+
+ assert match.rule.id == TransformTests.ID
+ assert match.filename == TransformTests.FILE_NAME
+ assert match.lineno == TransformTests.LINENO
+ assert match.match_type == TransformTests.MATCH_TYPE
+ result.matches = [match]
+
+ return result, config_options
+
+
+def test_transform_na(
+ caplog: pytest.LogCaptureFixture,
+ monkeypatch: pytest.MonkeyPatch,
+ test_result: tuple[LintResult, Options],
+) -> None:
+ """Test the transformer is not available.
+
+ :param caplog: Log capture fixture
+ :param monkeypatch: Monkeypatch
+ :param test_result: Test result fixture
+ """
+ result = test_result[0]
+ options = test_result[1]
+
+ _isinstance = builtins.isinstance
+ called = False
+
+ def mp_isinstance(t_object: Any, classinfo: type) -> bool:
+ if classinfo is TransformMixin:
+ nonlocal called
+ called = True
+ return False
+ return _isinstance(t_object, classinfo)
+
+ monkeypatch.setattr(builtins, "isinstance", mp_isinstance)
+
+ transformer = Transformer(result=result, options=options)
+ with caplog.at_level(10):
+ transformer.run()
+
+ assert called
+ logs = [record for record in caplog.records if record.module == "transformer"]
+ assert len(logs) == 2
+
+ log_0 = f"{transformer.FIX_NA_MSG} {TransformTests.match_id()}"
+ assert logs[0].message == log_0
+ assert logs[0].levelname == "DEBUG"
+
+ log_1 = f"{transformer.DUMP_MSG} {TransformTests.rewrite_part()}"
+ assert logs[1].message == log_1
+ assert logs[1].levelname == "DEBUG"
+
+
+def test_transform_no_tb(
+ caplog: pytest.LogCaptureFixture,
+ test_result: tuple[LintResult, Options],
+) -> None:
+ """Test the transformer does not traceback.
+
+ :param caplog: Log capture fixture
+ :param test_result: Test result fixture
+ :raises RuntimeError: If the rule is not a TransformMixin
+ """
+ result = test_result[0]
+ options = test_result[1]
+ exception_msg = "FixFailure"
+
+ def transform(*_args: Any, **_kwargs: Any) -> None:
+ """Raise an exception for the transform call.
+
+ :raises RuntimeError: Always
+ """
+ raise RuntimeError(exception_msg)
+
+ if isinstance(result.matches[0].rule, TransformMixin):
+ setattr(result.matches[0].rule, "transform", transform) # noqa: B010
+ else:
+ err = "Rule is not a TransformMixin"
+ raise TypeError(err)
+
+ transformer = Transformer(result=result, options=options)
+ with caplog.at_level(10):
+ transformer.run()
+
+ logs = [record for record in caplog.records if record.module == "transformer"]
+ assert len(logs) == 5
+
+ log_0 = f"{transformer.FIX_APPLY_MSG} {TransformTests.match_id()}"
+ assert logs[0].message == log_0
+ assert logs[0].levelname == "DEBUG"
+
+ log_1 = f"{transformer.FIX_FAILED_MSG} {TransformTests.match_id()}"
+ assert logs[1].message == log_1
+ assert logs[1].levelname == "ERROR"
+
+ log_2 = exception_msg
+ assert logs[2].message == log_2
+ assert logs[2].levelname == "ERROR"
+
+ log_3 = f"{transformer.FIX_ISSUE_MSG}"
+ assert logs[3].message == log_3
+ assert logs[3].levelname == "ERROR"
+
+ log_4 = f"{transformer.DUMP_MSG} {TransformTests.rewrite_part()}"
+ assert logs[4].message == log_4
+ assert logs[4].levelname == "DEBUG"
+
+
+def test_transform_applied(
+ caplog: pytest.LogCaptureFixture,
+ test_result: tuple[LintResult, Options],
+) -> None:
+ """Test the transformer is applied.
+
+ :param caplog: Log capture fixture
+ :param test_result: Test result fixture
+ """
+ result = test_result[0]
+ options = test_result[1]
+
+ transformer = Transformer(result=result, options=options)
+ with caplog.at_level(10):
+ transformer.run()
+
+ logs = [record for record in caplog.records if record.module == "transformer"]
+ assert len(logs) == 3
+
+ log_0 = f"{transformer.FIX_APPLY_MSG} {TransformTests.match_id()}"
+ assert logs[0].message == log_0
+ assert logs[0].levelname == "DEBUG"
+
+ log_1 = f"{transformer.FIX_APPLIED_MSG} {TransformTests.match_id()}"
+ assert logs[1].message == log_1
+ assert logs[1].levelname == "DEBUG"
+
+ log_2 = f"{transformer.DUMP_MSG} {TransformTests.rewrite_part()}"
+ assert logs[2].message == log_2
+ assert logs[2].levelname == "DEBUG"
+
+
+def test_transform_not_enabled(
+ caplog: pytest.LogCaptureFixture,
+ test_result: tuple[LintResult, Options],
+) -> None:
+ """Test the transformer is not enabled.
+
+ :param caplog: Log capture fixture
+ :param test_result: Test result fixture
+ """
+ result = test_result[0]
+ options = test_result[1]
+ options.write_list = []
+
+ transformer = Transformer(result=result, options=options)
+ with caplog.at_level(10):
+ transformer.run()
+
+ logs = [record for record in caplog.records if record.module == "transformer"]
+ assert len(logs) == 2
+
+ log_0 = f"{transformer.FIX_NE_MSG} {TransformTests.match_id()}"
+ assert logs[0].message == log_0
+ assert logs[0].levelname == "DEBUG"
+
+ log_1 = f"{transformer.DUMP_MSG} {TransformTests.rewrite_part()}"
+ assert logs[1].message == log_1
+ assert logs[1].levelname == "DEBUG"
+
+
+def test_transform_not_applied(
+ caplog: pytest.LogCaptureFixture,
+ test_result: tuple[LintResult, Options],
+) -> None:
+ """Test the transformer is not applied.
+
+ :param caplog: Log capture fixture
+ :param test_result: Test result fixture
+ :raises RuntimeError: If the rule is not a TransformMixin
+ """
+ result = test_result[0]
+ options = test_result[1]
+
+ called = False
+
+ def transform(match: MatchError, *_args: Any, **_kwargs: Any) -> None:
+ """Do not apply the transform.
+
+ :param match: Match object
+ :param _args: Arguments
+ :param _kwargs: Keyword arguments
+ """
+ nonlocal called
+ called = True
+ match.fixed = False
+
+ if isinstance(result.matches[0].rule, TransformMixin):
+ setattr(result.matches[0].rule, "transform", transform) # noqa: B010
+ else:
+ err = "Rule is not a TransformMixin"
+ raise TypeError(err)
+
+ transformer = Transformer(result=result, options=options)
+ with caplog.at_level(10):
+ transformer.run()
+
+ assert called
+ logs = [record for record in caplog.records if record.module == "transformer"]
+ assert len(logs) == 3
+
+ log_0 = f"{transformer.FIX_APPLY_MSG} {TransformTests.match_id()}"
+ assert logs[0].message == log_0
+ assert logs[0].levelname == "DEBUG"
+
+ log_1 = f"{transformer.FIX_NOT_APPLIED_MSG} {TransformTests.match_id()}"
+ assert logs[1].message == log_1
+ assert logs[1].levelname == "ERROR"
+
+ log_2 = f"{transformer.DUMP_MSG} {TransformTests.rewrite_part()}"
+ assert logs[2].message == log_2
+ assert logs[2].levelname == "DEBUG"
diff --git a/test/test_utils.py b/test/test_utils.py
index 1b9a2dc..6f728dc 100644
--- a/test/test_utils.py
+++ b/test/test_utils.py
@@ -51,37 +51,44 @@ runtime = Runtime(require_module=True)
@pytest.mark.parametrize(
- ("string", "expected_cmd", "expected_args", "expected_kwargs"),
+ ("string", "expected_args", "expected_kwargs"),
(
- pytest.param("", "", [], {}, id="blank"),
- pytest.param("vars:", "vars", [], {}, id="single_word"),
- pytest.param("hello: a=1", "hello", [], {"a": "1"}, id="string_module_and_arg"),
- pytest.param("action: hello a=1", "hello", [], {"a": "1"}, id="strips_action"),
+ pytest.param("", [], {}, id="a"),
+ pytest.param("a=1", [], {"a": "1"}, id="b"),
+ pytest.param("hello a=1", ["hello"], {"a": "1"}, id="c"),
pytest.param(
- "action: whatever bobbins x=y z=x c=3",
- "whatever",
- ["bobbins", "x=y", "z=x", "c=3"],
- {},
+ "whatever bobbins x=y z=x c=3",
+ ["whatever", "bobbins"],
+ {"x": "y", "z": "x", "c": "3"},
id="more_than_one_arg",
),
pytest.param(
- "action: command chdir=wxy creates=zyx tar xzf zyx.tgz",
- "command",
- ["tar", "xzf", "zyx.tgz"],
+ "command chdir=wxy creates=zyx tar xzf zyx.tgz",
+ ["command", "tar", "xzf", "zyx.tgz"],
{"chdir": "wxy", "creates": "zyx"},
id="command_with_args",
),
+ pytest.param(
+ "{{ varset }}.yml",
+ ["{{ varset }}.yml"],
+ {},
+ id="x",
+ ),
+ pytest.param(
+ "foo bar.yml",
+ ["foo bar.yml"],
+ {},
+ id="path-with-spaces",
+ ),
),
)
def test_tokenize(
string: str,
- expected_cmd: str,
expected_args: Sequence[str],
expected_kwargs: dict[str, Any],
) -> None:
"""Test that tokenize works for different input types."""
- (cmd, args, kwargs) = utils.tokenize(string)
- assert cmd == expected_cmd
+ (args, kwargs) = utils.tokenize(string)
assert args == expected_args
assert kwargs == expected_kwargs
@@ -113,37 +120,42 @@ def test_normalize(
alternate_forms: tuple[dict[str, Any]],
) -> None:
"""Test that tasks specified differently are normalized same way."""
- normal_form = utils.normalize_task(reference_form, "tasks.yml")
+ task = utils.Task(reference_form, filename="tasks.yml")
+ normal_form = task._normalize_task() # noqa: SLF001
for form in alternate_forms:
- assert normal_form == utils.normalize_task(form, "tasks.yml")
+ task2 = utils.Task(form, filename="tasks.yml")
+ assert normal_form == task2._normalize_task() # noqa: SLF001
def test_normalize_complex_command() -> None:
"""Test that tasks specified differently are normalized same way."""
- task1 = {
- "name": "hello",
- "action": {"module": "pip", "name": "df", "editable": "false"},
- }
- task2 = {"name": "hello", "pip": {"name": "df", "editable": "false"}}
- task3 = {"name": "hello", "pip": "name=df editable=false"}
- task4 = {"name": "hello", "action": "pip name=df editable=false"}
- assert utils.normalize_task(task1, "tasks.yml") == utils.normalize_task(
- task2,
- "tasks.yml",
+ task1 = utils.Task(
+ {
+ "name": "hello",
+ "action": {"module": "pip", "name": "df", "editable": "false"},
+ },
+ filename="tasks.yml",
)
- assert utils.normalize_task(task2, "tasks.yml") == utils.normalize_task(
- task3,
- "tasks.yml",
+ task2 = utils.Task(
+ {"name": "hello", "pip": {"name": "df", "editable": "false"}},
+ filename="tasks.yml",
)
- assert utils.normalize_task(task3, "tasks.yml") == utils.normalize_task(
- task4,
- "tasks.yml",
+ task3 = utils.Task(
+ {"name": "hello", "pip": "name=df editable=false"},
+ filename="tasks.yml",
)
+ task4 = utils.Task(
+ {"name": "hello", "action": "pip name=df editable=false"},
+ filename="tasks.yml",
+ )
+ assert task1._normalize_task() == task2._normalize_task() # noqa: SLF001
+ assert task2._normalize_task() == task3._normalize_task() # noqa: SLF001
+ assert task3._normalize_task() == task4._normalize_task() # noqa: SLF001
@pytest.mark.parametrize(
- ("task", "expected_form"),
+ ("task_raw", "expected_form"),
(
pytest.param(
{
@@ -191,8 +203,12 @@ def test_normalize_complex_command() -> None:
),
),
)
-def test_normalize_task_v2(task: dict[str, Any], expected_form: dict[str, Any]) -> None:
+def test_normalize_task_v2(
+ task_raw: dict[str, Any],
+ expected_form: dict[str, Any],
+) -> None:
"""Check that it normalizes task and returns the expected form."""
+ task = utils.Task(task_raw)
assert utils.normalize_task_v2(task) == expected_form
@@ -262,8 +278,8 @@ def test_template(template: str, output: str) -> None:
def test_task_to_str_unicode() -> None:
"""Ensure that extracting messages from tasks preserves Unicode."""
- task = {"fail": {"msg": "unicode é ô à"}}
- result = utils.task_to_str(utils.normalize_task(task, "filename.yml"))
+ task = utils.Task({"fail": {"msg": "unicode é ô à"}}, filename="filename.yml")
+ result = utils.task_to_str(task._normalize_task()) # noqa: SLF001
assert result == "fail msg=unicode é ô à"
@@ -447,3 +463,20 @@ def test_task_in_list(file: str, names: list[str], positions: list[str]) -> None
for index, task in enumerate(tasks):
assert task.name == names[index]
assert task.position == positions[index]
+
+
+def test_find_children_in_module(default_rules_collection: RulesCollection) -> None:
+ """Verify correct function of find_children() in tasks."""
+ lintable = Lintable("plugins/modules/fake_module.py")
+ children = Runner(
+ rules=default_rules_collection,
+ ).find_children(lintable)
+ assert len(children) == 1
+ child = children[0]
+
+ # Parent is a python file
+ assert lintable.base_kind == "text/python"
+
+ # Child correctly looks like a YAML file
+ assert child.base_kind == "text/yaml"
+ assert child.content.startswith("---")
diff --git a/test/test_verbosity.py b/test/test_verbosity.py
index d3ddb3c..38df170 100644
--- a/test/test_verbosity.py
+++ b/test/test_verbosity.py
@@ -1,4 +1,5 @@
"""Tests related to our logging/verbosity setup."""
+
from __future__ import annotations
from pathlib import Path
@@ -6,6 +7,7 @@ from pathlib import Path
import pytest
from ansiblelint.testing import run_ansible_lint
+from ansiblelint.text import strip_ansi_escape
# substrs is a list of tuples, where:
@@ -83,6 +85,9 @@ def test_verbosity(
else:
result = run_ansible_lint(str(fakerole), cwd=project_path)
+ result.stderr = strip_ansi_escape(result.stderr)
+ result.stdout = strip_ansi_escape(result.stdout)
+ assert result.returncode == 2, result
for substr, invert in substrs:
if invert:
assert substr not in result.stderr, result.stderr
diff --git a/test/test_with_skip_tagid.py b/test/test_with_skip_tagid.py
index 5fbea8f..a2a46c3 100644
--- a/test/test_with_skip_tagid.py
+++ b/test/test_with_skip_tagid.py
@@ -1,4 +1,5 @@
"""Tests related to skip tag id."""
+
from ansiblelint.rules import RulesCollection
from ansiblelint.rules.yaml_rule import YamllintRule
from ansiblelint.runner import Runner
@@ -26,7 +27,7 @@ def test_negative_with_id() -> None:
def test_negative_with_tag() -> None:
"""Negative test with_tag."""
- with_tag = "trailing-spaces"
+ with_tag = "yaml[trailing-spaces]"
bad_runner = Runner(FILE, rules=collection, tags=frozenset([with_tag]))
errs = bad_runner.run()
assert len(errs) == 1
@@ -39,6 +40,13 @@ def test_positive_skip_id() -> None:
assert [] == good_runner.run()
+def test_positive_skip_id_2() -> None:
+ """Positive test skip_id."""
+ skip_id = "key-order"
+ good_runner = Runner(FILE, rules=collection, tags=frozenset([skip_id]))
+ assert [] == good_runner.run()
+
+
def test_positive_skip_tag() -> None:
"""Positive test skip_tag."""
skip_tag = "yaml[trailing-spaces]"
diff --git a/test/test_yaml_utils.py b/test/test_yaml_utils.py
index 5546e58..f4d9b46 100644
--- a/test/test_yaml_utils.py
+++ b/test/test_yaml_utils.py
@@ -1,16 +1,18 @@
"""Tests for yaml-related utility functions."""
+
+# pylint: disable=too-many-lines
from __future__ import annotations
from io import StringIO
from pathlib import Path
-from typing import TYPE_CHECKING, Any
+from typing import TYPE_CHECKING, Any, cast
import pytest
from ruamel.yaml.main import YAML
from yamllint.linter import run as run_yamllint
import ansiblelint.yaml_utils
-from ansiblelint.file_utils import Lintable
+from ansiblelint.file_utils import Lintable, cwd
from ansiblelint.utils import task_in_list
if TYPE_CHECKING:
@@ -202,8 +204,7 @@ def test_custom_ruamel_yaml_emitter(
assert output == expected_output
-@pytest.fixture(name="yaml_formatting_fixtures")
-def fixture_yaml_formatting_fixtures(fixture_filename: str) -> tuple[str, str, str]:
+def load_yaml_formatting_fixtures(fixture_filename: str) -> tuple[str, str, str]:
"""Get the contents for the formatting fixture files.
To regenerate these fixtures, please run ``pytest --regenerate-formatting-fixtures``.
@@ -220,30 +221,67 @@ def fixture_yaml_formatting_fixtures(fixture_filename: str) -> tuple[str, str, s
@pytest.mark.parametrize(
- "fixture_filename",
+ ("before", "after", "version"),
+ (
+ pytest.param("---\nfoo: bar\n", "---\nfoo: bar\n", None, id="1"),
+ # verify that 'on' is not translated to bool (1.2 behavior)
+ pytest.param("---\nfoo: on\n", "---\nfoo: on\n", None, id="2"),
+ # When version is manually mentioned by us, we expect to output without version directive
+ pytest.param("---\nfoo: on\n", "---\nfoo: on\n", (1, 2), id="3"),
+ pytest.param("---\nfoo: on\n", "---\nfoo: true\n", (1, 1), id="4"),
+ pytest.param("%YAML 1.1\n---\nfoo: on\n", "---\nfoo: true\n", (1, 1), id="5"),
+ # verify that in-line directive takes precedence but dumping strips if we mention a specific version
+ pytest.param("%YAML 1.1\n---\nfoo: on\n", "---\nfoo: true\n", (1, 2), id="6"),
+ # verify that version directive are kept if present
+ pytest.param("%YAML 1.1\n---\nfoo: on\n", "---\nfoo: true\n", None, id="7"),
+ pytest.param(
+ "%YAML 1.2\n---\nfoo: on\n",
+ "%YAML 1.2\n---\nfoo: on\n",
+ None,
+ id="8",
+ ),
+ pytest.param("---\nfoo: YES\n", "---\nfoo: true\n", (1, 1), id="9"),
+ pytest.param("---\nfoo: YES\n", "---\nfoo: YES\n", (1, 2), id="10"),
+ pytest.param("---\nfoo: YES\n", "---\nfoo: YES\n", None, id="11"),
+ ),
+)
+def test_fmt(before: str, after: str, version: tuple[int, int] | None) -> None:
+ """Tests behavior of formatter in regards to different YAML versions, specified or not."""
+ yaml = ansiblelint.yaml_utils.FormattedYAML(version=version)
+ data = yaml.load(before)
+ result = yaml.dumps(data)
+ assert result == after
+
+
+@pytest.mark.parametrize(
+ ("fixture_filename", "version"),
(
- "fmt-1.yml",
- "fmt-2.yml",
- "fmt-3.yml",
+ pytest.param("fmt-1.yml", (1, 1), id="1"),
+ pytest.param("fmt-2.yml", (1, 1), id="2"),
+ pytest.param("fmt-3.yml", (1, 1), id="3"),
+ pytest.param("fmt-4.yml", (1, 1), id="4"),
+ pytest.param("fmt-5.yml", (1, 1), id="5"),
+ pytest.param("fmt-hex.yml", (1, 1), id="hex"),
),
)
def test_formatted_yaml_loader_dumper(
- yaml_formatting_fixtures: tuple[str, str, str],
- fixture_filename: str, # noqa: ARG001
+ fixture_filename: str,
+ version: tuple[int, int],
) -> None:
"""Ensure that FormattedYAML loads/dumps formatting fixtures consistently."""
- # pylint: disable=unused-argument
- before_content, prettier_content, after_content = yaml_formatting_fixtures
+ before_content, prettier_content, after_content = load_yaml_formatting_fixtures(
+ fixture_filename,
+ )
assert before_content != prettier_content
assert before_content != after_content
- yaml = ansiblelint.yaml_utils.FormattedYAML()
+ yaml = ansiblelint.yaml_utils.FormattedYAML(version=version)
- data_before = yaml.loads(before_content)
+ data_before = yaml.load(before_content)
dump_from_before = yaml.dumps(data_before)
- data_prettier = yaml.loads(prettier_content)
+ data_prettier = yaml.load(prettier_content)
dump_from_prettier = yaml.dumps(data_prettier)
- data_after = yaml.loads(after_content)
+ data_after = yaml.load(after_content)
dump_from_after = yaml.dumps(data_after)
# comparing data does not work because the Comment objects
@@ -274,7 +312,7 @@ def fixture_lintable(file_path: str) -> Lintable:
def fixture_ruamel_data(lintable: Lintable) -> CommentedMap | CommentedSeq:
"""Return the loaded YAML data for the Lintable."""
yaml = ansiblelint.yaml_utils.FormattedYAML()
- data: CommentedMap | CommentedSeq = yaml.loads(lintable.content)
+ data: CommentedMap | CommentedSeq = yaml.load(lintable.content)
return data
@@ -384,7 +422,7 @@ def fixture_ruamel_data(lintable: Lintable) -> CommentedMap | CommentedSeq:
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 10,
+ 12,
[1],
id="4_play_playbook-first_line_in_play_2",
),
@@ -402,7 +440,7 @@ def fixture_ruamel_data(lintable: Lintable) -> CommentedMap | CommentedSeq:
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 19,
+ 21,
[2],
id="4_play_playbook-first_line_in_play_3",
),
@@ -420,7 +458,7 @@ def fixture_ruamel_data(lintable: Lintable) -> CommentedMap | CommentedSeq:
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 28,
+ 31,
[3],
id="4_play_playbook-first_line_in_play_4",
),
@@ -601,7 +639,7 @@ def test_get_path_to_play(
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 7,
+ 8,
[0, "tasks", 0],
id="4_play_playbook-play_1_first_line_task_1",
),
@@ -613,7 +651,7 @@ def test_get_path_to_play(
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 10,
+ 13,
[],
id="4_play_playbook-play_2_line_before_tasks",
),
@@ -625,7 +663,7 @@ def test_get_path_to_play(
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 13,
+ 15,
[1, "tasks", 0],
id="4_play_playbook-play_2_first_line_task_1",
),
@@ -643,7 +681,7 @@ def test_get_path_to_play(
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 19,
+ 23,
[],
id="4_play_playbook-play_3_line_before_tasks",
),
@@ -655,7 +693,7 @@ def test_get_path_to_play(
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 23,
+ 25,
[2, "tasks", 0],
id="4_play_playbook-play_3_first_line_task_1",
),
@@ -673,7 +711,7 @@ def test_get_path_to_play(
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 28,
+ 33,
[],
id="4_play_playbook-play_4_line_before_tasks",
),
@@ -685,13 +723,13 @@ def test_get_path_to_play(
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 32,
+ 35,
[3, "tasks", 0],
id="4_play_playbook-play_4_first_line_task_1",
),
pytest.param(
"examples/playbooks/rule-partial-become-without-become-pass.yml",
- 33,
+ 39,
[3, "tasks", 0],
id="4_play_playbook-play_4_middle_line_task_1",
),
@@ -730,12 +768,12 @@ def test_get_path_to_play(
pytest.param(
"examples/playbooks/include.yml",
14,
- [0, "tasks", 1],
+ [0, "tasks", 2],
id="playbook-multi_tasks_blocks-tasks_last_task_before_handlers",
),
pytest.param(
"examples/playbooks/include.yml",
- 16,
+ 17,
[0, "handlers", 0],
id="playbook-multi_tasks_blocks-handlers_task",
),
@@ -953,3 +991,35 @@ def test_deannotate(
) -> None:
"""Ensure deannotate works as intended."""
assert ansiblelint.yaml_utils.deannotate(before) == after
+
+
+def test_yamllint_incompatible_config() -> None:
+ """Ensure we can detect incompatible yamllint settings."""
+ with (cwd(Path("examples/yamllint/incompatible-config")),):
+ config = ansiblelint.yaml_utils.load_yamllint_config()
+ assert config.incompatible
+
+
+@pytest.mark.parametrize(
+ ("yaml_version", "explicit_start"),
+ (
+ pytest.param((1, 1), True),
+ pytest.param((1, 1), False),
+ ),
+)
+def test_document_start(
+ yaml_version: tuple[int, int] | None,
+ explicit_start: bool,
+) -> None:
+ """Ensure the explicit_start config option from .yamllint is applied correctly."""
+ config = ansiblelint.yaml_utils.FormattedYAML.default_config
+ config["explicit_start"] = explicit_start
+
+ yaml = ansiblelint.yaml_utils.FormattedYAML(
+ version=yaml_version,
+ config=cast(dict[str, bool | int | str], config),
+ )
+ assert (
+ yaml.dumps(yaml.load(_SINGLE_QUOTE_WITHOUT_INDENTS)).startswith("---")
+ == explicit_start
+ )