mirror of
https://github.com/ZwareBear/awx.git
synced 2026-05-15 00:58:37 -05:00
Merge branch 'devel' into merge-devel
This commit is contained in:
@@ -6,7 +6,7 @@ import json
|
||||
|
||||
# Django
|
||||
from django.db import models
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
# Tower
|
||||
@@ -34,7 +34,9 @@ class TowerSettings(CreatedModifiedModel):
|
||||
)
|
||||
description = models.TextField()
|
||||
category = models.CharField(max_length=128)
|
||||
value = models.TextField()
|
||||
value = models.TextField(
|
||||
blank=True,
|
||||
)
|
||||
value_type = models.CharField(
|
||||
max_length=12,
|
||||
choices=SETTINGS_TYPE_CHOICES
|
||||
@@ -54,9 +56,12 @@ class TowerSettings(CreatedModifiedModel):
|
||||
elif self.value_type == 'password':
|
||||
converted_type = self.value
|
||||
elif self.value_type == 'list':
|
||||
converted_type = [x.strip() for x in self.value.split(',')]
|
||||
if self.value:
|
||||
converted_type = [x.strip() for x in self.value.split(',')]
|
||||
else:
|
||||
converted_type = []
|
||||
elif self.value_type == 'bool':
|
||||
converted_type = smart_text(self.value).lower() in ('true', 'yes', '1')
|
||||
converted_type = force_text(self.value).lower() in ('true', 'yes', '1')
|
||||
elif self.value_type == 'string':
|
||||
converted_type = self.value
|
||||
else:
|
||||
@@ -69,8 +74,11 @@ class TowerSettings(CreatedModifiedModel):
|
||||
if self.value_type == 'json':
|
||||
self.value = json.dumps(value)
|
||||
elif self.value_type == 'list':
|
||||
self.value = ','.join(value)
|
||||
try:
|
||||
self.value = ','.join(map(force_text, value))
|
||||
except TypeError:
|
||||
self.value = force_text(value)
|
||||
elif self.value_type == 'bool':
|
||||
self.value = smart_text(bool(value))
|
||||
self.value = force_text(bool(value))
|
||||
else:
|
||||
self.value = smart_text(value)
|
||||
self.value = force_text(value)
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from rest_framework.test import (
|
||||
APIRequestFactory,
|
||||
force_authenticate,
|
||||
)
|
||||
|
||||
from awx.main.models.credential import Credential
|
||||
from awx.main.models.projects import Project
|
||||
from awx.main.models.jobs import JobTemplate
|
||||
from awx.main.models.ha import Instance
|
||||
from awx.main.models.inventory import (
|
||||
Inventory,
|
||||
Group,
|
||||
)
|
||||
from awx.main.models.projects import Project
|
||||
from awx.main.models.jobs import JobTemplate
|
||||
from awx.main.models.organization import (
|
||||
Organization,
|
||||
Team,
|
||||
)
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
@pytest.fixture
|
||||
def user():
|
||||
@@ -62,7 +69,11 @@ def user_project(user):
|
||||
return Project.objects.create(name="test-user-project", created_by=owner, description="test-user-project-desc")
|
||||
|
||||
@pytest.fixture
|
||||
def organization():
|
||||
def instance(settings):
|
||||
return Instance.objects.create(uuid=settings.SYSTEM_UUID, primary=True, hostname="instance.example.org")
|
||||
|
||||
@pytest.fixture
|
||||
def organization(instance):
|
||||
return Organization.objects.create(name="test-org", description="test-org-desc")
|
||||
|
||||
@pytest.fixture
|
||||
@@ -92,3 +103,30 @@ def permissions():
|
||||
'update':False, 'delete':False, 'scm_update':False, 'execute':False, 'use':True,},
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def post():
|
||||
def rf(_cls, _user, _url, pk=None, kwargs={}, middleware=None):
|
||||
view = _cls.as_view()
|
||||
request = APIRequestFactory().post(_url, kwargs, format='json')
|
||||
if middleware:
|
||||
middleware.process_request(request)
|
||||
force_authenticate(request, user=_user)
|
||||
response = view(request, pk=pk)
|
||||
if middleware:
|
||||
middleware.process_response(request, response)
|
||||
return response
|
||||
return rf
|
||||
|
||||
@pytest.fixture
|
||||
def get():
|
||||
def rf(_cls, _user, _url, pk=None, middleware=None):
|
||||
view = _cls.as_view()
|
||||
request = APIRequestFactory().get(_url, format='json')
|
||||
if middleware:
|
||||
middleware.process_request(request)
|
||||
force_authenticate(request, user=_user)
|
||||
response = view(request, pk=pk)
|
||||
if middleware:
|
||||
middleware.process_response(request, response)
|
||||
return response
|
||||
return rf
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
from awx.api.views import (
|
||||
ActivityStreamList,
|
||||
ActivityStreamDetail,
|
||||
OrganizationList,
|
||||
)
|
||||
from awx.main.middleware import ActivityStreamMiddleware
|
||||
from awx.main.models.activity_stream import ActivityStream
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
def mock_feature_enabled(feature, bypass_database=None):
|
||||
return True
|
||||
|
||||
@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
|
||||
@pytest.mark.django_db
|
||||
def test_get_activity_stream_list(monkeypatch, organization, get, user):
|
||||
url = reverse('api:activity_stream_list')
|
||||
response = get(ActivityStreamList, user('admin', True), url)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
|
||||
@pytest.mark.django_db
|
||||
def test_basic_fields(monkeypatch, organization, get, user):
|
||||
u = user('admin', True)
|
||||
activity_stream = ActivityStream.objects.latest('pk')
|
||||
activity_stream.actor = u
|
||||
activity_stream.save()
|
||||
|
||||
aspk = activity_stream.pk
|
||||
url = reverse('api:activity_stream_detail', args=(aspk,))
|
||||
response = get(ActivityStreamDetail, user('admin', True), url, pk=aspk)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert 'related' in response.data
|
||||
assert 'organization' in response.data['related']
|
||||
assert 'summary_fields' in response.data
|
||||
assert 'organization' in response.data['summary_fields']
|
||||
assert response.data['summary_fields']['organization'][0]['name'] == 'test-org'
|
||||
|
||||
@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
|
||||
@pytest.mark.django_db
|
||||
def test_middleware_actor_added(monkeypatch, post, get, user):
|
||||
u = user('admin-poster', True)
|
||||
|
||||
url = reverse('api:organization_list')
|
||||
response = post(OrganizationList, u, url,
|
||||
kwargs=dict(name='test-org', description='test-desc'),
|
||||
middleware=ActivityStreamMiddleware())
|
||||
assert response.status_code == 201
|
||||
|
||||
org_id = response.data['id']
|
||||
activity_stream = ActivityStream.objects.filter(organization__pk=org_id).first()
|
||||
|
||||
url = reverse('api:activity_stream_detail', args=(activity_stream.pk,))
|
||||
response = get(ActivityStreamDetail, u, url, pk=activity_stream.pk)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.data['summary_fields']['actor']['username'] == 'admin-poster'
|
||||
@@ -1,55 +0,0 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
from awx.main.tests.base import BaseTest
|
||||
|
||||
class ActivityStreamTest(BaseTest):
|
||||
|
||||
def collection(self):
|
||||
return reverse('api:activity_stream_list')
|
||||
|
||||
def item(self, item_id):
|
||||
return reverse('api:activity_stream_detail', args=(item_id,))
|
||||
|
||||
def setUp(self):
|
||||
super(ActivityStreamTest, self).setUp()
|
||||
self.setup_instances()
|
||||
self.create_test_license_file()
|
||||
# TODO: Test non-enterprise license
|
||||
self.setup_users()
|
||||
self.org_created = self.post(reverse('api:organization_list'), dict(name='test org', description='test descr'), expect=201, auth=self.get_super_credentials())
|
||||
|
||||
def test_get_activity_stream_list(self):
|
||||
url = self.collection()
|
||||
|
||||
with self.current_user(self.super_django_user):
|
||||
self.options(url, expect=200)
|
||||
self.head(url, expect=200)
|
||||
response = self.get(url, expect=200)
|
||||
self.check_pagination_and_size(response, 1, previous=None, next=None)
|
||||
|
||||
def test_basic_fields(self):
|
||||
item_id = ActivityStream.objects.order_by('pk')[0].pk
|
||||
org_item = self.item(item_id)
|
||||
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.get(org_item, expect=200)
|
||||
|
||||
self.assertTrue("related" in response)
|
||||
self.assertTrue("organization" in response['related'])
|
||||
self.assertTrue("summary_fields" in response)
|
||||
self.assertTrue("organization" in response['summary_fields'])
|
||||
self.assertTrue(response['summary_fields']['organization'][0]['name'] == self.org_created['name'])
|
||||
|
||||
def test_changeby_user(self):
|
||||
item_id = ActivityStream.objects.order_by('pk')[0].pk
|
||||
org_item = self.item(item_id)
|
||||
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.get(org_item, expect=200)
|
||||
self.assertEqual(response['summary_fields']['actor']['username'], self.super_django_user.username)
|
||||
@@ -262,7 +262,9 @@ class OrganizationsTest(BaseTest):
|
||||
data1 = self.post(self.collection(), new_org, expect=201, auth=self.get_super_credentials())
|
||||
|
||||
# duplicate post results in 400
|
||||
self.post(self.collection(), new_org, expect=400, auth=self.get_super_credentials())
|
||||
response = self.post(self.collection(), new_org, expect=400, auth=self.get_super_credentials())
|
||||
self.assertTrue('name' in response, response)
|
||||
self.assertTrue('Name' in response['name'][0], response)
|
||||
|
||||
# look at what we got back from the post, make sure we added an org
|
||||
last_org = Organization.objects.order_by('-pk')[0]
|
||||
|
||||
@@ -231,12 +231,19 @@ class ProjectsTest(BaseTransactionTest):
|
||||
'description': 'Does amazing things',
|
||||
'local_path': os.path.basename(project_dir),
|
||||
'scm_type': None,
|
||||
'scm_update_on_launch': '',
|
||||
'scm_delete_on_update': None,
|
||||
'scm_clean': False,
|
||||
}
|
||||
# Adding a project with scm_type=None should work, but scm_type will be
|
||||
# changed to an empty string.
|
||||
# changed to an empty string. Other boolean fields should accept null
|
||||
# or an empty string for False, but save the value as a boolean.
|
||||
response = self.post(projects, project_data, expect=201,
|
||||
auth=self.get_super_credentials())
|
||||
self.assertEqual(response['scm_type'], u'')
|
||||
self.assertEqual(response['scm_update_on_launch'], False)
|
||||
self.assertEqual(response['scm_delete_on_update'], False)
|
||||
self.assertEqual(response['scm_clean'], False)
|
||||
|
||||
# can edit project using same local path.
|
||||
project_detail = reverse('api:project_detail', args=(response['id'],))
|
||||
@@ -494,7 +501,9 @@ class ProjectsTest(BaseTransactionTest):
|
||||
ssh_key_data = TEST_SSH_KEY_DATA_LOCKED,
|
||||
ssh_key_unlock = TEST_SSH_KEY_DATA_UNLOCK,
|
||||
ssh_password = 'narf',
|
||||
sudo_password = 'troz'
|
||||
sudo_password = 'troz',
|
||||
security_token = '',
|
||||
vault_password = None,
|
||||
)
|
||||
|
||||
# can add credentials to a user (if user or org admin or super user)
|
||||
@@ -561,13 +570,17 @@ class ProjectsTest(BaseTransactionTest):
|
||||
# Repeating the same POST should violate a unique constraint.
|
||||
with self.current_user(self.super_django_user):
|
||||
data = dict(name='xyz', user=self.super_django_user.pk)
|
||||
self.post(url, data, expect=400)
|
||||
response = self.post(url, data, expect=400)
|
||||
self.assertTrue('__all__' in response, response)
|
||||
self.assertTrue('already exists' in response['__all__'][0], response)
|
||||
|
||||
# Test with null where we expect a string value.
|
||||
# Test with null where we expect a string value. Value will be coerced
|
||||
# to an empty string.
|
||||
with self.current_user(self.super_django_user):
|
||||
data = dict(name='zyx', user=self.super_django_user.pk, kind='ssh',
|
||||
become_username=None)
|
||||
self.post(url, data, expect=400)
|
||||
response = self.post(url, data, expect=201)
|
||||
self.assertEqual(response['become_username'], '')
|
||||
|
||||
# Test with encrypted ssh key and no unlock password.
|
||||
with self.current_user(self.super_django_user):
|
||||
@@ -698,14 +711,15 @@ class ProjectsTest(BaseTransactionTest):
|
||||
# user=user.pk, # no need to specify, this will be automatically filled in
|
||||
inventory=inventory.pk,
|
||||
project=project.pk,
|
||||
permission_type=PERM_INVENTORY_DEPLOY
|
||||
permission_type=PERM_INVENTORY_DEPLOY,
|
||||
run_ad_hoc_commands=None,
|
||||
)
|
||||
team_permission = dict(
|
||||
name='team can deploy a certain project to a certain inventory',
|
||||
# team=team.pk, # no need to specify, this will be automatically filled in
|
||||
inventory=inventory.pk,
|
||||
project=project.pk,
|
||||
permission_type=PERM_INVENTORY_DEPLOY
|
||||
permission_type=PERM_INVENTORY_DEPLOY,
|
||||
)
|
||||
|
||||
url = reverse('api:user_permissions_list', args=(user.pk,))
|
||||
|
||||
@@ -35,6 +35,13 @@ TEST_TOWER_SETTINGS_MANIFEST = {
|
||||
"default": ["A", "Simple", "List"],
|
||||
"type": "list",
|
||||
"category": "test"
|
||||
},
|
||||
"TEST_SETTING_JSON": {
|
||||
"name": "A JSON Field",
|
||||
"description": "A JSON Field",
|
||||
"default": {"key": "value", "otherkey": ["list", "of", "things"]},
|
||||
"type": "json",
|
||||
"category": "test"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +53,7 @@ class SettingsTest(BaseTest):
|
||||
self.setup_instances()
|
||||
self.setup_users()
|
||||
|
||||
def get_settings(self, expected_count=4):
|
||||
def get_settings(self, expected_count=5):
|
||||
result = self.get(reverse('api:settings_list'), expect=200)
|
||||
self.assertEqual(result['count'], expected_count)
|
||||
return result['results']
|
||||
@@ -74,21 +81,29 @@ class SettingsTest(BaseTest):
|
||||
with self.current_user(self.super_django_user):
|
||||
self.get_settings(expected_count=len(TEST_TOWER_SETTINGS_MANIFEST))
|
||||
|
||||
def test_set_and_reset_settings(self):
|
||||
def set_and_reset_setting(self, key, values, expected_values=()):
|
||||
settings_reset = reverse('api:settings_reset')
|
||||
setting = self.get_individual_setting(key)
|
||||
self.assertEqual(setting['value'], TEST_TOWER_SETTINGS_MANIFEST[key]['default'])
|
||||
for n, value in enumerate(values):
|
||||
self.set_setting(key, value)
|
||||
setting = self.get_individual_setting(key)
|
||||
if len(expected_values) > n:
|
||||
self.assertEqual(setting['value'], expected_values[n])
|
||||
else:
|
||||
self.assertEqual(setting['value'], value)
|
||||
self.post(settings_reset, data={"key": key}, expect=204)
|
||||
setting = self.get_individual_setting(key)
|
||||
self.assertEqual(setting['value'], TEST_TOWER_SETTINGS_MANIFEST[key]['default'])
|
||||
|
||||
def test_set_and_reset_settings(self):
|
||||
with self.current_user(self.super_django_user):
|
||||
# Set and reset a single setting
|
||||
setting_int = self.get_individual_setting('TEST_SETTING_INT')
|
||||
self.assertEqual(setting_int['value'], TEST_TOWER_SETTINGS_MANIFEST['TEST_SETTING_INT']['default'])
|
||||
self.set_setting('TEST_SETTING_INT', 2)
|
||||
setting_int = self.get_individual_setting('TEST_SETTING_INT')
|
||||
self.assertEqual(setting_int['value'], 2)
|
||||
self.set_setting('TEST_SETTING_INT', 3)
|
||||
setting_int = self.get_individual_setting('TEST_SETTING_INT')
|
||||
self.assertEqual(setting_int['value'], 3)
|
||||
self.post(settings_reset, data={"key": 'TEST_SETTING_INT'}, expect=204)
|
||||
setting_int = self.get_individual_setting('TEST_SETTING_INT')
|
||||
self.assertEqual(setting_int['value'], TEST_TOWER_SETTINGS_MANIFEST['TEST_SETTING_INT']['default'])
|
||||
self.set_and_reset_setting('TEST_SETTING_INT', (2, 0))
|
||||
self.set_and_reset_setting('TEST_SETTING_STRING', ('blah', '', u'\u2620'))
|
||||
self.set_and_reset_setting('TEST_SETTING_BOOL', (True, False))
|
||||
# List values are always saved as strings.
|
||||
self.set_and_reset_setting('TEST_SETTING_LIST', ([4, 5, 6], [], [2]), (['4', '5', '6'], [], ['2']))
|
||||
self.set_and_reset_setting('TEST_SETTING_JSON', ({"k": "v"}, {}, [], [7, 8], 'str'))
|
||||
|
||||
def test_clear_all_settings(self):
|
||||
settings_list = reverse('api:settings_list')
|
||||
@@ -97,6 +112,7 @@ class SettingsTest(BaseTest):
|
||||
self.set_setting('TEST_SETTING_STRING', "foo")
|
||||
self.set_setting('TEST_SETTING_BOOL', False)
|
||||
self.set_setting('TEST_SETTING_LIST', [1,2,3])
|
||||
self.set_setting('TEST_SETTING_JSON', '{"key": "new value"}')
|
||||
all_settings = self.get_settings()
|
||||
for setting_entry in all_settings:
|
||||
self.assertNotEqual(setting_entry['value'],
|
||||
|
||||
@@ -207,6 +207,8 @@ class UsersTest(BaseTest):
|
||||
self.post(url, expect=403, data=new_super_user, auth=self.get_other_credentials())
|
||||
self.post(url, expect=403, data=new_super_user, auth=self.get_normal_credentials())
|
||||
self.post(url, expect=201, data=new_super_user, auth=self.get_super_credentials())
|
||||
new_super_user2 = dict(username='nommy2', password='cookie', is_superuser=None)
|
||||
self.post(url, expect=201, data=new_super_user2, auth=self.get_super_credentials())
|
||||
|
||||
def test_auth_token_login(self):
|
||||
auth_token_url = reverse('api:auth_token_view')
|
||||
|
||||
Reference in New Issue
Block a user