From 64b6b18a81c32da45668e5b418e033f4955475dc Mon Sep 17 00:00:00 2001 From: Aaron Tan Date: Tue, 29 Aug 2017 09:55:05 -0400 Subject: [PATCH] Wrap up Tower configuration unit tests --- awx/api/views.py | 1 - awx/conf/tests/functional/__init__.py | 0 awx/conf/tests/functional/conftest.py | 43 ++++ awx/conf/tests/functional/test_api.py | 350 ++++++++++++++++++++++++++ awx/conf/tests/unit/__init__.py | 0 5 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 awx/conf/tests/functional/__init__.py create mode 100644 awx/conf/tests/functional/conftest.py create mode 100644 awx/conf/tests/functional/test_api.py create mode 100644 awx/conf/tests/unit/__init__.py diff --git a/awx/api/views.py b/awx/api/views.py index cf40687256..0f5d7a8c45 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1,4 +1,3 @@ - # Copyright (c) 2015 Ansible, Inc. # All Rights Reserved. diff --git a/awx/conf/tests/functional/__init__.py b/awx/conf/tests/functional/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/awx/conf/tests/functional/conftest.py b/awx/conf/tests/functional/conftest.py new file mode 100644 index 0000000000..a3eb0328de --- /dev/null +++ b/awx/conf/tests/functional/conftest.py @@ -0,0 +1,43 @@ +import pytest + +from django.core.urlresolvers import resolve +from django.utils.six.moves.urllib.parse import urlparse +from django.contrib.auth.models import User + +from rest_framework.test import ( + APIRequestFactory, + force_authenticate, +) + + +@pytest.fixture +def normal_user(): + try: + user = User.objects.get(username='conf-normal') + except User.DoesNotExist: + user = User(username='conf-normal', is_superuser=False, password='conf-normal') + user.save() + return user + + +@pytest.fixture +def admin(): + try: + user = User.objects.get(username='conf-admin') + except User.DoesNotExist: + user = User(username='conf-admin', is_superuser=True, password='conf-admin') + user.save() + return user + + +@pytest.fixture +def api_request(admin): + def rf(verb, url, data=None, user=admin): + view, view_args, view_kwargs = resolve(urlparse(url)[2]) + request = getattr(APIRequestFactory(), verb)(url, data=data, format='json') + if user: + force_authenticate(request, user=user) + response = view(request, *view_args, **view_kwargs) + response.render() + return response + return rf diff --git a/awx/conf/tests/functional/test_api.py b/awx/conf/tests/functional/test_api.py new file mode 100644 index 0000000000..0e845238f6 --- /dev/null +++ b/awx/conf/tests/functional/test_api.py @@ -0,0 +1,350 @@ +import pytest +import mock + +from rest_framework import serializers + +from awx.api.versioning import reverse +from awx.main.utils.encryption import decrypt_field +from awx.conf import fields +from awx.conf.registry import settings_registry +from awx.conf.models import Setting + + +@pytest.fixture +def dummy_setting(): + class context_manager(object): + def __init__(self, name, **kwargs): + self.name = name + self.kwargs = kwargs + + def __enter__(self): + settings_registry.register(self.name, **(self.kwargs)) + + def __exit__(self, *args): + settings_registry.unregister(self.name) + + return context_manager + + +@pytest.fixture +def dummy_validate(): + class context_manager(object): + def __init__(self, category_slug, func): + self.category_slug = category_slug + self.func = func + + def __enter__(self): + settings_registry.register_validate(self.category_slug, self.func) + + def __exit__(self, *args): + settings_registry.unregister_validate(self.category_slug) + + return context_manager + + +@pytest.mark.django_db +def test_non_admin_user_does_not_see_categories(api_request, dummy_setting, normal_user): + with dummy_setting( + 'FOO_BAR', + field_class=fields.IntegerField, + category='FooBar', + category_slug='foobar' + ): + response = api_request( + 'get', + reverse('api:setting_category_list', + kwargs={'version': 'v2'}) + ) + assert response.data['results'] + response = api_request( + 'get', + reverse('api:setting_category_list', + kwargs={'version': 'v2'}), + user=normal_user + ) + assert not response.data['results'] + + +@pytest.mark.django_db +@mock.patch( + 'awx.conf.views.VERSION_SPECIFIC_CATEGORIES_TO_EXCLUDE', + { + 1: set([]), + 2: set(['foobar']), + } +) +def test_version_specific_category_slug_to_exclude_does_not_show_up(api_request, dummy_setting): + with dummy_setting( + 'FOO_BAR', + field_class=fields.IntegerField, + category='FooBar', + category_slug='foobar' + ): + response = api_request( + 'get', + reverse('api:setting_category_list', + kwargs={'version': 'v2'}) + ) + for item in response.data['results']: + assert item['slug'] != 'foobar' + response = api_request( + 'get', + reverse('api:setting_category_list', + kwargs={'version': 'v1'}) + ) + contains = False + for item in response.data['results']: + if item['slug'] != 'foobar': + contains = True + break + assert contains + + +@pytest.mark.django_db +def test_setting_singleton_detail_retrieve(api_request, dummy_setting): + with dummy_setting( + 'FOO_BAR_1', + field_class=fields.IntegerField, + category='FooBar', + category_slug='foobar' + ), dummy_setting( + 'FOO_BAR_2', + field_class=fields.IntegerField, + category='FooBar', + category_slug='foobar' + ): + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + assert response.status_code == 200 + assert 'FOO_BAR_1' in response.data and response.data['FOO_BAR_1'] is None + assert 'FOO_BAR_2' in response.data and response.data['FOO_BAR_2'] is None + + +@pytest.mark.django_db +def test_setting_singleton_detail_invalid_retrieve(api_request, dummy_setting, normal_user): + with dummy_setting( + 'FOO_BAR_1', + field_class=fields.IntegerField, + category='FooBar', + category_slug='foobar' + ), dummy_setting( + 'FOO_BAR_2', + field_class=fields.IntegerField, + category='FooBar', + category_slug='foobar' + ): + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'barfoo'}) + ) + assert response.status_code == 404 + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}), + user = normal_user + ) + assert response.status_code == 403 + + +@pytest.mark.django_db +def test_setting_signleton_retrieve_hierachy(api_request, dummy_setting): + with dummy_setting( + 'FOO_BAR', + field_class=fields.IntegerField, + default=0, + category='FooBar', + category_slug='foobar' + ): + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + assert response.data['FOO_BAR'] == 0 + s = Setting(key='FOO_BAR', value=1) + s.save() + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + assert response.data['FOO_BAR'] == 1 + + +@pytest.mark.django_db +def test_setting_signleton_retrieve_readonly(api_request, dummy_setting): + with dummy_setting( + 'FOO_BAR', + field_class=fields.IntegerField, + read_only=True, + default=2, + category='FooBar', + category_slug='foobar' + ): + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + assert response.data['FOO_BAR'] == 2 + + +@pytest.mark.django_db +def test_setting_singleton_update(api_request, dummy_setting): + with dummy_setting( + 'FOO_BAR', + field_class=fields.IntegerField, + category='FooBar', + category_slug='foobar' + ), mock.patch('awx.conf.views.handle_setting_changes'): + api_request( + 'patch', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}), + data={'FOO_BAR': 3} + ) + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + assert response.data['FOO_BAR'] == 3 + api_request( + 'patch', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}), + data={'FOO_BAR': 4} + ) + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + assert response.data['FOO_BAR'] == 4 + + +@pytest.mark.django_db +def test_setting_singleton_update_dont_change_readonly_fields(api_request, dummy_setting): + with dummy_setting( + 'FOO_BAR', + field_class=fields.IntegerField, + read_only=True, + default=4, + category='FooBar', + category_slug='foobar' + ), mock.patch('awx.conf.views.handle_setting_changes'): + api_request( + 'patch', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}), + data={'FOO_BAR': 5} + ) + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + assert response.data['FOO_BAR'] == 4 + + +@pytest.mark.django_db +def test_setting_singleton_update_dont_change_encripted_mark(api_request, dummy_setting): + with dummy_setting( + 'FOO_BAR', + field_class=fields.CharField, + encrypted=True, + category='FooBar', + category_slug='foobar' + ), mock.patch('awx.conf.views.handle_setting_changes'): + api_request( + 'patch', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}), + data={'FOO_BAR': 'password'} + ) + assert Setting.objects.get(key='FOO_BAR').value.startswith('$encrypted$') + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + assert response.data['FOO_BAR'] == '$encrypted$' + api_request( + 'patch', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}), + data={'FOO_BAR': '$encrypted$'} + ) + assert decrypt_field(Setting.objects.get(key='FOO_BAR'), 'value') == 'password' + api_request( + 'patch', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}), + data={'FOO_BAR': 'new_pw'} + ) + assert decrypt_field(Setting.objects.get(key='FOO_BAR'), 'value') == 'new_pw' + + +@pytest.mark.django_db +def test_setting_singleton_update_runs_custom_validate(api_request, dummy_setting, dummy_validate): + + def func_raising_exception(serializer, attrs): + raise serializers.ValidationError('Error') + + with dummy_setting( + 'FOO_BAR', + field_class=fields.IntegerField, + category='FooBar', + category_slug='foobar' + ), dummy_validate( + 'foobar', func_raising_exception + ), mock.patch('awx.conf.views.handle_setting_changes'): + response = api_request( + 'patch', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}), + data={'FOO_BAR': 23} + ) + assert response.status_code == 400 + + +@pytest.mark.django_db +def test_setting_singleton_delete(api_request, dummy_setting): + with dummy_setting( + 'FOO_BAR', + field_class=fields.IntegerField, + category='FooBar', + category_slug='foobar' + ), mock.patch('awx.conf.views.handle_setting_changes'): + api_request( + 'delete', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + assert not response.data['FOO_BAR'] + + +@pytest.mark.django_db +def test_setting_singleton_delete_no_read_only_fields(api_request, dummy_setting): + with dummy_setting( + 'FOO_BAR', + field_class=fields.IntegerField, + read_only=True, + default=23, + category='FooBar', + category_slug='foobar' + ), mock.patch('awx.conf.views.handle_setting_changes'): + api_request( + 'delete', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + response = api_request( + 'get', + reverse('api:setting_singleton_detail', kwargs={'category_slug': 'foobar'}) + ) + assert response.data['FOO_BAR'] == 23 + + +@pytest.mark.django_db +def test_setting_logging_test(api_request): + with mock.patch('awx.conf.views.BaseHTTPSHandler.perform_test') as mock_func: + api_request( + 'post', + reverse('api:setting_logging_test'), + data={'LOG_AGGREGATOR_HOST': 'http://foobar', 'LOG_AGGREGATOR_TYPE': 'logstash'} + ) + test_arguments = mock_func.call_args[0][0] + assert test_arguments.LOG_AGGREGATOR_HOST == 'http://foobar' + assert test_arguments.LOG_AGGREGATOR_TYPE == 'logstash' + assert test_arguments.LOG_AGGREGATOR_LEVEL == 'DEBUG' diff --git a/awx/conf/tests/unit/__init__.py b/awx/conf/tests/unit/__init__.py new file mode 100644 index 0000000000..e69de29bb2