1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 08:21:15 +03:00

Merge branch 'test-labels' into devel

This commit is contained in:
Chris Meyers 2016-04-01 13:36:41 -04:00
commit ff5935ccfd
5 changed files with 307 additions and 12 deletions

View File

@ -1565,6 +1565,14 @@ class JobOptionsSerializer(BaseSerializer):
args=(obj.cloud_credential.pk,)) args=(obj.cloud_credential.pk,))
return res return res
def _summary_field_labels(self, obj):
return [{'id': x.id, 'name': x.name} for x in obj.labels.all().order_by('-name')[:10]]
def get_summary_fields(self, obj):
res = super(JobOptionsSerializer, self).get_summary_fields(obj)
res['labels'] = self._summary_field_labels(obj)
return res
def to_representation(self, obj): def to_representation(self, obj):
ret = super(JobOptionsSerializer, self).to_representation(obj) ret = super(JobOptionsSerializer, self).to_representation(obj)
if obj is None: if obj is None:
@ -1622,6 +1630,9 @@ class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer):
res['callback'] = reverse('api:job_template_callback', args=(obj.pk,)) res['callback'] = reverse('api:job_template_callback', args=(obj.pk,))
return res return res
def _recent_jobs(self, obj):
return [{'id': x.id, 'status': x.status, 'finished': x.finished} for x in obj.jobs.filter(active=True).order_by('-created')[:10]]
def get_summary_fields(self, obj): def get_summary_fields(self, obj):
d = super(JobTemplateSerializer, self).get_summary_fields(obj) d = super(JobTemplateSerializer, self).get_summary_fields(obj)
if obj.survey_spec is not None and ('name' in obj.survey_spec and 'description' in obj.survey_spec): if obj.survey_spec is not None and ('name' in obj.survey_spec and 'description' in obj.survey_spec):
@ -1640,8 +1651,7 @@ class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer):
else: else:
d['can_copy'] = False d['can_copy'] = False
d['can_edit'] = False d['can_edit'] = False
d['recent_jobs'] = [{'id': x.id, 'status': x.status, 'finished': x.finished} for x in obj.jobs.order_by('-created')[:10]] d['recent_jobs'] = self._recent_jobs(obj)
d['labels'] = [{'id': x.id, 'name': x.name} for x in obj.labels.all().order_by('-name')[:10]]
return d return d
def validate(self, attrs): def validate(self, attrs):
@ -1683,11 +1693,6 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer):
res['relaunch'] = reverse('api:job_relaunch', args=(obj.pk,)) res['relaunch'] = reverse('api:job_relaunch', args=(obj.pk,))
return res return res
def get_summary_fields(self, obj):
d = super(JobSerializer, self).get_summary_fields(obj)
d['labels'] = [{'id': x.id, 'name': x.name} for x in obj.labels.all().order_by('-name')[:10]]
return d
def to_internal_value(self, data): def to_internal_value(self, data):
# When creating a new job and a job template is specified, populate any # When creating a new job and a job template is specified, populate any
# fields not provided in data from the job template. # fields not provided in data from the job template.

View File

@ -400,14 +400,16 @@ def fact_services_json():
def permission_inv_read(organization, inventory, team): def permission_inv_read(organization, inventory, team):
return Permission.objects.create(inventory=inventory, team=team, permission_type=PERM_INVENTORY_READ) return Permission.objects.create(inventory=inventory, team=team, permission_type=PERM_INVENTORY_READ)
@pytest.fixture @pytest.fixture
def job_template_labels(organization): def job_template(organization):
jt = JobTemplate(name='test-job_template') jt = JobTemplate(name='test-job_template')
jt.save() jt.save()
jt.labels.create(name="label-1", organization=organization)
jt.labels.create(name="label-2", organization=organization)
return jt return jt
@pytest.fixture
def job_template_labels(organization, job_template):
job_template.labels.create(name="label-1", organization=organization)
job_template.labels.create(name="label-2", organization=organization)
return job_template

View File

@ -0,0 +1,80 @@
# Python
import pytest
# DRF
from rest_framework import status
from rest_framework.response import Response
# AWX
from awx.api.generics import ParentMixin, SubListCreateAttachDetachAPIView
@pytest.fixture
def get_object_or_404(mocker):
# pytest patch without return_value generates a random value, we are counting on this
return mocker.patch('awx.api.generics.get_object_or_404')
@pytest.fixture
def get_object_or_400(mocker):
return mocker.patch('awx.api.generics.get_object_or_400')
@pytest.fixture
def mock_response_new(mocker):
m = mocker.patch('awx.api.generics.Response.__new__')
m.return_value = m
return m
@pytest.fixture
def parent_relationship_factory(mocker):
def rf(serializer_class, relationship_name, relationship_value=mocker.Mock()):
mock_parent_relationship = mocker.MagicMock(**{'%s.add.return_value' % relationship_name: relationship_value})
mocker.patch('awx.api.generics.ParentMixin.get_parent_object', return_value=mock_parent_relationship)
serializer = serializer_class()
[setattr(serializer, x, '') for x in ['relationship', 'model', 'parent_model']]
serializer.relationship = relationship_name
return (serializer, mock_parent_relationship)
return rf
# TODO: Test create and associate failure (i.e. id doesn't exist or record already exists)
# TODO: Mock and check return (Response)
class TestSubListCreateAttachDetachAPIView:
def test_attach_create_and_associate(self, mocker, get_object_or_400, parent_relationship_factory, mock_response_new):
(serializer, mock_parent_relationship) = parent_relationship_factory(SubListCreateAttachDetachAPIView, 'wife')
create_return_value = mocker.MagicMock(status_code=status.HTTP_201_CREATED)
serializer.create = mocker.Mock(return_value=create_return_value)
mock_request = mocker.MagicMock(data=dict())
ret = serializer.attach(mock_request, None, None)
assert ret == mock_response_new
serializer.create.assert_called_with(mock_request, None, None)
mock_parent_relationship.wife.add.assert_called_with(get_object_or_400.return_value)
mock_response_new.assert_called_with(Response, create_return_value.data, status=status.HTTP_201_CREATED, headers={'Location': create_return_value['Location']})
def test_attach_associate_only(self, mocker, get_object_or_400, parent_relationship_factory, mock_response_new):
(serializer, mock_parent_relationship) = parent_relationship_factory(SubListCreateAttachDetachAPIView, 'wife')
serializer.create = mocker.Mock(return_value=mocker.MagicMock())
mock_request = mocker.MagicMock(data=dict(id=1))
ret = serializer.attach(mock_request, None, None)
assert ret == mock_response_new
serializer.create.assert_not_called()
mock_parent_relationship.wife.add.assert_called_with(get_object_or_400.return_value)
mock_response_new.assert_called_with(Response, status=status.HTTP_204_NO_CONTENT)
class TestParentMixin:
def test_get_parent_object(self, mocker, get_object_or_404):
parent_mixin = ParentMixin()
parent_mixin.lookup_field = 'foo'
parent_mixin.kwargs = dict(foo='bar')
parent_mixin.parent_model = 'parent_model'
mock_parent_mixin = mocker.MagicMock(wraps=parent_mixin)
return_value = mock_parent_mixin.get_parent_object()
get_object_or_404.assert_called_with(parent_mixin.parent_model, **parent_mixin.kwargs)
assert get_object_or_404.return_value == return_value

View File

@ -0,0 +1,156 @@
# Python
import pytest
import mock
# AWX
from awx.api.serializers import JobTemplateSerializer, JobSerializer, JobOptionsSerializer
from awx.main.models import Label, Job
@pytest.fixture
def job_template(mocker):
return mocker.MagicMock(pk=5)
@pytest.fixture
def job(mocker, job_template):
return mocker.MagicMock(pk=5, job_template=job_template)
@pytest.fixture
def labels(mocker):
return [Label(id=x, name='label-%d' % x) for x in xrange(0, 25)]
@pytest.fixture
def jobs(mocker):
return [Job(id=x, name='job-%d' % x) for x in xrange(0, 25)]
class GetRelatedMixin:
def _assert(self, model_obj, related, resource_name, related_resource_name):
assert related_resource_name in related
assert related[related_resource_name] == '/api/v1/%s/%d/%s/' % (resource_name, model_obj.pk, related_resource_name)
def _mock_and_run(self, serializer_class, model_obj):
serializer = serializer_class()
related = serializer.get_related(model_obj)
return related
def _test_get_related(self, serializer_class, model_obj, resource_name, related_resource_name):
related = self._mock_and_run(serializer_class, model_obj)
self._assert(model_obj, related, resource_name, related_resource_name)
return related
class GetSummaryFieldsMixin:
def _assert(self, summary, summary_field_name):
assert summary_field_name in summary
def _mock_and_run(self, serializer_class, model_obj):
serializer = serializer_class()
return serializer.get_summary_fields(model_obj)
def _test_get_summary_fields(self, serializer_class, model_obj, summary_field_name):
summary = self._mock_and_run(serializer_class, model_obj)
self._assert(summary, summary_field_name)
return summary
@mock.patch('awx.api.serializers.UnifiedJobTemplateSerializer.get_related', lambda x,y: {})
@mock.patch('awx.api.serializers.JobOptionsSerializer.get_related', lambda x,y: {})
class TestJobTemplateSerializerGetRelated(GetRelatedMixin):
@pytest.mark.parametrize("related_resource_name", [
'jobs',
'schedules',
'activity_stream',
'launch',
'notifiers_any',
'notifiers_success',
'notifiers_error',
'survey_spec',
'labels',
'callback',
])
def test_get_related(self, job_template, related_resource_name):
self._test_get_related(JobTemplateSerializer, job_template, 'job_templates', related_resource_name)
def test_callback_absent(self, job_template):
job_template.host_config_key = None
related = self._mock_and_run(JobTemplateSerializer, job_template)
assert 'callback' not in related
class TestJobTemplateSerializerGetSummaryFields(GetSummaryFieldsMixin):
def test__recent_jobs(self, mocker, job_template, jobs):
job_template.jobs.filter = mocker.MagicMock(**{'order_by.return_value': jobs})
job_template.jobs.filter.return_value = job_template.jobs.filter
serializer = JobTemplateSerializer()
recent_jobs = serializer._recent_jobs(job_template)
job_template.jobs.filter.assert_called_with(active=True)
job_template.jobs.filter.order_by.assert_called_with('-created')
assert len(recent_jobs) == 10
for x in jobs[:10]:
assert recent_jobs == [{'id': x.id, 'status': x.status, 'finished': x.finished} for x in jobs[:10]]
def test_survey_spec_exists(self, mocker, job_template):
job_template.survey_spec = {'name': 'blah', 'description': 'blah blah'}
self._test_get_summary_fields(JobTemplateSerializer, job_template, 'survey')
def test_survey_spec_absent(self, mocker, job_template):
job_template.survey_spec = None
summary = self._mock_and_run(JobTemplateSerializer, job_template)
assert 'survey' not in summary
@pytest.mark.skip(reason="RBAC needs to land")
def test_can_copy_true(self, mocker, job_template):
pass
@pytest.mark.skip(reason="RBAC needs to land")
def test_can_copy_false(self, mocker, job_template):
pass
@pytest.mark.skip(reason="RBAC needs to land")
def test_can_edit_true(self, mocker, job_template):
pass
@pytest.mark.skip(reason="RBAC needs to land")
def test_can_edit_false(self, mocker, job_template):
pass
@mock.patch('awx.api.serializers.UnifiedJobTemplateSerializer.get_related', lambda x,y: {})
@mock.patch('awx.api.serializers.JobOptionsSerializer.get_related', lambda x,y: {})
class TestJobSerializerGetRelated(GetRelatedMixin):
@pytest.mark.parametrize("related_resource_name", [
'job_events',
'job_plays',
'job_tasks',
'relaunch',
'labels',
])
def test_get_related(self, mocker, job, related_resource_name):
self._test_get_related(JobSerializer, job, 'jobs', related_resource_name)
def test_job_template_present(self, job):
job.job_template.active = True
serializer = JobSerializer()
related = serializer.get_related(job)
assert 'job_template' in related
def test_job_template_absent(self, job):
job.job_template.active = False
serializer = JobSerializer()
related = serializer.get_related(job)
assert 'job_template' not in related
@mock.patch('awx.api.serializers.BaseSerializer.get_summary_fields', lambda x,y: {})
class TestJobOptionsSerializerGetSummaryFields(GetSummaryFieldsMixin):
def test__summary_field_labels_10_max(self, mocker, job_template, labels):
job_template.labels.all = mocker.MagicMock(**{'order_by.return_value': labels})
job_template.labels.all.return_value = job_template.labels.all
serializer = JobOptionsSerializer()
summary_labels = serializer._summary_field_labels(job_template)
job_template.labels.all.order_by.assert_called_with('-name')
assert len(summary_labels) == 10
assert summary_labels == [{'id': x.id, 'name': x.name} for x in labels[:10]]
def test_labels_exists(self, mocker, job_template):
self._test_get_summary_fields(JobOptionsSerializer, job_template, 'labels')

View File

@ -0,0 +1,52 @@
# Python
import pytest
# AWX
from awx.api.views import ApiV1RootView
@pytest.fixture
def mock_response_new(mocker):
m = mocker.patch('awx.api.views.Response.__new__')
m.return_value = m
return m
class TestApiV1RootView:
def test_get_endpoints(self, mocker, mock_response_new):
endpoints = [
'authtoken',
'ping',
'config',
'settings',
'me',
'dashboard',
'organizations',
'users',
'projects',
'teams',
'credentials',
'inventory',
'inventory_scripts',
'inventory_sources',
'groups',
'hosts',
'job_templates',
'jobs',
'ad_hoc_commands',
'system_job_templates',
'system_jobs',
'schedules',
'notifiers',
'notifications',
'labels',
'unified_job_templates',
'unified_jobs',
'activity_stream',
]
view = ApiV1RootView()
ret = view.get(mocker.MagicMock())
assert ret == mock_response_new
data_arg = mock_response_new.mock_calls[0][1][1]
for endpoint in endpoints:
assert endpoint in data_arg