diff --git a/example/tests/test_utils.py b/example/tests/test_utils.py deleted file mode 100644 index 4fff3225..00000000 --- a/example/tests/test_utils.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Test rest_framework_json_api's utils functions. -""" -from rest_framework_json_api import utils - -from example.serializers import AuthorSerializer, EntrySerializer -from example.tests import TestBase - - -class GetRelatedResourceTests(TestBase): - """ - Ensure the `get_related_resource_type` function returns correct types. - """ - - def test_reverse_relation(self): - """ - Ensure reverse foreign keys have their types identified correctly. - """ - serializer = EntrySerializer() - field = serializer.fields['comments'] - - self.assertEqual(utils.get_related_resource_type(field), 'comments') - - def test_m2m_relation(self): - """ - Ensure m2ms have their types identified correctly. - """ - serializer = EntrySerializer() - field = serializer.fields['authors'] - - self.assertEqual(utils.get_related_resource_type(field), 'authors') - - def test_m2m_reverse_relation(self): - """ - Ensure reverse m2ms have their types identified correctly. - """ - serializer = AuthorSerializer() - field = serializer.fields['entries'] - - self.assertEqual(utils.get_related_resource_type(field), 'entries') diff --git a/example/tests/unit/test_utils.py b/example/tests/unit/test_utils.py deleted file mode 100644 index f2822f2b..00000000 --- a/example/tests/unit/test_utils.py +++ /dev/null @@ -1,129 +0,0 @@ -import pytest -from django.contrib.auth import get_user_model -from django.test import override_settings -from rest_framework import serializers -from rest_framework.generics import GenericAPIView -from rest_framework.response import Response -from rest_framework.views import APIView - -from rest_framework_json_api import utils -from rest_framework_json_api.utils import get_included_serializers - -from example.serializers import AuthorSerializer, BlogSerializer, CommentSerializer, EntrySerializer - -pytestmark = pytest.mark.django_db - - -class NonModelResourceSerializer(serializers.Serializer): - class Meta: - resource_name = 'users' - - -class ResourceSerializer(serializers.ModelSerializer): - class Meta: - fields = ('username',) - model = get_user_model() - - -def test_get_resource_name(): - view = APIView() - context = {'view': view} - with override_settings(JSON_API_FORMAT_TYPES=None): - assert 'APIViews' == utils.get_resource_name(context), 'not formatted' - - context = {'view': view} - with override_settings(JSON_API_FORMAT_TYPES='dasherize'): - assert 'api-views' == utils.get_resource_name(context), 'derived from view' - - view.model = get_user_model() - assert 'users' == utils.get_resource_name(context), 'derived from view model' - - view.resource_name = 'custom' - assert 'custom' == utils.get_resource_name(context), 'manually set on view' - - view.response = Response(status=403) - assert 'errors' == utils.get_resource_name(context), 'handles 4xx error' - - view.response = Response(status=500) - assert 'errors' == utils.get_resource_name(context), 'handles 500 error' - - view = GenericAPIView() - view.serializer_class = ResourceSerializer - context = {'view': view} - assert 'users' == utils.get_resource_name(context), 'derived from serializer' - - view.serializer_class.Meta.resource_name = 'rcustom' - assert 'rcustom' == utils.get_resource_name(context), 'set on serializer' - - view = GenericAPIView() - view.serializer_class = NonModelResourceSerializer - context = {'view': view} - assert 'users' == utils.get_resource_name(context), 'derived from non-model serializer' - - -@pytest.mark.parametrize("format_type,output", [ - ('camelize', {'fullName': {'last-name': 'a', 'first-name': 'b'}}), - ('capitalize', {'FullName': {'last-name': 'a', 'first-name': 'b'}}), - ('dasherize', {'full-name': {'last-name': 'a', 'first-name': 'b'}}), - ('underscore', {'full_name': {'last-name': 'a', 'first-name': 'b'}}), -]) -def test_format_field_names(settings, format_type, output): - settings.JSON_API_FORMAT_FIELD_NAMES = format_type - - value = {'full_name': {'last-name': 'a', 'first-name': 'b'}} - assert utils.format_field_names(value, format_type) == output - - -def test_format_value(): - assert utils.format_value('first_name', 'camelize') == 'firstName' - assert utils.format_value('first_name', 'capitalize') == 'FirstName' - assert utils.format_value('first_name', 'dasherize') == 'first-name' - assert utils.format_value('first-name', 'underscore') == 'first_name' - - -def test_format_resource_type(): - assert utils.format_resource_type('first_name', 'capitalize') == 'FirstNames' - assert utils.format_resource_type('first_name', 'camelize') == 'firstNames' - - -class SerializerWithIncludedSerializers(EntrySerializer): - included_serializers = { - 'blog': BlogSerializer, - 'authors': 'example.serializers.AuthorSerializer', - 'comments': 'example.serializers.CommentSerializer', - 'self': 'self' # this wouldn't make sense in practice (and would be prohibited by - # IncludedResourcesValidationMixin) but it's useful for the test - } - - -def test_get_included_serializers_against_class(): - klass = SerializerWithIncludedSerializers - included_serializers = get_included_serializers(klass) - expected_included_serializers = { - 'blog': BlogSerializer, - 'authors': AuthorSerializer, - 'comments': CommentSerializer, - 'self': klass - } - assert included_serializers.keys() == klass.included_serializers.keys(), ( - 'the keys must be preserved' - ) - - assert included_serializers == expected_included_serializers - - -def test_get_included_serializers_against_instance(): - klass = SerializerWithIncludedSerializers - instance = klass() - included_serializers = get_included_serializers(instance) - expected_included_serializers = { - 'blog': BlogSerializer, - 'authors': AuthorSerializer, - 'comments': CommentSerializer, - 'self': klass - } - assert included_serializers.keys() == klass.included_serializers.keys(), ( - 'the keys must be preserved' - ) - - assert included_serializers == expected_included_serializers diff --git a/tests/models.py b/tests/models.py new file mode 100644 index 00000000..e91911ce --- /dev/null +++ b/tests/models.py @@ -0,0 +1,37 @@ +from django.db import models + + +class DJAModel(models.Model): + """ + Base for test models that sets app_label, so they play nicely. + """ + + class Meta: + app_label = 'tests' + abstract = True + + +class BasicModel(DJAModel): + text = models.CharField(max_length=100) + + +# Models for relations tests +# ManyToMany +class ManyToManyTarget(DJAModel): + name = models.CharField(max_length=100) + + +class ManyToManySource(DJAModel): + name = models.CharField(max_length=100) + targets = models.ManyToManyField(ManyToManyTarget, related_name='sources') + + +# ForeignKey +class ForeignKeyTarget(DJAModel): + name = models.CharField(max_length=100) + + +class ForeignKeySource(DJAModel): + name = models.CharField(max_length=100) + target = models.ForeignKey(ForeignKeyTarget, related_name='sources', + on_delete=models.CASCADE) diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 00000000..8f272e1a --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,240 @@ +import pytest +from django.db import models +from rest_framework import status +from rest_framework.generics import GenericAPIView +from rest_framework.response import Response +from rest_framework.views import APIView + +from rest_framework_json_api import serializers +from rest_framework_json_api.utils import ( + format_field_names, + format_resource_type, + format_value, + get_included_serializers, + get_related_resource_type, + get_resource_name +) +from tests.models import ( + BasicModel, + DJAModel, + ForeignKeySource, + ForeignKeyTarget, + ManyToManySource, + ManyToManyTarget +) + + +def test_get_resource_name_no_view(): + assert get_resource_name({}) is None + + +@pytest.mark.parametrize("format_type,pluralize_type,output", [ + (False, False, 'APIView'), + (False, True, 'APIViews'), + ('dasherize', False, 'api-view'), + ('dasherize', True, 'api-views'), +]) +def test_get_resource_name_from_view(settings, format_type, pluralize_type, output): + settings.JSON_API_FORMAT_TYPES = format_type + settings.JSON_API_PLURALIZE_TYPES = pluralize_type + + view = APIView() + context = {'view': view} + assert output == get_resource_name(context) + + +@pytest.mark.parametrize("format_type,pluralize_type", [ + (False, False), + (False, True), + ('dasherize', False), + ('dasherize', True), +]) +def test_get_resource_name_from_view_custom_resource_name(settings, format_type, pluralize_type): + settings.JSON_API_FORMAT_TYPES = format_type + settings.JSON_API_PLURALIZE_TYPES = pluralize_type + + view = APIView() + view.resource_name = 'custom' + context = {'view': view} + assert 'custom' == get_resource_name(context) + + +@pytest.mark.parametrize("format_type,pluralize_type,output", [ + (False, False, 'BasicModel'), + (False, True, 'BasicModels'), + ('dasherize', False, 'basic-model'), + ('dasherize', True, 'basic-models'), +]) +def test_get_resource_name_from_model(settings, format_type, pluralize_type, output): + settings.JSON_API_FORMAT_TYPES = format_type + settings.JSON_API_PLURALIZE_TYPES = pluralize_type + + view = APIView() + view.model = BasicModel + context = {'view': view} + assert output == get_resource_name(context) + + +@pytest.mark.parametrize("format_type,pluralize_type,output", [ + (False, False, 'BasicModel'), + (False, True, 'BasicModels'), + ('dasherize', False, 'basic-model'), + ('dasherize', True, 'basic-models'), +]) +def test_get_resource_name_from_model_serializer_class(settings, format_type, + pluralize_type, output): + class BasicModelSerializer(serializers.ModelSerializer): + class Meta: + fields = ('text',) + model = BasicModel + + settings.JSON_API_FORMAT_TYPES = format_type + settings.JSON_API_PLURALIZE_TYPES = pluralize_type + + view = GenericAPIView() + view.serializer_class = BasicModelSerializer + context = {'view': view} + assert output == get_resource_name(context) + + +@pytest.mark.parametrize("format_type,pluralize_type", [ + (False, False), + (False, True), + ('dasherize', False), + ('dasherize', True), +]) +def test_get_resource_name_from_model_serializer_class_custom_resource_name(settings, + format_type, + pluralize_type): + class BasicModelSerializer(serializers.ModelSerializer): + class Meta: + fields = ('text',) + model = BasicModel + + settings.JSON_API_FORMAT_TYPES = format_type + settings.JSON_API_PLURALIZE_TYPES = pluralize_type + + view = GenericAPIView() + view.serializer_class = BasicModelSerializer + view.serializer_class.Meta.resource_name = 'custom' + + context = {'view': view} + assert 'custom' == get_resource_name(context) + + +@pytest.mark.parametrize("format_type,pluralize_type", [ + (False, False), + (False, True), + ('dasherize', False), + ('dasherize', True), +]) +def test_get_resource_name_from_plain_serializer_class(settings, format_type, pluralize_type): + class PlainSerializer(serializers.Serializer): + class Meta: + resource_name = 'custom' + + settings.JSON_API_FORMAT_TYPES = format_type + settings.JSON_API_PLURALIZE_TYPES = pluralize_type + + view = GenericAPIView() + view.serializer_class = PlainSerializer + + context = {'view': view} + assert 'custom' == get_resource_name(context) + + +@pytest.mark.parametrize("status_code", [ + status.HTTP_400_BAD_REQUEST, + status.HTTP_403_FORBIDDEN, + status.HTTP_500_INTERNAL_SERVER_ERROR +]) +def test_get_resource_name_with_errors(status_code): + view = APIView() + context = {'view': view} + view.response = Response(status=status_code) + assert 'errors' == get_resource_name(context) + + +@pytest.mark.parametrize("format_type,output", [ + ('camelize', {'fullName': {'last-name': 'a', 'first-name': 'b'}}), + ('capitalize', {'FullName': {'last-name': 'a', 'first-name': 'b'}}), + ('dasherize', {'full-name': {'last-name': 'a', 'first-name': 'b'}}), + ('underscore', {'full_name': {'last-name': 'a', 'first-name': 'b'}}), +]) +def test_format_field_names(settings, format_type, output): + settings.JSON_API_FORMAT_FIELD_NAMES = format_type + + value = {'full_name': {'last-name': 'a', 'first-name': 'b'}} + assert format_field_names(value, format_type) == output + + +@pytest.mark.parametrize("format_type,output", [ + (None, 'first_name'), + ('camelize', 'firstName'), + ('capitalize', 'FirstName'), + ('dasherize', 'first-name'), + ('underscore', 'first_name') +]) +def test_format_value(settings, format_type, output): + assert format_value('first_name', format_type) == output + + +@pytest.mark.parametrize("resource_type,pluralize,output", [ + (None, None, 'ResourceType'), + ('camelize', False, 'resourceType'), + ('camelize', True, 'resourceTypes'), +]) +def test_format_resource_type(settings, resource_type, pluralize, output): + assert format_resource_type('ResourceType', resource_type, pluralize) == output + + +@pytest.mark.parametrize('model_class,field,output', [ + (ManyToManySource, 'targets', 'ManyToManyTarget'), + (ManyToManyTarget, 'sources', 'ManyToManySource'), + (ForeignKeySource, 'target', 'ForeignKeyTarget'), + (ForeignKeyTarget, 'sources', 'ForeignKeySource'), +]) +def test_get_related_resource_type(model_class, field, output): + class RelatedResourceTypeSerializer(serializers.ModelSerializer): + class Meta: + model = model_class + fields = (field,) + + serializer = RelatedResourceTypeSerializer() + field = serializer.fields[field] + assert get_related_resource_type(field) == output + + +class ManyToManyTargetSerializer(serializers.ModelSerializer): + class Meta: + model = ManyToManyTarget + + +def test_get_included_serializers(): + class IncludedSerializersModel(DJAModel): + self = models.ForeignKey('self', on_delete=models.CASCADE) + target = models.ForeignKey(ManyToManyTarget, on_delete=models.CASCADE) + other_target = models.ForeignKey(ManyToManyTarget, on_delete=models.CASCADE) + + class Meta: + app_label = 'tests' + + class IncludedSerializersSerializer(serializers.ModelSerializer): + included_serializers = { + 'self': 'self', + 'target': ManyToManyTargetSerializer, + 'other_target': 'tests.test_utils.ManyToManyTargetSerializer' + } + + class Meta: + model = IncludedSerializersModel + fields = ('self', 'other_target', 'target') + + included_serializers = get_included_serializers(IncludedSerializersSerializer) + expected_included_serializers = { + 'self': IncludedSerializersSerializer, + 'target': ManyToManyTargetSerializer, + 'other_target': ManyToManyTargetSerializer + } + + assert included_serializers == expected_included_serializers