Repository: ansible-collections/community.general

Issue:
puppet module - noop parameter with boolean value fails with "parameter takes exactly 3 arguments (2 given)"
### Summary

Puppet module parameter 'noop' is not working with community.general version 6.6.0.

    - community.general.puppet:
        noop: false

Fails with the following error:

ansible_collections.community.general.plugins.module_utils.cmd_runner.FormatError: Failed to format parameter noop with value False: __call__() takes exactly 3 arguments (2 given)

Other module parameters such as 'timeout' are working fine. Issue is specific to 'noop' parameter.

### Issue Type

Bug Report

### Component Name

community.general.puppet


### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.14.4]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/runner/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /home/runner/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.9.13 (main, Nov  9 2022, 13:16:24) [GCC 8.5.0 20210514 (Red Hat 8.5.0-15)] (/usr/bin/python3.9)
  jinja version = 3.1.2
  libyaml = True
```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general
# /usr/share/ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.general 6.6.0
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed
CONFIG_FILE() = /etc/ansible/ansible.cfg
```


### OS / Environment

Ansible Automation Platform 2.3 running on RHEL8. Target is RHEL7 with Puppet installed.

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
  tasks:

    - community.general.puppet:
        noop: false
```


### Expected Results

Expect puppet module to accept 'noop' parameter values of true and false.

### Actual Results

```console (paste below)
ansible_collections.community.general.plugins.module_utils.cmd_runner.FormatError: Failed to format parameter noop with value False: __call__() takes exactly 3 arguments (2 given)
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/puppet.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/puppet.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @emonty
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
CC @russoz

Resolving patch:
diff --git a/plugins/module_utils/puppet.py b/plugins/module_utils/puppet.py
--- a/plugins/module_utils/puppet.py
+++ b/plugins/module_utils/puppet.py
@@ -63,11 +63,7 @@ def _prepare_base_cmd():
         return cmd
 
     def noop_func(v):
-        _noop = cmd_runner_fmt.as_map({
-            True: "--noop",
-            False: "--no-noop",
-        })
-        return _noop(module.check_mode or v)
+        return ["--noop"] if module.check_mode or v else ["--no-noop"]
 
     _logdest_map = {
         "syslog": ["--logdest", "syslog"],


Test patch:
diff --git a/tests/unit/plugins/modules/test_puppet.py b/tests/unit/plugins/modules/test_puppet.py
--- a/tests/unit/plugins/modules/test_puppet.py
+++ b/tests/unit/plugins/modules/test_puppet.py
@@ -14,6 +14,7 @@
 
 import json
 
+from collections import namedtuple
 from ansible_collections.community.general.plugins.modules import puppet
 
 import pytest
@@ -21,6 +22,10 @@
 TESTED_MODULE = puppet.__name__
 
 
+ModuleTestCase = namedtuple("ModuleTestCase", ["id", "input", "output", "run_command_calls"])
+RunCmdCall = namedtuple("RunCmdCall", ["command", "environ", "rc", "out", "err"])
+
+
 @pytest.fixture
 def patch_get_bin_path(mocker):
     """
@@ -32,106 +37,156 @@ def mockie(self, path, *args, **kwargs):
 
 
 TEST_CASES = [
-    [
-        {},
-        {
-            "id": "puppet_agent_plain",
-            "run_command.calls": [
-                (
-                    ["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
-                    {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False},
-                    (0, "blah, anything", "",),  # output rc, out, err
-                ),
-                (
-                    [
-                        "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
-                        "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0"
-                    ],
-                    {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False},
-                    (0, "", "",),  # output rc, out, err
-                ),
-            ],
-            "changed": False,
-        }
-    ],
-    [
-        {
-            "certname": "potatobox"
-        },
-        {
-            "id": "puppet_agent_certname",
-            "run_command.calls": [
-                (
-                    ["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
-                    {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False},
-                    (0, "blah, anything", "",),  # output rc, out, err
-                ),
-                (
-                    [
-                        "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
-                        "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--certname=potatobox"
-                    ],
-                    {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False},
-                    (0, "", "",),  # output rc, out, err
-                ),
-            ],
-            "changed": False,
-        }
-    ],
-    [
-        {
-            "tags": ["a", "b", "c"]
-        },
-        {
-            "id": "puppet_agent_tags_abc",
-            "run_command.calls": [
-                (
-                    ["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
-                    {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False},
-                    (0, "blah, anything", "",),  # output rc, out, err
-                ),
-                (
-                    [
-                        "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
-                        "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--tags", "a,b,c"
-                    ],
-                    {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False},
-                    (0, "", "",),  # output rc, out, err
-                ),
-            ],
-            "changed": False,
-        }
-    ],
-    [
-        {
-            "skip_tags": ["d", "e", "f"]
-        },
-        {
-            "id": "puppet_agent_skip_tags_def",
-            "run_command.calls": [
-                (
-                    ["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
-                    {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False},
-                    (0, "blah, anything", "",),  # output rc, out, err
-                ),
-                (
-                    [
-                        "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
-                        "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--skip_tags", "d,e,f"
-                    ],
-                    {"environ_update": {"LANGUAGE": "C", "LC_ALL": "C"}, "check_rc": False},
-                    (0, "", "",),  # output rc, out, err
-                ),
-            ],
-            "changed": False,
-        }
-    ]
+    ModuleTestCase(
+        id="puppet_agent_plain",
+        input={},
+        output=dict(changed=False),
+        run_command_calls=[
+            RunCmdCall(
+                command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="blah, anything",
+                err="",
+            ),
+            RunCmdCall(
+                command=[
+                    "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
+                    "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0"
+                ],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="",
+                err="",
+            ),
+        ]
+    ),
+    ModuleTestCase(
+        id="puppet_agent_certname",
+        input={"certname": "potatobox"},
+        output=dict(changed=False),
+        run_command_calls=[
+            RunCmdCall(
+                command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="blah, anything",
+                err="",
+            ),
+            RunCmdCall(
+                command=[
+                    "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
+                    "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--certname=potatobox"
+                ],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="",
+                err="",
+            ),
+        ]
+    ),
+    ModuleTestCase(
+        id="puppet_agent_tags_abc",
+        input={"tags": ["a", "b", "c"]},
+        output=dict(changed=False),
+        run_command_calls=[
+            RunCmdCall(
+                command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="blah, anything",
+                err="",
+            ),
+            RunCmdCall(
+                command=[
+                    "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
+                    "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--tags", "a,b,c"
+                ],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="",
+                err="",
+            ),
+        ]
+    ),
+    ModuleTestCase(
+        id="puppet_agent_skip_tags_def",
+        input={"skip_tags": ["d", "e", "f"]},
+        output=dict(changed=False),
+        run_command_calls=[
+            RunCmdCall(
+                command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="blah, anything",
+                err="",
+            ),
+            RunCmdCall(
+                command=[
+                    "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
+                    "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--skip_tags", "d,e,f"
+                ],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="",
+                err="",
+            ),
+        ]
+    ),
+    ModuleTestCase(
+        id="puppet_agent_noop_false",
+        input={"noop": False},
+        output=dict(changed=False),
+        run_command_calls=[
+            RunCmdCall(
+                command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="blah, anything",
+                err="",
+            ),
+            RunCmdCall(
+                command=[
+                    "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
+                    "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--no-noop"
+                ],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="",
+                err="",
+            ),
+        ]
+    ),
+    ModuleTestCase(
+        id="puppet_agent_noop_true",
+        input={"noop": True},
+        output=dict(changed=False),
+        run_command_calls=[
+            RunCmdCall(
+                command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="blah, anything",
+                err="",
+            ),
+            RunCmdCall(
+                command=[
+                    "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
+                    "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--noop"
+                ],
+                environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+                rc=0,
+                out="",
+                err="",
+            ),
+        ]
+    ),
 ]
-TEST_CASES_IDS = [item[1]["id"] for item in TEST_CASES]
+TEST_CASES_IDS = [item.id for item in TEST_CASES]
 
 
 @pytest.mark.parametrize("patch_ansible_module, testcase",
-                         TEST_CASES,
+                         [[x.input, x] for x in TEST_CASES],
                          ids=TEST_CASES_IDS,
                          indirect=["patch_ansible_module"])
 @pytest.mark.usefixtures("patch_ansible_module")
@@ -140,8 +195,10 @@ def test_puppet(mocker, capfd, patch_get_bin_path, testcase):
     Run unit tests for test cases listen in TEST_CASES
     """
 
+    run_cmd_calls = testcase.run_command_calls
+
     # Mock function used for running commands first
-    call_results = [item[2] for item in testcase["run_command.calls"]]
+    call_results = [(x.rc, x.out, x.err) for x in run_cmd_calls]
     mock_run_command = mocker.patch(
         "ansible.module_utils.basic.AnsibleModule.run_command",
         side_effect=call_results)
@@ -152,18 +209,19 @@ def test_puppet(mocker, capfd, patch_get_bin_path, testcase):
 
     out, err = capfd.readouterr()
     results = json.loads(out)
+    print("testcase =\n%s" % str(testcase))
     print("results =\n%s" % results)
 
-    assert mock_run_command.call_count == len(testcase["run_command.calls"])
+    assert mock_run_command.call_count == len(run_cmd_calls)
     if mock_run_command.call_count:
         call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
-        expected_call_args_list = [(item[0], item[1]) for item in testcase["run_command.calls"]]
+        expected_call_args_list = [(item.command, item.environ) for item in run_cmd_calls]
         print("call args list =\n%s" % call_args_list)
         print("expected args list =\n%s" % expected_call_args_list)
         assert call_args_list == expected_call_args_list
 
-    assert results.get("changed", False) == testcase["changed"]
+    assert results.get("changed", False) == testcase.output["changed"]
     if "failed" in testcase:
-        assert results.get("failed", False) == testcase["failed"]
+        assert results.get("failed", False) == testcase.output["failed"]
     if "msg" in testcase:
-        assert results.get("msg", "") == testcase["msg"]
+        assert results.get("msg", "") == testcase.output["msg"]