Files
charm-keystone-ldap/unit_tests/test_lib_charm_openstack_keystone_ldap.py
Hervé Beraud f721b78c22 Use unittest.mock instead of mock
The mock third party library was needed for mock support in py2
runtimes. Since we now only support py36 and later, we can use the
standard lib unittest.mock module instead.

Note that https://github.com/openstack/charms.openstack is used during tests
and he need `mock`, unfortunatelly it doesn't declare `mock` in its
requirements so it retrieve mock from other charm project (cross dependency).
So we depend on charms.openstack first and when
Ib1ed5b598a52375e29e247db9ab4786df5b6d142 will be merged then CI
will pass without errors.

Depends-On: Ib1ed5b598a52375e29e247db9ab4786df5b6d142
Change-Id: I107cabe4608950da02ef2b89e811791ad912cf7e
2021-12-15 09:47:59 +00:00

350 lines
13 KiB
Python

# Copyright 2016 Canonical Ltd
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from unittest import mock
import textwrap
import charms_openstack.test_utils as test_utils
import charm.openstack.keystone_ldap as keystone_ldap
from charms_openstack.charm import provide_charm_instance
class Helper(test_utils.PatchHelper):
def setUp(self):
super().setUp()
self.patch_release(keystone_ldap.KeystoneLDAPCharm.release)
class TestKeystoneLDAPCharm(Helper):
@mock.patch('charmhelpers.core.hookenv.config')
def test_required_configuration(self, config):
reply = {
'ldap-server': 'myserver',
'ldap-user': 'myusername',
'ldap-password': 'mypassword',
'ldap-suffix': 'suffix'
}
def mock_config(key=None):
if key:
return reply.get(key)
return reply
config.side_effect = mock_config
with provide_charm_instance() as kldap_charm:
self.assertTrue(kldap_charm.configuration_complete())
for required_config in reply:
orig = reply[required_config]
reply[required_config] = None
self.assertFalse(kldap_charm.configuration_complete())
reply[required_config] = orig
self.assertTrue(kldap_charm.configuration_complete())
@mock.patch('charmhelpers.core.hookenv.service_name')
@mock.patch('charmhelpers.core.hookenv.config')
def test_domain_name(self, config,
service_name):
config.return_value = None
service_name.return_value = 'testdomain'
with provide_charm_instance() as kldap_charm:
self.assertEqual('testdomain',
kldap_charm.domain_name)
self.assertEqual(
'/etc/keystone/domains/keystone.testdomain.conf',
kldap_charm.configuration_file)
config.assert_called_with('domain-name')
config.return_value = 'userdomain'
self.assertEqual('userdomain',
kldap_charm.domain_name)
self.assertEqual(
'/etc/keystone/domains/keystone.userdomain.conf',
kldap_charm.configuration_file)
@mock.patch('charmhelpers.contrib.openstack.utils.is_unit_upgrading_set')
@mock.patch('charmhelpers.contrib.openstack.utils.snap_install_requested')
@mock.patch('charmhelpers.core.hookenv.config')
@mock.patch('charmhelpers.core.hookenv.status_set')
@mock.patch('charmhelpers.core.hookenv.application_version_set')
def test_assess_status(self,
application_version_set,
status_set,
config, snap_install_requested,
is_unit_upgrading_set):
reply = {
'ldap-server': 'myserver',
'ldap-user': 'myusername',
'ldap-password': 'mypassword',
'ldap-suffix': 'suffix'
}
def mock_config(key=None):
if key:
return reply.get(key)
return reply
config.side_effect = mock_config
snap_install_requested.return_value = False
is_unit_upgrading_set.return_value = False
with provide_charm_instance() as kldap_charm:
# Check that active status is set correctly
kldap_charm.assess_status()
status_set.assert_called_with('active', mock.ANY)
application_version_set.assert_called_with(
kldap_charm.application_version
)
# Check that blocked status is set correctly
reply['ldap-server'] = None
kldap_charm.assess_status()
status_set.assert_called_with('blocked', mock.ANY)
application_version_set.assert_called_with(
kldap_charm.application_version
)
@mock.patch('charmhelpers.core.hookenv.config')
def test_render_config(self, config):
self.patch_object(keystone_ldap.ch_host, 'file_hash')
self.patch_object(keystone_ldap.core.templating, 'render')
reply = {
'ldap-server': 'myserver',
'ldap-user': 'myusername',
'ldap-password': 'mypassword',
'ldap-suffix': 'suffix',
'domain-name': 'userdomain',
}
def mock_config(key=None):
if key:
return reply.get(key)
return reply
config.side_effect = mock_config
self.file_hash.side_effect = ['aaa', 'aaa']
mock_trigger = mock.MagicMock()
with provide_charm_instance() as kldap_charm:
# Ensure a basic level of function from render_config
kldap_charm.render_config(mock_trigger)
self.render.assert_called_with(
source=keystone_ldap.KEYSTONE_CONF_TEMPLATE,
template_loader=mock.ANY,
target='/etc/keystone/domains/keystone.userdomain.conf',
context=mock.ANY
)
self.assertFalse(mock_trigger.called)
# Ensure that change in file contents results in call
# to restart trigger function passed to render_config
self.file_hash.side_effect = ['aaa', 'bbb']
kldap_charm.render_config(mock_trigger)
self.assertTrue(mock_trigger.called)
@mock.patch('charmhelpers.core.hookenv.config')
@mock.patch('charmhelpers.core.hookenv.service_name')
def test_render_config_tls(self, service_name, config):
self.patch_object(keystone_ldap.ch_host, 'file_hash')
self.patch_object(keystone_ldap.ch_host, 'write_file')
self.patch_object(keystone_ldap.core.templating, 'render')
self.patch_object(keystone_ldap.core.templating, 'render')
reply = {
'ldap-server': 'myserver',
'ldap-user': 'myusername',
'ldap-password': 'mypassword',
'ldap-suffix': 'suffix',
'domain-name': 'userdomain',
'tls-ca-ldap': textwrap.dedent("""
-----BEGIN CERTIFICATE-----
MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
jVaMaA==
-----END CERTIFICATE-----
""")
}
def mock_config(key=None):
if key:
return reply.get(key)
return reply
config.side_effect = mock_config
svc_name = 'keystone_ldap'
service_name.return_value = svc_name
self.file_hash.side_effect = [
'templatehash',
'templatehash',
'de3d5930e6e6b3fdb385f60a05206588',
'de3d5930e6e6b3fdb385f60a05206588',
]
mock_trigger = mock.MagicMock()
with provide_charm_instance() as kldap_charm:
# Ensure a basic level of function from render_config
kldap_charm.render_config(mock_trigger)
self.render.assert_called_with(
source=keystone_ldap.KEYSTONE_CONF_TEMPLATE,
template_loader=mock.ANY,
target='/etc/keystone/domains/keystone.userdomain.conf',
context=mock.ANY
)
self.write_file.assert_called_with(
keystone_ldap.BACKEND_CA_CERT.format(svc_name),
reply['tls-ca-ldap'],
owner='root',
group='root',
perms=0o644,
)
self.assertFalse(mock_trigger.called)
# template file change leads to restart without a change
# in a cert
self.file_hash.side_effect = [
'oldtemplatehash',
'newtemplatehash',
'de3d5930e6e6b3fdb385f60a05206588',
'de3d5930e6e6b3fdb385f60a05206588',
]
kldap_charm.render_config(mock_trigger)
self.assertTrue(mock_trigger.called)
# cert change without template change
self.file_hash.side_effect = [
'templatehash',
'templatehash',
'deadbeefdeadbeefdeadbeefdeadbeef',
'de3d5930e6e6b3fdb385f60a05206588',
]
kldap_charm.render_config(mock_trigger)
self.assertTrue(mock_trigger.called)
@mock.patch('charmhelpers.core.hookenv.config')
@mock.patch('os.path.exists')
@mock.patch('os.unlink')
def test_remove_config(self, unlink, exists, config):
exists.return_value = True
self.patch_object(keystone_ldap.ch_host, 'file_hash')
reply = {
'ldap-server': 'myserver',
'ldap-user': 'myusername',
'ldap-password': 'mypassword',
'ldap-suffix': 'suffix',
'domain-name': 'userdomain',
}
def mock_config(key=None):
if key:
return reply.get(key)
return reply
config.side_effect = mock_config
with provide_charm_instance() as kldap_charm:
# Ensure a basic level of function from render_config
cf = keystone_ldap.DOMAIN_CONF.format(reply['domain-name'])
kldap_charm.remove_config()
exists.assert_called_once_with(cf)
unlink.assert_called_once_with(cf)
class TestKeystoneLDAPAdapters(Helper):
@mock.patch('charmhelpers.contrib.openstack.utils.config_flags_parser',
wraps=None)
@mock.patch('charmhelpers.core.hookenv.config')
def test_config_adapter(self, config,
config_flags_parser):
reply = {
'ldap-config-flags':
'user_id_attribute=cn,user_name_attribute=cn,'
'group_members_are_ids=False,query_scope=one,'
'use_pool=True',
'ldap-group-members-are-ids': True,
'ldap-user-name-attribute': 'sn',
'ldap-query-scope': '',
'ldap-use-pool': None,
}
parsed_config_flags = {
'user_id_attribute': 'cn',
'user_name_attribute': 'cn',
'group_members_are_ids': False,
'query_scope': 'one',
'use_pool': True,
}
final_ldap_config_flags = {
'user_id_attribute': 'cn',
'query_scope': 'one',
'use_pool': True,
}
def mock_config(key=None):
if key:
return reply.get(key)
return reply
config.side_effect = mock_config
config_flags_parser.return_value = parsed_config_flags
# verify that the class is created with a
# KeystoneLDAPConfigurationAdapter
adapter = keystone_ldap.KeystoneLDAPConfigurationAdapter()
# ensure that the relevant things got put on.
self.assertEqual(final_ldap_config_flags, adapter.ldap_options)
self.assertTrue(config_flags_parser.called)
@mock.patch('charmhelpers.core.hookenv.config')
def test_config_adapter_empty(self, config):
reply = {
'ldap-config-flags': ''
}
def mock_config(key=None):
if key:
return reply.get(key)
return reply
config.side_effect = mock_config
# verify that the class is created with a
# KeystoneLDAPConfigurationAdapter
adapter = keystone_ldap.KeystoneLDAPConfigurationAdapter()
# ensure that the relevant things got put on.
self.assertEqual({}, adapter.ldap_options)