Skip to content

Commit 41a67a3

Browse files
committed
adding more tests
1 parent fe2aef1 commit 41a67a3

File tree

5 files changed

+93
-153
lines changed

5 files changed

+93
-153
lines changed

readthedocs/restapi/urls.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,14 @@
4949
url(r'index_search/',
5050
search_views.index_search,
5151
name='index_search'),
52-
url(r'search/$', views.search_views.search, name='api_search'),
52+
# url(r'search/$', views.search_views.search, name='api_search'),
5353
url(r'search/project/$',
5454
search_views.project_search,
5555
name='api_project_search'),
5656
url(r'search/section/$',
5757
search_views.section_search,
5858
name='api_section_search'),
59+
url(r'^docsearch/$', PageSearchAPIView.as_view(), name='doc_search'),
5960
]
6061

6162
task_urls = [
@@ -86,15 +87,11 @@
8687
name='api_webhook'),
8788
]
8889

89-
api_search_urls = [
90-
url(r'^docsearch/$', PageSearchAPIView.as_view(), name='doc_search'),
91-
]
9290

9391
urlpatterns += function_urls
9492
urlpatterns += task_urls
9593
urlpatterns += search_urls
9694
urlpatterns += integration_urls
97-
urlpatterns += api_search_urls
9895

9996
try:
10097
from readthedocsext.donate.restapi.urls import urlpatterns as sustainability_urls

readthedocs/search/api.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from rest_framework import generics
2+
from rest_framework import exceptions
3+
from rest_framework.exceptions import ValidationError
24

35
from readthedocs.search.documents import PageDocument
46
from readthedocs.search.filters import SearchFilterBackend
@@ -19,6 +21,20 @@ def get_queryset(self):
1921
So for searching, its possible to return ``Search`` object instead of queryset.
2022
The ``filter_backends`` and ``pagination_class`` is compatible with ``Search``
2123
"""
24+
# Validate all the required params are there
25+
self.validate_query_params()
2226
query = self.request.query_params.get('query', '')
2327
queryset = PageDocument.simple_search(query=query)
2428
return queryset
29+
30+
def validate_query_params(self):
31+
required_query_params = set(['query', 'project', 'version'])
32+
request_params = set(self.request.query_params.keys())
33+
missing_params = required_query_params - request_params
34+
if missing_params:
35+
errors = []
36+
for param in missing_params:
37+
e = {param: "This query param is required"}
38+
errors.append(e)
39+
40+
raise ValidationError(errors)

readthedocs/search/documents.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,9 @@ def faceted_search(cls, query, projects_list=None, versions_list=None, using=Non
9090
def simple_search(cls, query, using=None, index=None):
9191
es_search = cls.search(using=using, index=index)
9292
es_query = cls.get_es_query(query=query)
93+
highlighted_fields = [f.split('^', 1)[0] for f in cls.search_fields]
9394

94-
es_search = es_search.query(es_query).highlight('content')
95+
es_search = es_search.query(es_query).highlight(*highlighted_fields)
9596
return es_search
9697

9798
@classmethod

readthedocs/search/serializers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33

44
class PageSearchSerializer(serializers.Serializer):
5+
project = serializers.CharField()
6+
version = serializers.CharField()
57
title = serializers.CharField()
68
path = serializers.CharField()
79
highlight = serializers.SerializerMethodField()

readthedocs/search/tests/test_api.py

Lines changed: 71 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import pytest
22
from django.core.urlresolvers import reverse
3+
from django_dynamic_fixture import G
34

5+
from readthedocs.builds.models import Version
6+
from readthedocs.projects.models import HTMLFile
47
from readthedocs.search.tests.utils import get_search_query_from_project_file
58

69

710
@pytest.mark.django_db
811
@pytest.mark.search
9-
class TestPageSearch(object):
12+
class TestDocumentSearch(object):
1013
url = reverse('doc_search')
1114

1215
@pytest.mark.parametrize('data_type', ['content', 'headers', 'title'])
@@ -16,149 +19,70 @@ def test_search_works(self, api_client, project, data_type, page_num):
1619
data_type=data_type)
1720

1821
version = project.versions.all()[0]
19-
url = reverse('doc_search')
20-
resp = api_client.get(url, {'project': project.slug, 'version': version.slug, 'query': query})
21-
data = resp.data
22-
assert len(data['results']) == 1
23-
24-
# @pytest.mark.parametrize('case', ['upper', 'lower', 'title'])
25-
# def test_file_search_case_insensitive(self, client, project, case):
26-
# """Check File search is case insensitive
27-
#
28-
# It tests with uppercase, lowercase and camelcase
29-
# """
30-
# query_text = get_search_query_from_project_file(project_slug=project.slug)
31-
#
32-
# cased_query = getattr(query_text, case)
33-
# query = cased_query()
34-
#
35-
# result, _ = self._get_search_result(url=self.url, client=client,
36-
# search_params={'q': query, 'type': 'file'})
37-
#
38-
# assert len(result) == 1
39-
# # Check the actual text is in the result, not the cased one
40-
# assert query_text in result.text()
41-
#
42-
# def test_file_search_exact_match(self, client, project):
43-
# """Check quoted query match exact phrase
44-
#
45-
# Making a query with quoted text like ``"foo bar"`` should match
46-
# exactly ``foo bar`` phrase.
47-
# """
48-
#
49-
# # `Github` word is present both in `kuma` and `pipeline` files
50-
# # But the phrase Github can is available only in kuma docs.
51-
# # So search with this phrase to check
52-
# query = r'"GitHub can"'
53-
#
54-
# result, _ = self._get_search_result(url=self.url, client=client,
55-
# search_params={'q': query, 'type': 'file'})
56-
#
57-
# assert len(result) == 1
58-
#
59-
# def test_page_search_not_return_removed_page(self, client, project):
60-
# """Check removed page are not in the search index"""
61-
# query = get_search_query_from_project_file(project_slug=project.slug)
62-
# # Make a query to check it returns result
63-
# result, _ = self._get_search_result(url=self.url, client=client,
64-
# search_params={'q': query, 'type': 'file'})
65-
# assert len(result) == 1
66-
#
67-
# # Delete all the HTML files of the project
68-
# HTMLFile.objects.filter(project=project).delete()
69-
# # Run the query again and this time there should not be any result
70-
# result, _ = self._get_search_result(url=self.url, client=client,
71-
# search_params={'q': query, 'type': 'file'})
72-
# assert len(result) == 0
73-
#
74-
# def test_file_search_show_projects(self, client, all_projects):
75-
# """Test that search result page shows list of projects while searching for files"""
76-
#
77-
# # `Github` word is present both in `kuma` and `pipeline` files
78-
# # so search with this phrase
79-
# result, page = self._get_search_result(url=self.url, client=client,
80-
# search_params={'q': 'GitHub', 'type': 'file'})
81-
#
82-
# # There should be 2 search result
83-
# assert len(result) == 2
84-
#
85-
# # there should be 2 projects in the left side column
86-
# content = page.find('.navigable .project-list')
87-
# assert len(content) == 2
88-
# text = content.text()
89-
#
90-
# # kuma and pipeline should be there
91-
# assert 'kuma' and 'pipeline' in text
92-
#
93-
# def test_file_search_filter_by_project(self, client):
94-
# """Test that search result are filtered according to project"""
95-
#
96-
# # `Github` word is present both in `kuma` and `pipeline` files
97-
# # so search with this phrase but filter through `kuma` project
98-
# search_params = {'q': 'GitHub', 'type': 'file', 'project': 'kuma'}
99-
# result, page = self._get_search_result(url=self.url, client=client,
100-
# search_params=search_params)
101-
#
102-
# # There should be 1 search result as we have filtered
103-
# assert len(result) == 1
104-
# content = page.find('.navigable .project-list')
105-
#
106-
# # kuma should should be there only
107-
# assert 'kuma' in result.text()
108-
# assert 'pipeline' not in result.text()
109-
#
110-
# # But there should be 2 projects in the left side column
111-
# # as the query is present in both projects
112-
# content = page.find('.navigable .project-list')
113-
# if len(content) != 2:
114-
# pytest.xfail("failing because currently all projects are not showing in project list")
115-
# else:
116-
# assert 'kuma' and 'pipeline' in content.text()
117-
#
118-
# @pytest.mark.xfail(reason="Versions are not showing correctly! Fixme while rewrite!")
119-
# def test_file_search_show_versions(self, client, all_projects, es_index, settings):
120-
# # override the settings to index all versions
121-
# settings.INDEX_ONLY_LATEST = False
122-
#
123-
# project = all_projects[0]
124-
# # Create some versions of the project
125-
# versions = [G(Version, project=project) for _ in range(3)]
126-
#
127-
# query = get_search_query_from_project_file(project_slug=project.slug)
128-
#
129-
# result, page = self._get_search_result(url=self.url, client=client,
130-
# search_params={'q': query, 'type': 'file'})
131-
#
132-
# # There should be only one result because by default
133-
# # only latest version result should be there
134-
# assert len(result) == 1
135-
#
136-
# content = page.find('.navigable .version-list')
137-
# # There should be total 4 versions
138-
# # one is latest, and other 3 that we created above
139-
# assert len(content) == 4
140-
#
141-
# project_versions = [v.slug for v in versions] + [LATEST]
142-
# content_versions = []
143-
# for element in content:
144-
# text = element.text_content()
145-
# # strip and split to keep the version slug only
146-
# slug = text.strip().split('\n')[0]
147-
# content_versions.append(slug)
148-
#
149-
# assert sorted(project_versions) == sorted(content_versions)
150-
#
151-
# def test_file_search_subprojects(self, client, all_projects, es_index):
152-
# """File search should return results from subprojects also"""
153-
# project = all_projects[0]
154-
# subproject = all_projects[1]
155-
# # Add another project as subproject of the project
156-
# project.add_subproject(subproject)
157-
#
158-
# # Now search with subproject content but explicitly filter by the parent project
159-
# query = get_search_query_from_project_file(project_slug=subproject.slug)
160-
# search_params = {'q': query, 'type': 'file', 'project': project.slug}
161-
# result, page = self._get_search_result(url=self.url, client=client,
162-
# search_params=search_params)
163-
#
164-
# assert len(result) == 1
22+
search_params = {'project': project.slug, 'version': version.slug, 'query': query}
23+
resp = api_client.get(self.url, search_params)
24+
assert resp.status_code == 200
25+
26+
data = resp.data['results']
27+
assert len(data) == 1
28+
project_data = data[0]
29+
assert project_data['project'] == project.slug
30+
31+
# Check highlight return correct object
32+
all_highlights = project_data['highlight'][data_type]
33+
for highlight in all_highlights:
34+
# Make it lower because our search is case insensitive
35+
assert query.lower() in highlight.lower()
36+
37+
def test_doc_search_filter_by_project(self, api_client):
38+
"""Test Doc search result are filtered according to project"""
39+
40+
# `Github` word is present both in `kuma` and `pipeline` files
41+
# so search with this phrase but filter through `kuma` project
42+
search_params = {'query': 'GitHub', 'project': 'kuma', 'version': 'latest'}
43+
resp = api_client.get(self.url, search_params)
44+
assert resp.status_code == 200
45+
46+
data = resp.data['results']
47+
assert len(data) == 1
48+
assert data[0]['project'] == 'kuma'
49+
50+
def test_doc_search_filter_by_version(self, api_client, project):
51+
"""Test Doc search result are filtered according to version"""
52+
query = get_search_query_from_project_file(project_slug=project.slug)
53+
latest_version = project.versions.all()[0]
54+
# Create another version
55+
dummy_version = G(Version, project=project)
56+
# Create HTMLFile same as the latest version
57+
latest_version_files = HTMLFile.objects.all().filter(version=latest_version)
58+
for f in latest_version_files:
59+
f.version = dummy_version
60+
# Make primary key to None, so django will create new object
61+
f.pk = None
62+
f.save()
63+
64+
search_params = {'query': query, 'project': project.slug, 'version': dummy_version.slug}
65+
resp = api_client.get(self.url, search_params)
66+
assert resp.status_code == 200
67+
68+
data = resp.data['results']
69+
assert len(data) == 1
70+
assert data[0]['project'] == project.slug
71+
72+
def test_doc_search_subprojects(self, api_client, all_projects):
73+
"""Test Document search return results from subprojects also"""
74+
project = all_projects[0]
75+
subproject = all_projects[1]
76+
version = project.versions.all()[0]
77+
# Add another project as subproject of the project
78+
project.add_subproject(subproject)
79+
80+
# Now search with subproject content but explicitly filter by the parent project
81+
query = get_search_query_from_project_file(project_slug=subproject.slug)
82+
search_params = {'query': query, 'project': project.slug, 'version': version.slug}
83+
resp = api_client.get(self.url, search_params)
84+
assert resp.status_code == 200
85+
86+
data = resp.data['results']
87+
assert len(data) == 1
88+
assert data[0]['project'] == subproject.slug

0 commit comments

Comments
 (0)