django-configurations/tests/test_values.py
Christopher Broderick 448c5ab1b6 Address flake8 issues
2024-11-07 13:33:00 +00:00

528 lines
22 KiB
Python

import decimal
import os
from contextlib import contextmanager
from django import VERSION as DJANGO_VERSION
from django.test import TestCase
from django.core.exceptions import ImproperlyConfigured
from unittest.mock import patch
from configurations.values import (Value, BooleanValue, IntegerValue,
FloatValue, DecimalValue, ListValue,
TupleValue, SingleNestedTupleValue,
SingleNestedListValue, SetValue,
DictValue, URLValue, EmailValue, IPValue,
RegexValue, PathValue, SecretValue,
DatabaseURLValue, EmailURLValue,
CacheURLValue, BackendsValue,
CastingMixin, SearchURLValue,
setup_value, PositiveIntegerValue)
@contextmanager
def env(**kwargs):
with patch.dict(os.environ, clear=True, **kwargs):
yield
class FailingCasterValue(CastingMixin, Value):
caster = 'non.existing.caster'
class ValueTests(TestCase):
def test_value_with_default(self):
value = Value('default', environ=False)
self.assertEqual(type(value), str)
self.assertEqual(value, 'default')
self.assertEqual(str(value), 'default')
def test_value_with_default_and_late_binding(self):
value = Value('default', environ=False, late_binding=True)
self.assertEqual(type(value), Value)
with env(DJANGO_TEST='override'):
self.assertEqual(value.setup('TEST'), 'default')
value = Value(environ_name='TEST')
self.assertEqual(type(value), str)
self.assertEqual(value, 'override')
self.assertEqual(str(value), 'override')
self.assertEqual(f'{value}', 'override')
self.assertEqual('%s' % value, 'override')
value = Value(environ_name='TEST', late_binding=True)
self.assertEqual(type(value), Value)
self.assertEqual(value.value, 'override')
self.assertEqual(str(value), 'override')
self.assertEqual(f'{value}', 'override')
self.assertEqual('%s' % value, 'override')
self.assertEqual(repr(value), repr('override'))
def test_value_truthy(self):
value = Value('default')
self.assertTrue(bool(value))
def test_value_falsey(self):
value = Value()
self.assertFalse(bool(value))
@patch.dict(os.environ, clear=True, DJANGO_TEST='override')
def test_env_var(self):
value = Value('default')
self.assertEqual(value.setup('TEST'), 'override')
self.assertEqual(str(value), 'override')
self.assertNotEqual(value.setup('TEST'), value.default)
self.assertEqual(value.to_python(os.environ['DJANGO_TEST']),
value.setup('TEST'))
def test_value_reuse(self):
value1 = Value('default')
value2 = Value(value1)
self.assertEqual(value1.setup('TEST1'), 'default')
self.assertEqual(value2.setup('TEST2'), 'default')
with env(DJANGO_TEST1='override1', DJANGO_TEST2='override2'):
self.assertEqual(value1.setup('TEST1'), 'override1')
self.assertEqual(value2.setup('TEST2'), 'override2')
def test_value_var_equal(self):
value1 = Value('default')
value2 = Value('default')
self.assertEqual(value1, value2)
self.assertTrue(value1 in ['default'])
def test_env_var_prefix(self):
with patch.dict(os.environ, clear=True, ACME_TEST='override'):
value = Value('default', environ_prefix='ACME')
self.assertEqual(value.setup('TEST'), 'override')
with patch.dict(os.environ, clear=True, TEST='override'):
value = Value('default', environ_prefix='')
self.assertEqual(value.setup('TEST'), 'override')
with patch.dict(os.environ, clear=True, ACME_TEST='override'):
value = Value('default', environ_prefix='ACME_')
self.assertEqual(value.setup('TEST'), 'override')
def test_boolean_values_true(self):
value = BooleanValue(False)
for truthy in value.true_values:
with env(DJANGO_TEST=truthy):
self.assertTrue(bool(value.setup('TEST')))
def test_boolean_values_faulty(self):
self.assertRaises(ValueError, BooleanValue, 'false')
def test_boolean_values_false(self):
value = BooleanValue(True)
for falsy in value.false_values:
with env(DJANGO_TEST=falsy):
self.assertFalse(bool(value.setup('TEST')))
def test_boolean_values_nonboolean(self):
value = BooleanValue(True)
with env(DJANGO_TEST='nonboolean'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_boolean_values_assign_false_to_another_booleanvalue(self):
value1 = BooleanValue(False)
value2 = BooleanValue(value1)
self.assertFalse(value1.setup('TEST1'))
self.assertFalse(value2.setup('TEST2'))
def test_integer_values(self):
value = IntegerValue(1)
with env(DJANGO_TEST='2'):
self.assertEqual(value.setup('TEST'), 2)
with env(DJANGO_TEST='noninteger'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_positive_integer_values(self):
value = PositiveIntegerValue(1)
with env(DJANGO_TEST='2'):
self.assertEqual(value.setup('TEST'), 2)
with env(DJANGO_TEST='noninteger'):
self.assertRaises(ValueError, value.setup, 'TEST')
with env(DJANGO_TEST='-1'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_float_values(self):
value = FloatValue(1.0)
with env(DJANGO_TEST='2.0'):
self.assertEqual(value.setup('TEST'), 2.0)
with env(DJANGO_TEST='noninteger'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_decimal_values(self):
value = DecimalValue(decimal.Decimal(1))
with env(DJANGO_TEST='2'):
self.assertEqual(value.setup('TEST'), decimal.Decimal(2))
with env(DJANGO_TEST='nondecimal'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_failing_caster(self):
self.assertRaises(ImproperlyConfigured, FailingCasterValue)
def test_list_values_default(self):
value = ListValue()
with env(DJANGO_TEST='2,2'):
self.assertEqual(value.setup('TEST'), ['2', '2'])
with env(DJANGO_TEST='2, 2 ,'):
self.assertEqual(value.setup('TEST'), ['2', '2'])
with env(DJANGO_TEST=''):
self.assertEqual(value.setup('TEST'), [])
def test_list_values_separator(self):
value = ListValue(separator=':')
with env(DJANGO_TEST='/usr/bin:/usr/sbin:/usr/local/bin'):
self.assertEqual(value.setup('TEST'),
['/usr/bin', '/usr/sbin', '/usr/local/bin'])
def test_List_values_converter(self):
value = ListValue(converter=int)
with env(DJANGO_TEST='2,2'):
self.assertEqual(value.setup('TEST'), [2, 2])
value = ListValue(converter=float)
with env(DJANGO_TEST='2,2'):
self.assertEqual(value.setup('TEST'), [2.0, 2.0])
def test_list_values_custom_converter(self):
value = ListValue(converter=lambda x: x * 2)
with env(DJANGO_TEST='2,2'):
self.assertEqual(value.setup('TEST'), ['22', '22'])
def test_list_values_converter_exception(self):
value = ListValue(converter=int)
with env(DJANGO_TEST='2,b'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_tuple_values_default(self):
value = TupleValue()
with env(DJANGO_TEST='2,2'):
self.assertEqual(value.setup('TEST'), ('2', '2'))
with env(DJANGO_TEST='2, 2 ,'):
self.assertEqual(value.setup('TEST'), ('2', '2'))
with env(DJANGO_TEST=''):
self.assertEqual(value.setup('TEST'), ())
def test_single_nested_list_values_default(self):
value = SingleNestedListValue()
with env(DJANGO_TEST='2,3;4,5'):
expected = [['2', '3'], ['4', '5']]
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST='2;3;4;5'):
expected = [['2'], ['3'], ['4'], ['5']]
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST='2,3,4,5'):
expected = [['2', '3', '4', '5']]
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST='2, 3 , ; 4 , 5 ; '):
expected = [['2', '3'], ['4', '5']]
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST=''):
self.assertEqual(value.setup('TEST'), [])
def test_single_nested_list_values_separator(self):
value = SingleNestedListValue(seq_separator=':')
with env(DJANGO_TEST='2,3:4,5'):
self.assertEqual(value.setup('TEST'), [['2', '3'], ['4', '5']])
def test_single_nested_list_values_converter(self):
value = SingleNestedListValue(converter=int)
with env(DJANGO_TEST='2,3;4,5'):
self.assertEqual(value.setup('TEST'), [[2, 3], [4, 5]])
def test_single_nested_list_values_converter_default(self):
value = SingleNestedListValue([['2', '3'], ['4', '5']], converter=int)
self.assertEqual(value.value, [[2, 3], [4, 5]])
def test_single_nested_tuple_values_default(self):
value = SingleNestedTupleValue()
with env(DJANGO_TEST='2,3;4,5'):
expected = (('2', '3'), ('4', '5'))
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST='2;3;4;5'):
expected = (('2',), ('3',), ('4',), ('5',))
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST='2,3,4,5'):
expected = (('2', '3', '4', '5'),)
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST='2, 3 , ; 4 , 5 ; '):
expected = (('2', '3'), ('4', '5'))
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST=''):
self.assertEqual(value.setup('TEST'), ())
def test_single_nested_tuple_values_separator(self):
value = SingleNestedTupleValue(seq_separator=':')
with env(DJANGO_TEST='2,3:4,5'):
self.assertEqual(value.setup('TEST'), (('2', '3'), ('4', '5')))
def test_single_nested_tuple_values_converter(self):
value = SingleNestedTupleValue(converter=int)
with env(DJANGO_TEST='2,3;4,5'):
self.assertEqual(value.setup('TEST'), ((2, 3), (4, 5)))
def test_single_nested_tuple_values_converter_default(self):
value = SingleNestedTupleValue((('2', '3'), ('4', '5')), converter=int)
self.assertEqual(value.value, ((2, 3), (4, 5)))
def test_set_values_default(self):
value = SetValue()
with env(DJANGO_TEST='2,2'):
self.assertEqual(value.setup('TEST'), {'2', '2'})
with env(DJANGO_TEST='2, 2 ,'):
self.assertEqual(value.setup('TEST'), {'2', '2'})
with env(DJANGO_TEST=''):
self.assertEqual(value.setup('TEST'), set())
def test_dict_values_default(self):
value = DictValue()
with env(DJANGO_TEST='{2: 2}'):
self.assertEqual(value.setup('TEST'), {2: 2})
expected = {2: 2, '3': '3', '4': [1, 2, 3]}
with env(DJANGO_TEST="{2: 2, '3': '3', '4': [1, 2, 3]}"):
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST="""{
2: 2,
'3': '3',
'4': [1, 2, 3],
}"""):
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST=''):
self.assertEqual(value.setup('TEST'), {})
with env(DJANGO_TEST='spam'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_email_values(self):
value = EmailValue('spam@eg.gs')
with env(DJANGO_TEST='spam@sp.am'):
self.assertEqual(value.setup('TEST'), 'spam@sp.am')
with env(DJANGO_TEST='spam'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_url_values(self):
value = URLValue('http://eggs.spam')
with env(DJANGO_TEST='http://spam.eggs'):
self.assertEqual(value.setup('TEST'), 'http://spam.eggs')
with env(DJANGO_TEST='httb://spam.eggs'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_url_values_with_no_default(self):
value = URLValue() # no default
with env(DJANGO_TEST='http://spam.eggs'):
self.assertEqual(value.setup('TEST'), 'http://spam.eggs')
def test_url_values_with_wrong_default(self):
self.assertRaises(ValueError, URLValue, 'httb://spam.eggs')
def test_ip_values(self):
value = IPValue('0.0.0.0')
with env(DJANGO_TEST='127.0.0.1'):
self.assertEqual(value.setup('TEST'), '127.0.0.1')
with env(DJANGO_TEST='::1'):
self.assertEqual(value.setup('TEST'), '::1')
with env(DJANGO_TEST='spam.eggs'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_regex_values(self):
value = RegexValue('000--000', regex=r'\d+--\d+')
with env(DJANGO_TEST='123--456'):
self.assertEqual(value.setup('TEST'), '123--456')
with env(DJANGO_TEST='123456'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_path_values_with_check(self):
value = PathValue()
with env(DJANGO_TEST='/'):
self.assertEqual(value.setup('TEST'), '/')
with env(DJANGO_TEST='~/'):
self.assertEqual(value.setup('TEST'), os.path.expanduser('~'))
with env(DJANGO_TEST='/does/not/exist'):
self.assertRaises(ValueError, value.setup, 'TEST')
def test_path_values_no_check(self):
value = PathValue(check_exists=False)
with env(DJANGO_TEST='/'):
self.assertEqual(value.setup('TEST'), '/')
with env(DJANGO_TEST='~/spam/eggs'):
self.assertEqual(value.setup('TEST'),
os.path.join(os.path.expanduser('~'),
'spam', 'eggs'))
with env(DJANGO_TEST='/does/not/exist'):
self.assertEqual(value.setup('TEST'), '/does/not/exist')
def test_secret_value(self):
# no default allowed, only environment values are
self.assertRaises(ValueError, SecretValue, 'default')
value = SecretValue()
self.assertRaises(ValueError, value.setup, 'TEST')
with env(DJANGO_SECRET_KEY='123'):
self.assertEqual(value.setup('SECRET_KEY'), '123')
value = SecretValue(environ_name='FACEBOOK_API_SECRET',
environ_prefix=None,
late_binding=True)
self.assertRaises(ValueError, value.setup, 'TEST')
with env(FACEBOOK_API_SECRET='123'):
self.assertEqual(value.setup('TEST'), '123')
def test_database_url_value(self):
value = DatabaseURLValue()
self.assertEqual(value.default, {})
with env(DATABASE_URL='sqlite://'):
settings_value = value.setup('DATABASE_URL')
# Compare the embedded dicts in the "default" entry so that the difference can be seen if
# it fails ... DatabaseURLValue(|) uses an external app that can add additional entries
self.assertDictEqual(
{
'CONN_HEALTH_CHECKS': False,
'CONN_MAX_AGE': 0,
'DISABLE_SERVER_SIDE_CURSORS': False,
'ENGINE': 'django.db.backends.sqlite3',
'HOST': '',
'NAME': ':memory:',
'PASSWORD': '',
'PORT': '',
'USER': '',
},
settings_value['default']
)
def test_database_url_additional_args(self):
def mock_database_url_caster(self, url, engine=None):
return {'URL': url, 'ENGINE': engine}
with patch('configurations.values.DatabaseURLValue.caster',
mock_database_url_caster):
value = DatabaseURLValue(
engine='django_mysqlpool.backends.mysqlpool')
with env(DATABASE_URL='sqlite://'):
self.assertEqual(value.setup('DATABASE_URL'), {
'default': {
'URL': 'sqlite://',
'ENGINE': 'django_mysqlpool.backends.mysqlpool'
}
})
def test_email_url_value(self):
value = EmailURLValue()
self.assertEqual(value.default, {})
with env(EMAIL_URL='smtps://user@domain.com:password@smtp.example.com:587'): # noqa: E501
self.assertEqual(value.setup('EMAIL_URL'), {
'EMAIL_BACKEND': 'django.core.mail.backends.smtp.EmailBackend',
'EMAIL_FILE_PATH': '',
'EMAIL_HOST': 'smtp.example.com',
'EMAIL_HOST_PASSWORD': 'password',
'EMAIL_HOST_USER': 'user@domain.com',
'EMAIL_PORT': 587,
'EMAIL_TIMEOUT': None,
'EMAIL_USE_SSL': False,
'EMAIL_USE_TLS': True})
with env(EMAIL_URL='console://'):
self.assertEqual(value.setup('EMAIL_URL'), {
'EMAIL_BACKEND': 'django.core.mail.backends.console.EmailBackend', # noqa: E501
'EMAIL_FILE_PATH': '',
'EMAIL_HOST': None,
'EMAIL_HOST_PASSWORD': None,
'EMAIL_HOST_USER': None,
'EMAIL_PORT': None,
'EMAIL_TIMEOUT': None,
'EMAIL_USE_SSL': False,
'EMAIL_USE_TLS': False})
with env(EMAIL_URL='smtps://user@domain.com:password@smtp.example.com:wrong'): # noqa: E501
self.assertRaises(ValueError, value.setup, 'TEST')
def test_cache_url_value(self):
cache_setting = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache' if DJANGO_VERSION < (4,) else 'django.core.cache.backends.redis.RedisCache', # noqa: E501
'LOCATION': 'redis://host:6379/1',
}
}
cache_url = 'redis://user@host:6379/1'
value = CacheURLValue(cache_url)
self.assertEqual(value.default, cache_setting)
value = CacheURLValue()
self.assertEqual(value.default, {})
with env(CACHE_URL='redis://user@host:6379/1'):
self.assertEqual(value.setup('CACHE_URL'), cache_setting)
with env(CACHE_URL='wrong://user@host:port/1'):
with self.assertRaises(Exception) as cm:
value.setup('TEST')
self.assertEqual(cm.exception.args[0], 'Unknown backend: "wrong"')
with env(CACHE_URL='redis://user@host:port/1'):
with self.assertRaises(ValueError) as cm:
value.setup('TEST')
self.assertEqual(
cm.exception.args[0],
"Cannot interpret cache URL value 'redis://user@host:port/1'")
def test_search_url_value(self):
value = SearchURLValue()
self.assertEqual(value.default, {})
with env(SEARCH_URL='elasticsearch://127.0.0.1:9200/index'):
self.assertEqual(value.setup('SEARCH_URL'), {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine', # noqa: E501
'URL': 'http://127.0.0.1:9200',
'INDEX_NAME': 'index',
}})
def test_backend_list_value(self):
backends = ['django.middleware.common.CommonMiddleware']
value = BackendsValue(backends)
self.assertEqual(value.setup('TEST'), backends)
backends = ['non.existing.Backend']
self.assertRaises(ValueError, BackendsValue, backends)
def test_tuple_value(self):
value = TupleValue(None)
self.assertEqual(value.default, ())
self.assertEqual(value.value, ())
value = TupleValue((1, 2))
self.assertEqual(value.default, (1, 2))
self.assertEqual(value.value, (1, 2))
def test_set_value(self):
value = SetValue()
self.assertEqual(value.default, set())
self.assertEqual(value.value, set())
value = SetValue([1, 2])
self.assertEqual(value.default, {1, 2})
self.assertEqual(value.value, {1, 2})
def test_setup_value(self):
class Target:
pass
value = EmailURLValue()
with env(EMAIL_URL='smtps://user@domain.com:password@smtp.example.com:587'): # noqa: E501
setup_value(Target, 'EMAIL', value)
self.assertEqual(Target.EMAIL, {
'EMAIL_BACKEND': 'django.core.mail.backends.smtp.EmailBackend',
'EMAIL_FILE_PATH': '',
'EMAIL_HOST': 'smtp.example.com',
'EMAIL_HOST_PASSWORD': 'password',
'EMAIL_HOST_USER': 'user@domain.com',
'EMAIL_PORT': 587,
'EMAIL_TIMEOUT': None,
'EMAIL_USE_SSL': False,
'EMAIL_USE_TLS': True
})
self.assertEqual(
Target.EMAIL_BACKEND,
'django.core.mail.backends.smtp.EmailBackend')
self.assertEqual(Target.EMAIL_FILE_PATH, '')
self.assertEqual(Target.EMAIL_HOST, 'smtp.example.com')
self.assertEqual(Target.EMAIL_HOST_PASSWORD, 'password')
self.assertEqual(Target.EMAIL_HOST_USER, 'user@domain.com')
self.assertEqual(Target.EMAIL_PORT, 587)
self.assertEqual(Target.EMAIL_USE_TLS, True)