def test_module_utils_basic_assible_module_find_mount_point(self): from assible.module_utils import basic basic._ASSIBLE_ARGS = None am = basic.AssibleModule(argument_spec=dict(), ) def _mock_ismount(path): if path == b'/': return True return False with patch('os.path.ismount', side_effect=_mock_ismount): self.assertEqual( am.find_mount_point('/root/fs/../mounted/path/to/whatever'), '/') def _mock_ismount(path): if path == b'/subdir/mount': return True if path == b'/': return True return False with patch('os.path.ismount', side_effect=_mock_ismount): self.assertEqual( am.find_mount_point('/subdir/mount/path/to/whatever'), '/subdir/mount')
def test_validate_basic_auth_arg(mocker, stdin): kwargs = dict(argument_spec=basic_auth_argument_spec()) am = basic.AssibleModule(**kwargs) assert isinstance(am.params['api_username'], string_types) assert isinstance(am.params['api_password'], string_types) assert isinstance(am.params['api_url'], string_types) assert isinstance(am.params['validate_certs'], bool)
def test_tmpdir_property(self, monkeypatch, args, expected, stat_exists): makedirs = {'called': False} def mock_mkdtemp(prefix, dir): return os.path.join(dir, prefix) def mock_makedirs(path, mode): makedirs['called'] = True makedirs['path'] = path makedirs['mode'] = mode return monkeypatch.setattr(tempfile, 'mkdtemp', mock_mkdtemp) monkeypatch.setattr(os.path, 'exists', lambda x: stat_exists) monkeypatch.setattr(os, 'makedirs', mock_makedirs) monkeypatch.setattr(shutil, 'rmtree', lambda x: None) monkeypatch.setattr( basic, '_ASSIBLE_ARGS', to_bytes(json.dumps({'ASSIBLE_MODULE_ARGS': args}))) with patch('time.time', return_value=42): am = basic.AssibleModule(argument_spec={}) actual_tmpdir = am.tmpdir assert actual_tmpdir == expected # verify subsequent calls always produces the same tmpdir assert am.tmpdir == actual_tmpdir if not stat_exists: assert makedirs['called'] expected = os.path.expanduser(os.path.expandvars(am._remote_tmp)) assert makedirs['path'] == expected assert makedirs['mode'] == 0o700
def test_options_type_list(self, stdin, options_argspec_list, expected): """Test that a basic creation with required and required_if works""" # should test ok, tests basic foo requirement and required_if am = basic.AssibleModule(**options_argspec_list) assert isinstance(am.params['foobar'], list) assert am.params['foobar'] == expected
def test_set_cwd_unreadable_use_home(self, monkeypatch): '''cwd and instance tmpdir are unreadable, use home''' def mock_getcwd(): return '/tmp' def mock_access(path, perm): if path in ['/tmp', '/tmp2'] and perm == 4: return False return True def mock_expandvars(var): if var == '$HOME': return '/home/foobar' return var def mock_gettempdir(): return '/tmp/testdir' def mock_chdir(path): if path == '/tmp': raise Exception() return monkeypatch.setattr(os, 'getcwd', mock_getcwd) monkeypatch.setattr(os, 'chdir', mock_chdir) monkeypatch.setattr(os, 'access', mock_access) monkeypatch.setattr(os.path, 'expandvars', mock_expandvars) monkeypatch.setattr(basic, '_ASSIBLE_ARGS', to_bytes(json.dumps({'ASSIBLE_MODULE_ARGS': {}}))) with patch('time.time', return_value=42): am = basic.AssibleModule(argument_spec={}) am._tmpdir = '/tmp2' result = am._set_cwd() assert result == '/home/foobar'
def test_deprecated_alias(self, capfd, mocker, stdin, complex_argspec): """Test a deprecated alias""" am = basic.AssibleModule(**complex_argspec) assert "Alias 'zodraz' is deprecated." in get_deprecation_messages( )[0]['msg'] assert get_deprecation_messages()[0]['version'] == '9.99'
def test_complex_duplicate_warning(self, stdin, complex_argspec): """Test that the complex argspec issues a warning if we specify an option both with its canonical name and its alias""" am = basic.AssibleModule(**complex_argspec) assert isinstance(am.params['foo'], str) assert 'Both option foo and its alias dup are set.' in get_warning_messages( ) assert am.params['foo'] == 'hello2'
def test_list_with_elements_path(self, capfd, mocker, stdin, complex_argspec): """Test choices with list""" am = basic.AssibleModule(**complex_argspec) assert isinstance(am.params['bar3'], list) assert am.params['bar3'][0].startswith('/') assert am.params['bar3'][1] == 'test/'
def test_validator_string_type(mocker, stdin): # Custom callable that is 'str' argspec = {'arg': {'type': str}} am = basic.AssibleModule(argspec) assert isinstance(am.params['arg'], string_types) assert am.params['arg'] == '123'
def test_elements_path_in_option(self, mocker, stdin, options_argspec_dict): """Test that the complex argspec works with elements path type""" am = basic.AssibleModule(**options_argspec_dict) assert isinstance(am.params['foobar']['bar4'][0], str) assert am.params['foobar']['bar4'][0].startswith('/')
def test_no_log_true(stdin, capfd): """Explicitly mask an argument (no_log=True).""" arg_spec = {"arg_pass": {"no_log": True}} am = basic.AssibleModule(arg_spec) # no_log=True is picked up by both am._log_invocation and list_no_log_values # (called by am._handle_no_log_values). As a result, we can check for the # value in am.no_log_values. assert "testing" in am.no_log_values
def test_validator_fail(stdin, capfd, argspec, expected): with pytest.raises(SystemExit): basic.AssibleModule(argument_spec=argspec) out, err = capfd.readouterr() assert not err assert expected in json.loads(out)['msg'] assert json.loads(out)['failed']
def test_validator_function(mocker, stdin): # Type is a callable MOCK_VALIDATOR_SUCCESS = mocker.MagicMock(return_value=27) argspec = {'arg': {'type': MOCK_VALIDATOR_SUCCESS}} am = basic.AssibleModule(argspec) assert isinstance(am.params['arg'], integer_types) assert am.params['arg'] == 27
def test_list_with_elements_callable_str(self, capfd, mocker, stdin, complex_argspec): """Test choices with list""" am = basic.AssibleModule(**complex_argspec) assert isinstance(am.params['bar_str'], list) assert isinstance(am.params['bar_str'][0], string_types) assert isinstance(am.params['bar_str'][1], string_types) assert am.params['bar_str'][0] == '867' assert am.params['bar_str'][1] == '5309'
def test_no_log_none(stdin, capfd): """Allow Assible to make the decision by matching the argument name against PASSWORD_MATCH.""" arg_spec = {"arg_pass": {}} am = basic.AssibleModule(arg_spec) # Omitting no_log is only picked up by _log_invocation, so the value never # makes it into am.no_log_values. Instead we can check for the warning # emitted by am._log_invocation. assert len(get_warning_messages()) > 0
def test_complex_type_fallback(self, mocker, stdin, complex_argspec): """Test that the complex argspec works if we get a required parameter via fallback""" environ = os.environ.copy() environ['BAZ'] = 'test data' mocker.patch('assible.module_utils.basic.os.environ', environ) am = basic.AssibleModule(**complex_argspec) assert isinstance(am.params['baz'], str) assert am.params['baz'] == 'test data'
def test_fail_required_together(self, capfd, stdin, complex_argspec): """Fail because only one of a required_together pair of parameters was specified""" with pytest.raises(SystemExit): am = basic.AssibleModule(**complex_argspec) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert results['msg'] == "parameters are required together: bam, baz"
def test_fallback_in_option(self, mocker, stdin, options_argspec_dict): """Test that the complex argspec works if we get a required parameter via fallback""" environ = os.environ.copy() environ['BAZ'] = 'test data' mocker.patch('assible.module_utils.basic.os.environ', environ) am = basic.AssibleModule(**options_argspec_dict) assert isinstance(am.params['foobar']['baz'], str) assert am.params['foobar']['baz'] == 'test data'
def test_fail_validate_options_list(self, capfd, stdin, options_argspec_list, expected): """Fail because one of a required_together pair of parameters has a default and the other was not specified""" with pytest.raises(SystemExit): am = basic.AssibleModule(**options_argspec_list) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert expected in results['msg']
def test_fail_mutually_exclusive(self, capfd, stdin, complex_argspec): """Fail because of mutually exclusive parameters""" with pytest.raises(SystemExit): am = basic.AssibleModule(**complex_argspec) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert results[ 'msg'] == "parameters are mutually exclusive: bar|bam, bing|bang|bong"
def validate_config(spec, data): """ Validate if the input data against the AssibleModule spec format :param spec: Assible argument spec :param data: Data to be validated :return: """ params = basic._ASSIBLE_ARGS basic._ASSIBLE_ARGS = to_bytes(json.dumps({'ASSIBLE_MODULE_ARGS': data})) validated_data = basic.AssibleModule(spec).params basic._ASSIBLE_ARGS = params return validated_data
def test_fail_list_with_choices(self, capfd, mocker, stdin, complex_argspec): """Fail because one of the items is not in the choice""" with pytest.raises(SystemExit): basic.AssibleModule(**complex_argspec) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert results[ 'msg'] == "value of zardoz2 must be one or more of: one, two, three. Got no match for: four, five"
def test_module_utils_basic_assible_module_user_and_group(self): from assible.module_utils import basic basic._ASSIBLE_ARGS = None am = basic.AssibleModule(argument_spec=dict(), ) mock_stat = MagicMock() mock_stat.st_uid = 0 mock_stat.st_gid = 0 with patch('os.lstat', return_value=mock_stat): self.assertEqual(am.user_and_group('/path/to/file'), (0, 0))
def test_fail_required_together_and_default(self, capfd, stdin, complex_argspec): """Fail because one of a required_together pair of parameters has a default and the other was not specified""" complex_argspec['argument_spec']['baz'] = {'default': 42} with pytest.raises(SystemExit): am = basic.AssibleModule(**complex_argspec) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert results['msg'] == "parameters are required together: bam, baz"
def test_validator_basic_types(argspec, expected, stdin): am = basic.AssibleModule(argspec) if 'type' in argspec['arg']: if argspec['arg']['type'] == 'int': type_ = integer_types else: type_ = getattr(builtins, argspec['arg']['type']) else: type_ = str assert isinstance(am.params['arg'], type_) assert am.params['arg'] == expected
def test_fail_required_together_and_fallback(self, capfd, mocker, stdin, complex_argspec): """Fail because one of a required_together pair of parameters has a fallback and the other was not specified""" environ = os.environ.copy() environ['BAZ'] = 'test data' mocker.patch('assible.module_utils.basic.os.environ', environ) with pytest.raises(SystemExit): am = basic.AssibleModule(**complex_argspec) out, err = capfd.readouterr() results = json.loads(out) assert results['failed'] assert results['msg'] == "parameters are required together: bam, baz"
def test_module_utils_basic_assible_module_set_owner_if_different(self): from assible.module_utils import basic basic._ASSIBLE_ARGS = None am = basic.AssibleModule(argument_spec=dict(), ) self.assertEqual( am.set_owner_if_different('/path/to/file', None, True), True) self.assertEqual( am.set_owner_if_different('/path/to/file', None, False), False) am.user_and_group = MagicMock(return_value=(500, 500)) with patch('os.lchown', return_value=None) as m: self.assertEqual( am.set_owner_if_different('/path/to/file', 0, False), True) m.assert_called_with(b'/path/to/file', 0, -1) def _mock_getpwnam(*args, **kwargs): mock_pw = MagicMock() mock_pw.pw_uid = 0 return mock_pw m.reset_mock() with patch('pwd.getpwnam', side_effect=_mock_getpwnam): self.assertEqual( am.set_owner_if_different('/path/to/file', 'root', False), True) m.assert_called_with(b'/path/to/file', 0, -1) with patch('pwd.getpwnam', side_effect=KeyError): self.assertRaises(SystemExit, am.set_owner_if_different, '/path/to/file', 'root', False) m.reset_mock() am.check_mode = True self.assertEqual( am.set_owner_if_different('/path/to/file', 0, False), True) self.assertEqual(m.called, False) am.check_mode = False with patch('os.lchown', side_effect=OSError) as m: self.assertRaises(SystemExit, am.set_owner_if_different, '/path/to/file', 'root', False)
def test_set_cwd(self, monkeypatch): '''make sure /tmp is used''' def mock_getcwd(): return '/tmp' def mock_access(path, perm): return True def mock_chdir(path): pass monkeypatch.setattr(os, 'getcwd', mock_getcwd) monkeypatch.setattr(os, 'access', mock_access) monkeypatch.setattr(basic, '_ASSIBLE_ARGS', to_bytes(json.dumps({'ASSIBLE_MODULE_ARGS': {}}))) with patch('time.time', return_value=42): am = basic.AssibleModule(argument_spec={}) result = am._set_cwd() assert result == '/tmp'
def test_list_with_choices(self, capfd, mocker, stdin, complex_argspec): """Test choices with list""" am = basic.AssibleModule(**complex_argspec) assert isinstance(am.params['zardoz2'], list) assert am.params['zardoz2'] == ['one', 'three']
def test_complex_required(self, stdin, complex_argspec): """Test that the complex argspec works if we give it its required param as either the canonical or aliased name""" am = basic.AssibleModule(**complex_argspec) assert isinstance(am.params['foo'], str) assert am.params['foo'] == 'hello'