Ejemplo n.º 1
0
def test_mocked_request_with_get_queryset_get_serializer_class(no_warnings):
    class M4(models.Model):
        pass

    class XSerializer(serializers.ModelSerializer):
        class Meta:
            fields = '__all__'
            model = M4

    class XViewset(viewsets.ReadOnlyModelViewSet):
        def get_serializer_class(self):
            assert not self.request.user.is_authenticated
            assert self.action in ['retrieve', 'list']
            return XSerializer

        def get_queryset(self):
            assert not self.request.user.is_authenticated
            assert self.request.method == 'GET'
            return M4.objects.none()

    generate_schema('x', XViewset)
Ejemplo n.º 2
0
def test_manual_security_method_addition(no_warnings):
    @extend_schema(responses=OpenApiTypes.FLOAT)
    @api_view(['GET'])
    def view_func(request, format=None):
        pass  # pragma: no cover

    schema = generate_schema('/x/', view_function=view_func)
    operation_security = schema['paths']['/x/']['get']['security']
    schema_security = schema['components']['securitySchemes']
    assert len(operation_security) == 4 and any(
        ['apiKeyAuth' in os for os in operation_security])
    assert len(schema_security) == 3 and 'apiKeyAuth' in schema_security
Ejemplo n.º 3
0
def test_extend_schema_no_req_no_res(no_warnings):
    class XAPIView(APIView):
        @extend_schema(request=None, responses=None)
        def post(self, request):
            pass  # pragma: no cover

    schema = generate_schema('/x', view=XAPIView)
    validate_schema(schema)
    operation = schema['paths']['/x']['post']
    assert 'requestBody' not in operation
    assert len(operation['responses']['200']) == 1
    assert 'description' in operation['responses']['200']
def test_token_auth_with_bearer_keyword(no_warnings):
    class CustomTokenAuthentication(TokenAuthentication):
        keyword = 'Bearer'

    @extend_schema(responses=OpenApiTypes.FLOAT)
    @api_view(['GET'])
    def view_func(request, format=None):
        pass  # pragma: no cover
    view_func.cls.authentication_classes = [CustomTokenAuthentication]

    schema = generate_schema('x', view_function=view_func)
    assert schema['components']['securitySchemes']['tokenAuth']['scheme'] == 'bearer'
Ejemplo n.º 5
0
def test_simplejwt(no_warnings):
    router = routers.SimpleRouter()
    router.register('x', XViewset, basename="x")

    urlpatterns = [
        *router.urls,
        path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
        path('token-sliding/', TokenObtainSlidingView.as_view(), name='token_obtain_sliding'),
        path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    ]

    schema = generate_schema(None, patterns=urlpatterns)
    assert_schema(schema, 'tests/contrib/test_simplejwt.yml')
Ejemplo n.º 6
0
def test_unable_to_follow_field_source_through_intermediate_property_warning(
        capsys):
    class FailingFieldSourceTraversalModel1(models.Model):
        @property
        def x(self):  # missing type hint emits warning
            return  # pragma: no cover

    class XSerializer(serializers.ModelSerializer):
        x = serializers.ReadOnlyField(source='x.y')

        class Meta:
            model = FailingFieldSourceTraversalModel1
            fields = '__all__'

    class XAPIView(APIView):
        @extend_schema(responses=XSerializer)
        def get(self, request):
            pass  # pragma: no cover

    generate_schema('x', view=XAPIView)
    assert ('could not follow field source through intermediate property'
            ) in capsys.readouterr().err
def test_enum_collision_without_override(capsys):
    class XSerializer(serializers.Serializer):
        foo = serializers.ChoiceField(choices=[('A', 'A')])

    class YSerializer(serializers.Serializer):
        foo = serializers.ChoiceField(choices=[('A', 'A'), ('B', 'B')])

    class ZSerializer(serializers.Serializer):
        foo = serializers.ChoiceField(choices=[('A', 'A'), ('B', 'B')])

    class XAPIView(APIView):
        @extend_schema(responses=ZSerializer)
        def get(self, request):
            pass  # pragma: no cover

        @extend_schema(request=XSerializer, responses=YSerializer)
        def post(self, request):
            pass  # pragma: no cover

    generate_schema('x', view=XAPIView)
    assert 'enum naming encountered a non-optimally resolvable' in capsys.readouterr(
    ).err
Ejemplo n.º 8
0
def test_global_enum_naming_override(no_warnings):
    # the override will prevent the warning for multiple names
    class XSerializer(serializers.Serializer):
        foo = serializers.ChoiceField(choices=language_choices)
        bar = serializers.ChoiceField(choices=language_choices)

    class XView(generics.RetrieveAPIView):
        serializer_class = XSerializer

    schema = generate_schema('/x', view=XView)
    assert 'LanguageEnum' in schema['components']['schemas']['X']['properties']['foo']['$ref']
    assert 'LanguageEnum' in schema['components']['schemas']['X']['properties']['bar']['$ref']
    assert len(schema['components']['schemas']) == 2
Ejemplo n.º 9
0
def test_path_implicit_required(no_warnings):
    class M2Serializer(serializers.Serializer):
        pass  # pragma: no cover

    class M2Viewset(viewsets.GenericViewSet):
        serializer_class = M2Serializer

        @extend_schema(parameters=[OpenApiParameter('id', str, 'path')])
        def retrieve(self, request, *args, **kwargs):
            pass  # pragma: no cover

    schema = generate_schema('m2', M2Viewset)
    validate_schema(schema)
Ejemplo n.º 10
0
def test_view_function_extension(no_warnings):
    class FixXFunctionView(OpenApiViewExtension):
        target_class = 'tests.test_extensions.x_view_function'

        def view_replacement(self):
            fixed = extend_schema(responses=OpenApiTypes.FLOAT)(
                self.target_class)
            return fixed

    schema = generate_schema('x', view_function=x_view_function)
    operation = schema['paths']['/x']['get']
    assert get_response_schema(operation)['type'] == 'number'
    assert operation['description'].strip() == 'underspecified library view'
def test_multiple_media_types(no_warnings):
    @extend_schema(responses={
        (200, 'application/json'): OpenApiTypes.OBJECT,
        (200, 'application/pdf'): OpenApiTypes.BINARY,
    })
    class XAPIView(APIView):
        def get(self, request):
            pass  # pragma: no cover

    schema = generate_schema('x', view=XAPIView)
    content = schema['paths']['/x']['get']['responses']['200']['content']
    assert content['application/pdf']['schema']['format'] == 'binary'
    assert content['application/json']['schema']['type'] == 'object'
def test_api_view_decorator_multi(no_warnings):

    @extend_schema(request=OpenApiTypes.FLOAT, responses=OpenApiTypes.INT, methods=['POST'])
    @extend_schema(responses=OpenApiTypes.FLOAT, methods=['GET'])
    @api_view(['GET', 'POST'])
    def pi(request):
        pass  # pragma: no cover

    schema = generate_schema('x', view_function=pi)
    operation = schema['paths']['/x']['get']
    assert get_response_schema(operation)['type'] == 'number'
    operation = schema['paths']['/x']['post']
    assert get_request_schema(operation)['type'] == 'number'
    assert get_response_schema(operation)['type'] == 'integer'
def test_drf_jwt(no_warnings):
    from rest_framework_jwt.views import obtain_jwt_token

    router = routers.SimpleRouter()
    router.register('x', XViewset, basename="x")

    urlpatterns = [
        *router.urls,
        path('api-token-auth/', obtain_jwt_token, name='get_token'),
    ]

    schema = generate_schema(None, patterns=urlpatterns)

    assert_schema(schema, 'tests/contrib/test_drf_jwt.yml')
Ejemplo n.º 14
0
def test_simplejwt_non_std_header_name(no_warnings):
    schema = generate_schema('/x', XViewset)
    assert schema['components']['securitySchemes'] == {
        'jwtAuth': {
            'type':
            'apiKey',
            'in':
            'header',
            'name':
            'X-token',
            'description':
            'Token-based authentication with required prefix "Bearer"'
        }
    }
Ejemplo n.º 15
0
def test_extend_schema_field_exclusion(no_warnings):
    @extend_schema_field(None)
    class CustomField(serializers.IntegerField):
        pass  # pragma: no cover

    class XSerializer(serializers.Serializer):
        id = serializers.IntegerField()
        hidden = CustomField()

    class XView(generics.CreateAPIView):
        serializer_class = XSerializer

    schema = generate_schema('/x', view=XView)
    assert 'hidden' not in schema['components']['schemas']['X']['properties']
Ejemplo n.º 16
0
def test_extend_schema_serializer_field_exclusion(no_warnings):
    @extend_schema_serializer(exclude_fields=['hidden1', 'hidden2'])
    class XSerializer(serializers.Serializer):
        integer = serializers.IntegerField()
        hidden1 = serializers.IntegerField()
        hidden2 = serializers.CharField()

    class XView(generics.ListCreateAPIView):
        serializer_class = XSerializer

    schema = generate_schema('/x', view=XView)
    assert 'integer' in schema['components']['schemas']['X']['properties']
    assert 'hidden1' not in schema['components']['schemas']['X']['properties']
    assert 'hidden2' not in schema['components']['schemas']['X']['properties']
Ejemplo n.º 17
0
def test_view_function_extension(no_warnings):
    class FixXFunctionView(OpenApiViewExtension):
        target_class = 'tests.test_extensions.x_view_function'

        def view_replacement(self):
            fixed = extend_schema(responses=OpenApiTypes.FLOAT)(
                self.target_class)
            return fixed

    schema = generate_schema('x', view_function=x_view_function)
    validate_schema(schema)
    operation = schema['paths']['/x']['get']
    assert operation['responses']['200']['content']['application/json'][
        'schema']['type'] == 'number'
Ejemplo n.º 18
0
def test_layered_extend_schema_on_view_and_method_with_meta(no_warnings):
    class XSerializer(serializers.Serializer):
        field = serializers.IntegerField()

    @extend_schema(tags=['view_tag2'])
    @extend_schema(tags=['view_tag'],
                   description='view_desc',
                   summary='view_sum')
    class XViewset(mixins.ListModelMixin, mixins.CreateModelMixin,
                   viewsets.GenericViewSet):
        serializer_class = XSerializer

        @extend_schema(tags=['create_tag2'])
        @extend_schema(tags=['create_tag'], description='create_desc')
        def create(self, request, *args, **kwargs):
            super().create(request, *args, **kwargs)  # pragma: no cover

        @extend_schema(tags=['extended_action_tag2'])
        @extend_schema(tags=['extended_action_tag'],
                       description='extended_action_desc')
        @action(detail=False, methods=['GET'])
        def extended_action(self, request):
            return Response()  # pragma: no cover

        @action(detail=False, methods=['GET'])
        def raw_action(self, request):
            return Response()  # pragma: no cover

    schema = generate_schema('x', XViewset)
    create_op = schema['paths']['/x/']['post']
    list_op = schema['paths']['/x/']['get']
    raw_action_op = schema['paths']['/x/raw_action/']['get']
    extended_action_op = schema['paths']['/x/extended_action/']['get']

    assert create_op['tags'][0] == 'create_tag2'
    assert create_op['description'] == 'create_desc'
    assert create_op['summary'] == 'view_sum'

    assert list_op['tags'][0] == 'view_tag2'
    assert list_op['description'] == 'view_desc'
    assert list_op['summary'] == 'view_sum'

    assert raw_action_op['tags'][0] == 'view_tag2'
    assert raw_action_op['description'] == 'view_desc'
    assert raw_action_op['summary'] == 'view_sum'

    assert extended_action_op['tags'][0] == 'extended_action_tag2'
    assert extended_action_op['description'] == 'extended_action_desc'
    assert extended_action_op['summary'] == 'view_sum'
Ejemplo n.º 19
0
def test_view_extension(no_warnings):
    class FixXView(OpenApiViewExtension):
        target_class = 'tests.test_extensions.XView'

        def view_replacement(self):
            class Fixed(self.target_class):
                @extend_schema(responses=OpenApiTypes.FLOAT)
                def get(self, request):
                    pass  # pragma: no cover

            return Fixed

    schema = generate_schema('x', view=XView)
    operation = schema['paths']['/x']['get']
    assert get_response_schema(operation)['type'] == 'number'
Ejemplo n.º 20
0
def test_resolvable_enum_collision(no_warnings):
    class XSerializer(serializers.Serializer):
        foo = serializers.ChoiceField(choices=[('A', 'A')])

    class YSerializer(serializers.Serializer):
        foo = serializers.ChoiceField(choices=[('A', 'A'), ('B', 'B')])

    class XAPIView(APIView):
        @extend_schema(request=XSerializer, responses=YSerializer)
        def post(self, request):
            pass  # pragma: no cover

    schema = generate_schema('x', view=XAPIView)
    assert 'XFooEnum' in schema['components']['schemas']
    assert 'YFooEnum' in schema['components']['schemas']
def test_list_serializer_with_field_child_on_extend_schema():
    class XAPIView(APIView):
        @extend_schema(
            request=serializers.ListSerializer(child=serializers.IntegerField()),
            responses=serializers.ListSerializer(child=serializers.IntegerField()),
        )
        def post(self, request):
            pass  # pragma: no cover

    schema = generate_schema('x', view=XAPIView)
    req_schema = get_request_schema(schema['paths']['/x']['post'])
    res_schema = get_response_schema(schema['paths']['/x']['post'])
    for s in [req_schema, res_schema]:
        assert s['type'] == 'array'
        assert s['items']['type'] == 'integer'
def test_operation_id_collision_resolution(capsys):
    @extend_schema(responses=OpenApiTypes.FLOAT)
    @api_view(['GET'])
    def view_func(request, format=None):
        pass  # pragma: no cover

    urlpatterns = [
        path('pi/<int:foo>', view_func),
        path('pi/', view_func),
    ]
    schema = generate_schema(None, patterns=urlpatterns)

    assert schema['paths']['/pi/']['get']['operationId'] == 'pi_retrieve'
    assert schema['paths']['/pi/{foo}']['get']['operationId'] == 'pi_retrieve_2'
    assert 'operationId "pi_retrieve" has collisions' in capsys.readouterr().err
Ejemplo n.º 23
0
def test_queryset_filter_and_ordering_only_on_list(no_warnings):
    class XViewset(viewsets.ReadOnlyModelViewSet):
        queryset = SimpleModel.objects.none()
        serializer_class = SimpleSerializer
        filter_backends = (filters.SearchFilter, filters.OrderingFilter)

    schema = generate_schema('x', XViewset)

    retrieve_parameters = schema['paths']['/x/']['get']['parameters']
    assert len(retrieve_parameters) == 2
    assert retrieve_parameters[0]['name'] == 'ordering'
    assert retrieve_parameters[1]['name'] == 'search'

    list_parameters = schema['paths']['/x/{id}/']['get']['parameters']
    assert len(list_parameters) == 1
    assert list_parameters[0]['name'] == 'id'
Ejemplo n.º 24
0
def test_component_split_nested_explicit_ro_wo_serializer(no_warnings):
    class NestedSerializer(serializers.Serializer):
        field = serializers.IntegerField()

    class XSerializer(serializers.Serializer):
        ro = NestedSerializer(read_only=True)
        wo = NestedSerializer(write_only=True, required=False)

    class XView(generics.ListCreateAPIView):
        serializer_class = XSerializer

    schema = generate_schema('/x', view=XView)
    assert 'NestedRequest' in schema['components']['schemas']
    assert 'Nested' in schema['components']['schemas']
    assert len(schema['components']['schemas']['X']['properties']) == 1
    assert len(schema['components']['schemas']['XRequest']['properties']) == 1
Ejemplo n.º 25
0
def test_manual_decimal_validator():
    # manually test this validator as it is not part of the default workflow
    class XSerializer(serializers.Serializer):
        field = serializers.CharField(validators=[
            validators.DecimalValidator(max_digits=4, decimal_places=2)
        ])

    @extend_schema(request=XSerializer, responses=XSerializer)
    @api_view(['POST'])
    def view_func(request, format=None):
        pass  # pragma: no cover

    schema = generate_schema('x', view_function=view_func)
    field = schema['components']['schemas']['X']['properties']['field']
    assert field['maximum'] == 100
    assert field['minimum'] == -100
Ejemplo n.º 26
0
def test_exclude_discovered_parameter(no_warnings):
    @extend_schema_view(list=extend_schema(parameters=[
        # keep 'offset', remove 'limit', and add 'random'
        OpenApiParameter('limit', exclude=True),
        OpenApiParameter('random', bool),
    ]))
    class XViewset(viewsets.ReadOnlyModelViewSet):
        queryset = SimpleModel.objects.all()
        serializer_class = SimpleSerializer
        pagination_class = pagination.LimitOffsetPagination

    schema = generate_schema('x', XViewset)
    parameters = schema['paths']['/x/']['get']['parameters']
    assert len(parameters) == 2
    assert parameters[0]['name'] == 'offset'
    assert parameters[1]['name'] == 'random'
Ejemplo n.º 27
0
def test_serializer_field_extension(no_warnings):
    class Base64FieldExtension(OpenApiSerializerFieldExtension):
        target_class = 'tests.test_extensions.Base64Field'

        def map_serializer_field(self, auto_schema, direction):
            return build_basic_type(OpenApiTypes.BYTE)

    class XSerializer(serializers.Serializer):
        hash = Base64Field()

    class XViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
        serializer_class = XSerializer

    schema = generate_schema('x', XViewset)
    assert schema['components']['schemas']['X']['properties']['hash']['type'] == 'string'
    assert schema['components']['schemas']['X']['properties']['hash']['format'] == 'byte'
Ejemplo n.º 28
0
def test_multi_method_action(no_warnings):
    class DummySerializer(serializers.Serializer):
        id = serializers.UUIDField()

    class UpdateSerializer(serializers.Serializer):
        id = serializers.UUIDField()

    class CreateSerializer(serializers.Serializer):
        id = serializers.UUIDField()

    class XViewset(viewsets.GenericViewSet):
        serializer_class = DummySerializer

        # basic usage
        @extend_schema(request=UpdateSerializer, methods=['PUT'])
        @extend_schema(request=CreateSerializer, methods=['POST'])
        @action(detail=False, methods=['PUT', 'POST'])
        def multi(self, request, *args, **kwargs):
            pass  # pragma: no cover

        # bolt-on decorator variation
        @extend_schema(request=CreateSerializer)
        @action(detail=False, methods=['POST'])
        def multi2(self, request, *args, **kwargs):
            pass  # pragma: no cover

        @extend_schema(request=UpdateSerializer)
        @multi2.mapping.put
        def multi2put(self, request, *args, **kwargs):
            pass  # pragma: no cover

    schema = generate_schema('x', XViewset)
    validate_schema(schema)

    def get_req_body(s):
        return s['requestBody']['content']['application/json']['schema'][
            '$ref']

    assert get_req_body(
        schema['paths']['/x/multi/']['put']) == '#/components/schemas/Update'
    assert get_req_body(
        schema['paths']['/x/multi/']['post']) == '#/components/schemas/Create'

    assert get_req_body(
        schema['paths']['/x/multi2/']['put']) == '#/components/schemas/Update'
    assert get_req_body(
        schema['paths']['/x/multi2/']['post']) == '#/components/schemas/Create'
Ejemplo n.º 29
0
def test_list_serializer_with_pagination(no_warnings):
    class GenreSerializer(serializers.Serializer):
        genre = serializers.CharField()

    class XViewSet(viewsets.GenericViewSet):
        pagination_class = pagination.LimitOffsetPagination

        @extend_schema(responses=GenreSerializer(many=True))
        @action(methods=["GET"], detail=False)
        def genre(self, request, *args, **kwargs):
            pass  # pragma: no cover

    schema = generate_schema('/x', XViewSet)
    response = get_response_schema(schema['paths']['/x/genre/']['get'])
    assert response['$ref'] == '#/components/schemas/PaginatedGenreList'
    assert 'PaginatedGenreList' in schema['components']['schemas']
    assert 'Genre' in schema['components']['schemas']
Ejemplo n.º 30
0
def test_type_hint_extraction(no_warnings, type_hint, ref_schema):
    def func() -> type_hint:
        pass  # pragma: no cover

    # check expected resolution
    schema = resolve_type_hint(typing.get_type_hints(func).get('return'))
    assert json.dumps(schema) == json.dumps(ref_schema)

    # check schema validity
    class XSerializer(serializers.Serializer):
        x = serializers.SerializerMethodField()
    XSerializer.get_x = func

    class XView(generics.RetrieveAPIView):
        serializer_class = XSerializer

    validate_schema(generate_schema('/x', view=XView))