summaryrefslogtreecommitdiffstats
path: root/ansible_collections/sensu/sensu_go/tests/unit
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/sensu/sensu_go/tests/unit
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/sensu/sensu_go/tests/unit')
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py316
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py54
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py58
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py158
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py152
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py295
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py169
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py285
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py491
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py53
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py332
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py251
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py47
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py327
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py83
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py85
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py128
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py152
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py269
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py199
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py132
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py85
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py286
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py94
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py91
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py59
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py93
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py326
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py126
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py49
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py41
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py118
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py136
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py142
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py214
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py85
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py86
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py69
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py182
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py120
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py66
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py101
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py105
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py519
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py63
-rw-r--r--ansible_collections/sensu/sensu_go/tests/unit/requirements.txt1
60 files changed, 8175 insertions, 0 deletions
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py
new file mode 100644
index 000000000..100b30c6b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/action/test_bonsai_asset.py
@@ -0,0 +1,316 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible.playbook.task import Task
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ bonsai, errors,
+)
+from ansible_collections.sensu.sensu_go.plugins.action import bonsai_asset
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestValidate:
+ @pytest.mark.parametrize("name,args,required,typ", [
+ # Required values must match the selected type.
+ ("a", dict(a=3), True, int),
+ ("a", dict(a=3.3), True, float),
+ ("a", dict(a="b"), True, str),
+ ("a", dict(a=[]), True, list),
+ ("a", dict(a={}), True, dict),
+ # Optional values are not checked for type-correctness if they are
+ # missing.
+ ("a", dict(), False, int),
+ ("a", dict(), False, float),
+ ("a", dict(), False, str),
+ ("a", dict(), False, list),
+ ("a", dict(), False, dict),
+ ])
+ def test_valid_values(self, name, args, required, typ):
+ bonsai_asset.validate(name, args, required, typ)
+
+ def test_missing_required(self):
+ with pytest.raises(errors.Error, match="required"):
+ bonsai_asset.validate("a", {}, True, str)
+
+ def test_invalid_type(self):
+ with pytest.raises(errors.Error, match="should"):
+ bonsai_asset.validate("a", dict(a=3), True, str)
+
+ def test_invalid_type_for_optional_value(self):
+ with pytest.raises(errors.Error, match="should"):
+ bonsai_asset.validate("a", dict(a=3), False, dict)
+
+
+class TestValidateArguments:
+ def test_valid_minimal_args(self):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version="1.2.3",
+ ))
+
+ def test_valid_all_args(self):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version="1.2.3", rename="def",
+ labels={}, annotations={},
+ ))
+
+ def test_valid_unicode_strings_python2(self):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name=u"abc", version=u"1.2.3", rename=u"def",
+ labels={}, annotations={},
+ ))
+
+ def test_invalid_name(self):
+ with pytest.raises(errors.Error, match="name"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name=1.234, version="1.2.3",
+ ))
+
+ def test_missing_name(self):
+ with pytest.raises(errors.Error, match="name"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ version="1.2.3",
+ ))
+
+ def test_invalid_version(self):
+ with pytest.raises(errors.Error, match="version"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version=1.2,
+ ))
+
+ def test_missing_version(self):
+ with pytest.raises(errors.Error, match="version"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc",
+ ))
+
+ def test_invalid_rename(self):
+ with pytest.raises(errors.Error, match="rename"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version="1.2.3", rename=1,
+ ))
+
+ def test_invalid_labels(self):
+ with pytest.raises(errors.Error, match="labels"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version="1.2.3", labels=1,
+ ))
+
+ def test_invalid_annotations(self):
+ with pytest.raises(errors.Error, match="annotations"):
+ bonsai_asset.ActionModule.validate_arguments(dict(
+ name="abc", version="1.2.3", annotations=1,
+ ))
+
+
+class TestBuildAssetArgs:
+ def test_no_additional_metadata(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(name="test/asset", version="1.2.3"),
+ dict(builds=[], labels=None, annotations=None),
+ )
+
+ assert result == dict(
+ name="test/asset",
+ state="present",
+ builds=[],
+ )
+
+ def test_bonsai_metadata_only(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(name="test/asset", version="1.2.3"),
+ dict(builds=[], labels=dict(a="b"), annotations=dict(c="d")),
+ )
+
+ assert result == dict(
+ name="test/asset",
+ state="present",
+ builds=[],
+ annotations=dict(c="d"),
+ labels=dict(a="b"),
+ )
+
+ def test_user_metadata_only(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(
+ name="test/asset",
+ version="1.2.3",
+ labels=dict(my="label"),
+ annotations=dict(my="annotation"),
+ ),
+ dict(builds=[1, 2, 3], labels=None, annotations=None),
+ )
+
+ assert result == dict(
+ name="test/asset",
+ state="present",
+ builds=[1, 2, 3],
+ annotations=dict(my="annotation"),
+ labels=dict(my="label"),
+ )
+
+ def test_mixed_metadata(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(
+ name="test/asset",
+ version="1.2.3",
+ labels=dict(my="label"),
+ annotations=dict(my="annotation"),
+ ),
+ dict(builds=[], labels=dict(my="x", a="b"), annotations=dict(my="c")),
+ )
+
+ assert result == dict(
+ name="test/asset",
+ state="present",
+ builds=[],
+ annotations=dict(my="annotation"),
+ labels=dict(my="label", a="b"),
+ )
+
+ def test_rename(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(name="test/asset", version="1.2.3", rename="my-asset"),
+ dict(builds=[], labels=None, annotations=None),
+ )
+
+ assert result == dict(
+ name="my-asset",
+ state="present",
+ builds=[],
+ )
+
+ def test_auth_passthrough(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(
+ auth=dict(url="http://localhost:1234"),
+ name="test/asset",
+ version="1.2.3",
+ ),
+ dict(builds=[], labels=None, annotations=None),
+ )
+
+ assert result == dict(
+ auth=dict(url="http://localhost:1234"),
+ name="test/asset",
+ state="present",
+ builds=[],
+ )
+
+ def test_namespace_passthrough(self):
+ result = bonsai_asset.ActionModule.build_asset_args(
+ dict(namespace='default', name="test/asset", version="1.2.3"),
+ dict(builds=[], labels=None, annotations=None),
+ )
+
+ assert result == dict(
+ name="test/asset",
+ namespace='default',
+ state="present",
+ builds=[],
+ )
+
+
+class TestDownloadAssetDefinition:
+ def get_mock_action(self, mocker, result):
+ action = bonsai_asset.ActionModule(
+ mocker.MagicMock(), mocker.MagicMock(), mocker.MagicMock(), loader=None,
+ templar=None, shared_loader_obj=None,
+ )
+ action._execute_module = mocker.MagicMock(return_value=result)
+ return action
+
+ def test_download_on_control_node(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ bonsai_params.return_value = dict(sample="value")
+ action = self.get_mock_action(mocker, {})
+
+ result = action.download_asset_definition(
+ on_remote=False, name="test/asset", version="1.2.3", task_vars=None,
+ )
+
+ assert result == dict(sample="value")
+ bonsai_params.assert_called_once()
+ action._execute_module.assert_not_called()
+
+ def test_fail_download_on_control_node(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ bonsai_params.side_effect = errors.BonsaiError("Bonsai bad")
+ action = self.get_mock_action(mocker, {})
+
+ with pytest.raises(errors.Error, match="Bonsai bad"):
+ action.download_asset_definition(
+ on_remote=False, name="test/asset", version="1.2.3", task_vars=None,
+ )
+
+ bonsai_params.assert_called_once()
+ action._execute_module.assert_not_called()
+
+ def test_download_on_target_node(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ action = self.get_mock_action(mocker, dict(asset="sample"))
+
+ result = action.download_asset_definition(
+ on_remote=True, name="test/asset", version="1.2.3", task_vars=None,
+ )
+
+ assert result == "sample"
+ bonsai_params.assert_not_called()
+ action._execute_module.assert_called_once()
+
+ def test_fail_on_target_node(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ action = self.get_mock_action(mocker, dict(failed=True, msg="Bad err"))
+
+ with pytest.raises(errors.Error, match="Bad err"):
+ action.download_asset_definition(
+ on_remote=True, name="test/asset", version="1.2.3", task_vars=None,
+ )
+
+ bonsai_params.assert_not_called()
+ action._execute_module.assert_called_once()
+
+
+class TestRun:
+ def test_success(self, mocker):
+ task = mocker.MagicMock(Task, async_val=0, args=dict(
+ name="test/asset",
+ version="1.2.3",
+ ))
+ action = bonsai_asset.ActionModule(
+ task, mocker.MagicMock(), mocker.MagicMock(), loader=None,
+ templar=None, shared_loader_obj=None,
+ )
+ action._execute_module = mocker.MagicMock(return_value=dict(a=3))
+ action.download_asset_definition = mocker.MagicMock(
+ return_value=dict(builds=[], labels=None, annotations=None),
+ )
+
+ result = action.run()
+
+ assert result == dict(a=3)
+
+ def test_fail(self, mocker):
+ task = mocker.MagicMock(Task, async_val=0, args=dict(
+ name="test/asset",
+ ))
+ action = bonsai_asset.ActionModule(
+ task, mocker.MagicMock(), mocker.MagicMock(), loader=None,
+ templar=None, shared_loader_obj=None,
+ )
+ action._execute_module = mocker.MagicMock(return_value=dict(a=3))
+ action.download_asset_definition = mocker.MagicMock(
+ return_value=dict(builds=[], labels=None, annotations=None),
+ )
+
+ result = action.run()
+
+ assert result["failed"] is True
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py
new file mode 100644
index 000000000..261080e3b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_backends.py
@@ -0,0 +1,54 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.filter import backends
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestBackends:
+ def test_backends_in_groups_no_ssl(self):
+ hostvars = {
+ "1.2.3.4": {"inventory_hostname": "1.2.3.4"},
+ "1.2.3.5": {"inventory_hostname": "1.2.3.5"},
+ "1.2.3.6": {"inventory_hostname": "1.2.3.6"},
+ "1.2.3.7": {"inventory_hostname": "1.2.3.7"},
+ }
+ groups = {"backends": ["1.2.3.4", "1.2.3.5"]}
+
+ assert backends.backends(hostvars, groups) == [
+ "ws://1.2.3.4:8081",
+ "ws://1.2.3.5:8081",
+ ]
+
+ def test_backends_in_groups_ssl(self):
+ hostvars = {
+ "1.2.3.4": {"inventory_hostname": "1.2.3.4"},
+ "1.2.3.5": {"inventory_hostname": "1.2.3.5"},
+ "1.2.3.6": {
+ "inventory_hostname": "1.2.3.6",
+ "api_key_file": "path/to/key.file",
+ },
+ "1.2.3.7": {"inventory_hostname": "1.2.3.7"},
+ }
+ groups = {"backends": ["1.2.3.6"]}
+
+ assert backends.backends(hostvars, groups) == ["wss://1.2.3.6:8081"]
+
+ def test_backends_not_in_groups(self):
+ hostvars = {
+ "1.2.3.4": {"inventory_hostname": "1.2.3.4"},
+ "1.2.3.5": {"inventory_hostname": "1.2.3.5"},
+ "1.2.3.6": {"inventory_hostname": "1.2.3.6"},
+ "1.2.3.7": {"inventory_hostname": "1.2.3.7"},
+ }
+ groups = {}
+
+ assert backends.backends(hostvars, groups) == []
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py
new file mode 100644
index 000000000..794727abf
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/filter/test_package_name.py
@@ -0,0 +1,58 @@
+# Copyright: (c) 2020, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.filter import package_name
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestPackageName:
+ def test_yum_latest_version(self):
+ assert "package" == package_name.package_name(
+ "yum", "package", "latest", "latest",
+ )
+
+ def test_yum_latest_build(self):
+ assert "package-123" == package_name.package_name(
+ "yum", "package", "123", "latest",
+ )
+
+ def test_yum_selected_build(self):
+ assert "package-123-456" == package_name.package_name(
+ "yum", "package", "123", "456",
+ )
+
+ def test_yum_ignore_build_if_latest_version(self):
+ assert "package" == package_name.package_name(
+ "yum", "package", "latest", "456",
+ )
+
+ def test_apt_latest_version(self):
+ assert "package" == package_name.package_name(
+ "apt", "package", "latest", "latest",
+ )
+
+ def test_apt_latest_build(self):
+ assert "package=123-*" == package_name.package_name(
+ "apt", "package", "123", "latest",
+ )
+
+ def test_apt_selected_build(self):
+ assert "package=123-456" == package_name.package_name(
+ "apt", "package", "123", "456",
+ )
+
+ def test_apt_ignore_build_if_latest_version(self):
+ assert "package" == package_name.package_name(
+ "apt", "package", "latest", "456",
+ )
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py
new file mode 100644
index 000000000..ae62f1f57
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_arguments.py
@@ -0,0 +1,158 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ arguments,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestGetSpec:
+ @pytest.mark.parametrize("param", [
+ "auth", "state", "name", "labels", "annotations",
+ ])
+ def test_valid_parameter(self, param):
+ assert set(arguments.get_spec(param).keys()) == set((param,))
+
+ def test_invalid_parameter(self):
+ with pytest.raises(KeyError):
+ arguments.get_spec("bad_parameter_name")
+
+ def test_multiple_parameters(self):
+ assert set(arguments.get_spec("auth", "name", "labels").keys()) == set(
+ ("auth", "name", "labels")
+ )
+
+
+class TestGetSpecPayload:
+ def test_no_key(self):
+ params = dict(
+ name="name",
+ key="value"
+ )
+
+ assert arguments.get_spec_payload(params) == dict()
+
+ def test_spec_payload(self):
+ params = dict(
+ name="name",
+ key="value",
+ )
+
+ assert arguments.get_spec_payload(params, "key") == dict(
+ key="value",
+ )
+
+
+class TestGetRenamedSpecPayload:
+ def test_no_mapping(self):
+ params = dict(
+ name="name",
+ key="value",
+ )
+ assert arguments.get_renamed_spec_payload(params, dict()) == dict()
+
+ def test_renamed_payload(self):
+ params = dict(
+ name="name",
+ key="value",
+ )
+ mapping = dict(
+ name="new_name",
+ )
+ assert arguments.get_renamed_spec_payload(params, mapping) == dict(
+ new_name="name",
+ )
+
+
+class TestGetMutationPayload:
+ def test_name_only(self):
+ params = dict(
+ name="name",
+ )
+
+ assert arguments.get_mutation_payload(params) == dict(
+ metadata=dict(
+ name="name",
+ ),
+ )
+
+ def test_name_and_namespace(self):
+ params = dict(
+ name="name",
+ namespace="space",
+ )
+
+ assert arguments.get_mutation_payload(params) == dict(
+ metadata=dict(
+ name="name",
+ namespace="space",
+ ),
+ )
+
+ def test_wanted_key(self):
+ params = dict(
+ name="name",
+ key="value",
+ )
+
+ assert arguments.get_mutation_payload(params, "key") == dict(
+ key="value",
+ metadata=dict(
+ name="name",
+ ),
+ )
+
+ def test_namespace_is_none(self):
+ params = dict(
+ name="name",
+ namespace=None,
+ )
+
+ with pytest.raises(AssertionError, match="BUG"):
+ arguments.get_mutation_payload(params)
+
+ def test_labels(self):
+ params = dict(
+ name="name",
+ labels=dict(
+ some="label",
+ numeric=3,
+ ),
+ )
+
+ assert arguments.get_mutation_payload(params) == dict(
+ metadata=dict(
+ name="name",
+ labels=dict(
+ some="label",
+ numeric="3",
+ ),
+ ),
+ )
+
+ def test_annotations(self):
+ params = dict(
+ name="name",
+ annotations=dict(
+ my="Annotation",
+ number=45,
+ ),
+ )
+
+ assert arguments.get_mutation_payload(params) == dict(
+ metadata=dict(
+ name="name",
+ annotations=dict(
+ my="Annotation",
+ number="45",
+ ),
+ ),
+ )
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py
new file mode 100644
index 000000000..cf0d8a492
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_bonsai.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ bonsai, errors, http,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestGet:
+ def test_url_construction(self, mocker):
+ http_mock = mocker.patch.object(bonsai, "http")
+ http_mock.request.return_value = http.Response(200, "{}")
+
+ bonsai.get("path")
+
+ assert http_mock.request.call_args[0] == (
+ "GET", "https://bonsai.sensu.io/api/v1/assets/path",
+ )
+
+ def test_bad_status(self, mocker):
+ http_mock = mocker.patch.object(bonsai, "http")
+ http_mock.request.return_value = http.Response(400, "{}")
+
+ with pytest.raises(errors.BonsaiError, match="400"):
+ bonsai.get("path")
+
+ def test_invalid_json(self, mocker):
+ http_mock = mocker.patch.object(bonsai, "http")
+ http_mock.request.return_value = http.Response(200, "{ a }")
+
+ with pytest.raises(errors.BonsaiError, match="JSON"):
+ bonsai.get("path")
+
+
+class TestGetAvailableAssetVersions:
+ def test_valid_data(self, mocker):
+ get = mocker.patch.object(bonsai, "get")
+ get.return_value = dict(
+ versions=[
+ dict(version="1.2.3", assets=[]),
+ dict(version="1.2.4", assets=[]),
+ dict(version="1.2.5", assets=[]),
+ ],
+ )
+
+ result = bonsai.get_available_asset_versions("namespace", "name")
+
+ assert set(("1.2.3", "1.2.4", "1.2.5")) == result
+ assert get.call_args[0] == ("namespace/name",)
+
+ @pytest.mark.parametrize("data", [
+ "invalid",
+ dict(invalid="toplevel"),
+ dict(versions="oh-no"),
+ dict(versions=["not", "ok"]),
+ dict(versions=[dict(invalid="internal")]),
+ ])
+ def test_invalid_data(self, mocker, data):
+ get = mocker.patch.object(bonsai, "get")
+ get.return_value = data
+
+ with pytest.raises(errors.BonsaiError, match="versions"):
+ bonsai.get_available_asset_versions("namespace", "name")
+
+
+class TestGetAssetVersionBuilds:
+ def test_url_construction(self, mocker):
+ get = mocker.patch.object(bonsai, "get")
+ get.return_value = dict(spec=dict(builds=[]))
+
+ bonsai.get_asset_version_builds("x", "y", "z")
+
+ assert get.call_args[0] == ("x/y/z/release_asset_builds",)
+
+ @pytest.mark.parametrize("data", [
+ "invalid",
+ dict(missing="spec"),
+ dict(spec="invalid"),
+ dict(spec=dict(missing="builds")),
+ ])
+ def test_invalid_data(self, mocker, data):
+ get = mocker.patch.object(bonsai, "get")
+ get.return_value = data
+
+ with pytest.raises(errors.BonsaiError, match="spec"):
+ bonsai.get_asset_version_builds("x", "y", "z")
+
+
+class TestGetAssetParameters:
+ def test_valid_all_data(self, mocker):
+ versions = mocker.patch.object(bonsai, "get_available_asset_versions")
+ versions.return_value = set(("t", "u", "v"))
+ builds = mocker.patch.object(bonsai, "get_asset_version_builds")
+ builds.return_value = dict(
+ metadata=dict(
+ annotations=dict(annotation="value"),
+ labels=dict(label="value"),
+ ),
+ spec=dict(builds=[1, 2, 3]),
+ )
+
+ result = bonsai.get_asset_parameters("x/y", "v")
+
+ assert result == dict(
+ labels=dict(label="value"),
+ annotations=dict(annotation="value"),
+ builds=[1, 2, 3],
+ )
+ assert versions.call_args[0] == ("x", "y")
+ assert builds.call_args[0] == ("x", "y", "v")
+
+ def test_valid_minimal_data(self, mocker):
+ versions = mocker.patch.object(bonsai, "get_available_asset_versions")
+ versions.return_value = set(("t", "u", "v"))
+ builds = mocker.patch.object(bonsai, "get_asset_version_builds")
+ builds.return_value = dict(
+ spec=dict(builds=[1, 2, 3]),
+ )
+
+ result = bonsai.get_asset_parameters("x/y", "v")
+
+ assert result == dict(
+ labels=None,
+ annotations=None,
+ builds=[1, 2, 3],
+ )
+ assert versions.call_args[0] == ("x", "y")
+ assert builds.call_args[0] == ("x", "y", "v")
+
+ def test_invalid_name(self, mocker):
+ with pytest.raises(errors.BonsaiError, match="names"):
+ bonsai.get_asset_parameters("x.y", "v")
+
+ def test_invalid_version(self, mocker):
+ versions = mocker.patch.object(bonsai, "get_available_asset_versions")
+ versions.return_value = set(("t", "u"))
+
+ with pytest.raises(errors.BonsaiError, match="Version"):
+ bonsai.get_asset_parameters("x/y", "v")
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py
new file mode 100644
index 000000000..ecc6fc54b
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_client.py
@@ -0,0 +1,295 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ client, errors, http
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestAuthHeader:
+ def test_using_valid_token(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, '{"access_token": "token"}')
+
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+
+ assert dict(Authorization="Bearer token") == c.auth_header
+ assert 1 == request.call_count
+ assert ("GET", "http://example.com/auth") == request.call_args[0]
+ assert "user" == request.call_args[1]["url_username"]
+ assert "pass" == request.call_args[1]["url_password"]
+
+ def test_cache_auth_headers_with_token(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, '{"access_token": "token"}')
+
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+ for i in range(5):
+ c.auth_header
+
+ assert 1 == request.call_count
+
+ def test_login_failure_token_bad_status(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(500, '{"access_token": "token"}')
+
+ with pytest.raises(errors.SensuError, match="500"):
+ client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ ).auth_header
+
+ def test_login_failure_token_bad_json(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, "{ not a json }")
+
+ with pytest.raises(errors.SensuError, match="JSON"):
+ client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ ).auth_header
+
+ def test_login_failure_token_missing_token(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, '{"access_bla": "token"}')
+
+ with pytest.raises(errors.SensuError, match="token"):
+ client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ ).auth_header
+
+
+class TestVersion:
+ def test_valid_version(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ mocker.patch.object(c, "get").return_value = http.Response(
+ 200, '{"sensu_backend":"5.21.0#sha-here"}',
+ )
+
+ assert c.version == "5.21.0"
+
+ def test_valid_version_is_cached(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ get = mocker.patch.object(c, "get")
+ get.return_value = http.Response(
+ 200, '{"sensu_backend":"5.21.0#sha-here"}',
+ )
+
+ for i in range(4):
+ c.version
+
+ get.assert_called_once()
+
+ def test_non_200_response(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ mocker.patch.object(c, "get").return_value = http.Response(
+ 400, '{"sensu_backend":"5.21.0#sha-here"}',
+ )
+
+ with pytest.raises(errors.SensuError, match="400"):
+ c.version
+
+ def test_bad_json_response(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ mocker.patch.object(c, "get").return_value = http.Response(
+ 200, '"sensu_backend',
+ )
+
+ with pytest.raises(errors.SensuError, match="JSON"):
+ c.version
+
+ def test_missing_backend_version_in_response(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ mocker.patch.object(c, "get").return_value = http.Response(200, '{}')
+
+ with pytest.raises(errors.SensuError, match="backend"):
+ c.version
+
+ def test_invalid_version(self, mocker):
+ c = client.Client("http://example.com/", "u", "p", None, True, None)
+ mocker.patch.object(c, "get").return_value = http.Response(
+ 200, '{"sensu_backend":"devel"}',
+ )
+
+ assert c.version == c.BAD_VERSION
+
+
+class TestRequest:
+ def test_request_payload_token(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.side_effect = (
+ http.Response(200, '{"access_token": "token"}'),
+ http.Response(200, "data"),
+ )
+
+ client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ ).request("PUT", "/path", dict(some="payload"))
+
+ request.assert_called_with(
+ "PUT", "http://example.com/path",
+ payload=dict(some="payload"),
+ headers=dict(Authorization="Bearer token"),
+ validate_certs=True,
+ ca_path=None,
+ )
+
+ def test_request_payload_api_key(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, "data")
+
+ client.Client(
+ "http://example.com/", None, None, "key", False, None,
+ ).request("PUT", "/path", dict(some="payload"))
+
+ request.assert_called_once_with(
+ "PUT", "http://example.com/path",
+ payload=dict(some="payload"),
+ headers=dict(Authorization="Key key"),
+ validate_certs=False,
+ ca_path=None,
+ )
+
+ def test_request_no_payload_token(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.side_effect = (
+ http.Response(200, '{"access_token": "token"}'),
+ http.Response(200, "data"),
+ )
+
+ client.Client(
+ "http://example.com/", "user", "pass", None, True, "/ca",
+ ).request("PUT", "/path")
+
+ request.assert_called_with(
+ "PUT", "http://example.com/path", payload=None,
+ headers=dict(Authorization="Bearer token"),
+ validate_certs=True,
+ ca_path="/ca",
+ )
+
+ def test_request_no_payload_api_key(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, "data")
+
+ client.Client(
+ "http://example.com/", "u", "p", "key", False, "/ca",
+ ).request("PUT", "/path")
+
+ request.assert_called_once_with(
+ "PUT", "http://example.com/path", payload=None,
+ headers=dict(Authorization="Key key"),
+ validate_certs=False,
+ ca_path="/ca",
+ )
+
+ @pytest.mark.parametrize("status", [401, 403])
+ def test_request_bad_credentials(self, status, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(status, "data")
+
+ with pytest.raises(errors.SensuError, match="credentials"):
+ client.Client(
+ "http://example.com/", None, None, "key", True, None,
+ ).request("PUT", "/path", dict(some="payload"))
+
+ request.assert_called_once_with(
+ "PUT", "http://example.com/path",
+ payload=dict(some="payload"),
+ headers=dict(Authorization="Key key"),
+ validate_certs=True,
+ ca_path=None,
+ )
+
+
+class TestGet:
+ def test_get(self, mocker):
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+ c.request = mocker.Mock()
+
+ c.get("/path")
+
+ c.request.assert_called_with("GET", "/path")
+
+
+class TestPut:
+ def test_put(self, mocker):
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+ c.request = mocker.Mock()
+
+ c.put("/path", {})
+
+ c.request.assert_called_with("PUT", "/path", {})
+
+
+class TestDelete:
+ def test_delete(self, mocker):
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+ c.request = mocker.Mock()
+
+ c.delete("/path")
+
+ c.request.assert_called_with("DELETE", "/path")
+
+
+class TestValidateAuthData:
+ def test_valid_creds(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(200, None)
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+
+ result = c.validate_auth_data("check_user", "check_pass")
+
+ assert result
+ assert 1 == request.call_count
+ assert ("GET", "http://example.com/auth/test") == request.call_args[0]
+ assert "check_user" == request.call_args[1]["url_username"]
+ assert "check_pass" == request.call_args[1]["url_password"]
+
+ def test_invalid_creds(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(401, None)
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+
+ result = c.validate_auth_data("check_user", "check_pass")
+
+ assert not result
+ assert 1 == request.call_count
+ assert ("GET", "http://example.com/auth/test") == request.call_args[0]
+ assert "check_user" == request.call_args[1]["url_username"]
+ assert "check_pass" == request.call_args[1]["url_password"]
+
+ def test_broken_backend(self, mocker):
+ request = mocker.patch.object(http, "request")
+ request.return_value = http.Response(500, None)
+ c = client.Client(
+ "http://example.com/", "user", "pass", None, True, None,
+ )
+
+ with pytest.raises(errors.SensuError, match="500"):
+ c.validate_auth_data("check_user", "check_pass")
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py
new file mode 100644
index 000000000..84fddb53a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_http.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import ssl
+import sys
+
+import pytest
+
+from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, http,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestResponse:
+ def test_with_valid_json(self):
+ resp = http.Response(201, '{"some": ["json", "data", 3]}')
+
+ assert 201 == resp.status
+ assert '{"some": ["json", "data", 3]}' == resp.data
+ assert {"some": ["json", "data", 3]} == resp.json
+
+ def test_with_invalid_json(self):
+ resp = http.Response(404, "")
+
+ assert 404 == resp.status
+ assert "" == resp.data
+ assert resp.json is None
+
+
+class TestRequest:
+ def test_ok_request(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ resp = http.request("GET", "example.com/path")
+
+ assert 200 == resp.status
+ assert "data" == resp.data
+ assert "GET" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+
+ def test_non_20x_status(self, mocker):
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.side_effect = HTTPError(
+ "url", 404, "missing", {}, None,
+ )
+
+ resp = http.request("GET", "example.com/bad")
+
+ assert 404 == resp.status
+ assert "missing" == resp.data
+ assert "GET" == open_url.call_args[1]["method"]
+ assert "example.com/bad" == open_url.call_args[1]["url"]
+
+ def test_url_error(self, mocker):
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.side_effect = URLError("Invalid")
+
+ with pytest.raises(errors.HttpError):
+ http.request("GET", "example.com/bad")
+
+ def test_payload_no_headers(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ http.request("PUT", "example.com/path", payload=dict(a=2))
+
+ assert "PUT" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+ assert '{"a":2}' == open_url.call_args[1]["data"]
+ headers = open_url.call_args[1]["headers"]
+ assert {"content-type": "application/json"} == headers
+
+ def test_payload_with_headers(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ http.request(
+ "PUT", "example.com/path", payload=dict(b=4), headers=dict(h="v"),
+ )
+
+ assert "PUT" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+ assert '{"b":4}' == open_url.call_args[1]["data"]
+ headers = open_url.call_args[1]["headers"]
+ assert {"content-type": "application/json", "h": "v"} == headers
+
+ def test_payload_overrides_data(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ http.request(
+ "PUT", "example.com/path", payload=dict(a=2), data="data",
+ )
+
+ assert "PUT" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+ assert '{"a":2}' == open_url.call_args[1]["data"]
+ headers = open_url.call_args[1]["headers"]
+ assert {"content-type": "application/json"} == headers
+
+ def test_data(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ http.request("PUT", "example.com/path", data="data")
+
+ assert "PUT" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+ assert "data" == open_url.call_args[1]["data"]
+ assert open_url.call_args[1]["headers"] is None
+
+ def test_kwargs(self, mocker):
+ data_resp = mocker.Mock()
+ data_resp.read.return_value = "data"
+ data_resp.getcode.return_value = 200
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.return_value = data_resp
+
+ http.request("PUT", "example.com/path", a=3, b="f")
+
+ assert "PUT" == open_url.call_args[1]["method"]
+ assert "example.com/path" == open_url.call_args[1]["url"]
+ assert 3 == open_url.call_args[1]["a"]
+ assert "f" == open_url.call_args[1]["b"]
+
+ def test_cert_error_ssl_module_present(self, mocker):
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.side_effect = ssl.CertificateError("Invalid")
+
+ with pytest.raises(errors.HttpError):
+ http.request("GET", "example.com/bad")
+
+ def test_cert_error_ssl_module_absent(self, mocker):
+ class Dummy(Exception):
+ pass
+
+ open_url = mocker.patch.object(http, "open_url")
+ open_url.side_effect = ssl.CertificateError("Invalid")
+ mocker.patch.object(http, "CertificateError", Dummy)
+
+ with pytest.raises(ssl.CertificateError):
+ http.request("GET", "example.com/bad")
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py
new file mode 100644
index 000000000..e4f6ff578
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_role_utils.py
@@ -0,0 +1,285 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import role_utils
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoSubjectsDiffer:
+ def test_different_lengths(self):
+ assert role_utils._do_subjects_differ(
+ [{"type": "a", "name": "a"}],
+ [{"type": "a", "name": "a"}, {"type": "a", "name": "b"}]
+ ) is True
+
+ def test_different_type_with_same_name(self):
+ assert role_utils._do_subjects_differ(
+ [{"type": "a", "name": "same"}],
+ [{"type": "b", "name": "same"}]
+ ) is True
+
+ def test_equal_with_different_order_within_type(self):
+ assert role_utils._do_subjects_differ(
+ [{"type": "a", "name": "a2"}, {"type": "a", "name": "a1"}],
+ [{"type": "a", "name": "a1"}, {"type": "a", "name": "a2"}]
+ ) is False
+
+ def test_equal_with_different_order_multiple_types(self):
+ assert role_utils._do_subjects_differ(
+ [
+ {"type": "a", "name": "a2"},
+ {"type": "b", "name": "b1"},
+ {"type": "a", "name": "a1"}
+ ],
+ [
+ {"type": "a", "name": "a1"},
+ {"type": "a", "name": "a2"},
+ {"type": "b", "name": "b1"}
+ ]
+ ) is False
+
+ def test_different(self):
+ assert role_utils._do_subjects_differ(
+ [
+ {"type": "a", "name": "a2"},
+ {"type": "b", "name": "b3"},
+ ],
+ [
+ {"type": "c", "name": "c2"},
+ {"type": "a", "name": "s2"},
+ ]
+ ) is True
+
+
+class TestDoRoleBindingsDiffer:
+ def test_equal_role_binding(self):
+ current = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "User", "name": "b"},
+ {"type": "Group", "name": "g"},
+ ]
+ }
+ desired = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "User", "name": "b"},
+ {"type": "Group", "name": "g"},
+ ]
+ }
+ assert role_utils.do_role_bindings_differ(current, desired) is False
+
+ def test_equal_role_binding_mixed_users_and_groups(self):
+ current = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "Group", "name": "g1"},
+ {"type": "User", "name": "u1"},
+ {"type": "Group", "name": "g2"},
+ {"type": "User", "name": "u2"},
+ ]
+ }
+ desired = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "User", "name": "u2"},
+ {"type": "User", "name": "u1"},
+ {"type": "Group", "name": "g2"},
+ {"type": "Group", "name": "g1"},
+ ]
+ }
+ assert role_utils.do_role_bindings_differ(current, desired) is False
+
+ def test_updated_role_binding_subjects(self):
+ current = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "User", "name": "b"},
+ ]
+ }
+ desired = {
+ 'role_ref': 'a',
+ 'subjects': [
+ {"type": "User", "name": "b"},
+ {"type": "Group", "name": "g"},
+ ]
+ }
+ assert role_utils.do_role_bindings_differ(current, desired) is True
+
+
+class TestRuleSets:
+ def test_all_keys_none(self):
+ assert role_utils._rule_set([{}]) == set(
+ ((frozenset(), frozenset(), frozenset()),)
+ )
+
+ def test_rules_multiple(self):
+ assert role_utils._rule_set([{
+ 'verbs': ['list', 'get'],
+ 'resources': ['entities', 'checks'],
+ 'resource_names': None
+ }, {
+ 'verbs': ['list', 'delete'],
+ 'resources': ['entities', 'checks'],
+ 'resource_names': None
+ }]) == set((
+ (frozenset(['delete', 'list']), frozenset(['checks', 'entities']), frozenset()),
+ (frozenset(['get', 'list']), frozenset(['checks', 'entities']), frozenset())
+ ))
+
+ def test_missing_key(self):
+ assert role_utils._rule_set([{
+ 'verbs': ['list', 'get'],
+ 'resources': ['entities', 'checks'],
+ }]) == set(
+ ((frozenset(['get', 'list']), frozenset(['checks', 'entities']), frozenset()),)
+ )
+
+
+class TestDoRulesDiffer:
+ def test_empty_values(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': []}],
+ [{'verbs': []}]
+ ) is False
+
+ def test_rules_when_current_values_are_none(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': None}],
+ [{'verbs': ['get', 'list']}]
+ ) is True
+
+ def test_rules_when_desired_values_are_none(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': ['get', 'list']}],
+ [{'verbs': None}]
+ ) is True
+
+ def test_rules_are_different(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': ['list', 'get']}],
+ [{'verbs': ['get', 'delete']}]
+ ) is True
+
+ def test_rules_with_additional_keys_in_current(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': ['list', 'get'], 'resources': ['checks', 'entities']}],
+ [{'verbs': ['get', 'list']}]
+ ) is True
+
+ def test_rules_are_the_same(self):
+ assert role_utils._do_rules_differ(
+ [{'verbs': ['list', 'get']}],
+ [{'verbs': ['get', 'list']}]
+ ) is False
+
+
+class TestDoRolesDiffer:
+ def test_rules_when_values_in_current_are_none(self):
+ current = {
+ 'rules': [{
+ 'resource_names': None
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'resource_names': ['check-cpu']
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is True
+
+ def test_rules_when_values_in_desired_are_none(self):
+ current = {
+ 'rules': [{
+ 'resource_names': ['check-cpu']
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'resource_names': None
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is True
+
+ def test_different_rules_order(self):
+ current = {
+ 'rules': [{
+ 'verbs': ['get', 'list'],
+ 'resources': ['entities', 'checks']
+ }, {
+ 'verbs': ['create', 'delete', 'update'],
+ 'resources': ['assets', 'hooks']
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'verbs': ['delete', 'create', 'update'],
+ 'resources': ['hooks', 'assets']
+ }, {
+ 'verbs': ['list', 'get'],
+ 'resources': ['checks', 'entities']
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is False
+
+ def test_key_missing_in_current(self):
+ current = {
+ 'rules': [{
+ 'verbs': ['update', 'create'],
+ 'resources': ['hooks', 'assets']
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'verbs': ['create', 'update'],
+ 'resources': ['hooks', 'assets'],
+ 'resource_names': ['check-cpu']
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is True
+
+ def test_key_missing_in_desired(self):
+ current = {
+ 'rules': [{
+ 'verbs': ['update', 'create'],
+ 'resources': ['hooks', 'assets'],
+ 'resource_names': ['check-cpu']
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'verbs': ['create', 'update'],
+ 'resources': ['hooks', 'assets']
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is True
+
+ def test_role_exists_but_with_additional_rules(self):
+ current = {
+ 'rules': [{
+ 'verbs': ['get', 'list'],
+ 'resources': ['entities', 'check']
+ }, {
+ 'verbs': ['create', 'update', 'delete'],
+ 'resources': ['assets', 'hooks']
+ }]
+ }
+ desired = {
+ 'rules': [{
+ 'verbs': ['list', 'get'],
+ 'resources': ['check', 'entities']
+ }]
+ }
+ assert role_utils.do_roles_differ(current, desired) is True
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py
new file mode 100644
index 000000000..f737dce0a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/module_utils/test_utils.py
@@ -0,0 +1,491 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2019, XLAB Steampunk <steampunk@xlab.si>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, http, utils,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSync:
+ def test_absent_no_current_object(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ changed, object = utils.sync("absent", client, "/path", {}, False)
+
+ assert changed is False
+ assert object is None
+
+ def test_absent_no_current_object_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ changed, object = utils.sync("absent", client, "/path", {}, True)
+
+ assert changed is False
+ assert object is None
+
+ def test_absent_current_object_present(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ client.delete.return_value = http.Response(204, "")
+
+ changed, object = utils.sync("absent", client, "/path", {}, False)
+
+ assert changed is True
+ assert object is None
+ client.delete.assert_called_with("/path")
+
+ def test_absent_current_object_present_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ client.delete.return_value = http.Response(204, "")
+
+ changed, object = utils.sync("absent", client, "/path", {}, True)
+
+ assert changed is True
+ assert object is None
+ client.delete.assert_not_called()
+
+ def test_present_no_current_object(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(404, ""),
+ http.Response(200, '{"new": "data"}'),
+ )
+ client.put.return_value = http.Response(201, "")
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, False,
+ )
+
+ assert changed is True
+ assert {"new": "data"} == object
+ client.put.assert_called_once_with("/path", {"my": "data"})
+
+ def test_present_no_current_object_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, True,
+ )
+
+ assert changed is True
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_current_object_differ(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(200, '{"current": "data"}'),
+ http.Response(200, '{"new": "data"}'),
+ )
+ client.put.return_value = http.Response(201, "")
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, False,
+ )
+
+ assert changed is True
+ assert {"new": "data"} == object
+ client.put.assert_called_once_with("/path", {"my": "data"})
+
+ def test_present_current_object_differ_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"current": "data"}')
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, True,
+ )
+
+ assert changed is True
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_current_object_does_not_differ(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"my": "data"}')
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, False,
+ )
+
+ assert changed is False
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_current_object_does_not_differ_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"my": "data"}')
+
+ changed, object = utils.sync(
+ "present", client, "/path", {"my": "data"}, True,
+ )
+
+ assert changed is False
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+
+class TestSyncV1:
+ def test_parameter_passthrough(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = (True, {
+ "metadata": {"name": "test", "namespace": "space"},
+ "spec": {"key": "value"},
+ })
+
+ changed, object = utils.sync_v1("absent", "c", "/path", {}, False)
+
+ assert changed is True
+ assert {
+ "metadata": {"name": "test", "namespace": "space"},
+ "key": "value",
+ }
+
+
+class TestDoDiffer:
+ def test_extra_keys_in_current_do_not_matter(self):
+ assert utils.do_differ({"a": "b", "c": 3}, {"a": "b"}) is False
+
+ def test_detect_different_values(self):
+ assert utils.do_differ({"a": "b"}, {"a": "c"}) is True
+
+ def test_detect_missing_keys_in_current(self):
+ assert utils.do_differ({"a": "b"}, {"c": "d"}) is True
+
+ def test_desired_none_values_are_ignored(self):
+ assert utils.do_differ({"a": "b"}, {"c": None}) is False
+
+ def test_metadata_ignores_created_by(self):
+ assert utils.do_differ(
+ dict(metadata=dict(a=1, created_by=2)),
+ dict(metadata=dict(a=1)),
+ ) is False
+
+ def test_metadata_detects_change(self):
+ assert utils.do_differ(
+ dict(metadata=dict(a=1)), dict(metadata=dict(a=2)),
+ ) is True
+
+ def test_metadata_detects_change_in_presence_of_created_by(self):
+ assert utils.do_differ(
+ dict(metadata=dict(a=1, created_by=2)),
+ dict(metadata=dict(a=2)),
+ ) is True
+
+ def test_ignore_keys_do_not_affect_the_outcome(self):
+ assert utils.do_differ(dict(a=1), dict(a=2), "a") is False
+
+ def test_ignore_keys_do_not_mask_other_differences(self):
+ assert utils.do_differ(dict(a=1, b=1), dict(a=2, b=2), "a") is True
+
+
+class TestDoDifferV1:
+ def test_extra_keys_in_current_do_not_matter(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": "b", "c": 3}}, {"spec": {"a": "b"}},
+ ) is False
+
+ def test_detect_different_values(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": "b"}}, {"spec": {"a": "c"}},
+ ) is True
+
+ def test_detect_missing_keys_in_current(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": "b"}}, {"spec": {"c": "d"}},
+ ) is True
+
+ def test_desired_none_values_are_ignored(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": "b"}}, {"spec": {"c": None}},
+ ) is False
+
+ def test_metadata_ignores_created_by(self):
+ assert utils.do_differ_v1(
+ {"metadata": {"a": 1, "created_by": 2}},
+ {"metadata": {"a": 1}},
+ ) is False
+
+ def test_metadata_detects_change(self):
+ assert utils.do_differ_v1(
+ {"metadata": {"a": 1}}, {"metadata": {"a": 2}},
+ ) is True
+
+ def test_metadata_detects_change_in_presence_of_created_by(self):
+ assert utils.do_differ_v1(
+ {"metadata": {"a": 1, "created_by": 2}},
+ {"metadata": {"a": 2}},
+ ) is True
+
+ def test_ignore_keys_do_not_affect_the_outcome(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": 1}}, {"spec": {"a": 2}}, "a",
+ ) is False
+
+ def test_ignore_keys_do_not_mask_other_differences(self):
+ assert utils.do_differ_v1(
+ {"spec": {"a": 1, "b": 1}}, {"spec": {"a": 2, "b": 2}}, "a",
+ ) is True
+
+
+class TestGet:
+ @pytest.mark.parametrize(
+ "status", [100, 201, 202, 203, 204, 400, 401, 403, 500, 501],
+ )
+ def test_abort_on_invalid_status(self, mocker, status):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(status, "")
+
+ with pytest.raises(errors.SyncError, match=str(status)):
+ utils.get(client, "/get")
+ client.get.assert_called_once_with("/get")
+
+ def test_abort_on_invalid_json(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, "")
+
+ with pytest.raises(errors.SyncError, match="JSON"):
+ utils.get(client, "/get")
+ client.get.assert_called_once_with("/get")
+
+ def test_ignore_invalid_json_on_404(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ object = utils.get(client, "/get")
+
+ assert object is None
+ client.get.assert_called_once_with("/get")
+
+ def test_valid_json(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"get": "data"}')
+
+ object = utils.get(client, "/get")
+
+ assert {"get": "data"} == object
+ client.get.assert_called_once_with("/get")
+
+
+class TestDelete:
+ @pytest.mark.parametrize(
+ "status", [100, 200, 201, 202, 203, 400, 401, 403, 500, 501],
+ )
+ def test_abort_on_invalid_status(self, mocker, status):
+ client = mocker.Mock()
+ client.delete.return_value = http.Response(status, "")
+
+ with pytest.raises(errors.SyncError, match=str(status)):
+ utils.delete(client, "/delete")
+ client.delete.assert_called_once_with("/delete")
+
+ def test_valid_delete(self, mocker):
+ client = mocker.Mock()
+ client.delete.return_value = http.Response(204, "{}")
+
+ object = utils.delete(client, "/delete")
+
+ assert object is None
+ client.delete.assert_called_once_with("/delete")
+
+
+class TestPut:
+ @pytest.mark.parametrize(
+ "status", [100, 202, 203, 204, 400, 401, 403, 500, 501],
+ )
+ def test_abort_on_invalid_status(self, mocker, status):
+ client = mocker.Mock()
+ client.put.return_value = http.Response(status, "")
+
+ with pytest.raises(errors.SyncError, match=str(status)):
+ utils.put(client, "/put", {"payload": "data"})
+ client.put.assert_called_once_with("/put", {"payload": "data"})
+
+ @pytest.mark.parametrize("status", [200, 201])
+ def test_valid_put(self, mocker, status):
+ client = mocker.Mock()
+ client.put.return_value = http.Response(status, '{"put": "data"}')
+
+ object = utils.put(client, "/put", {"payload": "data"})
+
+ assert object is None
+ client.put.assert_called_once_with("/put", {"payload": "data"})
+
+
+class TestDictToSingleItemDicts:
+ def test_conversion(self):
+ result = utils.dict_to_single_item_dicts({"a": 0, 1: "b"})
+
+ assert 2 == len(result)
+ for item in ({"a": 0}, {1: "b"}):
+ assert item in result
+
+
+class TestSingleItemDictsToDict:
+ def test_conversion(self):
+ assert dict(a=3, b=4, c=5) == utils.single_item_dicts_to_dict(
+ [dict(a=3), dict(b=4), dict(c=5)]
+ )
+
+
+class TestDictToKeyValueString:
+ def test_conversion(self):
+ result = utils.dict_to_key_value_strings({"a": 0, 1: "b"})
+
+ assert set(("a=0", "1=b")) == set(result)
+
+
+class TestBuildUrlPath:
+ @pytest.mark.parametrize("parts,expectation", [
+ ((), "/"),
+ ((None, None), "/"),
+ ((None, "a", "b", None, None, "c"), "/a/b/c"),
+ (("get/rid of+stuff",), "/get%2Frid%20of%2Bstuff"),
+ (("/", " ", "a"), "/%2F/%20/a"),
+ ])
+ def test_build_url_path_no_namespace(self, parts, expectation):
+ path = "/api/enterprise/store/v1" + expectation
+ assert path == utils.build_url_path(
+ "enterprise/store", "v1", None, *parts
+ )
+
+ @pytest.mark.parametrize("parts,expectation", [
+ ((), "/"),
+ ((None, None), "/"),
+ ((None, "a", "b", None, None, "c"), "/a/b/c"),
+ (("get/rid of+stuff",), "/get%2Frid%20of%2Bstuff"),
+ (("/", " ", "a"), "/%2F/%20/a"),
+ ])
+ def test_build_url_path_with_namespace(self, parts, expectation):
+ path = "/api/core/v2/namespaces/default" + expectation
+ assert path == utils.build_url_path(
+ "core", "v2", "default", *parts
+ )
+
+
+class TestBuildCoreV2Path:
+ def test_build_path_no_namespace(self):
+ assert utils.build_core_v2_path(None, "a").startswith(
+ "/api/core/v2/",
+ )
+
+ def test_build_url_with_namespace(self):
+ assert utils.build_core_v2_path("default", "a").startswith(
+ "/api/core/v2/namespaces/default/",
+ )
+
+
+class TestPrepareResultList:
+ @pytest.mark.parametrize("input,output", [
+ (None, []), # this is mosti likely result of a 404 status
+ ("a", ["a"]),
+ ([], []),
+ ([1, 2, 3], [1, 2, 3]),
+ ([None], [None]), # we leave lists intact, even if they contain None
+ ])
+ def test_list_construction(self, input, output):
+ assert output == utils.prepare_result_list(input)
+
+
+class TestConvertV1ToV2Response:
+ def test_none_passes_through(self):
+ assert utils.convert_v1_to_v2_response(None) is None
+
+ def test_spec_only_if_metadata_is_missing(self):
+ assert utils.convert_v1_to_v2_response(dict(
+ spec=dict(a=1, b=2),
+ )) == dict(a=1, b=2)
+
+ def test_add_metadata_from_toplevel(self):
+ assert utils.convert_v1_to_v2_response(dict(
+ metadata=dict(name="sample"),
+ spec=dict(a=1, b=2),
+ )) == dict(metadata=dict(name="sample"), a=1, b=2)
+
+
+class TestDoSecretsDiffer:
+ @pytest.mark.parametrize("current,desired", [
+ ( # All empty
+ [], [],
+ ),
+ ( # All is equal
+ [dict(name="a", secret="1"), dict(name="b", secret="2")],
+ [dict(name="a", secret="1"), dict(name="b", secret="2")],
+ ),
+ ( # Different order
+ [dict(name="a", secret="1"), dict(name="b", secret="2")],
+ [dict(name="b", secret="2"), dict(name="a", secret="1")],
+ ),
+ ])
+ def test_no_difference(self, current, desired):
+ assert utils.do_secrets_differ(
+ dict(secrets=current), dict(secrets=desired),
+ ) is False
+
+ @pytest.mark.parametrize("current,desired", [
+ ( # Different source for variable b
+ [dict(name="b", secret="2")], [dict(name="b", secret="3")],
+ ),
+ ( # Different name
+ [dict(name="a", secret="1")], [dict(name="b", secret="1")],
+ ),
+ ( # Different number of secrets
+ [dict(name="a", secret="1"), dict(name="b", secret="2")],
+ [dict(name="a", secret="1")],
+ ),
+ ])
+ def test_difference(self, current, desired):
+ assert utils.do_secrets_differ(
+ dict(secrets=current), dict(secrets=desired),
+ ) is True
+
+ @pytest.mark.parametrize("secrets,diff", [
+ # Missing secrets and empty list are the same
+ ([], False),
+ # None secrets are treated as empy list of secrets
+ (None, False),
+ # If anything is set, we have difference
+ ([dict(name="n", secret="s")], True),
+ ])
+ def test_missing_secrets(self, secrets, diff):
+ assert utils.do_secrets_differ(dict(), dict(secrets=secrets)) is diff
+ assert utils.do_secrets_differ(dict(secrets=secrets), dict()) is diff
+
+
+class TestDeprecate:
+ def test_ansible_lt_2_9_10(self, mocker):
+ module = mocker.MagicMock()
+ module.deprecate.side_effect = (
+ TypeError("Simulating Ansible 2.9.9 and older"),
+ None, # Success, since no exception is raised
+ )
+
+ utils.deprecate(module, "Test msg", "3.2.1")
+
+ assert module.deprecate.call_count == 2
+ assert module.deprecate.called_once_with("Test msg", version="3.2.1")
+
+ def test_ansible_ge_2_9_10(self, mocker):
+ module = mocker.MagicMock()
+
+ utils.deprecate(module, "Test msg", "3.2.1")
+
+ assert module.deprecate.called_once_with(
+ "Test msg", version="3.2.1", collection_name="sensu.sensu_go",
+ )
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py
new file mode 100644
index 000000000..2287dbc9e
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/common/utils.py
@@ -0,0 +1,53 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import json
+
+from ansible.module_utils import basic
+from ansible.module_utils._text import to_bytes
+
+from mock import patch
+
+
+def set_module_args(**args):
+ if '_ansible_remote_tmp' not in args:
+ args['_ansible_remote_tmp'] = '/tmp'
+ if '_ansible_keep_remote_files' not in args:
+ args['_ansible_keep_remote_files'] = False
+
+ args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
+ basic._ANSIBLE_ARGS = to_bytes(args)
+
+
+class AnsibleExitJson(Exception):
+ pass
+
+
+class AnsibleFailJson(Exception):
+ pass
+
+
+def exit_json(*args, **kwargs):
+ if 'changed' not in kwargs:
+ kwargs['changed'] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs):
+ kwargs['failed'] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class ModuleTestCase:
+ def setup_method(self):
+ self.mock_module = patch.multiple(
+ basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json,
+ )
+ self.mock_module.start()
+
+ def teardown_method(self):
+ self.mock_module.stop()
+
+
+def generate_name(test_case):
+ return test_case['name']
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py
new file mode 100644
index 000000000..a7204bb5a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ad_auth_provider.py
@@ -0,0 +1,332 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors,
+ utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import ad_auth_provider
+
+from .common.utils import (
+ AnsibleExitJson,
+ AnsibleFailJson,
+ ModuleTestCase,
+ set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ def test_no_changes(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(name="activedirectory"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="activedirectory",
+ created_by="me",
+ ),
+ )
+
+ assert ad_auth_provider.do_differ(current, desired) is False
+
+ def test_changes_are_detected(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(name="activedirectory"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="activedirectory",
+ created_by="me",
+ ),
+ )
+ assert ad_auth_provider.do_differ(current, desired) is True
+
+ def test_changes_are_detected_diff_servers_len(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ ),
+ dict(
+ host="127.0.0.2",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ ),
+ ],
+ ),
+ metadata=dict(name="activedirectory"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="activedirectory",
+ created_by="me",
+ ),
+ )
+ assert ad_auth_provider.do_differ(current, desired) is True
+
+ def test_changes_are_other_params(self):
+ desired = dict(
+ spec=dict(
+ servers=[],
+ groups_prefix="ad",
+ username_prefix="ad",
+ ),
+ metadata=dict(name="activedirectory"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[],
+ ),
+ metadata=dict(
+ name="activedirectory",
+ created_by="me",
+ ),
+ )
+ assert ad_auth_provider.do_differ(current, desired) is True
+
+
+class TestADAutProvider(ModuleTestCase):
+ def test_minimal_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="activedirectory",
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ ad_auth_provider.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[
+ 0
+ ]
+
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/activedirectory"
+ assert payload == dict(
+ type="ad",
+ api_version="authentication/v2",
+ metadata=dict(name="activedirectory"),
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=None,
+ insecure=False,
+ security="tls",
+ trusted_ca_file=None,
+ client_cert_file=None,
+ client_key_file=None,
+ default_upn_domain=None,
+ include_nested_groups=None,
+ binding=None,
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="group",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="sAMAccountName",
+ name_attribute="displayName",
+ object_class="person",
+ ),
+ )
+ ]
+ ),
+ )
+
+ assert check_mode is False
+
+ def test_all_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="activedirectory",
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ insecure=False,
+ security="tls",
+ trusted_ca_file="/path/to/trusted-certificate-authorities.pem",
+ client_cert_file="/path/to/ssl/cert.pem",
+ client_key_file="/path/to/ssl/key.pem",
+ default_upn_domain="example.org",
+ include_nested_groups=True,
+ binding=dict(
+ user_dn="cn=binder,dc=acme,dc=org",
+ password="YOUR_PASSWORD",
+ ),
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="group",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="sAMAccountName",
+ name_attribute="displayName",
+ object_class="person",
+ ),
+ )
+ ],
+ groups_prefix="ad",
+ username_prefix="ad",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ ad_auth_provider.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[
+ 0
+ ]
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/activedirectory"
+ assert payload == dict(
+ type="ad",
+ api_version="authentication/v2",
+ metadata=dict(name="activedirectory"),
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ insecure=False,
+ security="tls",
+ trusted_ca_file="/path/to/trusted-certificate-authorities.pem",
+ client_cert_file="/path/to/ssl/cert.pem",
+ client_key_file="/path/to/ssl/key.pem",
+ default_upn_domain="example.org",
+ include_nested_groups=True,
+ binding=dict(
+ user_dn="cn=binder,dc=acme,dc=org",
+ password="YOUR_PASSWORD",
+ ),
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="group",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="sAMAccountName",
+ name_attribute="displayName",
+ object_class="person",
+ ),
+ )
+ ],
+ groups_prefix="ad",
+ username_prefix="ad",
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ ad_auth_provider.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py
new file mode 100644
index 000000000..8e9298b2c
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset.py
@@ -0,0 +1,251 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import asset
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ def test_equal_assets_with_none_values(self):
+ assert asset.do_differ(
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "sha512": "a",
+ "url": "a",
+ "filters": None
+ },
+ ],
+ },
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "sha512": "a",
+ "url": "a",
+ "headers": None,
+ },
+ ],
+ },
+ ) is False
+
+ def test_equal_assets_with_different_build_content(self):
+ assert asset.do_differ(
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "url": "http://abc.com",
+ "sha512": "abc",
+ "headers": {
+ "foo": "bar",
+ "bar": "foo",
+ }
+ },
+ {
+ "url": "http://def.com",
+ "sha512": "def",
+ "filters": ["d == d", "e == e"],
+ },
+
+ ]
+ },
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "url": "http://def.com",
+ "sha512": "def",
+ "filters": ["e == e", "d == d"],
+ },
+ {
+ "url": "http://abc.com",
+ "sha512": "abc",
+ "headers": {
+ "bar": "foo",
+ "foo": "bar"
+ }
+ },
+ ]
+ },
+ ) is False
+
+ def test_updated_asset(self):
+ assert asset.do_differ(
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "url": "http://abc.com",
+ "sha512": "abc",
+ }
+ ],
+ "annotations": {
+ "foo": "bar",
+ }
+ },
+ {
+ "name": "asset",
+ "builds": [
+ {
+ "url": "http://def.com",
+ "sha512": "abc",
+ },
+ {
+ "url": "http://def.com",
+ "sha512": "abc",
+ "filters": ["abc == def"],
+ }
+ ],
+ },
+ ) is True
+
+ def test_different_assets_with_same_builds(self):
+ assert asset.do_differ(
+ {
+ "name": "a",
+ "builds": [
+ {
+ "url": "http://abc.com",
+ "sha512": "abc",
+ }
+ ],
+ "annotations": {
+ "foo": "bar",
+ }
+ },
+ {
+ "name": "b",
+ "builds": [
+ {
+ "url": "http://abc.com",
+ "sha512": "abc",
+ }
+ ],
+ "annotations": {
+ "bar": "foo"
+ }
+ },
+ ) is True
+
+
+class TestAsset(ModuleTestCase):
+ def test_minimal_asset_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_asset",
+ builds=[
+ dict(
+ url="http://example.com/asset.tar.gz",
+ sha512="sha512String",
+ ),
+ ]
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ asset.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/core/v2/namespaces/default/assets/test_asset"
+ assert payload == dict(
+ builds=[
+ dict(
+ url="http://example.com/asset.tar.gz",
+ sha512="sha512String",
+ ),
+ ],
+ metadata=dict(
+ name="test_asset",
+ namespace="default",
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_asset_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_asset",
+ namespace="my",
+ state="present",
+ builds=[
+ dict(
+ url="http://example.com/asset.tar.gz",
+ sha512="sha512String",
+ filters=["a", "b", "c"],
+ headers={"header": "h"},
+ ),
+ ],
+ labels={"region": "us-west-1"},
+ annotations={"playbook": 12345},
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ asset.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/core/v2/namespaces/my/assets/test_asset"
+ assert payload == dict(
+ builds=[
+ dict(
+ url="http://example.com/asset.tar.gz",
+ sha512="sha512String",
+ filters=["a", "b", "c"],
+ headers={"header": "h"},
+ )
+ ],
+ metadata=dict(
+ name="test_asset",
+ namespace="my",
+ labels={"region": "us-west-1"},
+ annotations={"playbook": "12345"},
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name="test_asset",
+ builds=[
+ dict(
+ url="http://example.com/asset.tar.gz",
+ sha512="sha512String",
+ )
+ ]
+
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ asset.main()
+
+ def test_failure_empty_builds(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name="test_asset",
+ builds=[],
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ asset.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py
new file mode 100644
index 000000000..aab09b571
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_asset_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import asset_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestAssetInfo(ModuleTestCase):
+ def test_get_all_assets(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ asset_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/assets"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_asset(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-asset")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ asset_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/assets/sample-asset"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_asset(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-asset")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ asset_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-asset")
+
+ with pytest.raises(AnsibleFailJson):
+ asset_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py
new file mode 100644
index 000000000..bd19bceb1
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_auth_provider_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import auth_provider_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestAuthProviderInfo(ModuleTestCase):
+ def test_get_all_auth_providers(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [dict(spec=dict(a=1)), dict(spec=dict(b=2))]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ auth_provider_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/authentication/v2/authproviders"
+ assert context.value.args[0]["objects"] == [dict(a=1), dict(b=2)]
+
+ def test_get_single_auth_provider(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = dict(spec=dict(a=1))
+ set_module_args(name="sample-auth-provider")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ auth_provider_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/authentication/v2/authproviders/sample-auth-provider"
+ assert context.value.args[0]["objects"] == [dict(a=1)]
+
+ def test_missing_single_auth_provider(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-auth-provider")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ auth_provider_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-auth-provider")
+
+ with pytest.raises(AnsibleFailJson):
+ auth_provider_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py
new file mode 100644
index 000000000..e935a7a9f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_bonsai_asset.py
@@ -0,0 +1,47 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import bonsai, errors
+from ansible_collections.sensu.sensu_go.plugins.modules import bonsai_asset
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestBonsaiAsset(ModuleTestCase):
+ def test_success(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ bonsai_params.return_value = dict(sample="value")
+
+ set_module_args(name="name", version="version")
+
+ with pytest.raises(AnsibleExitJson):
+ bonsai_asset.main()
+
+ def test_bonsai_failure(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ bonsai_params.side_effect = errors.BonsaiError("Bonsai bad")
+
+ set_module_args(name="name", version="version")
+
+ with pytest.raises(AnsibleFailJson):
+ bonsai_asset.main()
+
+ def test_validation_failure(self, mocker):
+ bonsai_params = mocker.patch.object(bonsai, "get_asset_parameters")
+ bonsai_params.return_value = dict(sample="value")
+
+ set_module_args(version="version")
+
+ with pytest.raises(AnsibleFailJson):
+ bonsai_asset.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py
new file mode 100644
index 000000000..8ea6ecb94
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check.py
@@ -0,0 +1,327 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import check
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoSetsDiffer:
+ @pytest.mark.parametrize("current,desired,diff", [
+ ([1, 2, 3], [1, 2, 3], False),
+ ([1, 2, 3], [3, 2, 1], False),
+ ([1, 2], [1, 2, 3], True),
+ ([1, 3], [2, 4], True),
+ ])
+ def test_comparison(self, current, desired, diff):
+ c = dict(k=current)
+ d = dict(k=desired)
+
+ assert check.do_sets_differ(c, d, "k") is diff
+
+ def test_missing_keys_are_treated_as_empty_sets(self):
+ current = dict(a=[])
+ desired = dict()
+
+ assert check.do_sets_differ(current, desired, "a") is False
+ assert check.do_sets_differ(desired, current, "a") is False
+
+ def test_nulls_are_treated_as_empty_sets(self):
+ current = dict(a=None)
+ desired = dict(a=[])
+
+ assert check.do_sets_differ(current, desired, "a") is False
+ assert check.do_sets_differ(desired, current, "a") is False
+
+
+class TestDoProxyRequestsDiffer:
+ def test_missing_proxy_requests_in_desired_is_ignored(self):
+ current = dict(proxy_requests=dict(entity_attributes=["a", "b"]))
+ desired = dict()
+
+ assert check.do_proxy_requests_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired,diff", [
+ (["a", "b"], ["a", "b"], False),
+ (["a", "b"], ["b", "a"], False),
+ (None, [], False),
+ (["a", "b"], ["c", "a"], True),
+ (["a", "b"], ["a", "b", "c"], True),
+ ])
+ def test_treat_entity_attributes_as_a_set(self, current, desired, diff):
+ c = dict(proxy_requests=dict(entity_attributes=current))
+ d = dict(proxy_requests=dict(entity_attributes=desired))
+
+ assert check.do_proxy_requests_differ(c, d) is diff
+
+ def test_ignore_missing_entity_attributes_in_desired(self):
+ current = dict(proxy_requests=dict(entity_attributes=["a", "b"]))
+ desired = dict(proxy_requests=dict())
+
+ assert check.do_proxy_requests_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired,diff", [
+ (dict(splay=False), dict(splay=False), False),
+ (dict(splay=False), dict(), False),
+ (dict(splay=False), dict(splay=True), True),
+ (dict(), dict(splay=True), True),
+ ])
+ def test_other_stuff_is_compared_as_usual(self, current, desired, diff):
+ c = dict(proxy_requests=current)
+ d = dict(proxy_requests=desired)
+
+ assert check.do_proxy_requests_differ(c, d) is diff
+
+
+class TestDoCheckHooksDiffer:
+ def test_missing_check_hooks_in_desired_is_ignored(self):
+ current = dict(check_hooks=[dict(warning=["a"])])
+ desired = dict()
+
+ assert check.do_check_hooks_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired,diff", [
+ (["a", "b"], ["a", "b"], False),
+ (["a", "b"], ["b", "a"], False),
+ (["a", "b"], ["c", "a"], True),
+ (["a", "b"], ["a", "b", "c"], True),
+ ])
+ def test_treat_hooks_as_a_set(self, current, desired, diff):
+ c = dict(check_hooks=[dict(warning=current)])
+ d = dict(check_hooks=[dict(warning=desired)])
+
+ assert check.do_check_hooks_differ(c, d) is diff
+
+
+class TestDoDiffer:
+ def test_no_difference(self):
+ assert not check.do_differ(
+ dict(
+ command="sleep",
+ subscriptions=["sub1", "sub2"],
+ handlers=["ha1", "ha2", "ha3"],
+ interval=123,
+ cron="* * * 3 2",
+ publish=False,
+ timeout=30,
+ ttl=60,
+ stdin=True,
+ low_flap_threshold=2,
+ high_flap_threshold=10,
+ runtime_assets=["asset1", "asset2"],
+ check_hooks=[
+ dict(warning=["hook0-1", "hook0-2"]),
+ dict(critical=["hook1-1", "hook1-2"]),
+ ],
+ proxy_entity_name="name",
+ proxy_requests=dict(
+ entity_attributes=["a1", "a2", "a3"],
+ splay=True,
+ splay_coverage=10,
+ ),
+ output_metric_format="influxdb_line",
+ output_metric_handlers=["mhandler1", "mhandler2"],
+ round_robin=False,
+ env_vars=["k1=v1", "k2=v2"],
+ ),
+ dict(
+ command="sleep",
+ subscriptions=["sub2", "sub1"],
+ handlers=["ha3", "ha1", "ha2"],
+ interval=123,
+ cron="* * * 3 2",
+ publish=False,
+ timeout=30,
+ ttl=60,
+ stdin=True,
+ low_flap_threshold=2,
+ high_flap_threshold=10,
+ runtime_assets=["asset2", "asset1"],
+ check_hooks=[
+ dict(critical=["hook1-2", "hook1-1"]),
+ dict(warning=["hook0-2", "hook0-1"]),
+ ],
+ proxy_entity_name="name",
+ proxy_requests=dict(
+ splay=True,
+ entity_attributes=["a3", "a2", "a1"],
+ splay_coverage=10,
+ ),
+ output_metric_format="influxdb_line",
+ output_metric_handlers=["mhandler2", "mhandler1"],
+ round_robin=False,
+ env_vars=["k2=v2", "k1=v1"],
+ )
+ )
+
+ @pytest.mark.parametrize("current,desired", [
+ ( # No diff in params, no secrets
+ dict(name="demo"),
+ dict(name="demo"),
+ ),
+ ( # No diff in params, no diff in secrets
+ dict(name="demo", secrets=[
+ dict(name="n1", secret="s1"), dict(name="n2", secret="s2"),
+ ]),
+ dict(name="demo", secrets=[
+ dict(name="n2", secret="s2"), dict(name="n1", secret="s1"),
+ ]),
+ ),
+ ])
+ def test_no_difference_secrets(self, current, desired):
+ assert check.do_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired", [
+ ( # Diff in params, no diff in secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="a", secret="1")]),
+ ),
+ ( # No diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="demo", secrets=[dict(name="b", secret="2")]),
+ ),
+ ( # Diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="b", secret="2")]),
+ ),
+ ])
+ def test_difference_secrets(self, current, desired):
+ assert check.do_differ(current, desired) is True
+
+
+class TestSensuGoCheck(ModuleTestCase):
+ def test_minimal_check_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_check",
+ command='echo "test"',
+ subscriptions=['switches'],
+ interval=60
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ check.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/core/v2/namespaces/default/checks/test_check"
+ assert payload == dict(
+ command='echo "test"',
+ subscriptions=['switches'],
+ interval=60,
+ metadata=dict(
+ name="test_check",
+ namespace="default",
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_check_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_check',
+ namespace='my',
+ state='absent',
+ command='/bin/true',
+ subscriptions=['checks', 'also_checks'],
+ handlers=['default', 'not_default'],
+ interval=30,
+ publish=True,
+ timeout=30,
+ ttl=100,
+ stdin=False,
+ low_flap_threshold=20,
+ high_flap_threshold=60,
+ proxy_entity_name='switch-dc-01',
+ proxy_requests=dict(
+ entity_attributes=['entity.entity_class == "proxy"'],
+ splay=True,
+ splay_coverage=90
+ ),
+ output_metric_format='nagios_perfdata',
+ output_metric_handlers=['influx-db'],
+ round_robin=True,
+ env_vars=dict(foo='bar'),
+ runtime_assets='awesomeness',
+ secrets=[dict(name="a", secret="b")],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ check.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == "absent"
+ assert path == "/api/core/v2/namespaces/my/checks/test_check"
+ assert payload == dict(
+ command='/bin/true',
+ subscriptions=['checks', 'also_checks'],
+ interval=30,
+ timeout=30,
+ publish=True,
+ handlers=['default', 'not_default'],
+ env_vars=['foo=bar'],
+ output_metric_handlers=['influx-db'],
+ ttl=100,
+ output_metric_format='nagios_perfdata',
+ proxy_entity_name='switch-dc-01',
+ proxy_requests=dict(entity_attributes=['entity.entity_class == "proxy"'],
+ splay=True,
+ splay_coverage=90),
+ high_flap_threshold=60,
+ low_flap_threshold=20,
+ round_robin=True,
+ stdin=False,
+ runtime_assets=['awesomeness'],
+ metadata=dict(
+ name="test_check",
+ namespace="my",
+ ),
+ secrets=[dict(name="a", secret="b")],
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_check',
+ command='/bin/true',
+ subscriptions=['checks', 'also_checks'],
+ handlers=['default', 'not_default'],
+ interval=30,
+ publish=True,
+ timeout=30,
+ ttl=100,
+ stdin=False,
+ low_flap_threshold=20,
+ high_flap_threshold=60,
+ proxy_entity_name='switch-dc-01',
+ proxy_requests=dict(
+ entity_attributes=['entity.entity_class == "proxy"'],
+ splay=True,
+ splay_coverage=90
+ ),
+ output_metric_format='nagios_perfdata',
+ output_metric_handlers=['influx-db'],
+ round_robin=True,
+ env_vars=dict(foo='bar'),
+ runtime_assets='awesomeness'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ check.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py
new file mode 100644
index 000000000..53ed5b7c6
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_check_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import check_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSensuGoCheckInfo(ModuleTestCase):
+ def test_get_all_checks(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ check_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/checks"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_check(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-check")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ check_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/checks/sample-check"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_check(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-check")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ check_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-check")
+
+ with pytest.raises(AnsibleFailJson):
+ check_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py
new file mode 100644
index 000000000..289933644
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster.py
@@ -0,0 +1,83 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestCluster(ModuleTestCase):
+ @pytest.mark.parametrize("params", [
+ {"name": "demo", "state": "absent"},
+ {"name": "demo", "api_urls": "url"},
+ ])
+ def test_minimal_parameters(self, mocker, params):
+ mocker.patch.object(utils, "sync_v1").return_value = True, {}
+ set_module_args(**params)
+
+ with pytest.raises(AnsibleExitJson):
+ cluster.main()
+
+ def test_all_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ state="present",
+ name="demo",
+ api_urls=["a", "b"],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/enterprise/federation/v1/clusters/demo"
+ assert payload == dict(
+ type="Cluster",
+ api_version="federation/v1",
+ metadata=dict(name="demo"),
+ spec=dict(api_urls=["a", "b"]),
+ )
+ assert check_mode is False
+
+ @pytest.mark.parametrize("skip", ["api_urls"])
+ def test_missing_required_param_present(self, mocker, skip):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ all_args = dict(name="demo", api_urls="url")
+ set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip))
+
+ with pytest.raises(AnsibleFailJson):
+ cluster.main()
+
+ sync_mock.assert_not_called()
+
+ def test_failure(self, mocker):
+ mocker.patch.object(utils, "sync_v1").side_effect = (
+ errors.Error("Bad error")
+ )
+ set_module_args(name="demo", state="absent")
+
+ with pytest.raises(AnsibleFailJson):
+ cluster.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py
new file mode 100644
index 000000000..45158dcd3
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_info.py
@@ -0,0 +1,85 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestClusterInfo(ModuleTestCase):
+ def test_all_parameters(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k1": "v1"}}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ name="demo",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_info.main()
+
+ def test_get_all_clusters(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [
+ {"spec": {"k1": "v1"}}, {"spec": {"k2": "v2"}},
+ ]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/federation/v1/clusters"
+ assert context.value.args[0]["objects"] == [
+ {"k1": "v1"}, {"k2": "v2"},
+ ]
+
+ def test_get_single_cluster(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k3": "v3"}}
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/federation/v1/clusters/demo"
+ assert context.value.args[0]["objects"] == [{"k3": "v3"}]
+
+ def test_missing_single_cluster(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleFailJson):
+ cluster_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py
new file mode 100644
index 000000000..48e939a03
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role.py
@@ -0,0 +1,128 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestClusterRole(ModuleTestCase):
+ def test_minimal_cluster_role_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_cluster_role',
+ rules=[
+ dict(
+ verbs=[],
+ resources=[]
+ ),
+ ]
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_role.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/clusterroles/test_cluster_role'
+ assert payload == dict(
+ rules=[
+ dict(
+ verbs=[],
+ resources=[],
+ resource_names=None,
+ )
+ ],
+ metadata=dict(
+ name='test_cluster_role',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_cluster_role_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_cluster_role',
+ state='present',
+ rules=[
+ dict(
+ verbs=['get', 'list', 'create'],
+ resources=['assets', 'entities'],
+ ),
+ dict(
+ verbs=['list'],
+ resources=['check'],
+ resource_names=['my-check'],
+ )
+ ],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_role.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/clusterroles/test_cluster_role'
+ assert payload == dict(
+ metadata=dict(
+ name='test_cluster_role',
+ ),
+ rules=[
+ dict(
+ verbs=['get', 'list', 'create'],
+ resources=['assets', 'entities'],
+ resource_names=None,
+ ),
+ dict(
+ verbs=['list'],
+ resources=['check'],
+ resource_names=['my-check'],
+ )
+ ],
+ )
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_cluster_role',
+ rules=[
+ dict(
+ verbs=[],
+ resources=[],
+ )
+ ],
+ )
+ with pytest.raises(AnsibleFailJson):
+ cluster_role.main()
+
+ def test_failure_invalid_verb(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_cluster_role',
+ rules=[
+ dict(
+ verbs=['list', 'invalid'],
+ resources=[],
+ ),
+ ]
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ cluster_role.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py
new file mode 100644
index 000000000..b18c2d226
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding.py
@@ -0,0 +1,152 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role_binding
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestClusterRoleBinding(ModuleTestCase):
+ def test_minimal_cluster_role_binding_parameters_users(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_cluster_role_binding',
+ cluster_role='test_cluster_role',
+ users=['test_user'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/clusterrolebindings/test_cluster_role_binding'
+ assert payload == dict(
+ role_ref=dict(
+ name='test_cluster_role',
+ type='ClusterRole',
+ ),
+ subjects=[
+ dict(
+ name='test_user',
+ type='User',
+ ),
+ ],
+ metadata=dict(
+ name='test_cluster_role_binding',
+ ),
+ )
+ assert check_mode is False
+
+ def test_minimal_cluster_role_binding_parameters_groups(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_cluster_role_binding',
+ cluster_role='test_cluster_role',
+ groups=['test_group'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/clusterrolebindings/test_cluster_role_binding'
+ assert payload == dict(
+ role_ref=dict(
+ name='test_cluster_role',
+ type='ClusterRole',
+ ),
+ subjects=[
+ dict(
+ name='test_group',
+ type='Group',
+ ),
+ ],
+ metadata=dict(
+ name='test_cluster_role_binding',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_cluster_role_binding_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_cluster_role_binding',
+ cluster_role='test_cluster_role',
+ users=['user_1', 'user_2'],
+ groups=['group_1', 'group_2'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ cluster_role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/clusterrolebindings/test_cluster_role_binding'
+ assert payload == dict(
+ metadata=dict(
+ name='test_cluster_role_binding',
+ ),
+ role_ref=dict(
+ name='test_cluster_role',
+ type='ClusterRole',
+ ),
+ subjects=[
+ dict(
+ name='group_1',
+ type='Group',
+ ),
+ dict(
+ name='group_2',
+ type='Group',
+ ),
+ dict(
+ name='user_1',
+ type='User',
+ ),
+ dict(
+ name='user_2',
+ type='User',
+ ),
+ ]
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_cluster_role_binding',
+ cluster_role='test_cluster_role',
+ users=['test_user'],
+ )
+ with pytest.raises(AnsibleFailJson):
+ cluster_role_binding.main()
+
+ def test_failure_missing_groups_or_users(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_cluster_role_binding',
+ cluster_role='test_cluster_role',
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ cluster_role_binding.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py
new file mode 100644
index 000000000..34e58451f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_binding_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role_binding_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestClusterRoleBindingInfo(ModuleTestCase):
+ def test_get_all_cluster_role_bindings(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_binding_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/clusterrolebindings"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_cluster_role_binding(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 1
+ set_module_args(name="test-cluster-role-binding")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_binding_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/clusterrolebindings/test-cluster-role-binding"
+ assert context.value.args[0]["objects"] == [1]
+
+ def test_missing_single_cluster_role_binding(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-cluster-role-binding")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_binding_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-cluster-role-binding")
+
+ with pytest.raises(AnsibleFailJson):
+ cluster_role_binding_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py
new file mode 100644
index 000000000..11eadb6a6
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_cluster_role_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import cluster_role_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestClusterRoleInfo(ModuleTestCase):
+ def test_get_all_cluster_roles(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/clusterroles"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_cluster_role(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 1
+ set_module_args(name="test-cluster-role")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/clusterroles/test-cluster-role"
+ assert context.value.args[0]["objects"] == [1]
+
+ def test_missing_single_cluster_role(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-cluster-role")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ cluster_role_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-cluster-role")
+
+ with pytest.raises(AnsibleFailJson):
+ cluster_role_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py
new file mode 100644
index 000000000..665b29ab8
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore.py
@@ -0,0 +1,269 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, http,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import datastore
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSync(ModuleTestCase):
+ def test_absent_no_current_object(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ changed, object = datastore.sync(
+ "absent", client, "/list", "/resource", {}, False,
+ )
+
+ assert changed is False
+ assert object is None
+
+ def test_absent_no_current_object_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, "")
+
+ changed, object = datastore.sync(
+ "absent", client, "/list", "/resource", {}, True,
+ )
+
+ assert changed is False
+ assert object is None
+
+ def test_absent_current_object_present(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ client.delete.return_value = http.Response(204, "")
+
+ changed, object = datastore.sync(
+ "absent", client, "/list", "/resource", {}, False,
+ )
+
+ assert changed is True
+ assert object is None
+ client.delete.assert_called_with("/resource")
+
+ def test_absent_current_object_present_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ client.delete.return_value = http.Response(204, "")
+
+ changed, object = datastore.sync(
+ "absent", client, "/list", "/resource", {}, True,
+ )
+
+ assert changed is True
+ assert object is None
+ client.delete.assert_not_called()
+
+ def test_present_current_object_differ(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(200, '{"spec": {"current": "data"}}'),
+ http.Response(200, '{"spec": {"new": "data"}}'),
+ )
+ client.put.return_value = http.Response(201, "")
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ False,
+ )
+
+ assert changed is True
+ assert {"new": "data"} == object
+ client.put.assert_called_once_with(
+ "/resource", {"spec": {"my": "data"}},
+ )
+
+ def test_present_current_object_differ_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = (
+ http.Response(200, '{"spec": {"current": "data"}}')
+ )
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ True,
+ )
+
+ assert changed is True
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_current_object_does_not_differ(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = (
+ http.Response(200, '{"spec": {"my": "data"}}')
+ )
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ False,
+ )
+
+ assert changed is False
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_current_object_does_not_differ_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = (
+ http.Response(200, '{"spec": {"my": "data"}}')
+ )
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ True,
+ )
+
+ assert changed is False
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ def test_present_no_current_object_empty_backend(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(404, ""),
+ http.Response(200, "[]"),
+ http.Response(200, '{"spec": {"new": "data"}}'),
+ )
+ client.put.return_value = http.Response(201, "")
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ False,
+ )
+
+ assert changed is True
+ assert {"new": "data"} == object
+ client.put.assert_called_once_with(
+ "/resource", {"spec": {"my": "data"}},
+ )
+
+ def test_present_no_current_object_empty_backend_check(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(404, ""),
+ http.Response(200, "[]"),
+ )
+
+ changed, object = datastore.sync(
+ "present", client, "/list", "/resource", {"spec": {"my": "data"}},
+ True,
+ )
+
+ assert changed is True
+ assert {"my": "data"} == object
+ client.put.assert_not_called()
+
+ @pytest.mark.parametrize("check", [False, True])
+ def test_present_no_current_object_non_empty_backend(self, mocker, check):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(404, ""),
+ http.Response(200, "[{}]"),
+ )
+
+ with pytest.raises(errors.Error, match="already active"):
+ datastore.sync(
+ "present", client, "/list", "/resource",
+ {"spec": {"my": "data"}}, check,
+ )
+
+ client.put.assert_not_called()
+
+
+class TestDatastore(ModuleTestCase):
+ def test_minimal_datastore_parameters_present(self, mocker):
+ sync_mock = mocker.patch.object(datastore, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_datastore",
+ dsn="my-dsn",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ datastore.main()
+
+ state, _client, list_path, resource_path, payload, check_mode = (
+ sync_mock.call_args[0]
+ )
+ assert state == "present"
+ assert resource_path == "/api/enterprise/store/v1/provider/test_datastore"
+ assert list_path == "/api/enterprise/store/v1/provider"
+ assert payload == dict(
+ type="PostgresConfig",
+ api_version="store/v1",
+ metadata=dict(name="test_datastore"),
+ spec=dict(dsn="my-dsn"),
+ )
+ assert check_mode is False
+
+ def test_minimal_datastore_parameters_absent(self, mocker):
+ sync_mock = mocker.patch.object(datastore, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_datastore",
+ state="absent",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ datastore.main()
+
+ state, _client, list_path, resource_path, _payload, check_mode = (
+ sync_mock.call_args[0]
+ )
+ assert state == "absent"
+ assert resource_path == "/api/enterprise/store/v1/provider/test_datastore"
+ assert list_path == "/api/enterprise/store/v1/provider"
+ assert check_mode is False
+
+ def test_all_datastore_parameters(self, mocker):
+ sync_mock = mocker.patch.object(datastore, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_datastore",
+ dsn="my-dsn",
+ pool_size=543,
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ datastore.main()
+
+ state, _client, list_path, resource_path, payload, check_mode = (
+ sync_mock.call_args[0]
+ )
+ assert state == "present"
+ assert resource_path == "/api/enterprise/store/v1/provider/test_datastore"
+ assert list_path == "/api/enterprise/store/v1/provider"
+ assert payload == dict(
+ type="PostgresConfig",
+ api_version="store/v1",
+ metadata=dict(name="test_datastore"),
+ spec=dict(dsn="my-dsn", pool_size=543),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(datastore, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name="test_datastore",
+ dsn="my-dsn",
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ datastore.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py
new file mode 100644
index 000000000..4a1efc9c2
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_datastore_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import datastore_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDatastoreInfo(ModuleTestCase):
+ def test_get_all_datastores(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [dict(spec=1), dict(spec=2)]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ datastore_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/store/v1/provider"
+ assert context.value.args[0]["objects"] == [1, 2]
+
+ def test_get_single_datastore(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = dict(spec=4)
+ set_module_args(name="sample-datastore")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ datastore_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/store/v1/provider/sample-datastore"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_datastore(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-datastore")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ datastore_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-datastore")
+
+ with pytest.raises(AnsibleFailJson):
+ datastore_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py
new file mode 100644
index 000000000..fb098ed87
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity.py
@@ -0,0 +1,199 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import entity
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ @pytest.mark.parametrize('current', [
+ dict(no=dict(system="here")),
+ dict(system=dict(here="is")),
+ ])
+ def test_no_system_in_desired(self, current):
+ assert entity.do_differ(current, {}) is False
+
+ def test_system_keys_not_in_current_are_ignored(self):
+ assert entity.do_differ(
+ dict(system=dict(a=1, b=2)),
+ dict(system=dict(a=1)),
+ ) is False
+
+ def test_actual_changes_are_detected(self):
+ assert entity.do_differ(
+ dict(system=dict(a=1, b=2)),
+ dict(system=dict(a=2)),
+ ) is True
+
+ def test_missing_keys_are_detected(self):
+ assert entity.do_differ(
+ dict(system=dict(b=2)),
+ dict(system=dict(a=2)),
+ ) is True
+
+ @pytest.mark.parametrize("current,desired", [
+ ([], None), ([], []),
+ (["a"], ["a"]),
+ (["a", "b"], ["b", "a"]),
+ ])
+ def test_no_diff_in_subscriptions(self, current, desired):
+ assert entity.do_differ(
+ dict(subscriptions=current), dict(subscriptions=desired),
+ ) is False
+
+ @pytest.mark.parametrize("current,desired", [
+ ([], ["a"]), (["a"], []),
+ (["a"], ["b"]),
+ (["a", "b"], ["a", "c"]),
+ ])
+ def test_diff_in_subscriptions(self, current, desired):
+ print((current, desired))
+ assert entity.do_differ(
+ dict(subscriptions=current), dict(subscriptions=desired),
+ ) is True
+
+
+class TestEntity(ModuleTestCase):
+ def test_minimal_entity_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_entity',
+ entity_class='proxy',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ entity.main()
+
+ state, _c, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/entities/test_entity'
+ assert payload == dict(
+ entity_class='proxy',
+ metadata=dict(
+ name='test_entity',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_minimal_entity_parameters_agent_class(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_entity',
+ entity_class='agent',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ entity.main()
+
+ state, _c, path, payload, check_mode, _d = sync_mock.call_args[0]
+ print(payload)
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/entities/test_entity'
+ assert payload == dict(
+ entity_class='agent',
+ metadata=dict(
+ name='test_entity',
+ namespace='default',
+ ),
+ subscriptions=['entity:test_entity'],
+ )
+ assert check_mode is False
+
+ def test_all_entity_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_entity',
+ namespace='my',
+ state='absent',
+ entity_class='proxy',
+ subscriptions=['web', 'prod'],
+ system=dict(
+ hostname='test-entity',
+ os='linux',
+ platform='ubuntu',
+ network=dict(
+ interfaces=[
+ dict(
+ name='lo',
+ addresses=['127.0.0.1/8', '::1/128']
+ ),
+ dict(
+ name='eth0',
+ mac='52:54:00:20:1b:3c',
+ addresses=['93.184.216.34/24']
+ )
+ ])
+ ),
+ last_seen=1522798317,
+ deregister=True,
+ deregistration_handler='email-handler',
+ redact=['password', 'pass', 'api_key'],
+ user='agent'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ entity.main()
+
+ state, _c, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == 'absent'
+ assert path == '/api/core/v2/namespaces/my/entities/test_entity'
+ assert payload == dict(
+ entity_class='proxy',
+ subscriptions=['web', 'prod'],
+ system=dict(
+ hostname='test-entity',
+ os='linux',
+ platform='ubuntu',
+ network=dict(
+ interfaces=[
+ dict(
+ name='lo',
+ addresses=['127.0.0.1/8', '::1/128']
+ ),
+ dict(
+ name='eth0',
+ mac='52:54:00:20:1b:3c',
+ addresses=['93.184.216.34/24']
+ )
+ ])
+ ),
+ last_seen=1522798317,
+ deregister=True,
+ deregistration=dict(handler='email-handler'),
+ redact=['password', 'pass', 'api_key'],
+ user='agent',
+ metadata=dict(
+ name='test_entity',
+ namespace='my'
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_entity',
+ entity_class='proxy'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ entity.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py
new file mode 100644
index 000000000..964dcaed0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_entity_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import entity_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestEntityInfo(ModuleTestCase):
+ def test_get_all_entities(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ entity_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/entities"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_entity(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-entity")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ entity_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/entities/sample-entity"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_entity(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-entity")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ entity_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-entity")
+
+ with pytest.raises(AnsibleFailJson):
+ entity_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py
new file mode 100644
index 000000000..982c97c57
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator.py
@@ -0,0 +1,132 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import etcd_replicator
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestEtcdReplicator(ModuleTestCase):
+ @pytest.mark.parametrize("params", [
+ {"name": "demo", "state": "absent"},
+ {"name": "demo", "insecure": True, "url": "url", "resource": "resource"},
+ {
+ "name": "demo",
+ "url": "url",
+ "resource": "resource",
+ "ca_cert": "ca_cert",
+ "cert": "cert",
+ "key": "key",
+ },
+ ])
+ def test_minimal_parameters(self, mocker, params):
+ mocker.patch.object(utils, "sync_v1").return_value = True, {}
+ set_module_args(**params)
+
+ with pytest.raises(AnsibleExitJson):
+ etcd_replicator.main()
+
+ def test_all_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ state="present",
+ name="demo",
+ ca_cert="ca_cert",
+ cert="cert",
+ key="key",
+ insecure=True,
+ url=["a", "b"],
+ api_version="api_version",
+ resource="resource",
+ namespace="namespace",
+ replication_interval=30,
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ etcd_replicator.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/enterprise/federation/v1/etcd-replicators/demo"
+ assert payload == dict(
+ type="EtcdReplicator",
+ api_version="federation/v1",
+ metadata=dict(name="demo"),
+ spec=dict(
+ ca_cert="ca_cert",
+ cert="cert",
+ key="key",
+ insecure=True,
+ url="a,b",
+ api_version="api_version",
+ resource="resource",
+ namespace="namespace",
+ replication_interval_seconds=30,
+ ),
+ )
+ assert check_mode is False
+
+ @pytest.mark.parametrize("skip", ["ca_cert", "cert", "key", "url", "resource"])
+ def test_missing_required_param_present_secure(self, mocker, skip):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ all_args = dict(
+ name="demo",
+ ca_cert="ca_cert",
+ cert="cert",
+ key="key",
+ url="url",
+ resource="resource",
+ )
+ set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip))
+
+ with pytest.raises(AnsibleFailJson):
+ etcd_replicator.main()
+
+ sync_mock.assert_not_called()
+
+ @pytest.mark.parametrize("skip", ["url", "resource"])
+ def test_missing_required_param_present_insecure(self, mocker, skip):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ all_args = dict(
+ name="demo",
+ insecure=True,
+ url="url",
+ resource="resource",
+ )
+ set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip))
+
+ with pytest.raises(AnsibleFailJson):
+ etcd_replicator.main()
+
+ sync_mock.assert_not_called()
+
+ def test_failure(self, mocker):
+ mocker.patch.object(utils, "sync_v1").side_effect = (
+ errors.Error("Bad error")
+ )
+ set_module_args(name="demo", state="absent")
+
+ with pytest.raises(AnsibleFailJson):
+ etcd_replicator.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py
new file mode 100644
index 000000000..e651dab76
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_etcd_replicator_info.py
@@ -0,0 +1,85 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import etcd_replicator_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestEtcdReplicatorInfo(ModuleTestCase):
+ def test_all_parameters(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k1": "v1"}}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ name="demo",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ etcd_replicator_info.main()
+
+ def test_get_all_secrets(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [
+ {"spec": {"k1": "v1"}}, {"spec": {"k2": "v2"}},
+ ]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ etcd_replicator_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/federation/v1/etcd-replicators"
+ assert context.value.args[0]["objects"] == [
+ {"k1": "v1"}, {"k2": "v2"},
+ ]
+
+ def test_get_single_replicator(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k3": "v3"}}
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ etcd_replicator_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/federation/v1/etcd-replicators/demo"
+ assert context.value.args[0]["objects"] == [{"k3": "v3"}]
+
+ def test_missing_single_replicator(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ etcd_replicator_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleFailJson):
+ etcd_replicator_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py
new file mode 100644
index 000000000..8808ec2c5
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event.py
@@ -0,0 +1,286 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, http,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import event
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestGetObjects:
+ def test_get_entity(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"entity": "entity"}')
+ resp = event.get_entity(client, 'default', 'entity')
+
+ assert resp == {'entity': 'entity'}
+
+ def test_get_entity_404(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, '')
+
+ with pytest.raises(errors.SyncError,
+ match="Entity with name 'entity' does not exist on remote."):
+ event.get_entity(client, 'default', 'entity')
+
+ def test_get_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"check": "check"}')
+ resp = event.get_check(client, 'default', 'check')
+
+ assert resp == {'check': 'check'}
+
+ def test_get_check_404(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(404, '')
+
+ with pytest.raises(errors.SyncError,
+ match="Check with name 'check' does not exist on remote."):
+ event.get_check(client, 'default', 'check')
+
+
+class TestEvent(ModuleTestCase):
+ def test_missing_entity_on_remote(self, mocker):
+ get_entity_mock = mocker.patch.object(event, 'get_entity')
+ get_entity_mock.side_effect = errors.SyncError('Error')
+
+ set_module_args(
+ entity='awesome_entity',
+ check='awesome_check',
+ )
+
+ with pytest.raises(AnsibleFailJson, match='Error'):
+ event.main()
+
+ def test_missing_check_on_remote(self, mocker):
+ mocker.patch.object(event, 'get_entity')
+ get_check_mock = mocker.patch.object(event, 'get_check')
+ get_check_mock.side_effect = errors.SyncError('Error')
+
+ set_module_args(
+ entity='awesome_entity',
+ check='awesome_check',
+ )
+
+ with pytest.raises(AnsibleFailJson, match='Error'):
+ event.main()
+
+ def test_minimal_event_parameters(self, mocker):
+ send_event_mock = mocker.patch.object(event, 'send_event')
+ send_event_mock.return_value = True, {}
+ get_entity_mock = mocker.patch.object(event, 'get_entity')
+ get_entity_mock.return_value = dict(
+ metadata=dict(
+ name='awesome_entity',
+ namespace='default'
+ ),
+ entity_class='proxy'
+ )
+ get_check_mock = mocker.patch.object(event, 'get_check')
+ get_check_mock.return_value = dict(
+ metadata=dict(
+ name='awesome_check',
+ namespace='default'
+ )
+ )
+
+ set_module_args(
+ entity='awesome_entity',
+ check='awesome_check',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ event.main()
+
+ _client, path, payload, check_mode = send_event_mock.call_args[0]
+ assert path == '/api/core/v2/namespaces/default/events/awesome_entity/awesome_check'
+ assert payload == dict(
+ metadata=dict(
+ namespace='default'
+ ),
+ entity=dict(
+ metadata=dict(
+ name='awesome_entity',
+ namespace='default'
+ ),
+ entity_class='proxy'
+ ),
+ check=dict(
+ metadata=dict(
+ name='awesome_check',
+ namespace='default'
+ )
+ )
+ )
+ assert check_mode is False
+
+ def test_all_event_parameters(self, mocker):
+ entity_object = dict(
+ metadata=dict(
+ name='awesome_entity',
+ namespace='default'
+ ),
+ entity_class='proxy'
+ )
+ check_object = dict(
+ metadata=dict(
+ name='awesome_check',
+ namespace='default'
+ ),
+ command="check-cpu.sh -w 75 -c 90",
+ handlers=["slack"],
+ interval=60,
+ publish=True,
+ subscriptions=["linux"],
+ )
+ send_event_mock = mocker.patch.object(event, 'send_event')
+ send_event_mock.return_value = True, {}
+ get_entity_mock = mocker.patch.object(event, 'get_entity')
+ get_entity_mock.return_value = entity_object
+ get_check_mock = mocker.patch.object(event, 'get_check')
+ get_check_mock.return_value = check_object
+
+ set_module_args(
+ namespace='my',
+ timestamp=1234567,
+ entity='awesome_entity',
+ check='awesome_check',
+ check_attributes=dict(
+ duration=1.945,
+ executed=1522100915,
+ history=[
+ dict(
+ executed=1552505193,
+ status=1
+ ),
+ dict(
+ executed=1552505293,
+ status=0
+ ),
+ dict(
+ executed=1552505393,
+ status=0
+ ),
+ dict(
+ executed=1552505493,
+ status=0
+ )
+ ],
+ issued=1552506033,
+ last_ok=1552506033,
+ output='10',
+ state='passing',
+ status='ok',
+ total_state_change=0
+ ),
+ metric_attributes=dict(
+ handlers=['handler1', 'handler2'],
+ points=[{
+ 'name': 'sensu-go-sandbox.curl_timings.time_total',
+ 'tags': [],
+ 'timestamp': 1552506033,
+ 'value': 0.005
+ }, {
+ 'name': 'sensu-go-sandbox.curl_timings.time_namelookup',
+ 'tags': [],
+ 'timestamp': 1552506033,
+ 'value': 0.004
+ }]
+ )
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ event.main()
+
+ _client, path, payload, check_mode = send_event_mock.call_args[0]
+ assert path == '/api/core/v2/namespaces/my/events/awesome_entity/awesome_check'
+ assert payload == dict(
+ metadata=dict(
+ namespace='my'
+ ),
+ timestamp=1234567,
+ entity=dict(
+ metadata=dict(
+ name='awesome_entity',
+ namespace='default'
+ ),
+ entity_class='proxy'
+ ),
+ check=dict(
+ metadata=dict(
+ name='awesome_check',
+ namespace='default'
+ ),
+ command="check-cpu.sh -w 75 -c 90",
+ handlers=["slack"],
+ interval=60,
+ publish=True,
+ subscriptions=["linux"],
+ duration=1.945,
+ executed=1522100915,
+ history=[
+ dict(
+ executed=1552505193,
+ status=1
+ ),
+ dict(
+ executed=1552505293,
+ status=0
+ ),
+ dict(
+ executed=1552505393,
+ status=0
+ ),
+ dict(
+ executed=1552505493,
+ status=0
+ )
+ ],
+ issued=1552506033,
+ last_ok=1552506033,
+ output='10',
+ state='passing',
+ status=0,
+ total_state_change=0
+ ),
+ metrics=dict(
+ handlers=['handler1', 'handler2'],
+ points=[{
+ 'name': 'sensu-go-sandbox.curl_timings.time_total',
+ 'tags': [],
+ 'timestamp': 1552506033,
+ 'value': 0.005
+ }, {
+ 'name': 'sensu-go-sandbox.curl_timings.time_namelookup',
+ 'tags': [],
+ 'timestamp': 1552506033,
+ 'value': 0.004
+ }]
+ )
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ get_entity_mock = mocker.patch.object(event, 'get_entity')
+ get_entity_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ entity='awesome_entity',
+ check=dict(
+ name='awesome_check'
+ )
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ event.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py
new file mode 100644
index 000000000..c9270892d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_event_info.py
@@ -0,0 +1,94 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import event_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestEventInfo(ModuleTestCase):
+ def test_get_all_events(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ event_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == '/api/core/v2/namespaces/my/events'
+ assert context.value.args[0]['objects'] == [1, 2, 3]
+
+ def test_get_events_by_entity(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = [1, 2]
+ set_module_args(
+ entity='simple-entity'
+ )
+
+ with pytest.raises(AnsibleExitJson) as context:
+ event_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == '/api/core/v2/namespaces/default/events/simple-entity'
+ assert context.value.args[0]['objects'] == [1, 2]
+
+ def test_get_events_by_check(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = [1, 2]
+ set_module_args(
+ check='simple-check'
+ )
+
+ with pytest.raises(AnsibleFailJson,
+ match=r"missing parameter\(s\) required by 'check': entity"):
+ event_info.main()
+
+ def test_get_single_event_by_entity_and_check(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = 4
+ set_module_args(
+ entity='simple-entity',
+ check='simple-check'
+ )
+
+ with pytest.raises(AnsibleExitJson) as context:
+ event_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == '/api/core/v2/namespaces/default/events/simple-entity/simple-check'
+ assert context.value.args[0]['objects'] == [4]
+
+ def test_no_event_by_entity_and_check(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(
+ entity='simple-entity',
+ check='simple-check'
+ )
+
+ with pytest.raises(AnsibleExitJson) as context:
+ event_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.side_effect = errors.Error('Bad error')
+ set_module_args(entity='simple-entity')
+
+ with pytest.raises(AnsibleFailJson):
+ event_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py
new file mode 100644
index 000000000..f92a52623
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter.py
@@ -0,0 +1,91 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import filter
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestFilter(ModuleTestCase):
+ def test_minimal_filter_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_filter',
+ action='allow',
+ expressions='event.check.occurences == 1',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ filter.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/filters/test_filter'
+ assert payload == dict(
+ action='allow',
+ expressions=['event.check.occurences == 1'],
+ metadata=dict(
+ name='test_filter',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_filter_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_filter',
+ namespace='my',
+ state='absent',
+ action='allow',
+ expressions='event.check.occurences == 1',
+ runtime_assets='awesomeness',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': 12345},
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ filter.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'absent'
+ assert path == '/api/core/v2/namespaces/my/filters/test_filter'
+ assert payload == dict(
+ action='allow',
+ expressions=['event.check.occurences == 1'],
+ runtime_assets=['awesomeness'],
+ metadata=dict(
+ name='test_filter',
+ namespace='my',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': '12345'},
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_filter',
+ action='deny',
+ expressions='event.check.occurences == 1',
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ filter.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py
new file mode 100644
index 000000000..d8bbcf41a
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_filter_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import filter_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestFilterInfo(ModuleTestCase):
+ def test_get_all_filters(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ filter_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/filters"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_filter(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-filter")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ filter_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/filters/sample-filter"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_filter(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-filter")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ filter_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-filter")
+
+ with pytest.raises(AnsibleFailJson):
+ filter_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py
new file mode 100644
index 000000000..bd6aa8059
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_handler_set.py
@@ -0,0 +1,59 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import handler_set
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestHandlerSet(ModuleTestCase):
+ def test_all_handler_set_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_handler',
+ namespace='my',
+ state='absent',
+ handlers=['tcp_handler', 'udp_handler']
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ handler_set.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "absent"
+ assert path == "/api/core/v2/namespaces/my/handlers/test_handler"
+ assert payload == dict(
+ type='set',
+ handlers=['tcp_handler', 'udp_handler'],
+ metadata=dict(
+ name="test_handler",
+ namespace="my",
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_handler',
+ state='absent',
+ handlers=['tcp_handler', 'udp_handler']
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ handler_set.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py
new file mode 100644
index 000000000..3b6e6da8d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook.py
@@ -0,0 +1,93 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import hook
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestHook(ModuleTestCase):
+ def test_minimal_hook_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_hook',
+ command='/bin/true',
+ timeout=10,
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ hook.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/hooks/test_hook'
+ assert payload == dict(
+ command='/bin/true',
+ timeout=10,
+ metadata=dict(
+ name='test_hook',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_hook_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_hook',
+ namespace='my',
+ state='absent',
+ command='/bin/true',
+ timeout=30,
+ stdin=True,
+ runtime_assets='awesomeness',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': 12345},
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ hook.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'absent'
+ assert path == '/api/core/v2/namespaces/my/hooks/test_hook'
+ assert payload == dict(
+ command='/bin/true',
+ timeout=30,
+ stdin=True,
+ runtime_assets=['awesomeness'],
+ metadata=dict(
+ name='test_hook',
+ namespace='my',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': '12345'},
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_hook',
+ command='/bin/true',
+ timeout=10
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ hook.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py
new file mode 100644
index 000000000..82269840f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_hook_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import hook_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestHookInfo(ModuleTestCase):
+ def test_get_all_hooks(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ hook_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/hooks"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_hook(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-hook")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ hook_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/hooks/sample-hook"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_hook(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-hook")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ hook_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-hook")
+
+ with pytest.raises(AnsibleFailJson):
+ hook_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py
new file mode 100644
index 000000000..4b9e1c463
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_ldap_auth_provider.py
@@ -0,0 +1,326 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors,
+ utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import ldap_auth_provider
+
+from .common.utils import (
+ AnsibleExitJson,
+ AnsibleFailJson,
+ ModuleTestCase,
+ set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ def test_no_changes(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(name="openldap"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="openldap",
+ created_by="me",
+ ),
+ )
+
+ assert ldap_auth_provider.do_differ(current, desired) is False
+
+ def test_changes_are_detected(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(name="openldap"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="openldap",
+ created_by="me",
+ ),
+ )
+ assert ldap_auth_provider.do_differ(current, desired) is True
+
+ def test_changes_are_detected_diff_servers_len(self):
+ desired = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ ),
+ dict(
+ host="127.0.0.2",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ ),
+ ],
+ ),
+ metadata=dict(name="openldap"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ ),
+ metadata=dict(
+ name="openldap",
+ created_by="me",
+ ),
+ )
+ assert ldap_auth_provider.do_differ(current, desired) is True
+
+ def test_changes_are_other_params(self):
+ desired = dict(
+ spec=dict(
+ servers=[],
+ groups_prefix="ldap",
+ username_prefix="ldap",
+ ),
+ metadata=dict(name="openldap"),
+ )
+ current = dict(
+ spec=dict(
+ servers=[],
+ ),
+ metadata=dict(
+ name="openldap",
+ created_by="me",
+ ),
+ )
+ assert ldap_auth_provider.do_differ(current, desired) is True
+
+
+class TestLDAPAutProvider(ModuleTestCase):
+ def test_minimal_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="openldap",
+ servers=[
+ dict(
+ host="127.0.0.1",
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ ),
+ )
+ ],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ ldap_auth_provider.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[
+ 0
+ ]
+
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/openldap"
+ assert payload == dict(
+ type="ldap",
+ api_version="authentication/v2",
+ metadata=dict(name="openldap"),
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=None,
+ insecure=False,
+ security="tls",
+ trusted_ca_file=None,
+ client_cert_file=None,
+ client_key_file=None,
+ binding=None,
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="groupOfNames",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="uid",
+ name_attribute="cn",
+ object_class="person",
+ ),
+ )
+ ]
+ ),
+ )
+
+ assert check_mode is False
+
+ def test_all_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="openldap",
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ insecure=False,
+ security="tls",
+ trusted_ca_file="/path/to/trusted-certificate-authorities.pem",
+ client_cert_file="/path/to/ssl/cert.pem",
+ client_key_file="/path/to/ssl/key.pem",
+ binding=dict(
+ user_dn="cn=binder,dc=acme,dc=org",
+ password="YOUR_PASSWORD",
+ ),
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="groupOfNames",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="uid",
+ name_attribute="cn",
+ object_class="person",
+ ),
+ )
+ ],
+ groups_prefix="ldap",
+ username_prefix="ldap",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ ldap_auth_provider.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[
+ 0
+ ]
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/openldap"
+ assert payload == dict(
+ type="ldap",
+ api_version="authentication/v2",
+ metadata=dict(name="openldap"),
+ spec=dict(
+ servers=[
+ dict(
+ host="127.0.0.1",
+ port=636,
+ insecure=False,
+ security="tls",
+ trusted_ca_file="/path/to/trusted-certificate-authorities.pem",
+ client_cert_file="/path/to/ssl/cert.pem",
+ client_key_file="/path/to/ssl/key.pem",
+ binding=dict(
+ user_dn="cn=binder,dc=acme,dc=org",
+ password="YOUR_PASSWORD",
+ ),
+ group_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="member",
+ name_attribute="cn",
+ object_class="groupOfNames",
+ ),
+ user_search=dict(
+ base_dn="dc=acme,dc=org",
+ attribute="uid",
+ name_attribute="cn",
+ object_class="person",
+ ),
+ )
+ ],
+ groups_prefix="ldap",
+ username_prefix="ldap",
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ ldap_auth_provider.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py
new file mode 100644
index 000000000..9cef32e76
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator.py
@@ -0,0 +1,126 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import mutator
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ @pytest.mark.parametrize("current,desired", [
+ ( # No diff in params, no secrets
+ dict(name="demo"),
+ dict(name="demo"),
+ ),
+ ( # No diff in params, no diff in secrets
+ dict(name="demo", secrets=[
+ dict(name="n1", secret="s1"), dict(name="n2", secret="s2"),
+ ]),
+ dict(name="demo", secrets=[
+ dict(name="n2", secret="s2"), dict(name="n1", secret="s1"),
+ ]),
+ ),
+ ])
+ def test_no_difference(self, current, desired):
+ assert mutator.do_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired", [
+ ( # Diff in params, no diff in secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="a", secret="1")]),
+ ),
+ ( # No diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="demo", secrets=[dict(name="b", secret="2")]),
+ ),
+ ( # Diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="b", secret="2")]),
+ ),
+ ])
+ def test_difference(self, current, desired):
+ assert mutator.do_differ(current, desired) is True
+
+
+class TestMutator(ModuleTestCase):
+ def test_minimal_mutator_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_mutator',
+ command='/bin/true',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ mutator.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/mutators/test_mutator'
+ assert payload == dict(
+ command='/bin/true',
+ metadata=dict(
+ name='test_mutator',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_mutator_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_mutator',
+ namespace='my',
+ state='absent',
+ command='/bin/true',
+ timeout=30,
+ runtime_assets='awesomeness',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': 12345},
+ secrets=[dict(name="a", secret="b")],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ mutator.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == 'absent'
+ assert path == '/api/core/v2/namespaces/my/mutators/test_mutator'
+ assert payload == dict(
+ command='/bin/true',
+ timeout=30,
+ runtime_assets=['awesomeness'],
+ metadata=dict(
+ name='test_mutator',
+ namespace='my',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': '12345'},
+ ),
+ secrets=[dict(name="a", secret="b")],
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_mutator',
+ command='/bion/true'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ mutator.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py
new file mode 100644
index 000000000..b1456c8fb
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_mutator_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import mutator_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestMutatorInfo(ModuleTestCase):
+ def test_get_all_mutators(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ mutator_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/mutators"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_mutator(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-mutator")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ mutator_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/mutators/sample-mutator"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_mutator(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-mutator")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ mutator_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-mutator")
+
+ with pytest.raises(AnsibleFailJson):
+ mutator_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py
new file mode 100644
index 000000000..e51a63af1
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace.py
@@ -0,0 +1,49 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import namespace
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestNamespace(ModuleTestCase):
+ def test_namespace(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='dev'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ namespace.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/dev'
+ assert payload == dict(
+ name='dev'
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='dev',
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ namespace.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py
new file mode 100644
index 000000000..431b6463f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_namespace_info.py
@@ -0,0 +1,41 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import namespace_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestNamespaceInfo(ModuleTestCase):
+ def test_get_namespaces(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ namespace_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ namespace_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py
new file mode 100644
index 000000000..0780cdc2f
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_oidc_auth_provider.py
@@ -0,0 +1,118 @@
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors,
+ utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import oidc_auth_provider
+
+from .common.utils import (
+ AnsibleExitJson,
+ AnsibleFailJson,
+ ModuleTestCase,
+ set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestADAutProvider(ModuleTestCase):
+ def test_minimal_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="oidc_name",
+ additional_scopes=["openid"],
+ client_id="a8e43af034e7f2608780",
+ client_secret="b63968394be6ed2edb61c93847ee792f31bf6216",
+ disable_offline_access=False,
+ server="https://oidc.example.com:9031",
+ username_claim="email",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ oidc_auth_provider.main()
+
+ state, _client, path, payload, check_mode = sync_v1_mock.call_args[
+ 0
+ ]
+
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/oidc_name"
+ assert payload == dict(
+ type="oidc",
+ api_version="authentication/v2",
+ metadata=dict(name="oidc_name"),
+ spec=dict(
+ additional_scopes=["openid"],
+ client_id="a8e43af034e7f2608780",
+ client_secret="b63968394be6ed2edb61c93847ee792f31bf6216",
+ disable_offline_access=False,
+ server="https://oidc.example.com:9031",
+ username_claim="email",
+ ),
+ )
+
+ assert check_mode is False
+
+ def test_all_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, "sync_v1")
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state="present",
+ name="oidc_name",
+ additional_scopes=["groups", "email", "username"],
+ client_id="a8e43af034e7f2608780",
+ client_secret="b63968394be6ed2edb61c93847ee792f31bf6216",
+ disable_offline_access=False,
+ redirect_uri="http://127.0.0.1:8080/api/enterprise/authentication/v2/oidc/callback",
+ server="https://oidc.example.com:9031",
+ groups_claim="groups",
+ groups_prefix="oidc:",
+ username_claim="email",
+ username_prefix="oidc:",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ oidc_auth_provider.main()
+
+ state, _client, path, payload, check_mode = sync_v1_mock.call_args[
+ 0
+ ]
+ assert state == "present"
+ assert path == "/api/enterprise/authentication/v2/authproviders/oidc_name"
+ assert payload == dict(
+ type="oidc",
+ api_version="authentication/v2",
+ metadata=dict(name="oidc_name"),
+ spec=dict(
+ additional_scopes=["groups", "email", "username"],
+ client_id="a8e43af034e7f2608780",
+ client_secret="b63968394be6ed2edb61c93847ee792f31bf6216",
+ disable_offline_access=False,
+ redirect_uri="http://127.0.0.1:8080/api/enterprise/authentication/v2/oidc/callback",
+ server="https://oidc.example.com:9031",
+ groups_claim="groups",
+ groups_prefix="oidc:",
+ username_claim="email",
+ username_prefix="oidc:",
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ oidc_auth_provider.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py
new file mode 100644
index 000000000..69c3254a3
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler.py
@@ -0,0 +1,136 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import pipe_handler
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ @pytest.mark.parametrize("current,desired", [
+ ( # No diff in params, no secrets
+ dict(name="demo"),
+ dict(name="demo"),
+ ),
+ ( # No diff in params, no diff in secrets
+ dict(name="demo", secrets=[
+ dict(name="n1", secret="s1"), dict(name="n2", secret="s2"),
+ ]),
+ dict(name="demo", secrets=[
+ dict(name="n2", secret="s2"), dict(name="n1", secret="s1"),
+ ]),
+ ),
+ ])
+ def test_no_difference(self, current, desired):
+ assert pipe_handler.do_differ(current, desired) is False
+
+ @pytest.mark.parametrize("current,desired", [
+ ( # Diff in params, no diff in secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="a", secret="1")]),
+ ),
+ ( # No diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="demo", secrets=[dict(name="b", secret="2")]),
+ ),
+ ( # Diff in params, missing and set secrets
+ dict(name="demo", secrets=[dict(name="a", secret="1")]),
+ dict(name="prod", secrets=[dict(name="b", secret="2")]),
+ ),
+ ])
+ def test_difference(self, current, desired):
+ assert pipe_handler.do_differ(current, desired) is True
+
+
+class TestPipeHandler(ModuleTestCase):
+ def test_minimal_pipe_handler_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_handler",
+ command='echo "test"'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ pipe_handler.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/core/v2/namespaces/default/handlers/test_handler"
+ assert payload == dict(
+ command='echo "test"',
+ type='pipe',
+ metadata=dict(
+ name="test_handler",
+ namespace="default",
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_pipe_handler_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_handler',
+ namespace='my',
+ state='absent',
+ command='/bin/true',
+ filters=['occurrences', 'production'],
+ mutator='only_check_output',
+ timeout=30,
+ env_vars=dict(foo='bar'),
+ runtime_assets='awesomeness',
+ secrets=[dict(name="a", secret="b")],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ pipe_handler.main()
+
+ state, _client, path, payload, check_mode, _d = sync_mock.call_args[0]
+ assert state == "absent"
+ assert path == "/api/core/v2/namespaces/my/handlers/test_handler"
+ assert payload == dict(
+ command='/bin/true',
+ type='pipe',
+ filters=['occurrences', 'production'],
+ mutator='only_check_output',
+ timeout=30,
+ env_vars=['foo=bar'],
+ runtime_assets=['awesomeness'],
+ metadata=dict(
+ name="test_handler",
+ namespace="my",
+ ),
+ secrets=[dict(name="a", secret="b")],
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_handler',
+ state='absent',
+ command='/bin/true',
+ filters=['occurrences', 'production'],
+ mutator='only_check_output',
+ timeout=30,
+ env_vars=dict(foo='bar'),
+ runtime_assets='awesomeness'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ pipe_handler.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py
new file mode 100644
index 000000000..2339e8551
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_pipe_handler_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import handler_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestHandlerInfo(ModuleTestCase):
+ def test_get_all_handlers(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ handler_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/handlers"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_handler(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-handler")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ handler_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/handlers/sample-handler"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_handler(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-handler")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ handler_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-handler")
+
+ with pytest.raises(AnsibleFailJson):
+ handler_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py
new file mode 100644
index 000000000..cd1ce4fbf
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role.py
@@ -0,0 +1,142 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import role
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestRole(ModuleTestCase):
+ def test_minimal_role_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role',
+ rules=[
+ dict(
+ verbs=[],
+ resources=[]
+ ),
+ ]
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/roles/test_role'
+ assert payload == dict(
+ rules=[
+ dict(
+ verbs=[],
+ resources=[],
+ resource_names=None,
+ )
+ ],
+ metadata=dict(
+ name='test_role',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_role_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role',
+ namespace='my',
+ state='present',
+ rules=[
+ dict(
+ verbs=['get', 'list', 'create'],
+ resources=['assets', 'entities'],
+ ),
+ dict(
+ verbs=['list'],
+ resources=['check'],
+ resource_names=['my-check'],
+ )
+ ],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/my/roles/test_role'
+ assert payload == dict(
+ metadata=dict(
+ name='test_role',
+ namespace='my',
+ ),
+ rules=[
+ dict(
+ verbs=['get', 'list', 'create'],
+ resources=['assets', 'entities'],
+ resource_names=None,
+ ),
+ dict(
+ verbs=['list'],
+ resources=['check'],
+ resource_names=['my-check'],
+ )
+ ],
+ )
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_role',
+ rules=[
+ dict(
+ verbs=[],
+ resources=[],
+ )
+ ],
+ )
+ with pytest.raises(AnsibleFailJson):
+ role.main()
+
+ def test_failure_invalid_verb(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_role',
+ rules=[
+ dict(
+ verbs=['list', 'invalid'],
+ resources=[],
+ ),
+ ]
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ role.main()
+
+ def test_failure_empty_rules(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_role',
+ rules=[]
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ role.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py
new file mode 100644
index 000000000..b22eaa7ef
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding.py
@@ -0,0 +1,214 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import role_binding
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestRoleBinding(ModuleTestCase):
+ def test_minimal_role_binding_parameters_users(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role_binding',
+ role='test_role',
+ users=['test_user'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/rolebindings/test_role_binding'
+ assert payload == dict(
+ role_ref=dict(
+ name='test_role',
+ type='Role',
+ ),
+ subjects=[
+ dict(
+ name='test_user',
+ type='User',
+ ),
+ ],
+ metadata=dict(
+ name='test_role_binding',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_minimal_role_binding_parameters_groups(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role_binding',
+ role='test_role',
+ groups=['test_group'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/rolebindings/test_role_binding'
+ assert payload == dict(
+ role_ref=dict(
+ name='test_role',
+ type='Role',
+ ),
+ subjects=[
+ dict(
+ name='test_group',
+ type='Group',
+ ),
+ ],
+ metadata=dict(
+ name='test_role_binding',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_role_binding_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role_binding',
+ namespace='my',
+ role='test_role',
+ users=['user_1', 'user_2'],
+ groups=['group_1', 'group_2'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/my/rolebindings/test_role_binding'
+ assert payload == dict(
+ metadata=dict(
+ name='test_role_binding',
+ namespace='my',
+ ),
+ role_ref=dict(
+ name='test_role',
+ type='Role',
+ ),
+ subjects=[
+ dict(
+ name='group_1',
+ type='Group',
+ ),
+ dict(
+ name='group_2',
+ type='Group',
+ ),
+ dict(
+ name='user_1',
+ type='User',
+ ),
+ dict(
+ name='user_2',
+ type='User',
+ ),
+ ]
+ )
+ assert check_mode is False
+
+ def test_role_binding_with_cluster_role(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_role_binding',
+ cluster_role='test_cluster_role',
+ users=['test_user'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ role_binding.main()
+
+ state, _client, path, payload, check_mode, _compare = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/rolebindings/test_role_binding'
+ assert payload == dict(
+ role_ref=dict(
+ name='test_cluster_role',
+ type='ClusterRole',
+ ),
+ subjects=[
+ dict(
+ name='test_user',
+ type='User',
+ ),
+ ],
+ metadata=dict(
+ name='test_role_binding',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_role_binding',
+ role='test_role',
+ users=['test_user'],
+ )
+ with pytest.raises(AnsibleFailJson):
+ role_binding.main()
+
+ def test_failure_role_and_cluster_role(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_role_binding',
+ role='test_role',
+ cluster_role='test_cluster_role',
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ role_binding.main()
+
+ def test_failure_missing_groups_or_users(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = Exception("Validation should fail but didn't")
+ set_module_args(
+ name='test_role_binding',
+ role='test_role',
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ role_binding.main()
+
+ @pytest.mark.parametrize("params,result", [
+ (
+ dict(role="test-role", cluster_role=None),
+ ("Role", "test-role"),
+ ),
+ (
+ dict(cluster_role="test-cluster-role", role=None),
+ ("ClusterRole", "test-cluster-role"),
+ ),
+ ])
+ def test_infer_role(self, params, result):
+ assert result == role_binding.infer_role(params)
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py
new file mode 100644
index 000000000..2f7725160
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_binding_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import role_binding_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestRoleBindingInfo(ModuleTestCase):
+ def test_get_all_role_bindings(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_binding_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/rolebindings"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_role_binding(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 1
+ set_module_args(name="test-role-binding")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_binding_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/rolebindings/test-role-binding"
+ assert context.value.args[0]["objects"] == [1]
+
+ def test_missing_single_role_binding(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-role-binding")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_binding_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-role-binding")
+
+ with pytest.raises(AnsibleFailJson):
+ role_binding_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py
new file mode 100644
index 000000000..a2bec291d
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_role_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import role_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestRoleInfo(ModuleTestCase):
+ def test_get_all_roles(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/roles"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_role(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 1
+ set_module_args(name="test-role")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/roles/test-role"
+ assert context.value.args[0]["objects"] == [1]
+
+ def test_missing_single_role(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-role")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ role_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-role")
+
+ with pytest.raises(AnsibleFailJson):
+ role_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py
new file mode 100644
index 000000000..5912ae738
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret.py
@@ -0,0 +1,85 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import secret
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSecret(ModuleTestCase):
+ @pytest.mark.parametrize("params", [
+ {"name": "demo", "provider": "env", "id": "MY_VAR"},
+ {"name": "demo", "state": "absent"},
+ ])
+ def test_minimal_parameters(self, mocker, params):
+ mocker.patch.object(utils, "sync_v1").return_value = True, {}
+ set_module_args(**params)
+
+ with pytest.raises(AnsibleExitJson):
+ secret.main()
+
+ def test_all_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ state="present",
+ name="demo",
+ namespace="ns",
+ provider="env",
+ id="MY_ENV_VAR",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ secret.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/enterprise/secrets/v1/namespaces/ns/secrets/demo"
+ assert payload == dict(
+ type="Secret",
+ api_version="secrets/v1",
+ metadata=dict(name="demo", namespace="ns"),
+ spec=dict(provider="env", id="MY_ENV_VAR"),
+ )
+ assert check_mode is False
+
+ @pytest.mark.parametrize("skip", ["provider", "id"])
+ def test_missing_required_param_present(self, mocker, skip):
+ sync_mock = mocker.patch.object(utils, "sync_v1")
+ all_args = dict(name="demo", provider="env", id="X")
+ set_module_args(**dict((k, v) for k, v in all_args.items() if k != skip))
+
+ with pytest.raises(AnsibleFailJson):
+ secret.main()
+
+ sync_mock.assert_not_called()
+
+ def test_failure(self, mocker):
+ mocker.patch.object(utils, "sync_v1").side_effect = (
+ errors.Error("Bad error")
+ )
+ set_module_args(name="demo", state="absent")
+
+ with pytest.raises(AnsibleFailJson):
+ secret.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py
new file mode 100644
index 000000000..161204f26
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secret_info.py
@@ -0,0 +1,86 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import secret_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSecretInfo(ModuleTestCase):
+ def test_all_parameters(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k1": "v1"}}
+ set_module_args(
+ auth=dict(
+ user="user",
+ password="pass",
+ url="http://127.0.0.1:1234",
+ api_key="123-key",
+ verify=False,
+ ca_path="/tmp/ca.bundle",
+ ),
+ name="demo",
+ namespace="ns",
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ secret_info.main()
+
+ def test_get_all_secrets(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [
+ {"spec": {"k1": "v1"}}, {"spec": {"k2": "v2"}},
+ ]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secret_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/secrets/v1/namespaces/default/secrets"
+ assert context.value.args[0]["objects"] == [
+ {"k1": "v1"}, {"k2": "v2"},
+ ]
+
+ def test_get_single_secret(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = {"spec": {"k3": "v3"}}
+ set_module_args(name="demo", namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secret_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/secrets/v1/namespaces/my/secrets/demo"
+ assert context.value.args[0]["objects"] == [{"k3": "v3"}]
+
+ def test_missing_single_secret(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secret_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="demo")
+
+ with pytest.raises(AnsibleFailJson):
+ secret_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py
new file mode 100644
index 000000000..cce081bf9
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_env.py
@@ -0,0 +1,69 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import secrets_provider_env
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSecretsProviderEnv(ModuleTestCase):
+ def test_no_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_v1_mock.return_value = True, {}
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson):
+ secrets_provider_env.main()
+
+ state, _client, path, payload, check_mode = sync_v1_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/enterprise/secrets/v1/providers/env'
+ assert payload == dict(
+ type='Env',
+ api_version="secrets/v1",
+ metadata=dict(name='env'),
+ spec={}
+ )
+ assert check_mode is False
+
+ def test_all_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ state='present',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ secrets_provider_env.main()
+
+ state, _client, path, payload, check_mode = sync_v1_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/enterprise/secrets/v1/providers/env'
+ assert payload == dict(
+ type='Env',
+ api_version="secrets/v1",
+ metadata=dict(name='env'),
+ spec={}
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_v1_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ secrets_provider_env.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py
new file mode 100644
index 000000000..695152493
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import secrets_provider_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSecretsProviderInfo(ModuleTestCase):
+ def test_get_all_secrets_providers(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [dict(spec=1), dict(spec=2)]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secrets_provider_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/secrets/v1/providers"
+ assert context.value.args[0]["objects"] == [1, 2]
+
+ def test_get_single_secrets_provider(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = dict(spec=4)
+ set_module_args(name="sample-secrets-provider")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secrets_provider_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/enterprise/secrets/v1/providers/sample-secrets-provider"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_secrets_provider(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-secrets-provider")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ secrets_provider_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-secrets-provider")
+
+ with pytest.raises(AnsibleFailJson):
+ secrets_provider_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py
new file mode 100644
index 000000000..fc4c17a87
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_secrets_provider_vault.py
@@ -0,0 +1,182 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import secrets_provider_vault
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestDoDiffer:
+ def test_fields_are_ignored(self):
+ desired = dict(
+ spec=dict(
+ client=dict(
+ address="https://my-vault.com",
+ tls=dict(
+ ca_cert="cert"
+ ),
+ ),
+ ),
+ metadata=dict(
+ name="my-vault"
+ )
+ )
+ current = dict(
+ spec=dict(
+ client=dict(
+ address="https://my-vault.com",
+ agent_address="", # extra field
+ tls=dict(
+ ca_cert="cert",
+ # extra fields
+ insecure=False,
+ ca_path="path",
+ tls_server_name="server",
+ )
+ ),
+ ),
+ metadata=dict(
+ name="my-vault",
+ created_by="me",
+ )
+ )
+
+ assert secrets_provider_vault.do_differ(current, desired) is False
+
+ def test_changes_are_detected(self):
+ desired = dict(
+ spec=dict(
+ client=dict(
+ address="https://my-vault.com",
+ tls=dict(
+ ca_cert="cert"
+ ),
+ ),
+ ),
+ metadata=dict(
+ name="my-vault"
+ )
+ )
+ current = dict(
+ spec=dict(
+ client=dict(
+ address="https://my-vault.com",
+ tls=dict(
+ ca_cert="new-cert",
+ cname="server",
+ ),
+ timeout='15s',
+ ),
+ ),
+ metadata=dict(
+ name="my-vault",
+ )
+ )
+ assert secrets_provider_vault.do_differ(current, desired) is True
+
+
+class TestSecretsProviderVault(ModuleTestCase):
+ def test_minimal_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ name='my-vault',
+ state='present',
+ address='https://my-vault.com',
+ token='AUTH_TOKEN',
+ version='v1',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ secrets_provider_vault.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/enterprise/secrets/v1/providers/my-vault'
+ assert payload == dict(
+ type='VaultProvider',
+ api_version="secrets/v1",
+ metadata=dict(name='my-vault'),
+ spec=dict(
+ client=dict(
+ address='https://my-vault.com',
+ token='AUTH_TOKEN',
+ version='v1',
+ ),
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_provider_parameters(self, mocker):
+ sync_v1_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_v1_mock.return_value = True, {}
+ set_module_args(
+ name='my-vault',
+ state='present',
+ address='https://my-vault.com',
+ token='AUTH_TOKEN',
+ version='v1',
+ tls=dict(
+ ca_cert='/etc/ssl/ca.crt',
+ client_cert='/etc/ssl/client.crt',
+ client_key='/etc/ssl/client.key',
+ cname='my-vault.com',
+ ),
+ timeout=1,
+ max_retries=2,
+ rate_limit=3,
+ burst_limit=4,
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ secrets_provider_vault.main()
+
+ state, _client, path, payload, check_mode, _do_differ = sync_v1_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/enterprise/secrets/v1/providers/my-vault'
+ assert payload == dict(
+ type='VaultProvider',
+ api_version="secrets/v1",
+ metadata=dict(name='my-vault'),
+ spec=dict(
+ client=dict(
+ address='https://my-vault.com',
+ token='AUTH_TOKEN',
+ version='v1',
+ tls=dict(
+ ca_cert='/etc/ssl/ca.crt',
+ client_cert='/etc/ssl/client.crt',
+ client_key='/etc/ssl/client.key',
+ cname='my-vault.com',
+ ),
+ timeout="1s",
+ max_retries=2,
+ rate_limiter=dict(
+ limit=3,
+ burst=4,
+ ),
+ ),
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync_v1')
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson):
+ secrets_provider_vault.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py
new file mode 100644
index 000000000..ad0642718
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence.py
@@ -0,0 +1,120 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import silence
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSilence(ModuleTestCase):
+ def test_minimal_silence_parameters_check(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ check='check'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ silence.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/silenced/%2A%3Acheck' # %2A = *, %3A = :
+ assert payload == dict(
+ check='check',
+ metadata=dict(
+ name='*:check',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_minimal_silence_parameters_subscription(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ subscription='subscription'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ silence.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'present'
+ assert path == '/api/core/v2/namespaces/default/silenced/subscription%3A%2A' # %3A = :, %2A = *
+ assert payload == dict(
+ subscription='subscription',
+ metadata=dict(
+ name='subscription:*',
+ namespace='default',
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_silence_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ namespace='my',
+ subscription='entity:test-entity',
+ check='check',
+ state='absent',
+ begin=1542671205,
+ expire=1542771205,
+ expire_on_resolve=True,
+ reason='because',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': 12345},
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ silence.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == 'absent'
+ assert path == '/api/core/v2/namespaces/my/silenced/entity%3Atest-entity%3Acheck' # %3A = :
+ assert payload == dict(
+ subscription='entity:test-entity',
+ check='check',
+ begin=1542671205,
+ expire=1542771205,
+ expire_on_resolve=True,
+ reason='because',
+ metadata=dict(
+ name='entity:test-entity:check',
+ namespace='my',
+ labels={'region': 'us-west-1'},
+ annotations={'playbook': '12345'},
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure_when_both_params_are_missing(self):
+ set_module_args()
+
+ with pytest.raises(AnsibleFailJson,
+ match='one of the following is required: subscription, check'):
+ silence.main()
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ subscription='subscription'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ silence.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py
new file mode 100644
index 000000000..7a2215411
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_silence_info.py
@@ -0,0 +1,66 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import silence_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSilenceInfo(ModuleTestCase):
+ def test_get_all_silences(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args(namespace="my")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ silence_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/my/silenced"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_silence(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(subscription="subscription")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ silence_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/namespaces/default/silenced/subscription%3A%2A" # %3A = :, %2A = *
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_silence(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(
+ subscription="missing",
+ check="missing",
+ )
+
+ with pytest.raises(AnsibleExitJson) as context:
+ silence_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(check="check")
+
+ with pytest.raises(AnsibleFailJson):
+ silence_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py
new file mode 100644
index 000000000..b4c72f194
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_socket_handler.py
@@ -0,0 +1,101 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import socket_handler
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSocketHandler(ModuleTestCase):
+ def test_minimal_socket_handler_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name="test_handler",
+ type='tcp',
+ host='10.0.1.99',
+ port=4444
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ socket_handler.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "present"
+ assert path == "/api/core/v2/namespaces/default/handlers/test_handler"
+ assert payload == dict(
+ type='tcp',
+ socket=dict(
+ host='10.0.1.99',
+ port=4444
+ ),
+ metadata=dict(
+ name="test_handler",
+ namespace="default",
+ ),
+ )
+ assert check_mode is False
+
+ def test_all_socket_handler_parameters(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_handler',
+ namespace='my',
+ state='absent',
+ type='udp',
+ filters=['occurrences', 'production'],
+ mutator='only_check_output',
+ timeout=30,
+ host='10.0.1.99',
+ port=4444
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ socket_handler.main()
+
+ state, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert state == "absent"
+ assert path == "/api/core/v2/namespaces/my/handlers/test_handler"
+ assert payload == dict(
+ type='udp',
+ filters=['occurrences', 'production'],
+ mutator='only_check_output',
+ timeout=30,
+ socket=dict(
+ host='10.0.1.99',
+ port=4444
+ ),
+ metadata=dict(
+ name="test_handler",
+ namespace="my",
+ ),
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(utils, "sync")
+ sync_mock.side_effect = errors.Error("Bad error")
+ set_module_args(
+ name='test_handler',
+ state='absent',
+ type='udp',
+ host='10.0.1.99',
+ port=4444
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ socket_handler.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py
new file mode 100644
index 000000000..3eb2796b9
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_tessen.py
@@ -0,0 +1,105 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, http,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import tessen
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestSync:
+ def test_remote_and_desired_equal(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ changed, object = tessen.sync(client, "/path", {}, False)
+
+ assert changed is False
+ assert object == {}
+
+ def test_remote_and_desired_not_equal(self, mocker):
+ client = mocker.Mock()
+ client.get.side_effect = (
+ http.Response(200, '{"opt_out": "false"}'),
+ http.Response(200, '{"opt_out": "true"}'),
+ )
+ client.put.return_value = http.Response(200, "")
+ changed, object = tessen.sync(client, "/path", {'opt_out': True}, False)
+
+ assert changed is True
+ assert object == {'opt_out': 'true'}
+ client.put.assert_called_once_with("/path", {'opt_out': True})
+
+ def test_remote_and_desired_equal_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{}')
+ changed, object = tessen.sync(client, "/path", {}, True)
+
+ assert changed is False
+ assert object == {}
+
+ def test_remote_and_desired_not_equal_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"opt_out": "false"}')
+ changed, object = tessen.sync(client, "/path", {'opt_out': True}, True)
+
+ assert changed is True
+ assert object == {'opt_out': True}
+ client.put.assert_not_called()
+
+
+class TestTessen(ModuleTestCase):
+ def test_enabled(self, mocker):
+ sync_mock = mocker.patch.object(tessen, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ state='enabled'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ tessen.main()
+
+ _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert path == '/api/core/v2/tessen'
+ assert payload == dict(
+ opt_out=False
+ )
+ assert check_mode is False
+
+ def test_disabled(self, mocker):
+ sync_mock = mocker.patch.object(tessen, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ state='disabled'
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ tessen.main()
+
+ _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert path == '/api/core/v2/tessen'
+ assert payload == dict(
+ opt_out=True
+ )
+ assert check_mode is False
+
+ def test_failure(self, mocker):
+ sync_mock = mocker.patch.object(tessen, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ state='enabled'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ tessen.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py
new file mode 100644
index 000000000..52e4a7698
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user.py
@@ -0,0 +1,519 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+from distutils import version
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ arguments, errors, http, utils
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import user
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestUpdatePassword:
+ @pytest.mark.parametrize('check', [False, True])
+ def test_password_is_valid(self, mocker, check):
+ client = mocker.Mock()
+ client.validate_auth_data.return_value = True
+
+ changed = user.update_password(client, '/path', 'user', 'pass', check)
+
+ assert changed is False
+ client.validate_auth_data.assert_called_once_with('user', 'pass')
+ client.put.assert_not_called()
+
+ def test_password_is_invalid_older_than_5_21_0(self, mocker):
+ client = mocker.Mock()
+ client.validate_auth_data.return_value = False
+ client.version = version.StrictVersion("5.20.2")
+ client.put.return_value = http.Response(201, '')
+
+ changed = user.update_password(client, '/path', 'user', 'pass', False)
+
+ assert changed is True
+ client.validate_auth_data.assert_called_once_with('user', 'pass')
+ client.put.assert_called_once_with('/path/password', dict(
+ username='user', password='pass',
+ ))
+
+ def test_password_is_invalid_5_21_0_or_newer(self, mocker):
+ client = mocker.Mock()
+ client.validate_auth_data.return_value = False
+ client.version = version.StrictVersion("5.21.0")
+ client.put.return_value = http.Response(201, '')
+
+ changed = user.update_password(client, '/path', 'user', 'pass', False)
+
+ assert changed is True
+ client.validate_auth_data.assert_called_once_with('user', 'pass')
+ client.put.assert_called_once()
+
+ path, payload = client.put.call_args[0]
+ assert path == '/path/reset_password'
+ assert payload['username'] == 'user'
+
+ # (tadeboro): We cannot validate the value without mocking the bcrypt.
+ # And I would rather see that our code gets tested by actually using
+ # the bcrypt rather than mocking it out. This way, the message
+ # encode/decode stuff gets put through its paces.
+ assert 'password_hash' in payload
+
+ def test_password_is_invalid_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.validate_auth_data.return_value = False
+
+ changed = user.update_password(client, '/path', 'user', 'pass', True)
+
+ assert changed is True
+ client.validate_auth_data.assert_called_once_with('user', 'pass')
+ client.put.assert_not_called()
+
+
+class TestUpdatePasswordHash:
+ @pytest.mark.parametrize('check', [False, True])
+ def test_sensu_go_older_than_5_21_0(self, mocker, check):
+ client = mocker.Mock()
+ client.version = version.StrictVersion("5.20.0")
+
+ with pytest.raises(errors.SensuError):
+ user.update_password_hash(client, '/path', 'user', 'hash', check)
+
+ client.put.assert_not_called()
+
+ def test_sensu_go_newer_than_5_21_0(self, mocker):
+ client = mocker.Mock()
+ client.version = version.StrictVersion("5.21.0")
+ client.put.return_value = http.Response(201, '')
+
+ changed = user.update_password_hash(
+ client, '/path', 'user', 'hash', False,
+ )
+
+ assert changed is True
+ client.put.assert_called_once()
+
+ path, payload = client.put.call_args[0]
+ assert path == '/path/reset_password'
+ assert payload['username'] == 'user'
+ assert payload['password_hash'] == 'hash'
+
+ def test_sensu_go_newer_than_5_21_0_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.version = version.StrictVersion("5.21.0")
+
+ changed = user.update_password_hash(
+ client, '/path', 'user', 'pass', True,
+ )
+
+ assert changed is True
+ client.put.assert_not_called()
+
+
+class TestUpdateGroups:
+ @pytest.mark.parametrize('check', [False, True])
+ def test_update_groups_no_change(self, mocker, check):
+ client = mocker.Mock()
+
+ result = user.update_groups(
+ client, '/path', ['a', 'b'], ['b', 'a'], check,
+ )
+
+ assert result is False
+ client.put.assert_not_called()
+ client.delete.assert_not_called()
+
+ def test_update_groups(self, mocker):
+ client = mocker.Mock()
+ client.put.side_effect = [
+ http.Response(201, ''), http.Response(201, ''),
+ ]
+ client.delete.side_effect = [
+ http.Response(204, ''), http.Response(204, ''),
+ ]
+
+ result = user.update_groups(
+ client, '/path', ['a', 'b', 'c'], ['e', 'd', 'c'], False,
+ )
+
+ assert result is True
+ client.put.assert_has_calls([
+ mocker.call('/path/groups/d', None),
+ mocker.call('/path/groups/e', None),
+ ], any_order=True)
+ client.delete.assert_has_calls([
+ mocker.call('/path/groups/a'),
+ mocker.call('/path/groups/b'),
+ ], any_order=True)
+
+ def test_update_groups_check_mode(self, mocker):
+ client = mocker.Mock()
+
+ result = user.update_groups(
+ client, '/path', ['a', 'b', 'c'], ['e', 'd', 'c'], True,
+ )
+
+ assert result is True
+ client.put.assert_not_called()
+ client.delete.assert_not_called()
+
+
+class TestUpdateState:
+ @pytest.mark.parametrize('check', [False, True])
+ @pytest.mark.parametrize('state', [False, True])
+ def test_update_state_no_change(self, mocker, check, state):
+ client = mocker.Mock()
+
+ result = user.update_state(client, '/path', state, state, check)
+
+ assert result is False
+ client.put.assert_not_called()
+ client.delete.assert_not_called()
+
+ def test_disable_user(self, mocker):
+ client = mocker.Mock()
+ client.delete.return_value = http.Response(204, '')
+
+ # Go from disabled == False to disabled == True
+ result = user.update_state(client, '/path', False, True, False)
+
+ assert result is True
+ client.put.assert_not_called()
+ client.delete.assert_called_once_with('/path')
+
+ def test_disable_user_check_mode(self, mocker):
+ client = mocker.Mock()
+
+ # Go from disabled == False to disabled == True
+ result = user.update_state(client, '/path', False, True, True)
+
+ assert result is True
+ client.put.assert_not_called()
+ client.delete.assert_not_called()
+
+ def test_enable_user(self, mocker):
+ client = mocker.Mock()
+ client.put.return_value = http.Response(201, '')
+
+ # Go from disabled == True to disabled == False
+ result = user.update_state(client, '/path', True, False, False)
+
+ assert result is True
+ client.put.assert_called_once_with('/path/reinstate', None)
+ client.delete.assert_not_called()
+
+ def test_enable_user_check_mode(self, mocker):
+ client = mocker.Mock()
+
+ # Go from disabled == True to disabled == False
+ result = user.update_state(client, '/path', True, False, True)
+
+ assert result is True
+ client.put.assert_not_called()
+ client.delete.assert_not_called()
+
+
+class TestSync:
+ def test_no_current_object(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ client.put.return_value = http.Response(201, '')
+
+ changed, result = user.sync(
+ None, client, '/path', {'password': 'data'}, False
+ )
+
+ assert changed is True
+ assert {'new': 'data'} == result
+ client.put.assert_called_once_with('/path', {'password': 'data'})
+
+ def test_no_current_object_check(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+
+ changed, result = user.sync(
+ None, client, '/path', {'password_hash': 'data'}, True
+ )
+
+ assert changed is True
+ assert {} == result
+ client.put.assert_not_called()
+
+ def test_password_update(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ p_mock.return_value = True
+ g_mock = mocker.patch.object(user, 'update_groups')
+ s_mock = mocker.patch.object(user, 'update_state')
+
+ changed, result = user.sync(
+ dict(old='data'), client, '/path',
+ dict(username='user', password='pass'), False
+ )
+
+ assert changed is True
+ assert dict(new='data') == result
+ p_mock.assert_called_once()
+ g_mock.assert_not_called()
+ s_mock.assert_not_called()
+
+ def test_password_update_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ p_mock.return_value = False
+ g_mock = mocker.patch.object(user, 'update_groups')
+ s_mock = mocker.patch.object(user, 'update_state')
+
+ changed, result = user.sync(
+ dict(old='data'), client, '/path',
+ dict(username='user', password='pass'), True
+ )
+
+ assert changed is False
+ assert dict(old='data', username='user') == result
+ p_mock.assert_called_once()
+ g_mock.assert_not_called()
+ s_mock.assert_not_called()
+
+ def test_password_hash_update(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ mock = mocker.patch.object(user, 'update_password_hash')
+ mock.return_value = True
+
+ changed, result = user.sync(
+ dict(old='data'), client, '/path',
+ dict(username='user', password_hash='pass'), False
+ )
+
+ assert changed is True
+ assert dict(new='data') == result
+ mock.assert_called_once()
+
+ def test_password_hash_update_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ mock = mocker.patch.object(user, 'update_password_hash')
+ mock.return_value = True
+
+ changed, result = user.sync(
+ dict(old='data'), client, '/path',
+ dict(username='user', password_hash='pass'), True
+ )
+
+ assert changed is True
+ assert dict(old='data', username='user') == result
+ mock.assert_called_once()
+
+ def test_when_password_is_set_we_ignore_hash(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ p_mock.return_value = True
+ h_mock = mocker.patch.object(user, 'update_password_hash')
+
+ user.sync(
+ dict(old='data'), client, '/path',
+ dict(username='user', password='pass', password_hash='hash'),
+ False
+ )
+
+ p_mock.assert_called_once()
+ h_mock.assert_not_called()
+
+ def test_groups_update(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ g_mock = mocker.patch.object(user, 'update_groups')
+ g_mock.return_value = False
+ s_mock = mocker.patch.object(user, 'update_state')
+
+ changed, result = user.sync(
+ dict(groups=['a']), client, '/path', dict(groups=['b']), False
+ )
+
+ assert changed is False
+ assert dict(new='data') == result
+ p_mock.assert_not_called()
+ g_mock.assert_called_once()
+ s_mock.assert_not_called()
+
+ def test_groups_update_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ g_mock = mocker.patch.object(user, 'update_groups')
+ g_mock.return_value = True
+ s_mock = mocker.patch.object(user, 'update_state')
+
+ changed, result = user.sync(
+ dict(x=3, groups=['a']), client, '/path', dict(groups=['b']), True
+ )
+
+ assert changed is True
+ assert dict(x=3, groups=['b']) == result
+ p_mock.assert_not_called()
+ g_mock.assert_called_once()
+ s_mock.assert_not_called()
+
+ def test_state_update(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ g_mock = mocker.patch.object(user, 'update_groups')
+ s_mock = mocker.patch.object(user, 'update_state')
+ s_mock.return_value = False
+
+ changed, result = user.sync(
+ dict(disabled=True), client, '/path', dict(disabled=False), False
+ )
+
+ assert changed is False
+ assert dict(new='data') == result
+ p_mock.assert_not_called()
+ g_mock.assert_not_called()
+ s_mock.assert_called_once()
+
+ def test_state_update_check_mode(self, mocker):
+ client = mocker.Mock()
+ client.get.return_value = http.Response(200, '{"new": "data"}')
+ p_mock = mocker.patch.object(user, 'update_password')
+ g_mock = mocker.patch.object(user, 'update_groups')
+ s_mock = mocker.patch.object(user, 'update_state')
+ s_mock.return_value = True
+
+ changed, result = user.sync(
+ dict(disabled=True), client, '/path', dict(disabled=False), True
+ )
+
+ assert changed is True
+ assert dict(disabled=False) == result
+ p_mock.assert_not_called()
+ g_mock.assert_not_called()
+ s_mock.assert_called_once()
+
+
+class TestUser(ModuleTestCase):
+ def test_minimal_user_parameters(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = None
+ sync_mock = mocker.patch.object(user, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='alice',
+ password='alice!?pass',
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ user.main()
+
+ result, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert path == '/api/core/v2/users/alice'
+ assert payload == dict(
+ username='alice',
+ password='alice!?pass',
+ disabled=False
+ )
+ assert check_mode is False
+
+ def test_minimal_parameters_on_existing_user(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = dict(username='alice')
+ sync_mock = mocker.patch.object(user, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(name='alice')
+
+ with pytest.raises(AnsibleExitJson):
+ user.main()
+
+ result, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert path == '/api/core/v2/users/alice'
+ assert payload == dict(username='alice', disabled=False)
+ assert check_mode is False
+
+ def test_all_user_parameters(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = None
+ sync_mock = mocker.patch.object(user, 'sync')
+ sync_mock.return_value = True, {}
+ set_module_args(
+ name='test_user',
+ state='disabled',
+ password='password',
+ groups=['dev', 'ops'],
+ )
+
+ with pytest.raises(AnsibleExitJson):
+ user.main()
+
+ result, _client, path, payload, check_mode = sync_mock.call_args[0]
+ assert path == '/api/core/v2/users/test_user'
+ assert payload == dict(
+ username='test_user',
+ password='password',
+ groups=['dev', 'ops'],
+ disabled=True
+ )
+ assert check_mode is False
+
+ def test_cannot_create_user_without_password(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = None
+ set_module_args(
+ name='test_user',
+ state='disabled',
+ groups=['dev', 'ops'],
+ )
+
+ with pytest.raises(AnsibleFailJson, match='without a password'):
+ user.main()
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.return_value = None
+ sync_mock = mocker.patch.object(user, 'sync')
+ sync_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_user',
+ password='password'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ user.main()
+
+ def test_failure_on_initial_get(self, mocker):
+ get_mock = mocker.patch.object(utils, 'get')
+ get_mock.side_effect = errors.Error('Bad error')
+ set_module_args(
+ name='test_user',
+ password='password'
+ )
+
+ with pytest.raises(AnsibleFailJson):
+ user.main()
+
+ def test_failure_on_missing_bcrypt_5_21_0_or_newer(self, mocker):
+ mocker.patch.object(arguments, 'get_sensu_client').return_value = (
+ mocker.MagicMock(version='5.22.3')
+ )
+ mocker.patch.object(user, 'HAS_BCRYPT', False)
+ set_module_args(
+ name='test_user',
+ password='password'
+ )
+
+ with pytest.raises(AnsibleFailJson, match='bcrypt'):
+ user.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py
new file mode 100644
index 000000000..09fc337f0
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/plugins/modules/test_user_info.py
@@ -0,0 +1,63 @@
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+from ansible_collections.sensu.sensu_go.plugins.module_utils import (
+ errors, utils,
+)
+from ansible_collections.sensu.sensu_go.plugins.modules import user_info
+
+from .common.utils import (
+ AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args,
+)
+
+pytestmark = pytest.mark.skipif(
+ sys.version_info < (2, 7), reason="requires python2.7 or higher"
+)
+
+
+class TestUserInfo(ModuleTestCase):
+ def test_get_all_users(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = [1, 2, 3]
+ set_module_args()
+
+ with pytest.raises(AnsibleExitJson) as context:
+ user_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/users"
+ assert context.value.args[0]["objects"] == [1, 2, 3]
+
+ def test_get_single_user(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = 4
+ set_module_args(name="sample-user")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ user_info.main()
+
+ _client, path = get_mock.call_args[0]
+ assert path == "/api/core/v2/users/sample-user"
+ assert context.value.args[0]["objects"] == [4]
+
+ def test_missing_single_user(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.return_value = None
+ set_module_args(name="sample-user")
+
+ with pytest.raises(AnsibleExitJson) as context:
+ user_info.main()
+
+ assert context.value.args[0]["objects"] == []
+
+ def test_failure(self, mocker):
+ get_mock = mocker.patch.object(utils, "get")
+ get_mock.side_effect = errors.Error("Bad error")
+ set_module_args(name="sample-user")
+
+ with pytest.raises(AnsibleFailJson):
+ user_info.main()
diff --git a/ansible_collections/sensu/sensu_go/tests/unit/requirements.txt b/ansible_collections/sensu/sensu_go/tests/unit/requirements.txt
new file mode 100644
index 000000000..7f0b6e759
--- /dev/null
+++ b/ansible_collections/sensu/sensu_go/tests/unit/requirements.txt
@@ -0,0 +1 @@
+bcrypt