mirror of
https://github.com/ansible/awx.git
synced 2026-04-07 02:59:21 -02:30
add unit tests for awx.conf
This commit is contained in:
2
awx/conf/tests/__init__.py
Normal file
2
awx/conf/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# Copyright (c) 2017 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
302
awx/conf/tests/unit/test_registry.py
Normal file
302
awx/conf/tests/unit/test_registry.py
Normal file
@@ -0,0 +1,302 @@
|
||||
# Copyright (c) 2017 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
from django.conf import LazySettings
|
||||
from django.core.cache.backends.locmem import LocMemCache
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework.fields import empty
|
||||
import pytest
|
||||
|
||||
from awx.conf import fields
|
||||
from awx.conf.settings import SettingsWrapper
|
||||
from awx.conf.registry import SettingsRegistry
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def reg(request):
|
||||
cache = LocMemCache(str(uuid4()), {}) # make a new random cache each time
|
||||
settings = LazySettings()
|
||||
registry = SettingsRegistry(settings)
|
||||
defaults = request.node.get_marker('defaults')
|
||||
if defaults:
|
||||
settings.configure(**defaults.kwargs)
|
||||
settings._wrapped = SettingsWrapper(settings._wrapped,
|
||||
cache,
|
||||
registry)
|
||||
return registry
|
||||
|
||||
|
||||
def test_simple_setting_registration(reg):
|
||||
assert reg.get_registered_settings() == []
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
assert reg.get_registered_settings() == ['AWX_SOME_SETTING_ENABLED']
|
||||
|
||||
|
||||
def test_simple_setting_unregistration(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
assert reg.get_registered_settings() == ['AWX_SOME_SETTING_ENABLED']
|
||||
|
||||
reg.unregister('AWX_SOME_SETTING_ENABLED')
|
||||
assert reg.get_registered_settings() == []
|
||||
|
||||
|
||||
def test_duplicate_setting_registration(reg):
|
||||
"ensure that settings cannot be registered twice."
|
||||
with pytest.raises(ImproperlyConfigured):
|
||||
for i in range(2):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
|
||||
|
||||
def test_field_class_required_for_registration(reg):
|
||||
"settings must specify a field class to register"
|
||||
with pytest.raises(ImproperlyConfigured):
|
||||
reg.register('AWX_SOME_SETTING_ENABLED')
|
||||
|
||||
|
||||
def test_get_registered_settings_by_slug(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
assert reg.get_registered_settings(category_slug='system') == [
|
||||
'AWX_SOME_SETTING_ENABLED'
|
||||
]
|
||||
assert reg.get_registered_settings(category_slug='other') == []
|
||||
|
||||
|
||||
def test_get_registered_read_only_settings(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
reg.register(
|
||||
'AWX_SOME_READ_ONLY',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
read_only=True
|
||||
)
|
||||
assert reg.get_registered_settings(read_only=True) ==[
|
||||
'AWX_SOME_READ_ONLY'
|
||||
]
|
||||
assert reg.get_registered_settings(read_only=False) == [
|
||||
'AWX_SOME_SETTING_ENABLED'
|
||||
]
|
||||
assert reg.get_registered_settings() == [
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
'AWX_SOME_READ_ONLY'
|
||||
]
|
||||
|
||||
|
||||
def test_get_registered_settings_with_required_features(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
feature_required='superpowers',
|
||||
)
|
||||
assert reg.get_registered_settings(features_enabled=[]) == []
|
||||
assert reg.get_registered_settings(features_enabled=['superpowers']) == [
|
||||
'AWX_SOME_SETTING_ENABLED'
|
||||
]
|
||||
|
||||
|
||||
def test_get_dependent_settings(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
reg.register(
|
||||
'AWX_SOME_DEPENDENT_SETTING',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
depends_on=['AWX_SOME_SETTING_ENABLED']
|
||||
)
|
||||
assert reg.get_dependent_settings('AWX_SOME_SETTING_ENABLED') == set([
|
||||
'AWX_SOME_DEPENDENT_SETTING'
|
||||
])
|
||||
|
||||
|
||||
def test_get_registered_categories(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
reg.register(
|
||||
'AWX_SOME_OTHER_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('OtherSystem'),
|
||||
category_slug='other-system'
|
||||
)
|
||||
assert reg.get_registered_categories() == {
|
||||
'all': _('All'),
|
||||
'changed': _('Changed'),
|
||||
'system': _('System'),
|
||||
'other-system': _('OtherSystem'),
|
||||
}
|
||||
|
||||
|
||||
def test_get_registered_categories_with_required_features(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
feature_required='superpowers'
|
||||
)
|
||||
reg.register(
|
||||
'AWX_SOME_OTHER_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category=_('OtherSystem'),
|
||||
category_slug='other-system',
|
||||
feature_required='sortapowers'
|
||||
)
|
||||
assert reg.get_registered_categories(features_enabled=[]) == {
|
||||
'all': _('All'),
|
||||
'changed': _('Changed'),
|
||||
}
|
||||
assert reg.get_registered_categories(features_enabled=['superpowers']) == {
|
||||
'all': _('All'),
|
||||
'changed': _('Changed'),
|
||||
'system': _('System'),
|
||||
}
|
||||
assert reg.get_registered_categories(features_enabled=['sortapowers']) == {
|
||||
'all': _('All'),
|
||||
'changed': _('Changed'),
|
||||
'other-system': _('OtherSystem'),
|
||||
}
|
||||
assert reg.get_registered_categories(
|
||||
features_enabled=['superpowers', 'sortapowers']
|
||||
) == {
|
||||
'all': _('All'),
|
||||
'changed': _('Changed'),
|
||||
'system': _('System'),
|
||||
'other-system': _('OtherSystem'),
|
||||
}
|
||||
|
||||
|
||||
def test_is_setting_encrypted(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
reg.register(
|
||||
'AWX_SOME_ENCRYPTED_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
encrypted=True
|
||||
)
|
||||
assert reg.is_setting_encrypted('AWX_SOME_SETTING_ENABLED') is False
|
||||
assert reg.is_setting_encrypted('AWX_SOME_ENCRYPTED_SETTING') is True
|
||||
|
||||
|
||||
def test_simple_field(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
placeholder='Example Value',
|
||||
feature_required='superpowers'
|
||||
)
|
||||
|
||||
field = reg.get_setting_field('AWX_SOME_SETTING')
|
||||
assert isinstance(field, fields.CharField)
|
||||
assert field.category == _('System')
|
||||
assert field.category_slug == 'system'
|
||||
assert field.default is empty
|
||||
assert field.placeholder == 'Example Value'
|
||||
assert field.feature_required == 'superpowers'
|
||||
|
||||
|
||||
def test_field_with_custom_attribute(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category_slug='system',
|
||||
)
|
||||
|
||||
field = reg.get_setting_field('AWX_SOME_SETTING_ENABLED',
|
||||
category_slug='other-system')
|
||||
assert field.category_slug == 'other-system'
|
||||
|
||||
|
||||
def test_field_with_custom_mixin(reg):
|
||||
class GreatMixin(object):
|
||||
|
||||
def is_great(self):
|
||||
return True
|
||||
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
category_slug='system',
|
||||
)
|
||||
|
||||
field = reg.get_setting_field('AWX_SOME_SETTING_ENABLED',
|
||||
mixin_class=GreatMixin)
|
||||
assert isinstance(field, fields.BooleanField)
|
||||
assert isinstance(field, GreatMixin)
|
||||
assert field.is_great() is True
|
||||
|
||||
|
||||
@pytest.mark.defaults(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_default_value_from_settings(reg):
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
|
||||
field = reg.get_setting_field('AWX_SOME_SETTING')
|
||||
assert field.default == 'DEFAULT'
|
||||
|
||||
|
||||
@pytest.mark.defaults(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_default_value_from_settings_with_custom_representation(reg):
|
||||
class LowercaseCharField(fields.CharField):
|
||||
|
||||
def to_representation(self, value):
|
||||
return value.lower()
|
||||
|
||||
reg.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=LowercaseCharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
)
|
||||
|
||||
field = reg.get_setting_field('AWX_SOME_SETTING')
|
||||
assert field.default == 'default'
|
||||
256
awx/conf/tests/unit/test_settings.py
Normal file
256
awx/conf/tests/unit/test_settings.py
Normal file
@@ -0,0 +1,256 @@
|
||||
# Copyright (c) 2017 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
from contextlib import contextmanager
|
||||
from uuid import uuid4
|
||||
import time
|
||||
|
||||
from django.conf import LazySettings
|
||||
from django.core.cache.backends.locmem import LocMemCache
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import fields
|
||||
import pytest
|
||||
|
||||
from awx.conf import models
|
||||
from awx.conf.settings import SettingsWrapper, SETTING_CACHE_NOTSET
|
||||
from awx.conf.registry import SettingsRegistry
|
||||
|
||||
|
||||
@contextmanager
|
||||
def apply_patches(_patches):
|
||||
[p.start() for p in _patches]
|
||||
yield
|
||||
[p.stop() for p in _patches]
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def settings(request):
|
||||
cache = LocMemCache(str(uuid4()), {}) # make a new random cache each time
|
||||
settings = LazySettings()
|
||||
registry = SettingsRegistry(settings)
|
||||
|
||||
# @pytest.mark.readonly can be used to mark specific setting values as
|
||||
# "read-only". This is analogous to manually specifying a setting on the
|
||||
# filesystem (e.g., in a local_settings.py in development, or in
|
||||
# /etc/tower/conf.d/<something>.py)
|
||||
readonly_marker = request.node.get_marker('readonly')
|
||||
defaults = readonly_marker.kwargs if readonly_marker else {}
|
||||
defaults['DEFAULTS_SNAPSHOT'] = {}
|
||||
settings.configure(**defaults)
|
||||
settings._wrapped = SettingsWrapper(settings._wrapped,
|
||||
cache,
|
||||
registry)
|
||||
return settings
|
||||
|
||||
|
||||
@pytest.mark.readonly(DEBUG=True)
|
||||
def test_unregistered_setting(settings):
|
||||
"native Django settings are not stored in DB, and aren't cached"
|
||||
assert settings.DEBUG is True
|
||||
assert settings.cache.get('DEBUG') is None
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_read_only_setting(settings):
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
assert len(settings.registry.get_registered_settings(read_only=False)) == 0
|
||||
settings = settings.registry.get_registered_settings(read_only=True)
|
||||
assert settings == ['AWX_SOME_SETTING']
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_read_only_setting_with_empty_default(settings):
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
default='',
|
||||
)
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
assert len(settings.registry.get_registered_settings(read_only=False)) == 0
|
||||
settings = settings.registry.get_registered_settings(read_only=True)
|
||||
assert settings == ['AWX_SOME_SETTING']
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_read_only_defaults_are_cached(settings):
|
||||
"read-only settings are stored in the cache"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
assert settings.cache.get('AWX_SOME_SETTING') == 'DEFAULT'
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_cache_respects_timeout(settings):
|
||||
"only preload the cache every SETTING_CACHE_TIMEOUT settings"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
cache_expiration = settings.cache.get('_awx_conf_preload_expires')
|
||||
assert cache_expiration > time.time()
|
||||
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
assert settings.cache.get('_awx_conf_preload_expires') == cache_expiration
|
||||
|
||||
|
||||
def test_default_setting(settings, mocker):
|
||||
"settings that specify a default are inserted into the cache"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
default='DEFAULT'
|
||||
)
|
||||
|
||||
settings_to_cache = mocker.Mock(**{'order_by.return_value': []})
|
||||
with mocker.patch('awx.conf.models.Setting.objects.filter', return_value=settings_to_cache):
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
assert settings.cache.get('AWX_SOME_SETTING') == 'DEFAULT'
|
||||
|
||||
|
||||
def test_empty_setting(settings, mocker):
|
||||
"settings with no default and no defined value are not valid"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
|
||||
settings_to_cache = [
|
||||
mocker.Mock(**{'order_by.return_value': []}),
|
||||
mocker.Mock(**{'order_by.return_value.first.return_value': None})
|
||||
]
|
||||
with mocker.patch('awx.conf.models.Setting.objects.filter', side_effect=settings_to_cache):
|
||||
with pytest.raises(AttributeError):
|
||||
settings.AWX_SOME_SETTING
|
||||
assert settings.cache.get('AWX_SOME_SETTING') == SETTING_CACHE_NOTSET
|
||||
|
||||
|
||||
def test_setting_from_db(settings, mocker):
|
||||
"settings can be loaded from the database"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system',
|
||||
default='DEFAULT'
|
||||
)
|
||||
|
||||
settings_to_cache = [
|
||||
mocker.Mock(**{'order_by.return_value': [
|
||||
mocker.Mock(key='AWX_SOME_SETTING', value='FROM_DB')
|
||||
]}),
|
||||
]
|
||||
with mocker.patch('awx.conf.models.Setting.objects.filter', side_effect=settings_to_cache):
|
||||
assert settings.AWX_SOME_SETTING == 'FROM_DB'
|
||||
assert settings.cache.get('AWX_SOME_SETTING') == 'FROM_DB'
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_read_only_setting_assignment(settings):
|
||||
"read-only settings cannot be overwritten"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
with pytest.raises(ImproperlyConfigured):
|
||||
settings.AWX_SOME_SETTING = 'CHANGED'
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
|
||||
|
||||
def test_db_setting_create(settings, mocker):
|
||||
"settings are stored in the database when set for the first time"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
|
||||
setting_list = mocker.Mock(**{'order_by.return_value.first.return_value': None})
|
||||
with apply_patches([
|
||||
mocker.patch('awx.conf.models.Setting.objects.filter',
|
||||
return_value=setting_list),
|
||||
mocker.patch('awx.conf.models.Setting.objects.create', mocker.Mock())
|
||||
]):
|
||||
settings.AWX_SOME_SETTING = 'NEW-VALUE'
|
||||
|
||||
models.Setting.objects.create.assert_called_with(
|
||||
key='AWX_SOME_SETTING',
|
||||
user=None,
|
||||
value='NEW-VALUE'
|
||||
)
|
||||
|
||||
|
||||
def test_db_setting_update(settings, mocker):
|
||||
"settings are updated in the database when their value changes"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
|
||||
existing_setting = mocker.Mock(key='AWX_SOME_SETTING', value='FROM_DB')
|
||||
setting_list = mocker.Mock(**{
|
||||
'order_by.return_value.first.return_value': existing_setting
|
||||
})
|
||||
with mocker.patch('awx.conf.models.Setting.objects.filter', return_value=setting_list):
|
||||
settings.AWX_SOME_SETTING = 'NEW-VALUE'
|
||||
|
||||
assert existing_setting.value == 'NEW-VALUE'
|
||||
existing_setting.save.assert_called_with(update_fields=['value'])
|
||||
|
||||
|
||||
def test_db_setting_deletion(settings, mocker):
|
||||
"settings are auto-deleted from the database"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
|
||||
existing_setting = mocker.Mock(key='AWX_SOME_SETTING', value='FROM_DB')
|
||||
with mocker.patch('awx.conf.models.Setting.objects.filter', return_value=[existing_setting]):
|
||||
del settings.AWX_SOME_SETTING
|
||||
|
||||
assert existing_setting.delete.call_count == 1
|
||||
|
||||
|
||||
@pytest.mark.readonly(AWX_SOME_SETTING='DEFAULT')
|
||||
def test_read_only_setting_deletion(settings):
|
||||
"read-only settings cannot be deleted"
|
||||
settings.registry.register(
|
||||
'AWX_SOME_SETTING',
|
||||
field_class=fields.CharField,
|
||||
category=_('System'),
|
||||
category_slug='system'
|
||||
)
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
with pytest.raises(ImproperlyConfigured):
|
||||
del settings.AWX_SOME_SETTING
|
||||
assert settings.AWX_SOME_SETTING == 'DEFAULT'
|
||||
Reference in New Issue
Block a user