diff --git a/awx/conf/tests/__init__.py b/awx/conf/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/awx/conf/tests/test_env.py b/awx/conf/tests/test_env.py new file mode 100644 index 0000000000..135c90d99b --- /dev/null +++ b/awx/conf/tests/test_env.py @@ -0,0 +1,6 @@ + + +# Ensure that our autouse overwrites are working +def test_cache(settings): + assert settings.CACHES['default']['BACKEND'] == 'django.core.cache.backends.locmem.LocMemCache' + assert settings.CACHES['default']['LOCATION'].startswith('unique-') diff --git a/awx/main/tests/conftest.py b/awx/main/tests/conftest.py index 946c930dc5..0c3cb513ec 100644 --- a/awx/main/tests/conftest.py +++ b/awx/main/tests/conftest.py @@ -106,3 +106,21 @@ def get_ssh_version(mocker): @pytest.fixture def job_template_with_survey_passwords_unit(job_template_with_survey_passwords_factory): return job_template_with_survey_passwords_factory(persisted=False) + + +@pytest.fixture +def mock_cache(): + class MockCache(object): + cache = {} + + def get(self, key, default=None): + return self.cache.get(key, default) + + def set(self, key, value, timeout=60): + self.cache[key] = value + + def delete(self, key): + del self.cache[key] + + return MockCache() + diff --git a/awx/main/tests/functional/conftest.py b/awx/main/tests/functional/conftest.py index d5b45cf728..28d7b65564 100644 --- a/awx/main/tests/functional/conftest.py +++ b/awx/main/tests/functional/conftest.py @@ -9,7 +9,6 @@ from six.moves import xrange # Django from django.core.urlresolvers import resolve -from django.core.cache import cache from django.utils.six.moves.urllib.parse import urlparse from django.utils import timezone from django.contrib.auth.models import User @@ -57,14 +56,6 @@ def swagger_autogen(requests=__SWAGGER_REQUESTS__): return requests -@pytest.fixture(autouse=True) -def clear_cache(): - ''' - Clear cache (local memory) for each test to prevent using cached settings. - ''' - cache.clear() - - @pytest.fixture(scope="session", autouse=True) def celery_memory_broker(): ''' diff --git a/awx/main/tests/functional/task_management/test_scheduler.py b/awx/main/tests/functional/task_management/test_scheduler.py index 116deba3ad..82625533c7 100644 --- a/awx/main/tests/functional/task_management/test_scheduler.py +++ b/awx/main/tests/functional/task_management/test_scheduler.py @@ -241,15 +241,16 @@ def test_shared_dependencies_launch(default_instance_group, job_template_factory @pytest.mark.django_db -def test_cleanup_interval(): - assert cache.get('last_celery_task_cleanup') is None +def test_cleanup_interval(mock_cache): + with mock.patch.multiple('awx.main.scheduler.task_manager.cache', get=mock_cache.get, set=mock_cache.set): + assert mock_cache.get('last_celery_task_cleanup') is None - TaskManager().cleanup_inconsistent_celery_tasks() - last_cleanup = cache.get('last_celery_task_cleanup') - assert isinstance(last_cleanup, datetime) + TaskManager().cleanup_inconsistent_celery_tasks() + last_cleanup = mock_cache.get('last_celery_task_cleanup') + assert isinstance(last_cleanup, datetime) - TaskManager().cleanup_inconsistent_celery_tasks() - assert cache.get('last_celery_task_cleanup') == last_cleanup + TaskManager().cleanup_inconsistent_celery_tasks() + assert cache.get('last_celery_task_cleanup') == last_cleanup class TestReaper(): @@ -326,7 +327,8 @@ class TestReaper(): @pytest.mark.django_db @mock.patch.object(JobNotificationMixin, 'send_notification_templates') @mock.patch.object(TaskManager, 'get_active_tasks', lambda self: ([], [])) - def test_cleanup_inconsistent_task(self, notify, active_tasks, considered_jobs, reapable_jobs, running_tasks, waiting_tasks, mocker): + def test_cleanup_inconsistent_task(self, notify, active_tasks, considered_jobs, reapable_jobs, running_tasks, waiting_tasks, mocker, settings): + settings.AWX_INCONSISTENT_TASK_INTERVAL = 0 tm = TaskManager() tm.get_running_tasks = mocker.Mock(return_value=(running_tasks, waiting_tasks)) diff --git a/awx/main/tests/test_env.py b/awx/main/tests/test_env.py new file mode 100644 index 0000000000..135c90d99b --- /dev/null +++ b/awx/main/tests/test_env.py @@ -0,0 +1,6 @@ + + +# Ensure that our autouse overwrites are working +def test_cache(settings): + assert settings.CACHES['default']['BACKEND'] == 'django.core.cache.backends.locmem.LocMemCache' + assert settings.CACHES['default']['LOCATION'].startswith('unique-') diff --git a/awx/main/tests/unit/models/test_survey_models.py b/awx/main/tests/unit/models/test_survey_models.py index 330014d481..3bc06edc87 100644 --- a/awx/main/tests/unit/models/test_survey_models.py +++ b/awx/main/tests/unit/models/test_survey_models.py @@ -253,7 +253,7 @@ def test_optional_survey_question_defaults( ('password', 'foo', 5, {'extra_vars': {'x': ''}}, {'x': ''}), ('password', ENCRYPTED_SECRET, 5, {'extra_vars': {'x': '$encrypted$'}}, {}), ('password', ENCRYPTED_SECRET, 10, {'extra_vars': {'x': '$encrypted$'}}, {'x': ENCRYPTED_SECRET}), - ], ids=DistinctParametrize()) +], ids=DistinctParametrize()) def test_survey_encryption_defaults(survey_spec_factory, question_type, default, maxlen, kwargs, expected): spec = survey_spec_factory([ { diff --git a/awx/main/tests/unit/utils/test_common.py b/awx/main/tests/unit/utils/test_common.py index fa4a038037..bb98b18bec 100644 --- a/awx/main/tests/unit/utils/test_common.py +++ b/awx/main/tests/unit/utils/test_common.py @@ -7,10 +7,10 @@ import pytest from uuid import uuid4 import json import yaml +import mock from backports.tempfile import TemporaryDirectory from django.conf import settings -from django.core.cache import cache from rest_framework.exceptions import ParseError @@ -26,14 +26,6 @@ from awx.main.models import ( ) -@pytest.fixture(autouse=True) -def clear_cache(): - ''' - Clear cache (local memory) for each test to prevent using cached settings. - ''' - cache.clear() - - @pytest.mark.parametrize('input_, output', [ ({"foo": "bar"}, {"foo": "bar"}), ('{"foo": "bar"}', {"foo": "bar"}), @@ -114,46 +106,48 @@ def test_get_type_for_model(model, name): @pytest.fixture -def memoized_function(mocker): - @common.memoize(track_function=True) - def myfunction(key, value): - if key not in myfunction.calls: - myfunction.calls[key] = 0 +def memoized_function(mocker, mock_cache): + with mock.patch('awx.main.utils.common.get_memoize_cache', return_value=mock_cache): + @common.memoize(track_function=True) + def myfunction(key, value): + if key not in myfunction.calls: + myfunction.calls[key] = 0 - myfunction.calls[key] += 1 + myfunction.calls[key] += 1 - if myfunction.calls[key] == 1: - return value - else: - return '%s called %s times' % (value, myfunction.calls[key]) - myfunction.calls = dict() - return myfunction + if myfunction.calls[key] == 1: + return value + else: + return '%s called %s times' % (value, myfunction.calls[key]) + myfunction.calls = dict() + return myfunction -def test_memoize_track_function(memoized_function): +def test_memoize_track_function(memoized_function, mock_cache): assert memoized_function('scott', 'scotterson') == 'scotterson' - assert cache.get('myfunction') == {u'scott-scotterson': 'scotterson'} + assert mock_cache.get('myfunction') == {u'scott-scotterson': 'scotterson'} assert memoized_function('scott', 'scotterson') == 'scotterson' assert memoized_function.calls['scott'] == 1 assert memoized_function('john', 'smith') == 'smith' - assert cache.get('myfunction') == {u'scott-scotterson': 'scotterson', u'john-smith': 'smith'} + assert mock_cache.get('myfunction') == {u'scott-scotterson': 'scotterson', u'john-smith': 'smith'} assert memoized_function('john', 'smith') == 'smith' - + assert memoized_function.calls['john'] == 1 -def test_memoize_delete(memoized_function): +def test_memoize_delete(memoized_function, mock_cache): assert memoized_function('john', 'smith') == 'smith' assert memoized_function('john', 'smith') == 'smith' assert memoized_function.calls['john'] == 1 - assert cache.get('myfunction') == {u'john-smith': 'smith'} + assert mock_cache.get('myfunction') == {u'john-smith': 'smith'} - common.memoize_delete('myfunction') + with mock.patch('awx.main.utils.common.memoize_delete', side_effect=mock_cache.delete): + common.memoize_delete('myfunction') - assert cache.get('myfunction') is None + assert mock_cache.get('myfunction') is None assert memoized_function('john', 'smith') == 'smith called 2 times' assert memoized_function.calls['john'] == 2 diff --git a/awx/main/utils/common.py b/awx/main/utils/common.py index a024e7d649..c9914cd1c3 100644 --- a/awx/main/utils/common.py +++ b/awx/main/utils/common.py @@ -127,12 +127,16 @@ class IllegalArgumentError(ValueError): pass +def get_memoize_cache(): + from django.core.cache import cache + return cache + + def memoize(ttl=60, cache_key=None, track_function=False): ''' Decorator to wrap a function and cache its result. ''' - from django.core.cache import cache - + cache = get_memoize_cache() def _memoizer(f, *args, **kwargs): if cache_key and track_function: @@ -160,8 +164,7 @@ def memoize(ttl=60, cache_key=None, track_function=False): def memoize_delete(function_name): - from django.core.cache import cache - + cache = get_memoize_cache() return cache.delete(function_name) diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 66e9c3ee9a..978cbf05c0 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -537,19 +537,12 @@ ASGI_AMQP = { } # Django Caching Configuration -if is_testing(): - CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - }, - } -else: - CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', - 'LOCATION': 'memcached:11211', - }, - } +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', + 'LOCATION': 'memcached:11211', + }, +} # Social Auth configuration. SOCIAL_AUTH_STRATEGY = 'social_django.strategy.DjangoStrategy' diff --git a/awx/settings/development.py b/awx/settings/development.py index e7dc0152f6..4a2bd1aa8d 100644 --- a/awx/settings/development.py +++ b/awx/settings/development.py @@ -9,6 +9,7 @@ import socket import copy import sys import traceback +import random # Centos-7 doesn't include the svg mime type # /usr/lib64/python/mimetypes.py @@ -20,6 +21,15 @@ from split_settings.tools import optional, include # Load default settings. from defaults import * # NOQA +# don't use memcache when running tests +if "pytest" in sys.modules: + CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + 'LOCATION': 'unique-{}'.format(random.randint(0, sys.maxint)), + }, + } + # awx-manage shell_plus --notebook NOTEBOOK_ARGUMENTS = [ '--NotebookApp.token=', diff --git a/awx/sso/tests/test_env.py b/awx/sso/tests/test_env.py new file mode 100644 index 0000000000..135c90d99b --- /dev/null +++ b/awx/sso/tests/test_env.py @@ -0,0 +1,6 @@ + + +# Ensure that our autouse overwrites are working +def test_cache(settings): + assert settings.CACHES['default']['BACKEND'] == 'django.core.cache.backends.locmem.LocMemCache' + assert settings.CACHES['default']['LOCATION'].startswith('unique-')