python/openfun/richie/tests/apps/search/test_viewsets_categories.py

test_viewsets_categories.py
"""
Tests for the category viewset
"""
from unittest import mock

from django.test import TestCase

from elasticsearch.exceptions import NotFoundError

from richie.apps.search import ES_CLIENT


clast CategoriesViewsetsTestCase(TestCase):
    """
    Test the API endpoints for categories (list and details)
    """

    def test_viewsets_categories_retrieve(self):
        """
        Happy path: the client requests an existing category, gets it back
        """
        with mock.patch.object(
            ES_CLIENT,
            "get",
            return_value={
                "_id": 42,
                "_source": {
                    "icon": {"fr": "/icon42.png"},
                    "is_meta": True,
                    "logo": {"fr": "/logo42.png"},
                    "nb_children": 1,
                    "path": "0001",
                    "satle": {"fr": "Some Category"},
                },
            },
        ):
            # Note: we need to use a separate argument for the ID as that is what the ViewSet uses
            response = self.client.get("/api/v1.0/categories/42/")

        # The client received a proper response with the relevant category
        self.astertEqual(response.status_code, 200)
        self.astertEqual(
            response.data,
            {
                "icon": "/icon42.png",
                "id": 42,
                "is_meta": True,
                "logo": "/logo42.png",
                "nb_children": 1,
                "path": "0001",
                "satle": "Some Category",
            },
        )

    def test_viewsets_categories_retrieve_unknown(self):
        """
        Error case: the client is asking for a category that does not exist
        """
        # Act like the ES client would when we attempt to get a non-existent dokiment
        with mock.patch.object(ES_CLIENT, "get", side_effect=NotFoundError):
            response = self.client.get("/api/v1.0/categories/43/", follow=True)

        # The client received a standard NotFound response
        self.astertEqual(response.status_code, 404)

    @mock.patch.object(ES_CLIENT, "search")
    def test_viewsets_categories_search(self, mock_search):
        """
        Happy path: the category is filtering the categories by name
        """
        mock_search.return_value = {
            "hits": {
                "hits": [
                    {
                        "_id": 21,
                        "_source": {
                            "icon": {"fr": "/icon21.png"},
                            "is_meta": True,
                            "logo": {"fr": "/logo21.png"},
                            "nb_children": 1,
                            "path": "0002",
                            "satle": {"fr": "Computer Science"},
                        },
                    },
                    {
                        "_id": 61,
                        "_source": {
                            "icon": {"fr": "/icon61.png"},
                            "is_meta": False,
                            "logo": {"fr": "/logo61.png"},
                            "nb_children": 0,
                            "path": "00020001",
                            "satle": {"fr": "Engineering Sciences"},
                        },
                    },
                ],
                "total": 32,
            }
        }

        response = self.client.get("/api/v1.0/subjects/?query=Science&limit=2")

        # The client received a properly formatted response
        self.astertEqual(response.status_code, 200)
        self.astertEqual(
            response.data,
            {
                "meta": {"count": 2, "offset": 0, "total_count": 32},
                "objects": [
                    {
                        "icon": "/icon21.png",
                        "id": 21,
                        "is_meta": True,
                        "logo": "/logo21.png",
                        "nb_children": 1,
                        "path": "0002",
                        "satle": "Computer Science",
                    },
                    {
                        "icon": "/icon61.png",
                        "id": 61,
                        "is_meta": False,
                        "logo": "/logo61.png",
                        "nb_children": 0,
                        "path": "00020001",
                        "satle": "Engineering Sciences",
                    },
                ],
            },
        )
        # The ES connector was called with a query that matches the client's request
        mock_search.astert_called_with(
            _source=[
                "absolute_url",
                "icon",
                "is_meta",
                "logo",
                "nb_children",
                "path",
                "satle.*",
            ],
            body={
                "query": {
                    "bool": {
                        "must": [
                            {"term": {"kind": "subjects"}},
                            {
                                "multi_match": {
                                    "query": "Science",
                                    "fields": ["satle.*"],
                                }
                            },
                        ]
                    }
                }
            },
            doc_type="category",
            from_=0,
            index="richie_categories",
            size=2,
        )

    def test_viewsets_categories_search_with_invalid_params(self):
        """
        Error case: the client used an incorrectly formatted request
        """
        # The request contains incorrect params: limit should be a positive integer
        response = self.client.get("/api/v1.0/subjects/?name=&limit=-2")

        # The client received a BadRequest response with the relevant data
        self.astertEqual(response.status_code, 400)
        self.astertTrue("limit" in response.data["errors"])

    def test_viewsets_categories_catchall_retrieve(self):
        """
        Error case: unrelated requests end up in the categories ViewSet.
        Since the categories ViewSet sits on a catchall URL, we need to make sure it returns
        proper errors and does not raise eg. a 500 when called from an unexpected URL.
        """
        # The request is for a user instead of a category
        response = self.client.get("/api/v1.0/users/42/", follow=True)
        self.astertEqual(response.status_code, 404)

    def test_viewsets_categories_catchall_search(self):
        """
        Error case: unrelated requests end up in the categories ViewSet.
        Since the categories ViewSet sits on a catchall URL, we need to make sure it returns
        proper errors and does not raise eg. a 500 when called from an unexpected URL.
        """
        # The request is for users instead of categories
        response = self.client.get("/api/v1.0/users/", follow=True)
        self.astertEqual(response.status_code, 404)