from __future__ import unicode_literals

import pytest
from django.test import TestCase

from rest_framework import status
from rest_framework.authentication import BasicAuthentication
from rest_framework.decorators import (
    action, api_view, authentication_classes, detail_route, list_route,
    parser_classes, permission_classes, renderer_classes, schema,
    throttle_classes
)
from rest_framework.parsers import JSONParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.schemas import AutoSchema
from rest_framework.test import APIRequestFactory
from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView


class DecoratorTestCase(TestCase):

    def setUp(self):
        self.factory = APIRequestFactory()

    def _finalize_response(self, request, response, *args, **kwargs):
        response.request = request
        return APIView.finalize_response(self, request, response, *args, **kwargs)

    def test_api_view_incorrect(self):
        """
        If @api_view is not applied correct, we should raise an assertion.
        """

        @api_view
        def view(request):
            return Response()

        request = self.factory.get('/')
        self.assertRaises(AssertionError, view, request)

    def test_api_view_incorrect_arguments(self):
        """
        If @api_view is missing arguments, we should raise an assertion.
        """

        with self.assertRaises(AssertionError):
            @api_view('GET')
            def view(request):
                return Response()

    def test_calling_method(self):

        @api_view(['GET'])
        def view(request):
            return Response({})

        request = self.factory.get('/')
        response = view(request)
        assert response.status_code == status.HTTP_200_OK

        request = self.factory.post('/')
        response = view(request)
        assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED

    def test_calling_put_method(self):

        @api_view(['GET', 'PUT'])
        def view(request):
            return Response({})

        request = self.factory.put('/')
        response = view(request)
        assert response.status_code == status.HTTP_200_OK

        request = self.factory.post('/')
        response = view(request)
        assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED

    def test_calling_patch_method(self):

        @api_view(['GET', 'PATCH'])
        def view(request):
            return Response({})

        request = self.factory.patch('/')
        response = view(request)
        assert response.status_code == status.HTTP_200_OK

        request = self.factory.post('/')
        response = view(request)
        assert response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED

    def test_renderer_classes(self):

        @api_view(['GET'])
        @renderer_classes([JSONRenderer])
        def view(request):
            return Response({})

        request = self.factory.get('/')
        response = view(request)
        assert isinstance(response.accepted_renderer, JSONRenderer)

    def test_parser_classes(self):

        @api_view(['GET'])
        @parser_classes([JSONParser])
        def view(request):
            assert len(request.parsers) == 1
            assert isinstance(request.parsers[0], JSONParser)
            return Response({})

        request = self.factory.get('/')
        view(request)

    def test_authentication_classes(self):

        @api_view(['GET'])
        @authentication_classes([BasicAuthentication])
        def view(request):
            assert len(request.authenticators) == 1
            assert isinstance(request.authenticators[0], BasicAuthentication)
            return Response({})

        request = self.factory.get('/')
        view(request)

    def test_permission_classes(self):

        @api_view(['GET'])
        @permission_classes([IsAuthenticated])
        def view(request):
            return Response({})

        request = self.factory.get('/')
        response = view(request)
        assert response.status_code == status.HTTP_403_FORBIDDEN

    def test_throttle_classes(self):
        class OncePerDayUserThrottle(UserRateThrottle):
            rate = '1/day'

        @api_view(['GET'])
        @throttle_classes([OncePerDayUserThrottle])
        def view(request):
            return Response({})

        request = self.factory.get('/')
        response = view(request)
        assert response.status_code == status.HTTP_200_OK

        response = view(request)
        assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS

    def test_schema(self):
        """
        Checks CustomSchema class is set on view
        """
        class CustomSchema(AutoSchema):
            pass

        @api_view(['GET'])
        @schema(CustomSchema())
        def view(request):
            return Response({})

        assert isinstance(view.cls.schema, CustomSchema)


class ActionDecoratorTestCase(TestCase):

    def test_defaults(self):
        @action(detail=True)
        def test_action(request):
            """Description"""

        assert test_action.mapping == {'get': 'test_action'}
        assert test_action.detail is True
        assert test_action.url_path == 'test_action'
        assert test_action.url_name == 'test-action'
        assert test_action.kwargs == {
            'name': 'Test action',
            'description': 'Description',
        }

    def test_detail_required(self):
        with pytest.raises(AssertionError) as excinfo:
            @action()
            def test_action(request):
                raise NotImplementedError

        assert str(excinfo.value) == "@action() missing required argument: 'detail'"

    def test_method_mapping_http_methods(self):
        # All HTTP methods should be mappable
        @action(detail=False, methods=[])
        def test_action():
            raise NotImplementedError

        for name in APIView.http_method_names:
            def method():
                raise NotImplementedError

            # Python 2.x compatibility - cast __name__ to str
            method.__name__ = str(name)
            getattr(test_action.mapping, name)(method)

        # ensure the mapping returns the correct method name
        for name in APIView.http_method_names:
            assert test_action.mapping[name] == name

    def test_view_name_kwargs(self):
        """
        'name' and 'suffix' are mutually exclusive kwargs used for generating
        a view's display name.
        """
        # by default, generate name from method
        @action(detail=True)
        def test_action(request):
            raise NotImplementedError

        assert test_action.kwargs == {
            'description': None,
            'name': 'Test action',
        }

        # name kwarg supersedes name generation
        @action(detail=True, name='test name')
        def test_action(request):
            raise NotImplementedError

        assert test_action.kwargs == {
            'description': None,
            'name': 'test name',
        }

        # suffix kwarg supersedes name generation
        @action(detail=True, suffix='Suffix')
        def test_action(request):
            raise NotImplementedError

        assert test_action.kwargs == {
            'description': None,
            'suffix': 'Suffix',
        }

        # name + suffix is a conflict.
        with pytest.raises(TypeError) as excinfo:
            action(detail=True, name='test name', suffix='Suffix')

        assert str(excinfo.value) == "`name` and `suffix` are mutually exclusive arguments."

    def test_method_mapping(self):
        @action(detail=False)
        def test_action(request):
            raise NotImplementedError

        @test_action.mapping.post
        def test_action_post(request):
            raise NotImplementedError

        # The secondary handler methods should not have the action attributes
        for name in ['mapping', 'detail', 'url_path', 'url_name', 'kwargs']:
            assert hasattr(test_action, name) and not hasattr(test_action_post, name)

    def test_method_mapping_already_mapped(self):
        @action(detail=True)
        def test_action(request):
            raise NotImplementedError

        msg = "Method 'get' has already been mapped to '.test_action'."
        with self.assertRaisesMessage(AssertionError, msg):
            @test_action.mapping.get
            def test_action_get(request):
                raise NotImplementedError

    def test_method_mapping_overwrite(self):
        @action(detail=True)
        def test_action():
            raise NotImplementedError

        msg = ("Method mapping does not behave like the property decorator. You "
               "cannot use the same method name for each mapping declaration.")
        with self.assertRaisesMessage(AssertionError, msg):
            @test_action.mapping.post
            def test_action():
                raise NotImplementedError

    def test_detail_route_deprecation(self):
        with pytest.warns(DeprecationWarning) as record:
            @detail_route()
            def view(request):
                raise NotImplementedError

        assert len(record) == 1
        assert str(record[0].message) == (
            "`detail_route` is deprecated and will be removed in "
            "3.10 in favor of `action`, which accepts a `detail` bool. Use "
            "`@action(detail=True)` instead."
        )

    def test_list_route_deprecation(self):
        with pytest.warns(DeprecationWarning) as record:
            @list_route()
            def view(request):
                raise NotImplementedError

        assert len(record) == 1
        assert str(record[0].message) == (
            "`list_route` is deprecated and will be removed in "
            "3.10 in favor of `action`, which accepts a `detail` bool. Use "
            "`@action(detail=False)` instead."
        )

    def test_route_url_name_from_path(self):
        # pre-3.8 behavior was to base the `url_name` off of the `url_path`
        with pytest.warns(DeprecationWarning):
            @list_route(url_path='foo_bar')
            def view(request):
                raise NotImplementedError

        assert view.url_path == 'foo_bar'
        assert view.url_name == 'foo-bar'

[((856, 875), 'rest_framework.test.APIRequestFactory', 'APIRequestFactory', ([], {}), '()\n', (873, 875), False, 'from rest_framework.test import APIRequestFactory\n'), ((997, 1064), 'rest_framework.views.APIView.finalize_response', 'APIView.finalize_response', (['self', 'request', 'response', '*args'], {}), '(self, request, response, *args, **kwargs)\n', (1022, 1064), False, 'from rest_framework.views import APIView\n'), ((1714, 1731), 'rest_framework.decorators.api_view', 'api_view', (["['GET']"], {}), "(['GET'])\n", (1722, 1731), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((2122, 2146), 'rest_framework.decorators.api_view', 'api_view', (["['GET', 'PUT']"], {}), "(['GET', 'PUT'])\n", (2130, 2146), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((2539, 2565), 'rest_framework.decorators.api_view', 'api_view', (["['GET', 'PATCH']"], {}), "(['GET', 'PATCH'])\n", (2547, 2565), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((2956, 2973), 'rest_framework.decorators.api_view', 'api_view', (["['GET']"], {}), "(['GET'])\n", (2964, 2973), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((2983, 3015), 'rest_framework.decorators.renderer_classes', 'renderer_classes', (['[JSONRenderer]'], {}), '([JSONRenderer])\n', (2999, 3015), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((3263, 3280), 'rest_framework.decorators.api_view', 'api_view', (["['GET']"], {}), "(['GET'])\n", (3271, 3280), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((3290, 3318), 'rest_framework.decorators.parser_classes', 'parser_classes', (['[JSONParser]'], {}), '([JSONParser])\n', (3304, 3318), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((3602, 3619), 'rest_framework.decorators.api_view', 'api_view', (["['GET']"], {}), "(['GET'])\n", (3610, 3619), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((3629, 3674), 'rest_framework.decorators.authentication_classes', 'authentication_classes', (['[BasicAuthentication]'], {}), '([BasicAuthentication])\n', (3651, 3674), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((3977, 3994), 'rest_framework.decorators.api_view', 'api_view', (["['GET']"], {}), "(['GET'])\n", (3985, 3994), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((4004, 4041), 'rest_framework.decorators.permission_classes', 'permission_classes', (['[IsAuthenticated]'], {}), '([IsAuthenticated])\n', (4022, 4041), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((4371, 4388), 'rest_framework.decorators.api_view', 'api_view', (["['GET']"], {}), "(['GET'])\n", (4379, 4388), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((4398, 4440), 'rest_framework.decorators.throttle_classes', 'throttle_classes', (['[OncePerDayUserThrottle]'], {}), '([OncePerDayUserThrottle])\n', (4414, 4440), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((4907, 4924), 'rest_framework.decorators.api_view', 'api_view', (["['GET']"], {}), "(['GET'])\n", (4915, 4924), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((5156, 5175), 'rest_framework.decorators.action', 'action', ([], {'detail': '(True)'}), '(detail=True)\n', (5162, 5175), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((5960, 5992), 'rest_framework.decorators.action', 'action', ([], {'detail': '(False)', 'methods': '[]'}), '(detail=False, methods=[])\n', (5966, 5992), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((6722, 6741), 'rest_framework.decorators.action', 'action', ([], {'detail': '(True)'}), '(detail=True)\n', (6728, 6741), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((6990, 7027), 'rest_framework.decorators.action', 'action', ([], {'detail': '(True)', 'name': '"""test name"""'}), "(detail=True, name='test name')\n", (6996, 7027), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((7276, 7312), 'rest_framework.decorators.action', 'action', ([], {'detail': '(True)', 'suffix': '"""Suffix"""'}), "(detail=True, suffix='Suffix')\n", (7282, 7312), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((7796, 7816), 'rest_framework.decorators.action', 'action', ([], {'detail': '(False)'}), '(detail=False)\n', (7802, 7816), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((8303, 8322), 'rest_framework.decorators.action', 'action', ([], {'detail': '(True)'}), '(detail=True)\n', (8309, 8322), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((8704, 8723), 'rest_framework.decorators.action', 'action', ([], {'detail': '(True)'}), '(detail=True)\n', (8710, 8723), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((1269, 1279), 'rest_framework.response.Response', 'Response', ([], {}), '()\n', (1277, 1279), False, 'from rest_framework.response import Response\n'), ((1587, 1602), 'rest_framework.decorators.api_view', 'api_view', (['"""GET"""'], {}), "('GET')\n", (1595, 1602), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((1778, 1790), 'rest_framework.response.Response', 'Response', (['{}'], {}), '({})\n', (1786, 1790), False, 'from rest_framework.response import Response\n'), ((2193, 2205), 'rest_framework.response.Response', 'Response', (['{}'], {}), '({})\n', (2201, 2205), False, 'from rest_framework.response import Response\n'), ((2612, 2624), 'rest_framework.response.Response', 'Response', (['{}'], {}), '({})\n', (2620, 2624), False, 'from rest_framework.response import Response\n'), ((3062, 3074), 'rest_framework.response.Response', 'Response', (['{}'], {}), '({})\n', (3070, 3074), False, 'from rest_framework.response import Response\n'), ((3472, 3484), 'rest_framework.response.Response', 'Response', (['{}'], {}), '({})\n', (3480, 3484), False, 'from rest_framework.response import Response\n'), ((3851, 3863), 'rest_framework.response.Response', 'Response', (['{}'], {}), '({})\n', (3859, 3863), False, 'from rest_framework.response import Response\n'), ((4088, 4100), 'rest_framework.response.Response', 'Response', (['{}'], {}), '({})\n', (4096, 4100), False, 'from rest_framework.response import Response\n'), ((4487, 4499), 'rest_framework.response.Response', 'Response', (['{}'], {}), '({})\n', (4495, 4499), False, 'from rest_framework.response import Response\n'), ((5003, 5015), 'rest_framework.response.Response', 'Response', (['{}'], {}), '({})\n', (5011, 5015), False, 'from rest_framework.response import Response\n'), ((5626, 5655), 'pytest.raises', 'pytest.raises', (['AssertionError'], {}), '(AssertionError)\n', (5639, 5655), False, 'import pytest\n'), ((5681, 5689), 'rest_framework.decorators.action', 'action', ([], {}), '()\n', (5687, 5689), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((7553, 7577), 'pytest.raises', 'pytest.raises', (['TypeError'], {}), '(TypeError)\n', (7566, 7577), False, 'import pytest\n'), ((7602, 7656), 'rest_framework.decorators.action', 'action', ([], {'detail': '(True)', 'name': '"""test name"""', 'suffix': '"""Suffix"""'}), "(detail=True, name='test name', suffix='Suffix')\n", (7608, 7656), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((9182, 9214), 'pytest.warns', 'pytest.warns', (['DeprecationWarning'], {}), '(DeprecationWarning)\n', (9194, 9214), False, 'import pytest\n'), ((9239, 9253), 'rest_framework.decorators.detail_route', 'detail_route', ([], {}), '()\n', (9251, 9253), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((9660, 9692), 'pytest.warns', 'pytest.warns', (['DeprecationWarning'], {}), '(DeprecationWarning)\n', (9672, 9692), False, 'import pytest\n'), ((9717, 9729), 'rest_framework.decorators.list_route', 'list_route', ([], {}), '()\n', (9727, 9729), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((10213, 10245), 'pytest.warns', 'pytest.warns', (['DeprecationWarning'], {}), '(DeprecationWarning)\n', (10225, 10245), False, 'import pytest\n'), ((10260, 10290), 'rest_framework.decorators.list_route', 'list_route', ([], {'url_path': '"""foo_bar"""'}), "(url_path='foo_bar')\n", (10270, 10290), False, 'from rest_framework.decorators import action, api_view, authentication_classes, detail_route, list_route, parser_classes, permission_classes, renderer_classes, schema, throttle_classes\n'), ((1657, 1667), 'rest_framework.response.Response', 'Response', ([], {}), '()\n', (1665, 1667), False, 'from rest_framework.response import Response\n')]