11"""
2- Tests for Documents API endpoint in impress's core app: list
2+ Tests for Documents API endpoint in impress's core app: search
33"""
44
5- import random
6- from json import loads as json_loads
7-
8- from django .test import RequestFactory
5+ from unittest import mock
96
107import pytest
118import responses
129from faker import Faker
10+ from rest_framework import response as drf_response
1311from rest_framework .test import APIClient
1412
15- from core import factories , models
13+ from core import factories
1614from core .services .search_indexers import get_document_indexer
1715
1816fake = Faker ()
1917pytestmark = pytest .mark .django_db
2018
2119
22- def build_search_url (** kwargs ):
23- """Build absolute uri for search endpoint with ORDERED query arguments"""
24- return (
25- RequestFactory ()
26- .get ("/api/v1.0/documents/search/" , dict (sorted (kwargs .items ())))
27- .build_absolute_uri ()
28- )
29-
30-
31- @pytest .mark .parametrize ("role" , models .LinkRoleChoices .values )
32- @pytest .mark .parametrize ("reach" , models .LinkReachChoices .values )
3320@responses .activate
34- def test_api_documents_search_anonymous (reach , role , indexer_settings ):
21+ def test_api_documents_search_anonymous (indexer_settings ):
3522 """
36- Anonymous users should not be allowed to search documents whatever the
37- link reach and link role
23+ Anonymous users should be allowed to search documents with Find.
3824 """
3925 indexer_settings .SEARCH_INDEXER_QUERY_URL = "http://find/api/v1.0/search"
4026
41- # Find response
27+ # mock Find response
4228 responses .add (
4329 responses .POST ,
4430 "http://find/api/v1.0/search" ,
4531 json = [],
4632 status = 200 ,
4733 )
4834
49- response = APIClient ().get ("/api/v1.0/documents/search/" , data = {"q" : "alpha" })
35+ with mock .patch (
36+ "core.services.search_indexers.FindDocumentIndexer.search_query"
37+ ) as search_query :
38+ q = "alpha"
39+ response = APIClient ().get ("/api/v1.0/documents/search/" , data = {"q" : q })
40+
41+ assert search_query .call_count == 1
42+ assert search_query .call_args [1 ] == {
43+ "data" : {
44+ "q" : q ,
45+ "visited" : [],
46+ "services" : ["docs" ],
47+ "nb_results" : 50 ,
48+ "order_by" : "updated_at" ,
49+ "order_direction" : "desc" ,
50+ "path" : None ,
51+ },
52+ "token" : None ,
53+ }
5054
5155 assert response .status_code == 200
5256 assert response .json () == {
@@ -59,100 +63,76 @@ def test_api_documents_search_anonymous(reach, role, indexer_settings):
5963
6064def test_api_documents_search_fall_back_on_search_list (indexer_settings ):
6165 """
62- Missing SEARCH_INDEXER_QUERY_URL, so the indexer is not properly configured.
63- Should fallback on title filter
66+ When indexer is not configured and no path is provided,
67+ should fall back on list method
6468 """
6569 indexer_settings .SEARCH_INDEXER_QUERY_URL = None
66-
6770 assert get_document_indexer () is None
6871
6972 user = factories .UserFactory ()
70- document = factories .DocumentFactory (title = "alpha" )
71- access = factories .UserDocumentAccessFactory (document = document , user = user )
72-
7373 client = APIClient ()
7474 client .force_login (user )
7575
76- response = client .get ("/api/v1.0/documents/search/" , data = {"q" : "alpha" })
76+ with mock .patch ("core.api.viewsets.DocumentViewSet.list" ) as mock_list :
77+ mocked_response = {
78+ "count" : 0 ,
79+ "next" : None ,
80+ "previous" : None ,
81+ "results" : [{"title" : "mocked list result" }],
82+ }
83+ mock_list .return_value = drf_response .Response (mocked_response )
7784
78- assert response .status_code == 200
79- content = response .json ()
80- results = content .pop ("results" )
81- assert content == {
82- "count" : 1 ,
83- "next" : None ,
84- "previous" : None ,
85- }
86- assert len (results ) == 1
87- assert results [0 ] == {
88- "id" : str (document .id ),
89- "abilities" : document .get_abilities (user ),
90- "ancestors_link_reach" : None ,
91- "ancestors_link_role" : None ,
92- "computed_link_reach" : document .computed_link_reach ,
93- "computed_link_role" : document .computed_link_role ,
94- "created_at" : document .created_at .isoformat ().replace ("+00:00" , "Z" ),
95- "creator" : str (document .creator .id ),
96- "depth" : 1 ,
97- "excerpt" : document .excerpt ,
98- "link_reach" : document .link_reach ,
99- "link_role" : document .link_role ,
100- "nb_accesses_ancestors" : 1 ,
101- "nb_accesses_direct" : 1 ,
102- "numchild" : 0 ,
103- "path" : document .path ,
104- "title" : document .title ,
105- "updated_at" : document .updated_at .isoformat ().replace ("+00:00" , "Z" ),
106- "deleted_at" : None ,
107- "user_role" : access .role ,
108- 'is_favorite' : False ,
109- }
85+ response = client .get ("/api/v1.0/documents/search/" , data = {"q" : "alpha" })
86+
87+ assert mock_list .call_count == 1
88+ assert mock_list .call_args [0 ][0 ].GET .get ("title" ) == "alpha"
89+
90+ assert response .json () == mocked_response
11091
11192
11293def test_api_documents_search_fallback_on_search_list_sub_docs (indexer_settings ):
11394 """
11495 When indexer is not configured and path parameter is provided,
115- should use _list_sub_docs to filter by path and title
96+ should call _list_descendants() method
11697 """
11798 indexer_settings .SEARCH_INDEXER_QUERY_URL = None
118-
11999 assert get_document_indexer () is None
120100
121101 user = factories .UserFactory ()
122-
123- # Create a parent document and children
124- parent = factories .DocumentFactory (title = "parent alpha" , users = [user ])
125- child1 = factories .DocumentFactory (title = "child alpha" , parent = parent , users = [user ])
126- child2 = factories .DocumentFactory (title = "child beta" , parent = parent , users = [user ])
127- other = factories .DocumentFactory (title = "other alpha" , users = [user ])
128-
129102 client = APIClient ()
130103 client .force_login (user )
131104
132- # Search with path filter - should return parent and child1 only
133- response = client .get (
134- "/api/v1.0/documents/search/" ,
135- data = {"q" : "alpha" , "path" : parent .path }
136- )
105+ parent = factories .DocumentFactory (title = "parent" , users = [user ])
137106
138- assert response .status_code == 200
139- content = response .json ()
140- results = content ["results" ]
141-
142- result_ids = {r ["id" ] for r in results }
143- assert str (parent .id ) in result_ids
144- assert str (child1 .id ) in result_ids
145- assert str (child2 .id ) not in result_ids
146- assert str (other .id ) not in result_ids
107+ with mock .patch (
108+ "core.api.viewsets.DocumentViewSet._list_descendants"
109+ ) as mock_list_descendants :
110+ mocked_response = {
111+ "count" : 0 ,
112+ "next" : None ,
113+ "previous" : None ,
114+ "results" : [{"title" : "mocked _list_descendants result" }],
115+ }
116+ mock_list_descendants .return_value = drf_response .Response (mocked_response )
117+
118+ response = client .get (
119+ "/api/v1.0/documents/search/" , data = {"q" : "alpha" , "path" : parent .path }
120+ )
121+
122+ assert mock_list_descendants .call_count == 1
123+ assert mock_list_descendants .call_args [0 ][0 ].GET .get ("title" ) == "alpha"
124+ assert mock_list_descendants .call_args [0 ][0 ].GET .get ("path" ) == parent .path
125+
126+ assert response .json () == mocked_response
147127
148128
149129@responses .activate
150130def test_api_documents_search_invalid_params (indexer_settings ):
151131 """Validate the format of documents as returned by the search view."""
152132 indexer_settings .SEARCH_INDEXER_QUERY_URL = "http://find/api/v1.0/search"
133+ assert get_document_indexer () is not None
153134
154135 user = factories .UserFactory ()
155-
156136 client = APIClient ()
157137 client .force_login (user )
158138
@@ -161,34 +141,24 @@ def test_api_documents_search_invalid_params(indexer_settings):
161141 assert response .status_code == 400
162142 assert response .json () == {"q" : ["This field is required." ]}
163143
164- response = client .get ("/api/v1.0/documents/search/" , data = {"q" : " " })
165-
166- assert response .status_code == 400
167- assert response .json () == {"q" : ["This field may not be blank." ]}
168-
169- response = client .get (
170- "/api/v1.0/documents/search/" , data = {"q" : "any" , "page" : "NaN" }
171- )
172-
173- assert response .status_code == 400
174- assert response .json () == {"page" : ["A valid integer is required." ]}
175-
176144
177145@responses .activate
178- def test_api_documents_search_format (indexer_settings ):
146+ def test_api_documents_search_success (indexer_settings ):
179147 """Validate the format of documents as returned by the search view."""
180148 indexer_settings .SEARCH_INDEXER_QUERY_URL = "http://find/api/v1.0/search"
181-
182149 assert get_document_indexer () is not None
183150
184- document = {"id" : "doc-123" , "title" : "alpha" , "path" : "path/to/alpha.pdf" }
151+ document = {"id" : "doc-123" , "title" : "alpha" , "path" : "path/to/alpha.pdf" }
185152
186153 # Find response
187154 responses .add (
188155 responses .POST ,
189156 "http://find/api/v1.0/search" ,
190157 json = [
191- {"_id" : str (document ["id" ]), "_source" : {"title" : document ["title" ], "path" : document ["path" ]}},
158+ {
159+ "_id" : str (document ["id" ]),
160+ "_source" : {"title" : document ["title" ], "path" : document ["path" ]},
161+ },
192162 ],
193163 status = 200 ,
194164 )
@@ -202,5 +172,6 @@ def test_api_documents_search_format(indexer_settings):
202172 "next" : None ,
203173 "previous" : None ,
204174 }
205- assert len (results ) == 1
206- assert results [0 ] == {'id' : document ["id" ], 'title' : document ["title" ], 'path' : document ["path" ]}
175+ assert results == [
176+ {"id" : document ["id" ], "title" : document ["title" ], "path" : document ["path" ]}
177+ ]
0 commit comments