Source code for resolwe.test.testcases.api

""".. Ignore pydocstyle D400.

.. autoclass:: resolwe.test.TransactionResolweAPITestCase
    :members:

.. autoclass:: resolwe.test.ResolweAPITestCase

"""

from django.conf import settings
from django.contrib.auth import get_user_model
from django.test import TestCase as DjangoTestCase
from django.urls import reverse

from rest_framework.test import (
    APIRequestFactory,
    APITransactionTestCase,
    force_authenticate,
)

from resolwe.test import TestCaseHelpers


[docs]class TransactionResolweAPITestCase(TestCaseHelpers, APITransactionTestCase): """Base class for testing Resolwe REST API. This class is derived from Django REST Framework's :drf:`APITransactionTestCase <testing/#test-cases>` class and has implemented some basic features that makes testing Resolwe API easier. These features includes following functions: .. automethod:: _get_list .. automethod:: _get_detail .. automethod:: _post .. automethod:: _patch .. automethod:: _delete .. automethod:: _detail_permissions It also has included 2 views made from referenced DRF's ``ViewSet``. First mimic list view and has following links between request's methods and ViewSet's methods: * ``GET`` -> ``list`` * ``POST`` -> ``create`` Second mimic detail view and has following links between request's methods and ViewSet's methods: * ``GET`` -> ``retrieve`` * ``PUT`` -> ``update`` * ``PATCH`` -> ``partial_update`` * ``DELETE`` -> ``destroy`` * ``POST`` -> ``permissions`` If any of the listed methods is not defined in the VievSet, corresponding link is omitted. .. note:: ``self.viewset`` (instance of DRF's ``Viewset``) and ``self.resource_name`` (string) must be defined before calling super ``setUp`` method to work properly. ``self.factory`` is instance of DRF's ``APIRequestFactory``. """
[docs] def setUp(self): """Prepare data.""" super().setUp() User = get_user_model() # TODO: Remove this when removing fixtures if User.objects.filter(pk=2).exists(): self.user1 = User.objects.get(pk=2) if User.objects.filter(pk=3).exists(): self.user2 = User.objects.get(pk=3) if User.objects.filter(pk=4).exists(): self.user3 = User.objects.get(pk=4) if User.objects.filter(pk=5).exists(): self.admin = User.objects.get(pk=5) # TODO: Change username to `admin` when fixtures are removed self.admin = User.objects.create_superuser( username="admin2", email="admin@test.com", password="admin" ) self.contributor = User.objects.create_user( username="contributor", email="contributor@genialis.com" ) self.user = User.objects.create_user(username="user", email="user@genialis.com") # Create the anonymous user (if it does not exist). # The user is created during migrations, but deleted between tests. if not User.objects.filter(username=settings.ANONYMOUS_USER_NAME).exists(): User.objects.create_user( username=settings.ANONYMOUS_USER_NAME, email=f"{settings.ANONYMOUS_USER_NAME}@genialis.com", ) if not hasattr(self, "viewset"): raise KeyError("`self.viewset` must be defined in child class") if not hasattr(self, "resource_name"): raise KeyError("`self.resource_name` must be defined in child class") self.factory = APIRequestFactory() list_url_mapping = {} if hasattr(self.viewset, "list"): list_url_mapping["get"] = "list" if hasattr(self.viewset, "create"): list_url_mapping["post"] = "create" if list_url_mapping: self.list_view = self.viewset.as_view(list_url_mapping) detail_url_mapping = {} if hasattr(self.viewset, "retrieve"): detail_url_mapping["get"] = "retrieve" if hasattr(self.viewset, "update"): detail_url_mapping["put"] = "update" if hasattr(self.viewset, "partial_update"): detail_url_mapping["patch"] = "partial_update" if hasattr(self.viewset, "destroy"): detail_url_mapping["delete"] = "destroy" if hasattr(self.viewset, "detail_permissions"): detail_url_mapping["post"] = "detail_permissions" if detail_url_mapping: self.detail_view = self.viewset.as_view(detail_url_mapping)
[docs] def detail_url(self, pk): """Get detail url.""" return reverse( "resolwe-api:{}-detail".format(self.resource_name), kwargs={"pk": pk} ) # noqa pylint: disable=no-member
[docs] def detail_permissions(self, pk): """Get detail permissions url.""" return reverse( "resolwe-api:{}-permissions".format(self.resource_name), kwargs={"pk": pk} ) # noqa pylint: disable=no-member
@property def list_url(self): """Get list url.""" return reverse("resolwe-api:{}-list".format(self.resource_name)) def _render_query_params(self, params): """Generate query parameters from given dict.""" if not params: return "" return "?" + "&".join( "{}={}".format(key, value) for key, value in params.items() )
[docs] def _get_list(self, user=None, query_params={}): """Make ``GET`` request to ``self.list_view`` view. If ``user`` is not ``None``, the given user is authenticated before making the request. :param user: User to authenticate in request :type user: :class:`~django.contrib.auth.models.User` or :data:`None` :return: API response object :rtype: :drf:`Response <responses/#response>` """ url = self.list_url + self._render_query_params(query_params) request = self.factory.get(url, format="json") force_authenticate(request, user) return self.list_view(request)
[docs] def _get_detail(self, pk, user=None, query_params={}): """Make ``GET`` request to ``self.detail_view`` view. If ``user`` is not ``None``, the given user is authenticated before making the request. :param int pk: Primary key of the coresponding object :param user: User to authenticate in request :type user: :class:`~django.contrib.auth.models.User` or :data:`None` :return: API response object :rtype: :drf:`Response <responses/#response>` """ url = self.detail_url(pk) + self._render_query_params(query_params) request = self.factory.get(url, format="json") force_authenticate(request, user) return self.detail_view(request, pk=pk)
[docs] def _post(self, data={}, user=None, query_params={}): """Make ``POST`` request to ``self.list_view`` view. If ``user`` is not ``None``, the given user is authenticated before making the request. :param dict data: data for posting in request's body :param user: User to authenticate in request :type user: :class:`~django.contrib.auth.models.User` or :data:`None` :return: API response object :rtype: :drf:`Response <responses/#response>` """ url = self.list_url + self._render_query_params(query_params) request = self.factory.post(url, data=data, format="json") force_authenticate(request, user) return self.list_view(request)
[docs] def _patch(self, pk, data={}, user=None, query_params={}): """Make ``PATCH`` request to ``self.detail_view`` view. If ``user`` is not ``None``, the given user is authenticated before making the request. :param int pk: Primary key of the coresponding object :param dict data: data for posting in request's body :param user: User to authenticate in request :type user: :class:`~django.contrib.auth.models.User` or :data:`None` :return: API response object :rtype: :drf:`Response <responses/#response>` """ url = self.detail_url(pk) + self._render_query_params(query_params) request = self.factory.patch(url, data=data, format="json") force_authenticate(request, user) return self.detail_view(request, pk=pk)
[docs] def _delete(self, pk, user=None, query_params={}): """Make ``DELETE`` request to ``self.detail_view`` view. If ``user`` is not ``None``, the given user is authenticated before making the request. :param int pk: Primary key of the coresponding object :param user: User to authenticate in request :type user: :class:`~django.contrib.auth.models.User` or :data:`None` :return: API response object :rtype: :drf:`Response <responses/#response>` """ url = self.detail_url(pk) + self._render_query_params(query_params) request = self.factory.delete(url, format="json") force_authenticate(request, user) return self.detail_view(request, pk=pk)
[docs] def _detail_permissions(self, pk, data={}, user=None): """Make ``POST`` request to ``self.detail_view`` view. If ``user`` is not ``None``, the given user is authenticated before making the request. :param int pk: Primary key of the coresponding object :param dict data: data for posting in request's body :param user: User to authenticate in request :type user: :class:`~django.contrib.auth.models.User` or :data:`None` :return: API response object :rtype: :drf:`Response <responses/#response>` """ request = self.factory.post( self.detail_permissions(pk), data=data, format="json" ) force_authenticate(request, user) return self.detail_view(request, pk=pk)
[docs] def assertKeys(self, data, wanted): """Assert dictionary keys.""" self.assertEqual(sorted(data.keys()), sorted(wanted))
[docs]class ResolweAPITestCase(TransactionResolweAPITestCase, DjangoTestCase): """Base class for writing Resolwe API tests. It is based on :class:`~.TransactionResolweAPITestCase` and Django's :class:`~django.test.TestCase`. The latter encloses the test code in a database transaction that is rolled back at the end of the test. """