diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:18:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:18:41 +0000 |
commit | b643c52cf29ce5bbab738b43290af3556efa1ca9 (patch) | |
tree | 21d5c53d7a9b696627a255777cefdf6f78968824 /ansible_collections/community/general/tests | |
parent | Releasing progress-linux version 9.5.1+dfsg-1~progress7.99u1. (diff) | |
download | ansible-b643c52cf29ce5bbab738b43290af3556efa1ca9.tar.xz ansible-b643c52cf29ce5bbab738b43290af3556efa1ca9.zip |
Merging upstream version 10.0.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/general/tests')
37 files changed, 918 insertions, 210 deletions
diff --git a/ansible_collections/community/general/tests/integration/targets/callback_timestamp/aliases b/ansible_collections/community/general/tests/integration/targets/callback_timestamp/aliases new file mode 100644 index 000000000..124adcfb8 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback_timestamp/aliases @@ -0,0 +1,6 @@ +# Copyright (c) 2024, kurokobo <kurokobo@protonmail.com> +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or <https://www.gnu.org/licenses/gpl-3.0.txt>) +# SPDX-License-Identifier: GPL-3.0-or-later + +azp/posix/1 +needs/target/callback diff --git a/ansible_collections/community/general/tests/integration/targets/callback_timestamp/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/callback_timestamp/tasks/main.yml new file mode 100644 index 000000000..5e0acc15f --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/callback_timestamp/tasks/main.yml @@ -0,0 +1,66 @@ +--- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +# Copyright (c) 2024, kurokobo <kurokobo@protonmail.com> +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Run tests + include_role: + name: callback + vars: + tests: + - name: Enable timestamp in the default length + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.timestamp + ANSIBLE_CALLBACK_TIMESTAMP_FORMAT_STRING: "15:04:05" + playbook: | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + expected_output: [ + "", + "PLAY [testhost] ******************************************************* 15:04:05", + "", + "TASK [Sample task name] *********************************************** 15:04:05", + "ok: [testhost] => {", + " \"msg\": \"sample debug msg\"", + "}", + "", + "PLAY RECAP ************************************************************ 15:04:05", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] + + - name: Enable timestamp in the longer length + environment: + ANSIBLE_NOCOLOR: 'true' + ANSIBLE_FORCE_COLOR: 'false' + ANSIBLE_STDOUT_CALLBACK: community.general.timestamp + ANSIBLE_CALLBACK_TIMESTAMP_FORMAT_STRING: "2006-01-02T15:04:05" + playbook: | + - hosts: testhost + gather_facts: false + tasks: + - name: Sample task name + debug: + msg: sample debug msg + expected_output: [ + "", + "PLAY [testhost] ******************************************** 2006-01-02T15:04:05", + "", + "TASK [Sample task name] ************************************ 2006-01-02T15:04:05", + "ok: [testhost] => {", + " \"msg\": \"sample debug msg\"", + "}", + "", + "PLAY RECAP ************************************************* 2006-01-02T15:04:05", + "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 " + ] diff --git a/ansible_collections/community/general/tests/integration/targets/cpanm/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/cpanm/tasks/main.yml index c9adc1ca6..89650154f 100644 --- a/ansible_collections/community/general/tests/integration/targets/cpanm/tasks/main.yml +++ b/ansible_collections/community/general/tests/integration/targets/cpanm/tasks/main.yml @@ -6,7 +6,8 @@ - name: bail out for non-supported platforms meta: end_play when: - - (ansible_os_family != "RedHat" or ansible_distribution_major_version|int < 7) + - (ansible_os_family != "RedHat" or ansible_distribution_major_version|int < 8) # TODO: bump back to 7 + - (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int < 8) # TODO: remove - ansible_os_family != "Debian" - name: install perl development package for Red Hat family diff --git a/ansible_collections/community/general/tests/integration/targets/django_manage/tasks/main.yaml b/ansible_collections/community/general/tests/integration/targets/django_manage/tasks/main.yaml index c07b53893..5307fb664 100644 --- a/ansible_collections/community/general/tests/integration/targets/django_manage/tasks/main.yaml +++ b/ansible_collections/community/general/tests/integration/targets/django_manage/tasks/main.yaml @@ -43,6 +43,11 @@ chdir: "{{ tmp_django_root.path }}/startproj" cmd: "{{ tmp_django_root.path }}/venv/bin/django-admin startapp app1" +- name: Make manage.py executable + file: + path: "{{ tmp_django_root.path }}/startproj/test_django_manage_1/manage.py" + mode: "0755" + - name: Check community.general.django_manage: project_path: "{{ tmp_django_root.path }}/startproj/test_django_manage_1" diff --git a/ansible_collections/community/general/tests/integration/targets/ejabberd_user/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ejabberd_user/tasks/main.yml index 33e07b785..349b3f952 100644 --- a/ansible_collections/community/general/tests/integration/targets/ejabberd_user/tasks/main.yml +++ b/ansible_collections/community/general/tests/integration/targets/ejabberd_user/tasks/main.yml @@ -10,7 +10,8 @@ - name: Bail out if not supported ansible.builtin.meta: end_play - when: ansible_distribution in ('Alpine', 'openSUSE Leap', 'CentOS', 'Fedora') + # TODO: remove Archlinux from the list + when: ansible_distribution in ('Alpine', 'openSUSE Leap', 'CentOS', 'Fedora', 'Archlinux') - name: Remove ejabberd diff --git a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/create_record.yml b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/create_record.yml index c3f1c1798..87056aa86 100644 --- a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/create_record.yml +++ b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/create_record.yml @@ -45,10 +45,10 @@ assert: that: - result is changed - - result.record['values'] == {{ item['values'] }} - - result.record.record == "{{ item.record }}" - - result.record.type == "{{ item.type }}" - - result.record.ttl == {{ item.ttl }} + - result.record['values'] == item['values'] + - result.record.record == item.record + - result.record.type == item.type + - result.record.ttl == item.ttl - name: test create a dns record idempotence community.general.gandi_livedns: @@ -63,7 +63,16 @@ assert: that: - result is not changed - - result.record['values'] == {{ item['values'] }} - - result.record.record == "{{ item.record }}" - - result.record.type == "{{ item.type }}" - - result.record.ttl == {{ item.ttl }} + - result.record['values'] == item['values'] + - result.record.record == item.record + - result.record.type == item.type + - result.record.ttl == item.ttl + +- name: test create a DNS record with personal access token + community.general.gandi_livedns: + personal_access_token: "{{ gandi_personal_access_token }}" + record: "{{ item.record }}" + domain: "{{ gandi_livedns_domain_name }}" + values: "{{ item['values'] }}" + ttl: "{{ item.ttl }}" + type: "{{ item.type }}" diff --git a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/update_record.yml b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/update_record.yml index a080560a7..5f19bfa24 100644 --- a/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/update_record.yml +++ b/ansible_collections/community/general/tests/integration/targets/gandi_livedns/tasks/update_record.yml @@ -17,10 +17,10 @@ assert: that: - result is changed - - result.record['values'] == {{ item.update_values | default(item['values']) }} - - result.record.record == "{{ item.record }}" - - result.record.type == "{{ item.type }}" - - result.record.ttl == {{ item.update_ttl | default(item.ttl) }} + - result.record['values'] == (item.update_values | default(item['values'])) + - result.record.record == item.record + - result.record.type == item.type + - result.record.ttl == (item.update_ttl | default(item.ttl)) - name: test update or add another dns record community.general.gandi_livedns: @@ -35,10 +35,10 @@ assert: that: - result is changed - - result.record['values'] == {{ item.update_values | default(item['values']) }} - - result.record.record == "{{ item.record }}" - - result.record.ttl == {{ item.update_ttl | default(item.ttl) }} - - result.record.type == "{{ item.type }}" + - result.record['values'] == (item.update_values | default(item['values'])) + - result.record.record == item.record + - result.record.ttl == (item.update_ttl | default(item.ttl)) + - result.record.type == item.type - name: test update or add another dns record idempotence community.general.gandi_livedns: @@ -53,7 +53,7 @@ assert: that: - result is not changed - - result.record['values'] == {{ item.update_values | default(item['values']) }} - - result.record.record == "{{ item.record }}" - - result.record.ttl == {{ item.update_ttl | default(item.ttl) }} - - result.record.type == "{{ item.type }}" + - result.record['values'] == (item.update_values | default(item['values'])) + - result.record.record == item.record + - result.record.ttl == (item.update_ttl | default(item.ttl)) + - result.record.type == item.type diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/homebrew/handlers/main.yml new file mode 100644 index 000000000..90a2e8017 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/homebrew/handlers/main.yml @@ -0,0 +1,11 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: uninstall docker + community.general.homebrew: + name: docker + state: absent + become: true + become_user: "{{ brew_stat.stat.pw_name }}" diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/casks.yml b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/casks.yml index 42d3515bf..ffbe67d15 100644 --- a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/casks.yml +++ b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/casks.yml @@ -12,13 +12,11 @@ - name: Find brew binary command: which brew register: brew_which - when: ansible_distribution in ['MacOSX'] - name: Get owner of brew binary stat: path: "{{ brew_which.stdout }}" register: brew_stat - when: ansible_distribution in ['MacOSX'] #- name: Use ignored-pinned option while upgrading all # homebrew: diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/docker.yml b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/docker.yml new file mode 100644 index 000000000..c7f282ba2 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/docker.yml @@ -0,0 +1,23 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: MACOS | Find brew binary + command: which brew + register: brew_which + +- name: MACOS | Get owner of brew binary + stat: + path: "{{ brew_which.stdout }}" + register: brew_stat + +- name: MACOS | Install docker + community.general.homebrew: + name: docker + state: present + force_formula: true + become: true + become_user: "{{ brew_stat.stat.pw_name }}" + notify: + - uninstall docker diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/formulae.yml b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/formulae.yml index 1db3ef1a6..1ca8d753e 100644 --- a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/formulae.yml +++ b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/formulae.yml @@ -12,13 +12,11 @@ - name: Find brew binary command: which brew register: brew_which - when: ansible_distribution in ['MacOSX'] - name: Get owner of brew binary stat: path: "{{ brew_which.stdout }}" register: brew_stat - when: ansible_distribution in ['MacOSX'] #- name: Use ignored-pinned option while upgrading all # homebrew: diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.yml index f5479917e..00d0bcf31 100644 --- a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.yml +++ b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.yml @@ -9,9 +9,8 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -- block: - - include_tasks: 'formulae.yml' - - when: ansible_distribution in ['MacOSX'] block: + - include_tasks: 'formulae.yml' - include_tasks: 'casks.yml' + - include_tasks: 'docker.yml' diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml index afad9740e..fa118ed1d 100644 --- a/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml +++ b/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml @@ -62,6 +62,7 @@ - result.existing == {} - result.end_state.alias == "{{ idp }}" - result.end_state.mappers != [] + - result.end_state.config.client_secret = "**********" - name: Update existing identity provider (no change) community.general.keycloak_identity_provider: diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh index 4e66476be..ada6908dd 100755 --- a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh +++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh @@ -14,3 +14,6 @@ ANSIBLE_MERGE_VARIABLES_PATTERN_TYPE=suffix \ ANSIBLE_LOG_PATH=/tmp/ansible-test-merge-variables \ ansible-playbook -i test_inventory_all_hosts.yml test_all_hosts.yml "$@" + +ANSIBLE_LOG_PATH=/tmp/ansible-test-merge-variables \ + ansible-playbook -i test_cross_host_merge_inventory.yml test_cross_host_merge_play.yml "$@" diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_cross_host_merge_inventory.yml b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_cross_host_merge_inventory.yml new file mode 100644 index 000000000..938457023 --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_cross_host_merge_inventory.yml @@ -0,0 +1,33 @@ +--- +# Copyright (c) 2020, Thales Netherlands +# Copyright (c) 2021, Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +common: + vars: + provider_instances: + servicedata1: + host: "{{ hostvars[groups['provider'] | first].inventory_hostname }}" + user: usr + pass: pwd + servicedata2: + host: down + user: usr2 + pass: pwd2 + hosts: + host1: + host2: + +consumer: + vars: + service_data: "{{ provider_instances.servicedata1 }}" + merge2__1: "{{ service_data }}" # service_data is a variable only known to host2, so normally it´s not available for host1 that is performing the merge + hosts: + host2: + +provider: + vars: + merge_result: "{{ lookup('community.general.merge_variables', 'merge2__', pattern_type='prefix', groups=['consumer']) }}" + hosts: + host1: diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_cross_host_merge_play.yml b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_cross_host_merge_play.yml new file mode 100644 index 000000000..51cd6f1ba --- /dev/null +++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_cross_host_merge_play.yml @@ -0,0 +1,21 @@ +--- +# Copyright (c) 2020, Thales Netherlands +# Copyright (c) 2021, Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Test merge_variables lookup plugin (merging host reference variables) + hosts: host1 + connection: local + gather_facts: false + tasks: + - name: Print merge result + ansible.builtin.debug: + msg: "{{ merge_result }}" + - name: Validate merge result + ansible.builtin.assert: + that: + - "merge_result | length == 3" + - "merge_result.host == 'host1'" + - "merge_result.user == 'usr'" + - "merge_result.pass == 'pwd'" diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/library/mdepfail.py b/ansible_collections/community/general/tests/integration/targets/module_helper/library/mdepfail.py index 92ebbde6e..b61c32a4d 100644 --- a/ansible_collections/community/general/tests/integration/targets/module_helper/library/mdepfail.py +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/library/mdepfail.py @@ -30,10 +30,10 @@ EXAMPLES = "" RETURN = "" +from ansible_collections.community.general.plugins.module_utils import deps from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper -from ansible.module_utils.basic import missing_required_lib -with ModuleHelper.dependency("nopackagewiththisname", missing_required_lib("nopackagewiththisname")): +with deps.declare("nopackagewiththisname"): import nopackagewiththisname # noqa: F401, pylint: disable=unused-import @@ -50,6 +50,7 @@ class MSimple(ModuleHelper): def __init_module__(self): self.vars.set('value', None) self.vars.set('abc', "abc", diff=True) + deps.validate(self.module) def __run__(self): if (0 if self.vars.a is None else self.vars.a) >= 100: diff --git a/ansible_collections/community/general/tests/integration/targets/module_helper/library/mstate.py b/ansible_collections/community/general/tests/integration/targets/module_helper/library/mstate.py index bfaab0375..b3b4ed5e6 100644 --- a/ansible_collections/community/general/tests/integration/targets/module_helper/library/mstate.py +++ b/ansible_collections/community/general/tests/integration/targets/module_helper/library/mstate.py @@ -49,6 +49,7 @@ class MState(StateModuleHelper): state=dict(type='str', choices=['join', 'b_x_a', 'c_x_a', 'both_x_a', 'nop'], default='join'), ), ) + use_old_vardict = False def __init_module__(self): self.vars.set('result', "abc", diff=True) diff --git a/ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml index 2a683617a..a2d8698d0 100644 --- a/ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml +++ b/ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml @@ -13,10 +13,9 @@ block: - name: Include test ansible.builtin.include_tasks: test.yml - # TODO: Find better package to install from a channel - microk8s installation takes multiple minutes, and even removal takes one minute! - # - name: Include test_channel - # ansible.builtin.include_tasks: test_channel.yml - # TODO: Find bettter package to download and install from sources - cider 1.6.0 takes over 35 seconds to install + - name: Include test_channel + ansible.builtin.include_tasks: test_channel.yml + # TODO: Find better package to download and install from sources - cider 1.6.0 takes over 35 seconds to install # - name: Include test_dangerous # ansible.builtin.include_tasks: test_dangerous.yml - name: Include test_3dash diff --git a/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_channel.yml b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_channel.yml index e9eb19c89..353735761 100644 --- a/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_channel.yml +++ b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_channel.yml @@ -5,47 +5,44 @@ # NOTE This is currently disabled for performance reasons! -- name: Make sure package is not installed (microk8s) +- name: Make sure package is not installed (wisdom) community.general.snap: - name: microk8s + name: wisdom state: absent # Test for https://github.com/ansible-collections/community.general/issues/1606 -- name: Install package (microk8s) +- name: Install package (wisdom) community.general.snap: - name: microk8s - classic: true + name: wisdom state: present - register: install_microk8s + register: install_wisdom -- name: Install package with channel (microk8s) +- name: Install package with channel (wisdom) community.general.snap: - name: microk8s - classic: true - channel: 1.20/stable + name: wisdom state: present - register: install_microk8s_chan + channel: latest/edge + register: install_wisdom_chan -- name: Install package with channel (microk8s) again +- name: Install package with channel (wisdom) again community.general.snap: - name: microk8s - classic: true - channel: 1.20/stable + name: wisdom state: present - register: install_microk8s_chan_again + channel: latest/edge + register: install_wisdom_chan_again -- name: Remove package (microk8s) +- name: Remove package (wisdom) community.general.snap: - name: microk8s + name: wisdom state: absent - register: remove_microk8s + register: remove_wisdom - assert: that: - - install_microk8s is changed - - install_microk8s_chan is changed - - install_microk8s_chan_again is not changed - - remove_microk8s is changed + - install_wisdom is changed + - install_wisdom_chan is changed + - install_wisdom_chan_again is not changed + - remove_wisdom is changed - name: Install package (shellcheck) community.general.snap: diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.13.txt b/ansible_collections/community/general/tests/sanity/ignore-2.13.txt index 0665ddc1a..6f6495dd1 100644 --- a/ansible_collections/community/general/tests/sanity/ignore-2.13.txt +++ b/ansible_collections/community/general/tests/sanity/ignore-2.13.txt @@ -1,4 +1,6 @@ .azure-pipelines/scripts/publish-codecov.py replace-urlopen +plugins/callback/timestamp.py validate-modules:invalid-documentation +plugins/callback/yaml.py validate-modules:invalid-documentation plugins/lookup/etcd.py validate-modules:invalid-documentation plugins/lookup/etcd3.py validate-modules:invalid-documentation plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice @@ -6,9 +8,6 @@ plugins/modules/iptables_state.py validate-modules:undocumented-parameter plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0 -plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0 -plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0 plugins/modules/read_csv.py validate-modules:invalid-documentation plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice plugins/modules/xfconf.py validate-modules:return-syntax-error diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.14.txt b/ansible_collections/community/general/tests/sanity/ignore-2.14.txt index fed147e44..24d752103 100644 --- a/ansible_collections/community/general/tests/sanity/ignore-2.14.txt +++ b/ansible_collections/community/general/tests/sanity/ignore-2.14.txt @@ -1,4 +1,6 @@ .azure-pipelines/scripts/publish-codecov.py replace-urlopen +plugins/callback/timestamp.py validate-modules:invalid-documentation +plugins/callback/yaml.py validate-modules:invalid-documentation plugins/lookup/etcd.py validate-modules:invalid-documentation plugins/lookup/etcd3.py validate-modules:invalid-documentation plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice @@ -7,9 +9,6 @@ plugins/modules/iptables_state.py validate-modules:undocumented-parameter plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0 -plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0 -plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0 plugins/modules/read_csv.py validate-modules:invalid-documentation plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt' diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.15.txt b/ansible_collections/community/general/tests/sanity/ignore-2.15.txt index d4c92c4d9..667c6cee4 100644 --- a/ansible_collections/community/general/tests/sanity/ignore-2.15.txt +++ b/ansible_collections/community/general/tests/sanity/ignore-2.15.txt @@ -5,9 +5,6 @@ plugins/modules/iptables_state.py validate-modules:undocumented-parameter plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0 -plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0 -plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0 plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt' plugins/modules/xfconf.py validate-modules:return-syntax-error diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.16.txt b/ansible_collections/community/general/tests/sanity/ignore-2.16.txt index 397c6d986..f6b058ec6 100644 --- a/ansible_collections/community/general/tests/sanity/ignore-2.16.txt +++ b/ansible_collections/community/general/tests/sanity/ignore-2.16.txt @@ -5,9 +5,6 @@ plugins/modules/iptables_state.py validate-modules:undocumented-parameter plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0 -plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0 -plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0 plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt' plugins/modules/udm_user.py import-3.12 # Uses deprecated stdlib library 'crypt' diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.17.txt b/ansible_collections/community/general/tests/sanity/ignore-2.17.txt index d75aaeac2..806c4c5fc 100644 --- a/ansible_collections/community/general/tests/sanity/ignore-2.17.txt +++ b/ansible_collections/community/general/tests/sanity/ignore-2.17.txt @@ -5,13 +5,11 @@ plugins/modules/iptables_state.py validate-modules:undocumented-parameter plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0 -plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0 -plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0 plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt' plugins/modules/udm_user.py import-3.12 # Uses deprecated stdlib library 'crypt' plugins/modules/xfconf.py validate-modules:return-syntax-error plugins/module_utils/univention_umc.py pylint:use-yield-from # suggested construct does not work with Python 2 tests/unit/compat/mock.py pylint:use-yield-from # suggested construct does not work with Python 2 +tests/unit/plugins/modules/helper.py pylint:use-yield-from # suggested construct does not work with Python 2 tests/unit/plugins/modules/test_gio_mime.yaml no-smart-quotes diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.18.txt b/ansible_collections/community/general/tests/sanity/ignore-2.18.txt index d75aaeac2..806c4c5fc 100644 --- a/ansible_collections/community/general/tests/sanity/ignore-2.18.txt +++ b/ansible_collections/community/general/tests/sanity/ignore-2.18.txt @@ -5,13 +5,11 @@ plugins/modules/iptables_state.py validate-modules:undocumented-parameter plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice plugins/modules/parted.py validate-modules:parameter-state-invalid-choice -plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0 -plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0 -plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0 plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt' plugins/modules/udm_user.py import-3.12 # Uses deprecated stdlib library 'crypt' plugins/modules/xfconf.py validate-modules:return-syntax-error plugins/module_utils/univention_umc.py pylint:use-yield-from # suggested construct does not work with Python 2 tests/unit/compat/mock.py pylint:use-yield-from # suggested construct does not work with Python 2 +tests/unit/plugins/modules/helper.py pylint:use-yield-from # suggested construct does not work with Python 2 tests/unit/plugins/modules/test_gio_mime.yaml no-smart-quotes diff --git a/ansible_collections/community/general/tests/unit/plugins/become/test_run0.py b/ansible_collections/community/general/tests/unit/plugins/become/test_run0.py new file mode 100644 index 000000000..7507c556e --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/become/test_run0.py @@ -0,0 +1,64 @@ +# Copyright (c) 2024 Ansible Project +# +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +import re + +from ansible import context + +from .helper import call_become_plugin + + +def test_run0_basic(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/sh" + run0_exe = "run0" + + success = "BECOME-SUCCESS-.+?" + + task = { + "become_method": "community.general.run0", + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + assert ( + re.match( + f"{run0_exe} --user=root {default_exe} -c 'echo {success}; {default_cmd}'", + cmd, + ) + is not None + ) + + +def test_run0_flags(mocker, parser, reset_cli_args): + options = parser.parse_args([]) + context._init_global_context(options) + + default_cmd = "/bin/foo" + default_exe = "/bin/sh" + run0_exe = "run0" + run0_flags = "--nice=15" + + success = "BECOME-SUCCESS-.+?" + + task = { + "become_method": "community.general.run0", + "become_flags": run0_flags, + } + var_options = {} + cmd = call_become_plugin(task, var_options, cmd=default_cmd, executable=default_exe) + assert ( + re.match( + f"{run0_exe} --user=root --nice=15 {default_exe} -c 'echo {success}; {default_cmd}'", + cmd, + ) + is not None + ) diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py index 66cb2f08b..ba8209439 100644 --- a/ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py +++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py @@ -18,6 +18,17 @@ from ansible_collections.community.general.plugins.lookup import merge_variables class TestMergeVariablesLookup(unittest.TestCase): + class HostVarsMock(dict): + + def __getattr__(self, item): + return super().__getitem__(item) + + def __setattr__(self, item, value): + return super().__setitem__(item, value) + + def raw_get(self, host): + return super().__getitem__(host) + def setUp(self): self.loader = DictDataLoader({}) self.templar = Templar(loader=self.loader, variables={}) @@ -141,25 +152,28 @@ class TestMergeVariablesLookup(unittest.TestCase): {'var': [{'item5': 'value5', 'item6': 'value6'}]}, ]) def test_merge_dict_group_all(self, mock_set_options, mock_get_option, mock_template): - results = self.merge_vars_lookup.run(['__merge_var'], { - 'inventory_hostname': 'host1', - 'hostvars': { - 'host1': { - 'group_names': ['dummy1'], - 'inventory_hostname': 'host1', - '1testlist__merge_var': { - 'var': [{'item1': 'value1', 'item2': 'value2'}] - } - }, - 'host2': { - 'group_names': ['dummy1'], - 'inventory_hostname': 'host2', - '2otherlist__merge_var': { - 'var': [{'item5': 'value5', 'item6': 'value6'}] - } + hostvars = self.HostVarsMock({ + 'host1': { + 'group_names': ['dummy1'], + 'inventory_hostname': 'host1', + '1testlist__merge_var': { + 'var': [{'item1': 'value1', 'item2': 'value2'}] + } + }, + 'host2': { + 'group_names': ['dummy1'], + 'inventory_hostname': 'host2', + '2otherlist__merge_var': { + 'var': [{'item5': 'value5', 'item6': 'value6'}] } } }) + variables = { + 'inventory_hostname': 'host1', + 'hostvars': hostvars + } + + results = self.merge_vars_lookup.run(['__merge_var'], variables) self.assertEqual(results, [ {'var': [ @@ -175,32 +189,35 @@ class TestMergeVariablesLookup(unittest.TestCase): {'var': [{'item5': 'value5', 'item6': 'value6'}]}, ]) def test_merge_dict_group_single(self, mock_set_options, mock_get_option, mock_template): - results = self.merge_vars_lookup.run(['__merge_var'], { - 'inventory_hostname': 'host1', - 'hostvars': { - 'host1': { - 'group_names': ['dummy1'], - 'inventory_hostname': 'host1', - '1testlist__merge_var': { - 'var': [{'item1': 'value1', 'item2': 'value2'}] - } - }, - 'host2': { - 'group_names': ['dummy1'], - 'inventory_hostname': 'host2', - '2otherlist__merge_var': { - 'var': [{'item5': 'value5', 'item6': 'value6'}] - } - }, - 'host3': { - 'group_names': ['dummy2'], - 'inventory_hostname': 'host3', - '3otherlist__merge_var': { - 'var': [{'item3': 'value3', 'item4': 'value4'}] - } + hostvars = self.HostVarsMock({ + 'host1': { + 'group_names': ['dummy1'], + 'inventory_hostname': 'host1', + '1testlist__merge_var': { + 'var': [{'item1': 'value1', 'item2': 'value2'}] + } + }, + 'host2': { + 'group_names': ['dummy1'], + 'inventory_hostname': 'host2', + '2otherlist__merge_var': { + 'var': [{'item5': 'value5', 'item6': 'value6'}] + } + }, + 'host3': { + 'group_names': ['dummy2'], + 'inventory_hostname': 'host3', + '3otherlist__merge_var': { + 'var': [{'item3': 'value3', 'item4': 'value4'}] } } }) + variables = { + 'inventory_hostname': 'host1', + 'hostvars': hostvars + } + + results = self.merge_vars_lookup.run(['__merge_var'], variables) self.assertEqual(results, [ {'var': [ @@ -216,32 +233,34 @@ class TestMergeVariablesLookup(unittest.TestCase): {'var': [{'item5': 'value5', 'item6': 'value6'}]}, ]) def test_merge_dict_group_multiple(self, mock_set_options, mock_get_option, mock_template): - results = self.merge_vars_lookup.run(['__merge_var'], { - 'inventory_hostname': 'host1', - 'hostvars': { - 'host1': { - 'group_names': ['dummy1'], - 'inventory_hostname': 'host1', - '1testlist__merge_var': { - 'var': [{'item1': 'value1', 'item2': 'value2'}] - } - }, - 'host2': { - 'group_names': ['dummy2'], - 'inventory_hostname': 'host2', - '2otherlist__merge_var': { - 'var': [{'item5': 'value5', 'item6': 'value6'}] - } - }, - 'host3': { - 'group_names': ['dummy3'], - 'inventory_hostname': 'host3', - '3otherlist__merge_var': { - 'var': [{'item3': 'value3', 'item4': 'value4'}] - } + hostvars = self.HostVarsMock({ + 'host1': { + 'group_names': ['dummy1'], + 'inventory_hostname': 'host1', + '1testlist__merge_var': { + 'var': [{'item1': 'value1', 'item2': 'value2'}] + } + }, + 'host2': { + 'group_names': ['dummy2'], + 'inventory_hostname': 'host2', + '2otherlist__merge_var': { + 'var': [{'item5': 'value5', 'item6': 'value6'}] + } + }, + 'host3': { + 'group_names': ['dummy3'], + 'inventory_hostname': 'host3', + '3otherlist__merge_var': { + 'var': [{'item3': 'value3', 'item4': 'value4'}] } } }) + variables = { + 'inventory_hostname': 'host1', + 'hostvars': hostvars + } + results = self.merge_vars_lookup.run(['__merge_var'], variables) self.assertEqual(results, [ {'var': [ @@ -257,26 +276,27 @@ class TestMergeVariablesLookup(unittest.TestCase): ['item5'], ]) def test_merge_list_group_multiple(self, mock_set_options, mock_get_option, mock_template): - print() - results = self.merge_vars_lookup.run(['__merge_var'], { - 'inventory_hostname': 'host1', - 'hostvars': { - 'host1': { - 'group_names': ['dummy1'], - 'inventory_hostname': 'host1', - '1testlist__merge_var': ['item1'] - }, - 'host2': { - 'group_names': ['dummy2'], - 'inventory_hostname': 'host2', - '2otherlist__merge_var': ['item5'] - }, - 'host3': { - 'group_names': ['dummy3'], - 'inventory_hostname': 'host3', - '3otherlist__merge_var': ['item3'] - } + hostvars = self.HostVarsMock({ + 'host1': { + 'group_names': ['dummy1'], + 'inventory_hostname': 'host1', + '1testlist__merge_var': ['item1'] + }, + 'host2': { + 'group_names': ['dummy2'], + 'inventory_hostname': 'host2', + '2otherlist__merge_var': ['item5'] + }, + 'host3': { + 'group_names': ['dummy3'], + 'inventory_hostname': 'host3', + '3otherlist__merge_var': ['item3'] } }) + variables = { + 'inventory_hostname': 'host1', + 'hostvars': hostvars + } + results = self.merge_vars_lookup.run(['__merge_var'], variables) self.assertEqual(results, [['item1', 'item5']]) diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py index 86576e8ce..6816afb34 100644 --- a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py @@ -7,6 +7,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type from sys import version_info +from functools import partial import pytest @@ -15,55 +16,64 @@ from ansible_collections.community.general.plugins.module_utils.cmd_runner impor TC_FORMATS = dict( - simple_boolean__true=(cmd_runner_fmt.as_bool, ("--superflag",), True, ["--superflag"]), - simple_boolean__false=(cmd_runner_fmt.as_bool, ("--superflag",), False, []), - simple_boolean__none=(cmd_runner_fmt.as_bool, ("--superflag",), None, []), - simple_boolean_both__true=(cmd_runner_fmt.as_bool, ("--superflag", "--falseflag"), True, ["--superflag"]), - simple_boolean_both__false=(cmd_runner_fmt.as_bool, ("--superflag", "--falseflag"), False, ["--falseflag"]), - simple_boolean_both__none=(cmd_runner_fmt.as_bool, ("--superflag", "--falseflag"), None, ["--falseflag"]), - simple_boolean_both__none_ig=(cmd_runner_fmt.as_bool, ("--superflag", "--falseflag", True), None, []), - simple_boolean_not__true=(cmd_runner_fmt.as_bool_not, ("--superflag",), True, []), - simple_boolean_not__false=(cmd_runner_fmt.as_bool_not, ("--superflag",), False, ["--superflag"]), - simple_boolean_not__none=(cmd_runner_fmt.as_bool_not, ("--superflag",), None, ["--superflag"]), - simple_optval__str=(cmd_runner_fmt.as_optval, ("-t",), "potatoes", ["-tpotatoes"]), - simple_optval__int=(cmd_runner_fmt.as_optval, ("-t",), 42, ["-t42"]), - simple_opt_val__str=(cmd_runner_fmt.as_opt_val, ("-t",), "potatoes", ["-t", "potatoes"]), - simple_opt_val__int=(cmd_runner_fmt.as_opt_val, ("-t",), 42, ["-t", "42"]), - simple_opt_eq_val__str=(cmd_runner_fmt.as_opt_eq_val, ("--food",), "potatoes", ["--food=potatoes"]), - simple_opt_eq_val__int=(cmd_runner_fmt.as_opt_eq_val, ("--answer",), 42, ["--answer=42"]), - simple_list_potato=(cmd_runner_fmt.as_list, (), "literal_potato", ["literal_potato"]), - simple_list_42=(cmd_runner_fmt.as_list, (), 42, ["42"]), - simple_map=(cmd_runner_fmt.as_map, ({'a': 1, 'b': 2, 'c': 3},), 'b', ["2"]), - simple_default_type__list=(cmd_runner_fmt.as_default_type, ("list",), [1, 2, 3, 5, 8], ["--1", "--2", "--3", "--5", "--8"]), - simple_default_type__bool_true=(cmd_runner_fmt.as_default_type, ("bool", "what"), True, ["--what"]), - simple_default_type__bool_false=(cmd_runner_fmt.as_default_type, ("bool", "what"), False, []), - simple_default_type__potato=(cmd_runner_fmt.as_default_type, ("any-other-type", "potato"), "42", ["--potato", "42"]), - simple_fixed_true=(cmd_runner_fmt.as_fixed, [("--always-here", "--forever")], True, ["--always-here", "--forever"]), - simple_fixed_false=(cmd_runner_fmt.as_fixed, [("--always-here", "--forever")], False, ["--always-here", "--forever"]), - simple_fixed_none=(cmd_runner_fmt.as_fixed, [("--always-here", "--forever")], None, ["--always-here", "--forever"]), - simple_fixed_str=(cmd_runner_fmt.as_fixed, [("--always-here", "--forever")], "something", ["--always-here", "--forever"]), + simple_boolean__true=(partial(cmd_runner_fmt.as_bool, "--superflag"), True, ["--superflag"], None), + simple_boolean__false=(partial(cmd_runner_fmt.as_bool, "--superflag"), False, [], None), + simple_boolean__none=(partial(cmd_runner_fmt.as_bool, "--superflag"), None, [], None), + simple_boolean_both__true=(partial(cmd_runner_fmt.as_bool, "--superflag", "--falseflag"), True, ["--superflag"], None), + simple_boolean_both__false=(partial(cmd_runner_fmt.as_bool, "--superflag", "--falseflag"), False, ["--falseflag"], None), + simple_boolean_both__none=(partial(cmd_runner_fmt.as_bool, "--superflag", "--falseflag"), None, ["--falseflag"], None), + simple_boolean_both__none_ig=(partial(cmd_runner_fmt.as_bool, "--superflag", "--falseflag", True), None, [], None), + simple_boolean_not__true=(partial(cmd_runner_fmt.as_bool_not, "--superflag"), True, [], None), + simple_boolean_not__false=(partial(cmd_runner_fmt.as_bool_not, "--superflag"), False, ["--superflag"], None), + simple_boolean_not__none=(partial(cmd_runner_fmt.as_bool_not, "--superflag"), None, ["--superflag"], None), + simple_optval__str=(partial(cmd_runner_fmt.as_optval, "-t"), "potatoes", ["-tpotatoes"], None), + simple_optval__int=(partial(cmd_runner_fmt.as_optval, "-t"), 42, ["-t42"], None), + simple_opt_val__str=(partial(cmd_runner_fmt.as_opt_val, "-t"), "potatoes", ["-t", "potatoes"], None), + simple_opt_val__int=(partial(cmd_runner_fmt.as_opt_val, "-t"), 42, ["-t", "42"], None), + simple_opt_eq_val__str=(partial(cmd_runner_fmt.as_opt_eq_val, "--food"), "potatoes", ["--food=potatoes"], None), + simple_opt_eq_val__int=(partial(cmd_runner_fmt.as_opt_eq_val, "--answer"), 42, ["--answer=42"], None), + simple_list_potato=(cmd_runner_fmt.as_list, "literal_potato", ["literal_potato"], None), + simple_list_42=(cmd_runner_fmt.as_list, 42, ["42"], None), + simple_list_min_len_ok=(partial(cmd_runner_fmt.as_list, min_len=1), 42, ["42"], None), + simple_list_min_len_fail=(partial(cmd_runner_fmt.as_list, min_len=10), 42, None, ValueError), + simple_list_max_len_ok=(partial(cmd_runner_fmt.as_list, max_len=1), 42, ["42"], None), + simple_list_max_len_fail=(partial(cmd_runner_fmt.as_list, max_len=2), [42, 42, 42], None, ValueError), + simple_map=(partial(cmd_runner_fmt.as_map, {'a': 1, 'b': 2, 'c': 3}), 'b', ["2"], None), + simple_default_type__list=(partial(cmd_runner_fmt.as_default_type, "list"), [1, 2, 3, 5, 8], ["--1", "--2", "--3", "--5", "--8"], None), + simple_default_type__bool_true=(partial(cmd_runner_fmt.as_default_type, "bool", "what"), True, ["--what"], None), + simple_default_type__bool_false=(partial(cmd_runner_fmt.as_default_type, "bool", "what"), False, [], None), + simple_default_type__potato=(partial(cmd_runner_fmt.as_default_type, "any-other-type", "potato"), "42", ["--potato", "42"], None), + simple_fixed_true=(partial(cmd_runner_fmt.as_fixed, ["--always-here", "--forever"]), True, ["--always-here", "--forever"], None), + simple_fixed_false=(partial(cmd_runner_fmt.as_fixed, ["--always-here", "--forever"]), False, ["--always-here", "--forever"], None), + simple_fixed_none=(partial(cmd_runner_fmt.as_fixed, ["--always-here", "--forever"]), None, ["--always-here", "--forever"], None), + simple_fixed_str=(partial(cmd_runner_fmt.as_fixed, ["--always-here", "--forever"]), "something", ["--always-here", "--forever"], None), ) if tuple(version_info) >= (3, 1): from collections import OrderedDict # needs OrderedDict to provide a consistent key order TC_FORMATS["simple_default_type__dict"] = ( # type: ignore - cmd_runner_fmt.as_default_type, - ("dict",), + partial(cmd_runner_fmt.as_default_type, "dict"), OrderedDict((('a', 1), ('b', 2))), - ["--a=1", "--b=2"] + ["--a=1", "--b=2"], + None ) TC_FORMATS_IDS = sorted(TC_FORMATS.keys()) -@pytest.mark.parametrize('func, fmt_opt, value, expected', +@pytest.mark.parametrize('func, value, expected, exception', (TC_FORMATS[tc] for tc in TC_FORMATS_IDS), ids=TC_FORMATS_IDS) -def test_arg_format(func, fmt_opt, value, expected): - fmt_func = func(*fmt_opt) - actual = fmt_func(value, ctx_ignore_none=True) - print("formatted string = {0}".format(actual)) - assert actual == expected, "actual = {0}".format(actual) +def test_arg_format(func, value, expected, exception): + fmt_func = func() + try: + actual = fmt_func(value, ctx_ignore_none=True) + print("formatted string = {0}".format(actual)) + assert actual == expected, "actual = {0}".format(actual) + except Exception as e: + if exception is None: + raise + assert isinstance(e, exception) TC_RUNNER = dict( diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_python_runner.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_python_runner.py new file mode 100644 index 000000000..015065bdd --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_python_runner.py @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2024, Alexei Znamensky <russoz@gmail.com> +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os + +import pytest + +from ansible_collections.community.general.tests.unit.compat.mock import MagicMock, PropertyMock +from ansible_collections.community.general.plugins.module_utils.cmd_runner import cmd_runner_fmt +from ansible_collections.community.general.plugins.module_utils.python_runner import PythonRunner + + +TC_RUNNER = dict( + # SAMPLE: This shows all possible elements of a test case. It does not actually run. + # + # testcase_name=( + # # input + # dict( + # args_bundle = dict( + # param1=dict( + # type="int", + # value=11, + # fmt_func=cmd_runner_fmt.as_opt_eq_val, + # fmt_arg="--answer", + # ), + # param2=dict( + # fmt_func=cmd_runner_fmt.as_bool, + # fmt_arg="--bb-here", + # ) + # ), + # runner_init_args = dict( + # command="testing", + # default_args_order=(), + # check_rc=False, + # force_lang="C", + # path_prefix=None, + # environ_update=None, + # ), + # runner_ctx_args = dict( + # args_order=['aa', 'bb'], + # output_process=None, + # ignore_value_none=True, + # ), + # ), + # # command execution + # dict( + # runner_ctx_run_args = dict(bb=True), + # rc = 0, + # out = "", + # err = "", + # ), + # # expected + # dict( + # results=(), + # run_info=dict( + # cmd=['/mock/bin/testing', '--answer=11', '--bb-here'], + # environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + # ), + # exc=None, + # ), + # ), + # + aa_bb=( + dict( + args_bundle=dict( + aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"), + bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"), + ), + runner_init_args=dict(command="testing"), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(bb=True), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/python', 'testing', '--answer=11', '--bb-here'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), + aa_bb_py3=( + dict( + args_bundle=dict( + aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"), + bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"), + ), + runner_init_args=dict(command="toasting", python="python3"), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(bb=True), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/mock/bin/python3', 'toasting', '--answer=11', '--bb-here'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), + aa_bb_abspath=( + dict( + args_bundle=dict( + aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"), + bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"), + ), + runner_init_args=dict(command="toasting", python="/crazy/local/bin/python3"), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(bb=True), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/crazy/local/bin/python3', 'toasting', '--answer=11', '--bb-here'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}, + args_order=('aa', 'bb'), + ), + ), + ), + aa_bb_venv=( + dict( + args_bundle=dict( + aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"), + bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"), + ), + runner_init_args=dict(command="toasting", venv="/venv"), + runner_ctx_args=dict(args_order=['aa', 'bb']), + ), + dict(runner_ctx_run_args=dict(bb=True), rc=0, out="", err=""), + dict( + run_info=dict( + cmd=['/venv/bin/python', 'toasting', '--answer=11', '--bb-here'], + environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C', 'VIRTUAL_ENV': '/venv', 'PATH': '/venv/bin'}, + args_order=('aa', 'bb'), + ), + ), + ), +) +TC_RUNNER_IDS = sorted(TC_RUNNER.keys()) + + +@pytest.mark.parametrize('runner_input, cmd_execution, expected', + (TC_RUNNER[tc] for tc in TC_RUNNER_IDS), + ids=TC_RUNNER_IDS) +def test_runner_context(runner_input, cmd_execution, expected): + arg_spec = {} + params = {} + arg_formats = {} + for k, v in runner_input['args_bundle'].items(): + try: + arg_spec[k] = {'type': v['type']} + except KeyError: + pass + try: + params[k] = v['value'] + except KeyError: + pass + try: + arg_formats[k] = v['fmt_func'](v['fmt_arg']) + except KeyError: + pass + + orig_results = tuple(cmd_execution[x] for x in ('rc', 'out', 'err')) + + print("arg_spec={0}\nparams={1}\narg_formats={2}\n".format( + arg_spec, + params, + arg_formats, + )) + + module = MagicMock() + type(module).argument_spec = PropertyMock(return_value=arg_spec) + type(module).params = PropertyMock(return_value=params) + module.get_bin_path.return_value = os.path.join( + runner_input["runner_init_args"].get("venv", "/mock"), + "bin", + runner_input["runner_init_args"].get("python", "python") + ) + module.run_command.return_value = orig_results + + runner = PythonRunner( + module=module, + arg_formats=arg_formats, + **runner_input['runner_init_args'] + ) + + def _extract_path(run_info): + path = run_info.get("environ_update", {}).get("PATH") + if path is not None: + run_info["environ_update"] = dict((k, v) + for k, v in run_info["environ_update"].items() + if k != "PATH") + return run_info, path + + def _assert_run_info_env_path(actual, expected): + actual2 = set(actual.split(":")) + assert expected in actual2, "Missing expected path {0} in output PATH: {1}".format(expected, actual) + + def _assert_run_info(actual, expected): + reduced = dict((k, actual[k]) for k in expected.keys()) + reduced, act_path = _extract_path(reduced) + expected, exp_path = _extract_path(expected) + if exp_path is not None: + _assert_run_info_env_path(act_path, exp_path) + assert reduced == expected, "{0}".format(reduced) + + def _assert_run(expected, ctx, results): + _assert_run_info(ctx.run_info, expected['run_info']) + assert results == expected.get('results', orig_results) + + exc = expected.get("exc") + if exc: + with pytest.raises(exc): + with runner.context(**runner_input['runner_ctx_args']) as ctx: + results = ctx.run(**cmd_execution['runner_ctx_run_args']) + _assert_run(expected, ctx, results) + + else: + with runner.context(**runner_input['runner_ctx_args']) as ctx: + results = ctx.run(**cmd_execution['runner_ctx_run_args']) + _assert_run(expected, ctx, results) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/helper.py b/ansible_collections/community/general/tests/unit/plugins/modules/helper.py index a7322bf4d..e012980af 100644 --- a/ansible_collections/community/general/tests/unit/plugins/modules/helper.py +++ b/ansible_collections/community/general/tests/unit/plugins/modules/helper.py @@ -9,7 +9,6 @@ __metaclass__ = type import sys import json from collections import namedtuple -from itertools import chain, repeat import pytest import yaml @@ -52,9 +51,9 @@ class _BaseContext(object): test_flags = self.test_flags() if test_flags.get("skip"): - pytest.skip() + pytest.skip(test_flags.get("skip")) if test_flags.get("xfail"): - pytest.xfail() + pytest.xfail(test_flags.get("xfail")) func() @@ -76,12 +75,21 @@ class _RunCmdContext(_BaseContext): self.mock_run_cmd = self._make_mock_run_cmd() def _make_mock_run_cmd(self): - call_results = [(x.rc, x.out, x.err) for x in self.run_cmd_calls] - error_call_results = (123, - "OUT: testcase has not enough run_command calls", - "ERR: testcase has not enough run_command calls") + def _results(): + for result in [(x.rc, x.out, x.err) for x in self.run_cmd_calls]: + yield result + raise Exception("testcase has not enough run_command calls") + + results = _results() + + def side_effect(self_, **kwargs): + result = next(results) + if kwargs.get("check_rc", False) and result[0] != 0: + raise Exception("rc = {0}".format(result[0])) + return result + mock_run_command = self.mocker.patch('ansible.module_utils.basic.AnsibleModule.run_command', - side_effect=chain(call_results, repeat(error_call_results))) + side_effect=side_effect) return mock_run_command def check_results(self, results): diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.yaml index 3ed718d48..4eed95720 100644 --- a/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.yaml +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.yaml @@ -7,6 +7,7 @@ - id: install_dancer_compatibility input: name: Dancer + mode: compatibility output: changed: true run_command_calls: @@ -23,6 +24,7 @@ - id: install_dancer_already_installed_compatibility input: name: Dancer + mode: compatibility output: changed: false run_command_calls: @@ -34,7 +36,6 @@ - id: install_dancer input: name: Dancer - mode: new output: changed: true run_command_calls: @@ -46,6 +47,7 @@ - id: install_distribution_file_compatibility input: name: MIYAGAWA/Plack-0.99_05.tar.gz + mode: compatibility output: changed: true run_command_calls: @@ -57,7 +59,6 @@ - id: install_distribution_file input: name: MIYAGAWA/Plack-0.99_05.tar.gz - mode: new output: changed: true run_command_calls: diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_django_command.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_command.py new file mode 100644 index 000000000..ffa9feb39 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_command.py @@ -0,0 +1,13 @@ +# Copyright (c) Alexei Znamensky (russoz@gmail.com) +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +from ansible_collections.community.general.plugins.modules import django_command +from .helper import Helper + + +Helper.from_module(django_command, __name__) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_django_command.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_command.yaml new file mode 100644 index 000000000..046dd87f0 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_django_command.yaml @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Copyright (c) Alexei Znamensky (russoz@gmail.com) +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +--- +- id: command_success + input: + command: check + extra_args: + - babaloo + - yaba + - daba + - doo + settings: whatever.settings + run_command_calls: + - command: [/testbin/python, -m, django, check, --no-color, --settings=whatever.settings, babaloo, yaba, daba, doo] + environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true} + rc: 0 + out: "whatever\n" + err: "" +- id: command_fail + input: + command: check + extra_args: + - babaloo + - yaba + - daba + - doo + settings: whatever.settings + output: + failed: true + run_command_calls: + - command: [/testbin/python, -m, django, check, --no-color, --settings=whatever.settings, babaloo, yaba, daba, doo] + environ: *env-def + rc: 1 + out: "whatever\n" + err: "" diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew.py index f849b433d..d04ca4de5 100644 --- a/ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew.py +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_homebrew.py @@ -2,23 +2,28 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function __metaclass__ = type from ansible_collections.community.general.tests.unit.compat import unittest -from ansible_collections.community.general.plugins.modules.homebrew import Homebrew +from ansible_collections.community.general.plugins.module_utils.homebrew import HomebrewValidate class TestHomebrewModule(unittest.TestCase): def setUp(self): - self.brew_app_names = [ - 'git-ssh', - 'awscli@1', - 'bash' + self.brew_app_names = ["git-ssh", "awscli@1", "bash"] + + self.invalid_names = [ + "git ssh", + "git*", ] def test_valid_package_names(self): for name in self.brew_app_names: - self.assertTrue(Homebrew.valid_package(name)) + self.assertTrue(HomebrewValidate.valid_package(name)) + + def test_invalid_package_names(self): + for name in self.invalid_names: + self.assertFalse(HomebrewValidate.valid_package(name)) diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.yaml index 308be9797..7909403cf 100644 --- a/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.yaml +++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.yaml @@ -190,3 +190,35 @@ rc: 0 out: "" err: "" +- id: puppet_agent_waitforlock + input: + waitforlock: 30 + output: + changed: false + run_command_calls: + - command: [/testbin/puppet, config, print, agent_disabled_lockfile] + environ: *env-def + rc: 0 + out: "blah, anything" + err: "" + - command: + - /testbin/timeout + - -s + - "9" + - 30m + - /testbin/puppet + - agent + - --onetime + - --no-daemonize + - --no-usecacheonfailure + - --no-splay + - --detailed-exitcodes + - --verbose + - --color + - "0" + - --waitforlock + - "30" + environ: *env-def + rc: 0 + out: "" + err: "" diff --git a/ansible_collections/community/general/tests/unit/plugins/plugin_utils/test_unsafe.py b/ansible_collections/community/general/tests/unit/plugins/plugin_utils/test_unsafe.py new file mode 100644 index 000000000..3f35ee933 --- /dev/null +++ b/ansible_collections/community/general/tests/unit/plugins/plugin_utils/test_unsafe.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2024, Felix Fontein <felix@fontein.de> +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +import pytest + +from ansible.utils.unsafe_proxy import AnsibleUnsafe + +from ansible_collections.community.general.plugins.plugin_utils.unsafe import ( + make_unsafe, +) + + +TEST_MAKE_UNSAFE = [ + ( + u'text', + [], + [ + (), + ], + ), + ( + u'{{text}}', + [ + (), + ], + [], + ), + ( + b'text', + [], + [ + (), + ], + ), + ( + b'{{text}}', + [ + (), + ], + [], + ), + ( + { + 'skey': 'value', + 'ukey': '{{value}}', + 1: [ + 'value', + '{{value}}', + { + 1.0: '{{value}}', + 2.0: 'value', + }, + ], + }, + [ + ('ukey', ), + (1, 1), + (1, 2, 1.0), + ], + [ + ('skey', ), + (1, 0), + (1, 2, 2.0), + ], + ), + ( + ['value', '{{value}}'], + [ + (1, ), + ], + [ + (0, ), + ], + ), +] + + +@pytest.mark.parametrize("value, check_unsafe_paths, check_safe_paths", TEST_MAKE_UNSAFE) +def test_make_unsafe(value, check_unsafe_paths, check_safe_paths): + unsafe_value = make_unsafe(value) + assert unsafe_value == value + for check_path in check_unsafe_paths: + obj = unsafe_value + for elt in check_path: + obj = obj[elt] + assert isinstance(obj, AnsibleUnsafe) + for check_path in check_safe_paths: + obj = unsafe_value + for elt in check_path: + obj = obj[elt] + assert not isinstance(obj, AnsibleUnsafe) + + +def test_make_unsafe_dict_key(): + value = { + b'test': 1, + u'test': 2, + } + unsafe_value = make_unsafe(value) + assert unsafe_value == value + for obj in unsafe_value: + assert not isinstance(obj, AnsibleUnsafe) + + value = { + b'{{test}}': 1, + u'{{test}}': 2, + } + unsafe_value = make_unsafe(value) + assert unsafe_value == value + for obj in unsafe_value: + assert isinstance(obj, AnsibleUnsafe) + + +def test_make_unsafe_set(): + value = set([b'test', u'test']) + unsafe_value = make_unsafe(value) + assert unsafe_value == value + for obj in unsafe_value: + assert not isinstance(obj, AnsibleUnsafe) + + value = set([b'{{test}}', u'{{test}}']) + unsafe_value = make_unsafe(value) + assert unsafe_value == value + for obj in unsafe_value: + assert isinstance(obj, AnsibleUnsafe) |