1# (c) 2018, NetApp, Inc 2# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 3 4''' unit test template for ONTAP Ansible module ''' 5 6from __future__ import print_function 7import json 8import pytest 9 10from units.compat import unittest 11from units.compat.mock import patch, Mock 12from ansible.module_utils import basic 13from ansible.module_utils._text import to_bytes 14import ansible.module_utils.netapp as netapp_utils 15 16from ansible.modules.storage.netapp.na_ontap_unix_group \ 17 import NetAppOntapUnixGroup as group_module # module under test 18 19if not netapp_utils.has_netapp_lib(): 20 pytestmark = pytest.mark.skip('skipping as missing required netapp_lib') 21 22 23def set_module_args(args): 24 """prepare arguments so that they will be picked up during module creation""" 25 args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) 26 basic._ANSIBLE_ARGS = to_bytes(args) # pylint: disable=protected-access 27 28 29class AnsibleExitJson(Exception): 30 """Exception class to be raised by module.exit_json and caught by the test case""" 31 pass 32 33 34class AnsibleFailJson(Exception): 35 """Exception class to be raised by module.fail_json and caught by the test case""" 36 pass 37 38 39def exit_json(*args, **kwargs): # pylint: disable=unused-argument 40 """function to patch over exit_json; package return data into an exception""" 41 if 'changed' not in kwargs: 42 kwargs['changed'] = False 43 raise AnsibleExitJson(kwargs) 44 45 46def fail_json(*args, **kwargs): # pylint: disable=unused-argument 47 """function to patch over fail_json; package return data into an exception""" 48 kwargs['failed'] = True 49 raise AnsibleFailJson(kwargs) 50 51 52class MockONTAPConnection(object): 53 ''' mock server connection to ONTAP host ''' 54 55 def __init__(self, kind=None, data=None): 56 ''' save arguments ''' 57 self.kind = kind 58 self.params = data 59 self.xml_in = None 60 self.xml_out = None 61 62 def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument 63 ''' mock invoke_successfully returning xml data ''' 64 self.xml_in = xml 65 if self.kind == 'group': 66 xml = self.build_group_info(self.params) 67 elif self.kind == 'group-fail': 68 raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test") 69 self.xml_out = xml 70 return xml 71 72 @staticmethod 73 def build_group_info(data): 74 ''' build xml data for vserser-info ''' 75 xml = netapp_utils.zapi.NaElement('xml') 76 attributes = \ 77 {'attributes-list': {'unix-group-info': {'group-name': data['name'], 78 'group-id': data['id']}}, 79 'num-records': 1} 80 xml.translate_struct(attributes) 81 return xml 82 83 84class TestMyModule(unittest.TestCase): 85 ''' a group of related Unit Tests ''' 86 87 def setUp(self): 88 self.mock_module_helper = patch.multiple(basic.AnsibleModule, 89 exit_json=exit_json, 90 fail_json=fail_json) 91 self.mock_module_helper.start() 92 self.addCleanup(self.mock_module_helper.stop) 93 self.server = MockONTAPConnection() 94 self.mock_group = { 95 'name': 'test', 96 'id': '11', 97 'vserver': 'something', 98 } 99 100 def mock_args(self): 101 return { 102 'name': self.mock_group['name'], 103 'id': self.mock_group['id'], 104 'vserver': self.mock_group['vserver'], 105 'hostname': 'test', 106 'username': 'test_user', 107 'password': 'test_pass!' 108 } 109 110 def get_group_mock_object(self, kind=None, data=None): 111 """ 112 Helper method to return an na_ontap_unix_group object 113 :param kind: passes this param to MockONTAPConnection() 114 :return: na_ontap_unix_group object 115 """ 116 obj = group_module() 117 obj.autosupport_log = Mock(return_value=None) 118 if data is None: 119 data = self.mock_group 120 obj.server = MockONTAPConnection(kind=kind, data=data) 121 return obj 122 123 def test_module_fail_when_required_args_missing(self): 124 ''' required arguments are reported as errors ''' 125 with pytest.raises(AnsibleFailJson) as exc: 126 set_module_args({}) 127 group_module() 128 129 def test_get_nonexistent_group(self): 130 ''' Test if get_unix_group returns None for non-existent group ''' 131 set_module_args(self.mock_args()) 132 result = self.get_group_mock_object().get_unix_group() 133 assert result is None 134 135 def test_get_existing_group(self): 136 ''' Test if get_unix_group returns details for existing group ''' 137 set_module_args(self.mock_args()) 138 result = self.get_group_mock_object('group').get_unix_group() 139 assert result['name'] == self.mock_group['name'] 140 141 def test_get_xml(self): 142 set_module_args(self.mock_args()) 143 obj = self.get_group_mock_object('group') 144 result = obj.get_unix_group() 145 assert obj.server.xml_in['query'] 146 assert obj.server.xml_in['query']['unix-group-info'] 147 group_info = obj.server.xml_in['query']['unix-group-info'] 148 assert group_info['group-name'] == self.mock_group['name'] 149 assert group_info['vserver'] == self.mock_group['vserver'] 150 151 def test_create_error_missing_params(self): 152 data = self.mock_args() 153 del data['id'] 154 set_module_args(data) 155 with pytest.raises(AnsibleFailJson) as exc: 156 self.get_group_mock_object('group').create_unix_group() 157 assert 'Error: Missing a required parameter for create: (id)' == exc.value.args[0]['msg'] 158 159 @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.create_unix_group') 160 def test_create_called(self, create_group): 161 set_module_args(self.mock_args()) 162 with pytest.raises(AnsibleExitJson) as exc: 163 self.get_group_mock_object().apply() 164 assert exc.value.args[0]['changed'] 165 create_group.assert_called_with() 166 167 def test_create_xml(self): 168 '''Test create ZAPI element''' 169 set_module_args(self.mock_args()) 170 create = self.get_group_mock_object() 171 with pytest.raises(AnsibleExitJson) as exc: 172 create.apply() 173 mock_key = { 174 'group-name': 'name', 175 'group-id': 'id', 176 } 177 for key in ['group-name', 'group-id']: 178 assert create.server.xml_in[key] == self.mock_group[mock_key[key]] 179 180 @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.modify_unix_group') 181 @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.delete_unix_group') 182 def test_delete_called(self, delete_group, modify_group): 183 ''' Test delete existing group ''' 184 data = self.mock_args() 185 data['state'] = 'absent' 186 set_module_args(data) 187 with pytest.raises(AnsibleExitJson) as exc: 188 self.get_group_mock_object('group').apply() 189 assert exc.value.args[0]['changed'] 190 delete_group.assert_called_with() 191 assert modify_group.call_count == 0 192 193 @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.get_unix_group') 194 @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.modify_unix_group') 195 def test_modify_called(self, modify_group, get_group): 196 ''' Test modify group group_id ''' 197 data = self.mock_args() 198 data['id'] = 20 199 set_module_args(data) 200 get_group.return_value = {'id': 10} 201 obj = self.get_group_mock_object('group') 202 with pytest.raises(AnsibleExitJson) as exc: 203 obj.apply() 204 get_group.assert_called_with() 205 modify_group.assert_called_with({'id': 20}) 206 207 def test_modify_only_id(self): 208 ''' Test modify group id ''' 209 set_module_args(self.mock_args()) 210 modify = self.get_group_mock_object('group') 211 modify.modify_unix_group({'id': 123}) 212 print(modify.server.xml_in.to_string()) 213 assert modify.server.xml_in['group-id'] == '123' 214 with pytest.raises(KeyError): 215 modify.server.xml_in['id'] 216 217 def test_modify_xml(self): 218 ''' Test modify group full_name ''' 219 set_module_args(self.mock_args()) 220 modify = self.get_group_mock_object('group') 221 modify.modify_unix_group({'id': 25}) 222 assert modify.server.xml_in['group-name'] == self.mock_group['name'] 223 assert modify.server.xml_in['group-id'] == '25' 224 225 @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.create_unix_group') 226 @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.delete_unix_group') 227 @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.modify_unix_group') 228 def test_do_nothing(self, modify, delete, create): 229 ''' changed is False and none of the opetaion methods are called''' 230 data = self.mock_args() 231 data['state'] = 'absent' 232 set_module_args(data) 233 obj = self.get_group_mock_object() 234 with pytest.raises(AnsibleExitJson) as exc: 235 obj.apply() 236 create.assert_not_called() 237 delete.assert_not_called() 238 modify.assert_not_called() 239 240 def test_get_exception(self): 241 set_module_args(self.mock_args()) 242 with pytest.raises(AnsibleFailJson) as exc: 243 self.get_group_mock_object('group-fail').get_unix_group() 244 assert 'Error getting UNIX group' in exc.value.args[0]['msg'] 245 246 def test_create_exception(self): 247 set_module_args(self.mock_args()) 248 with pytest.raises(AnsibleFailJson) as exc: 249 self.get_group_mock_object('group-fail').create_unix_group() 250 assert 'Error creating UNIX group' in exc.value.args[0]['msg'] 251 252 def test_modify_exception(self): 253 set_module_args(self.mock_args()) 254 with pytest.raises(AnsibleFailJson) as exc: 255 self.get_group_mock_object('group-fail').modify_unix_group({'id': '123'}) 256 assert 'Error modifying UNIX group' in exc.value.args[0]['msg'] 257 258 def test_delete_exception(self): 259 set_module_args(self.mock_args()) 260 with pytest.raises(AnsibleFailJson) as exc: 261 self.get_group_mock_object('group-fail').delete_unix_group() 262 assert 'Error removing UNIX group' in exc.value.args[0]['msg'] 263 264 @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.get_unix_group') 265 def test_add_user_exception(self, get_unix_group): 266 data = self.mock_args() 267 data['users'] = 'test_user' 268 set_module_args(data) 269 get_unix_group.side_effect = [ 270 {'users': []} 271 ] 272 with pytest.raises(AnsibleFailJson) as exc: 273 self.get_group_mock_object('group-fail').modify_users_in_group() 274 print(exc.value.args[0]['msg']) 275 assert 'Error adding user' in exc.value.args[0]['msg'] 276 277 @patch('ansible.modules.storage.netapp.na_ontap_unix_group.NetAppOntapUnixGroup.get_unix_group') 278 def test_delete_user_exception(self, get_unix_group): 279 data = self.mock_args() 280 data['users'] = '' 281 set_module_args(data) 282 get_unix_group.side_effect = [ 283 {'users': ['test_user']} 284 ] 285 with pytest.raises(AnsibleFailJson) as exc: 286 self.get_group_mock_object('group-fail').modify_users_in_group() 287 print(exc.value.args[0]['msg']) 288 assert 'Error deleting user' in exc.value.args[0]['msg'] 289