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 tests ONTAP Ansible module: na_ontap_user ''' 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_user \ 17 import NetAppOntapUser as my_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, parm1=None, parm2=None): 56 ''' save arguments ''' 57 self.type = kind 58 self.parm1 = parm1 59 self.parm2 = parm2 60 self.xml_in = None 61 self.xml_out = None 62 63 def invoke_successfully(self, xml, enable_tunneling): # pylint: disable=unused-argument 64 ''' mock invoke_successfully returning xml data ''' 65 self.xml_in = xml 66 if self.type == 'user': 67 xml = self.build_user_info(self.parm1, self.parm2) 68 elif self.type == 'user_fail': 69 raise netapp_utils.zapi.NaApiError(code='TEST', message="This exception is from the unit test") 70 self.xml_out = xml 71 return xml 72 73 @staticmethod 74 def set_vserver(vserver): 75 '''mock set vserver''' 76 pass 77 78 @staticmethod 79 def build_user_info(locked, role_name): 80 ''' build xml data for user-info ''' 81 xml = netapp_utils.zapi.NaElement('xml') 82 data = {'num-records': 1, 83 'attributes-list': {'security-login-account-info': {'is-locked': locked, 'role-name': role_name}}} 84 85 xml.translate_struct(data) 86 print(xml.to_string()) 87 return xml 88 89 90class TestMyModule(unittest.TestCase): 91 ''' a group of related Unit Tests ''' 92 93 def setUp(self): 94 self.mock_module_helper = patch.multiple(basic.AnsibleModule, 95 exit_json=exit_json, 96 fail_json=fail_json) 97 self.mock_module_helper.start() 98 self.addCleanup(self.mock_module_helper.stop) 99 self.server = MockONTAPConnection() 100 self.onbox = False 101 102 def set_default_args(self): 103 if self.onbox: 104 hostname = '10.10.10.10' 105 username = 'username' 106 password = 'password' 107 user_name = 'test' 108 vserver = 'ansible_test' 109 application = 'console' 110 authentication_method = 'password' 111 else: 112 hostname = 'hostname' 113 username = 'username' 114 password = 'password' 115 user_name = 'name' 116 vserver = 'vserver' 117 application = 'console' 118 authentication_method = 'password' 119 120 return dict({ 121 'hostname': hostname, 122 'username': username, 123 'password': password, 124 'name': user_name, 125 'vserver': vserver, 126 'applications': application, 127 'authentication_method': authentication_method 128 }) 129 130 def test_module_fail_when_required_args_missing(self): 131 ''' required arguments are reported as errors ''' 132 with pytest.raises(AnsibleFailJson) as exc: 133 set_module_args({}) 134 my_module() 135 print('Info: %s' % exc.value.args[0]['msg']) 136 137 def test_ensure_user_get_called(self): 138 ''' a more interesting test ''' 139 module_args = {} 140 module_args.update(self.set_default_args()) 141 module_args.update({'role_name': 'test'}) 142 set_module_args(module_args) 143 my_obj = my_module() 144 my_obj.server = self.server 145 user_info = my_obj.get_user() 146 print('Info: test_user_get: %s' % repr(user_info)) 147 assert user_info is None 148 149 def test_ensure_user_apply_called(self): 150 ''' creating user and checking idempotency ''' 151 module_args = {} 152 module_args.update(self.set_default_args()) 153 module_args.update({'name': 'create'}) 154 module_args.update({'role_name': 'test'}) 155 set_module_args(module_args) 156 my_obj = my_module() 157 if not self.onbox: 158 my_obj.server = self.server 159 with pytest.raises(AnsibleExitJson) as exc: 160 my_obj.apply() 161 print('Info: test_user_apply: %s' % repr(exc.value)) 162 assert exc.value.args[0]['changed'] 163 if not self.onbox: 164 my_obj.server = MockONTAPConnection('user', 'false') 165 with pytest.raises(AnsibleExitJson) as exc: 166 my_obj.apply() 167 print('Info: test_user_apply: %s' % repr(exc.value)) 168 assert exc.value.args[0]['changed'] 169 170 def test_ensure_user_apply_for_delete_called(self): 171 ''' deleting user and checking idempotency ''' 172 module_args = {} 173 module_args.update(self.set_default_args()) 174 module_args.update({'name': 'create'}) 175 module_args.update({'role_name': 'test'}) 176 set_module_args(module_args) 177 my_obj = my_module() 178 if not self.onbox: 179 my_obj.server = MockONTAPConnection('user', 'false', 'test') 180 with pytest.raises(AnsibleExitJson) as exc: 181 my_obj.apply() 182 print('Info: test_user_apply: %s' % repr(exc.value)) 183 assert not exc.value.args[0]['changed'] 184 module_args.update({'state': 'absent'}) 185 set_module_args(module_args) 186 my_obj = my_module() 187 if not self.onbox: 188 my_obj.server = MockONTAPConnection('user', 'false', 'test') 189 with pytest.raises(AnsibleExitJson) as exc: 190 my_obj.apply() 191 print('Info: test_user_delete: %s' % repr(exc.value)) 192 assert exc.value.args[0]['changed'] 193 194 def test_ensure_user_lock_called(self): 195 ''' changing user_lock to True and checking idempotency''' 196 module_args = {} 197 module_args.update(self.set_default_args()) 198 module_args.update({'name': 'create'}) 199 module_args.update({'role_name': 'test'}) 200 module_args.update({'lock_user': 'false'}) 201 set_module_args(module_args) 202 my_obj = my_module() 203 if not self.onbox: 204 my_obj.server = MockONTAPConnection('user', 'false', 'test') 205 with pytest.raises(AnsibleExitJson) as exc: 206 my_obj.apply() 207 print('Info: test_user_apply: %s' % repr(exc.value)) 208 assert not exc.value.args[0]['changed'] 209 module_args.update({'lock_user': 'true'}) 210 set_module_args(module_args) 211 my_obj = my_module() 212 if not self.onbox: 213 my_obj.server = MockONTAPConnection('user', 'false') 214 with pytest.raises(AnsibleExitJson) as exc: 215 my_obj.apply() 216 print('Info: test_user_lock: %s' % repr(exc.value)) 217 assert exc.value.args[0]['changed'] 218 219 def test_ensure_user_unlock_called(self): 220 ''' changing user_lock to False and checking idempotency''' 221 module_args = {} 222 module_args.update(self.set_default_args()) 223 module_args.update({'name': 'create'}) 224 module_args.update({'role_name': 'test'}) 225 module_args.update({'lock_user': 'false'}) 226 set_module_args(module_args) 227 my_obj = my_module() 228 if not self.onbox: 229 my_obj.server = MockONTAPConnection('user', 'false', 'test') 230 with pytest.raises(AnsibleExitJson) as exc: 231 my_obj.apply() 232 print('Info: test_user_apply: %s' % repr(exc.value)) 233 assert not exc.value.args[0]['changed'] 234 module_args.update({'lock_user': 'false'}) 235 set_module_args(module_args) 236 my_obj = my_module() 237 if not self.onbox: 238 my_obj.server = MockONTAPConnection('user', 'true', 'test') 239 with pytest.raises(AnsibleExitJson) as exc: 240 my_obj.apply() 241 print('Info: test_user_unlock: %s' % repr(exc.value)) 242 assert exc.value.args[0]['changed'] 243 244 def test_ensure_user_set_password_called(self): 245 ''' set password ''' 246 module_args = {} 247 module_args.update(self.set_default_args()) 248 module_args.update({'name': 'create'}) 249 module_args.update({'role_name': 'test'}) 250 module_args.update({'set_password': '123456'}) 251 set_module_args(module_args) 252 my_obj = my_module() 253 if not self.onbox: 254 my_obj.server = MockONTAPConnection('user', 'true') 255 with pytest.raises(AnsibleExitJson) as exc: 256 my_obj.apply() 257 print('Info: test_user_apply: %s' % repr(exc.value)) 258 assert exc.value.args[0]['changed'] 259 260 def test_ensure_user_role_update_called(self): 261 ''' set password ''' 262 module_args = {} 263 module_args.update(self.set_default_args()) 264 module_args.update({'name': 'create'}) 265 module_args.update({'role_name': 'test123'}) 266 module_args.update({'set_password': '123456'}) 267 set_module_args(module_args) 268 my_obj = my_module() 269 if not self.onbox: 270 my_obj.server = MockONTAPConnection('user', 'true') 271 with pytest.raises(AnsibleExitJson) as exc: 272 my_obj.apply() 273 print('Info: test_user_apply: %s' % repr(exc.value)) 274 assert exc.value.args[0]['changed'] 275 276 def test_ensure_user_role_update_additional_application_called(self): 277 ''' set password ''' 278 module_args = {} 279 module_args.update(self.set_default_args()) 280 module_args.update({'name': 'create'}) 281 module_args.update({'role_name': 'test123'}) 282 module_args.update({'application': 'http'}) 283 module_args.update({'set_password': '123456'}) 284 set_module_args(module_args) 285 my_obj = my_module() 286 if not self.onbox: 287 my_obj.server = MockONTAPConnection('user', 'true') 288 with pytest.raises(AnsibleExitJson) as exc: 289 my_obj.apply() 290 print('Info: test_user_apply: %s' % repr(exc.value)) 291 assert exc.value.args[0]['changed'] 292 293 def test_if_all_methods_catch_exception(self): 294 data = self.set_default_args() 295 data.update({'role_name': 'test'}) 296 set_module_args(data) 297 my_obj = my_module() 298 if not self.onbox: 299 my_obj.server = MockONTAPConnection('user_fail') 300 with pytest.raises(AnsibleFailJson) as exc: 301 my_obj.get_user() 302 assert 'Error getting user ' in exc.value.args[0]['msg'] 303 with pytest.raises(AnsibleFailJson) as exc: 304 my_obj.create_user(data['applications']) 305 assert 'Error creating user ' in exc.value.args[0]['msg'] 306 with pytest.raises(AnsibleFailJson) as exc: 307 my_obj.lock_given_user() 308 assert 'Error locking user ' in exc.value.args[0]['msg'] 309 with pytest.raises(AnsibleFailJson) as exc: 310 my_obj.unlock_given_user() 311 assert 'Error unlocking user ' in exc.value.args[0]['msg'] 312 with pytest.raises(AnsibleFailJson) as exc: 313 my_obj.delete_user(data['applications']) 314 assert 'Error removing user ' in exc.value.args[0]['msg'] 315 with pytest.raises(AnsibleFailJson) as exc: 316 my_obj.change_password() 317 assert 'Error setting password for user ' in exc.value.args[0]['msg'] 318 with pytest.raises(AnsibleFailJson) as exc: 319 my_obj.modify_user(data['applications']) 320 assert 'Error modifying user ' in exc.value.args[0]['msg'] 321