combo/tests/test_search.py

1524 lines
59 KiB
Python

import datetime
import json
import os
from unittest import mock
import pytest
from django.conf import settings
from django.contrib.auth.models import AnonymousUser, Group, User
from django.contrib.contenttypes.models import ContentType
from django.db import connection
from django.test import override_settings
from django.test.client import RequestFactory
from django.test.utils import CaptureQueriesContext
from django.urls import reverse
from django.utils.http import urlencode
from django.utils.timezone import now
from requests.exceptions import ConnectionError
from requests.models import Response
from combo.apps.search.engines import engines
from combo.apps.search.models import IndexedCell, SearchCell
from combo.apps.search.utils import index_site, search_site
from combo.data.models import JsonCell, LinkCell, LinkListCell, MenuCell, Page, PageSnapshot, TextCell
from .test_manager import login
pytestmark = pytest.mark.django_db
SEARCH_SERVICES = {
'search1': {
'label': 'Search 1',
'url': 'http://www.example.net/search/?q=%(q)s',
},
'search_tmpl': {
'label': 'Search with template',
'url': '[search_url]?q=%(q)s',
},
'search_alternate_key': {
'label': 'Search with alternate key',
'url': 'http://www.example.net/search/?q=%(q)s',
'data_key': 'results',
},
}
TEMPLATE_VARS = {'search_url': 'http://search.example.net/'}
class SearchServices:
def __init__(self, search_services):
self.search_services = search_services
def __enter__(self):
settings.COMBO_SEARCH_SERVICES = self.search_services
def __exit__(self, *args, **kwargs):
settings.COMBO_SEARCH_SERVICES = {}
def test_search_cell(settings, app):
settings.COMBO_SEARCH_SERVICES = SEARCH_SERVICES
page = Page(title='Search', slug='search_page', template_name='standard')
page.save()
cell = SearchCell(page=page, placeholder='content', order=0)
cell._search_services = {'data': ['search1']}
cell.input_placeholder = 'my placeholder'
cell.save()
# unknown cell pk
resp = app.get('/ajax/search/0/search1/?q=foo', status=404)
resp = cell.render({})
assert 'input' in resp
assert 'id="combo-search-input-%s"' % cell.pk in resp
assert 'autofocus' not in resp
assert 'placeholder="my placeholder"' in resp
cell.autofocus = True
cell.save()
resp = cell.render({})
assert 'h2' not in resp
assert 'autofocus' in resp
cell.slug = 'var-name'
cell.title = 'Custom Title'
context = {'request': RequestFactory().get('/?q_var_name=searchme')}
resp = cell.render(context)
assert '<h2>Custom Title</h2>' in resp
assert "$input.val('searchme');" in resp
with mock.patch('combo.apps.search.models.requests.get') as requests_get:
response = {'err': 0, 'data': []}
mock_json = mock.Mock()
mock_json.json.return_value = response
requests_get.return_value = mock_json
resp = app.get('/ajax/search/%s/search1/?q=foo' % cell.pk, status=200)
assert requests_get.call_args[0][0] == 'http://www.example.net/search/?q=foo'
assert '<li>' not in resp.text
assert 'no result found' in resp.text
resp = app.get('/ajax/search/%s/search1/?q=foo%%23bar' % cell.pk, status=200)
assert requests_get.call_args[0][0] == 'http://www.example.net/search/?q=foo%23bar'
assert '<li>' not in resp.text
assert 'no result found' in resp.text
response['data'] = [{'url': 'http://test', 'text': 'barbarbar'}]
resp = app.get('/ajax/search/%s/search1/?q=foo' % cell.pk, status=200)
assert resp.text.count('<li>') == 1
assert '<li><a href="http://test">barbarbar</a>' in resp.text
assert 'no result found' not in resp.text
response['data'] = [{'url': 'http://test', 'text': 'barbarbar', 'description': 'this is <b>html</b>'}]
resp = app.get('/ajax/search/%s/search1/?q=foo' % cell.pk, status=200)
assert resp.text.count('<li>') == 1
assert '<li><a href="http://test">barbarbar</a>' in resp.text
assert 'this is <b>html</b>' in resp.text
assert 'no result found' not in resp.text
resp = app.get('/ajax/search/%s/search1/?q=' % cell.pk, status=200)
assert '<li>' not in resp.text
assert 'no result found' not in resp.text
cell._search_services = {'data': ['search_alternate_key']}
cell.save()
response = {'results': [{'url': 'http://test', 'text': 'barbarbar'}]}
mock_json.json.return_value = response
resp = app.get('/ajax/search/%s/search_alternate_key/?q=foo' % cell.pk, status=200)
assert resp.text.count('<li>') == 1
assert '<li><a href="http://test">barbarbar</a>' in resp.text
# search engine does not return valid JSON
class FakedResponse(mock.Mock):
def json(self):
return json.loads(self.content)
requests_get.return_value = FakedResponse(content='notjson', status_code=200)
resp = app.get('/ajax/search/%s/search_alternate_key/?q=bar' % cell.pk, status=200)
assert requests_get.call_args[0][0] == 'http://www.example.net/search/?q=bar'
assert '<li>' not in resp.text
assert 'no result found' in resp.text
requests_get.return_value = FakedResponse(content='500withbadjson', status_code=500)
resp = app.get('/ajax/search/%s/search_alternate_key/?q=foo' % cell.pk, status=200)
assert requests_get.call_args[0][0] == 'http://www.example.net/search/?q=foo'
assert '<li>' not in resp.text
assert 'no result found' in resp.text
settings.TEMPLATE_VARS = TEMPLATE_VARS
cell._search_services = {'data': ['search_tmpl']}
cell.save()
with mock.patch('combo.apps.search.models.requests.get') as requests_get:
response = {'err': 0, 'data': []}
mock_json = mock.Mock()
mock_json.json.return_value = response
requests_get.return_value = mock_json
resp = app.get('/ajax/search/%s/search_tmpl/?q=foo' % cell.pk, status=200)
assert requests_get.call_args[0][0] == 'http://search.example.net/?q=foo'
# TEMPLATE_VARS are accessible in template
cell.slug = 'searchfoo'
cell.save()
templates_settings = [settings.TEMPLATES[0].copy()]
templates_settings[0]['DIRS'] = ['%s/templates-1' % os.path.abspath(os.path.dirname(__file__))]
with override_settings(TEMPLATES=templates_settings):
resp = app.get('/ajax/search/%s/search_tmpl/?q=bar' % cell.pk, status=200)
assert requests_get.call_args[0][0] == 'http://search.example.net/?q=bar'
assert 'searchfoo results.data=[]' in resp.text
assert 'search_url=http://search.example.net/' in resp.text
def test_search_cell_urlsplit(settings, app):
settings.KNOWN_SERVICES = {
'passerelle': {
'default': {
'title': 'test',
'url': 'http://example.org',
'secret': 'combo',
'orig': 'combo',
'backoffice-menu-url': 'http://example.org/manage/',
}
}
}
settings.COMBO_SEARCH_SERVICES = {
'search1': {
'label': 'Search 1',
'url': 'http://example.org/search/?q={{ q }}', # template var
'signature': 'foo',
},
}
page = Page.objects.create(title='Search', slug='search_page', template_name='standard')
cell = SearchCell.objects.create(
page=page, placeholder='content', order=0, _search_services={'data': ['search1']}
)
with mock.patch('combo.apps.search.models.requests.get') as requests_get:
response = {'err': 0, 'data': []}
mock_json = mock.Mock()
mock_json.json.return_value = response
requests_get.return_value = mock_json
resp = app.get('/ajax/search/%s/search1/?q=foo?' % cell.pk, status=200)
assert requests_get.call_args[0][0] == 'http://example.org/search/?q=foo?'
assert requests_get.call_args[1]['remote_service'] == 'auto'
with mock.patch('requests.Session.request') as mock_request:
response = {'err': 0, 'data': []}
mock_json = mock.Mock(status_code=200)
mock_json.json.return_value = response
mock_request.return_value = mock_json
resp = app.get('/ajax/search/%s/search1/?q=foo?' % cell.pk, status=200)
assert mock_request.call_args[0][1] == 'http://example.org/search/?orig=combo&q=foo?'
assert '<li>' not in resp.text
assert 'no result found' in resp.text
def test_search_global_context(app):
with SearchServices(SEARCH_SERVICES):
page = Page(title='Search', slug='search_page', template_name='standard')
page.save()
cell = SearchCell(page=page, placeholder='content', order=0)
cell._search_services = {'data': ['search1']}
cell.save()
assert cell.varname == ''
cell.slug = 'search-item'
cell.save()
assert cell.varname == 'search_item'
jsoncell = JsonCell(page=page, placeholder='content', order=0)
jsoncell.url = 'http://www.example.net/search/[search_item]/'
jsoncell.save()
url = (
reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.id, 'cell_reference': jsoncell.get_reference()},
)
+ '?search_item=foo'
)
with mock.patch('combo.utils.requests.get') as requests_get:
data = {'data': []}
requests_get.return_value = mock.Mock(json=lambda: data, status_code=200)
app.get(url)
assert requests_get.call_args[0][0] == 'http://www.example.net/search/foo/'
def test_search_percent_sign(app):
services = {
'search2': {
'label': 'Search 2',
'url': 'http://www.example.net/search/?email=test%40example.net&q=%(q)s',
'hit_url_template': 'http://example.net/{{id}}/',
'hit_label_template': '{{a}} {{b}}',
'hit_description_template': 'description {{a}}',
}
}
with SearchServices(services):
page = Page(title='Search', slug='search_page', template_name='standard')
page.save()
cell = SearchCell(page=page, placeholder='content', order=0)
cell._search_services = {'data': ['search2']}
cell.save()
with mock.patch('combo.apps.search.models.requests.get') as requests_get:
response = {
'err': 0,
'data': [{'id': '123', 'a': 'A', 'b': 'B'}],
}
mock_json = mock.Mock()
mock_json.json.return_value = response
requests_get.return_value = mock_json
resp = app.get('/ajax/search/%s/search2/?q=foo' % cell.pk, status=200)
assert resp.text.count('<li>') == 1
assert '<li><a href="http://example.net/123/">A B</a>' in resp.text
assert '<div>description A</div>' in resp.text
def test_search_custom_templates(app):
services = {
'search2': {
'label': 'Search 2',
'url': 'http://www.example.net/search/?q=%(q)s',
'hit_url_template': 'http://example.net/{{id}}/',
'hit_label_template': '{{a}} {{b}}',
'hit_description_template': 'description {{a}}',
}
}
with SearchServices(services):
page = Page(title='Search', slug='search_page', template_name='standard')
page.save()
cell = SearchCell(page=page, placeholder='content', order=0)
cell._search_services = {'data': ['search2']}
cell.save()
with mock.patch('combo.apps.search.models.requests.get') as requests_get:
response = {
'err': 0,
'data': [{'id': '123', 'a': 'A', 'b': 'B'}],
}
mock_json = mock.Mock()
mock_json.json.return_value = response
requests_get.return_value = mock_json
resp = app.get('/ajax/search/%s/search2/?q=foo' % cell.pk, status=200)
assert resp.text.count('<li>') == 1
assert '<li><a href="http://example.net/123/">A B</a>' in resp.text
assert '<div>description A</div>' in resp.text
def test_search_cell_visibility(settings, app):
request = RequestFactory().get('/')
page = Page.objects.create(title='example page', slug='example-page')
settings.COMBO_SEARCH_SERVICES = SEARCH_SERVICES
cell = SearchCell(page=page, order=0)
assert not cell.is_visible(request)
cell._search_services = {'data': ['_text']}
assert cell.is_visible(request)
def test_search_contents():
page = Page(title='example page', slug='example-page')
page.save()
# private cells are indexed
cell = TextCell(page=page, text='foobar', public=False, order=0)
assert cell.render_for_search().strip() == 'foobar'
# no indexation of empty cells (is_relevant check)
cell = TextCell(page=page, text='', order=0)
assert cell.render_for_search() == ''
# indexation
cell = TextCell(page=page, text='<p>foobar</p>', order=0)
assert cell.render_for_search().strip() == 'foobar'
# no indexation of menu cells
cell = MenuCell(page=page, order=0)
assert cell.exclude_from_search is True
def test_search_contents_index():
page = Page(title='example page', slug='example-page')
page.public = True
page.save()
cell = TextCell(page=page, placeholder='content', text='<p>foobar</p>', order=0)
cell.save()
request = RequestFactory().get('/')
request.user = AnonymousUser()
hits = search_site(request, 'foobar')
assert len(hits) == 0
index_site()
hits = search_site(request, 'foobar')
assert len(hits) == 1
def test_search_contents_technical_placeholder():
page = Page(title='example page', slug='example-page')
page.save()
TextCell(page=page, text='<p>foobar</p>', order=0, placeholder='_off').save()
TextCell(page=page, text='<p>barfoo</p>', order=0, placeholder='content').save()
request = RequestFactory().get('/')
request.user = AnonymousUser()
index_site()
hits = search_site(request, 'foobar')
assert len(hits) == 0
hits = search_site(request, 'barfoo')
assert len(hits) == 1
def test_search_api(app):
page = Page.objects.create(title='example page', slug='example-page')
TextCell.objects.create(page=page, placeholder='content', text='<p>foobar baz</p>', order=0)
second_page = Page.objects.create(
title='second page', slug='second-page', description='Foo Bar Description'
)
TextCell.objects.create(page=second_page, placeholder='content', text='<p>other baz</p>', order=0)
index_site()
cell = SearchCell.objects.create(
page=page, placeholder='content', _search_services={'data': ['_text']}, order=0
)
resp = app.get('/ajax/search/%s/_text/?q=foobar' % cell.id, status=200)
assert resp.text.count('<li') == 1
assert 'example page' in resp.text
resp = app.get('/ajax/search/%s/_text/?q=other' % cell.id, status=200)
assert resp.text.count('<li') == 1
result = resp.text
result = result.replace(' ', '')
result = result.replace('\n', '')
assert '<li><a href="/second-page/">second page</a></li>' in result
# indexed cell without page
cell_type = ContentType.objects.get_for_model(cell)
IndexedCell.objects.create(
title='other', cell_type=cell_type, cell_pk=cell.pk, public_access=True, url='fake'
)
# enable description
cell._search_services['options'] = {'_text': {'with_description': True}}
cell.save()
resp = app.get('/ajax/search/%s/_text/?q=other' % cell.id, status=200)
assert resp.text.count('<li') == 2
result = resp.text
result = result.replace(' ', '')
result = result.replace('\n', '')
assert '<li><a href="fake">other</a></li>' in result
assert '<li><a href="/second-page/">second page</a><div>Foo Bar Description</div></li>' in result
resp = app.get('/ajax/search/%s/_text/?q=baz' % cell.id, status=200)
assert resp.text.count('<li') == 2
resp = app.get('/ajax/search/%s/_text/?q=quux' % cell.id, status=200)
assert resp.text.count('<li') == 0
def test_search_on_root_page_api(settings, app):
settings.KNOWN_SERVICES = {}
# not indexed: with sub_slug
page = Page.objects.create(title='example page', slug='example-page', sub_slug='foo')
TextCell.objects.create(page=page, placeholder='content', text='<p>foobar baz</p>', order=0)
second_page = Page.objects.create(title='second page', slug='second-page')
TextCell.objects.create(page=second_page, placeholder='content', text='<p>other baz</p>', order=0)
sub_second_page = Page.objects.create(parent=second_page, title='sub second page', slug='sub-second-page')
TextCell.objects.create(page=sub_second_page, placeholder='content', text='<p>other baz</p>', order=0)
# not indexed: with snapshot
third_page = Page.objects.create(title='second page', slug='third-page')
TextCell.objects.create(page=third_page, placeholder='content', text='<p>other baz again</p>', order=0)
third_page.snapshot = PageSnapshot.objects.create(page=third_page)
third_page.save()
index_site()
cell = SearchCell.objects.create(
page=page, placeholder='content', _search_services={'data': ['_text']}, order=1
)
resp = app.get('/ajax/search/%s/_text/?q=baz' % cell.pk, status=200)
assert resp.text.count('<li') == 2
cell._search_services = {'data': ['_text_page_second-page']}
cell.save()
resp = app.get('/ajax/search/%s/_text_page_second-page/?q=baz' % cell.pk, status=200)
assert resp.text.count('<li') == 2
cell._search_services = {'data': ['_text_page_sub-second-page']}
cell.save()
resp = app.get('/ajax/search/%s/_text_page_sub-second-page/?q=baz' % cell.pk, status=200)
assert resp.text.count('<li') == 1
# invalid page, search everywhere
# with sub_slug
cell._search_services = {'data': ['_text_page_example-page']}
cell.save()
resp = app.get('/ajax/search/%s/_text_page_example-page/?q=baz' % cell.pk, status=200)
assert resp.text.count('<li') == 2
# with snapshot
cell._search_services = {'data': ['_text_page_third-page']}
cell.save()
resp = app.get('/ajax/search/%s/_text_page_third-page/?q=baz' % cell.pk, status=200)
assert resp.text.count('<li') == 2
# page does not exists, search everywhere
cell._search_services = {'data': ['_text_page_foo']}
cell.save()
resp = app.get('/ajax/search/%s/_text_page_foo/?q=baz' % cell.pk, status=200)
assert resp.text.count('<li') == 2
# slug is not unique, search everywhere
page.slug = 'sub-second-page'
page.sub_slug = ''
page.save()
cell._search_services = {'data': ['_text_page_sub-second-page']}
cell.save()
resp = app.get('/ajax/search/%s/_text_page_sub-second-page/?q=baz' % cell.pk, status=200)
assert resp.text.count('<li') == 2
# search with custom title
cell._search_services = {
'data': ['_text', '_text_page_sub-second-page'],
'options': {'_text_page_sub-second-page': {'title': 'Custom Title'}},
}
cell.save()
resp = app.get('/ajax/search/%s/_text_page_sub-second-page/?q=baz' % cell.pk, status=200)
assert 'Custom Title' in resp.text
assert resp.text.count('<li') == 2
def test_search_external_links(app):
page = Page(title='example page', slug='example-page')
page.save()
cell = SearchCell(page=page, placeholder='content', _search_services={'data': ['_text']}, order=0)
cell.save()
index_site()
request = RequestFactory().get('/')
request.user = AnonymousUser()
hits = search_site(request, 'foobar')
assert len(hits) == 0
LinkCell(title='foobar', url='http://example.net', page=page, placeholder='content', order=0).save()
index_site()
hits = search_site(request, 'foobar')
assert len(hits) == 1
assert hits[0]['text'] == 'foobar'
assert hits[0]['url'] == 'http://example.net'
# second link with same target
LinkCell(title='baz', url='http://example.net', page=page, placeholder='content', order=0).save()
index_site()
# add a second link with the same target
hits = search_site(request, 'baz')
assert len(hits) == 1
assert hits[0]['text'] in ('foobar', 'baz')
assert hits[0]['url'] == 'http://example.net'
hits = search_site(request, 'foobar')
assert len(hits) == 1
assert hits[0]['text'] in ('foobar', 'baz')
assert hits[0]['url'] == 'http://example.net'
# update cell to search content only on its page
cell._search_services = {'data': ['_text_page_%s' % page.slug]}
cell.save()
hits = search_site(request, 'foobar', pages=[page])
assert len(hits) == 1
def test_search_external_links_in_list_of_links(app):
page = Page(title='example page', slug='example-page')
page.save()
cell = SearchCell(page=page, placeholder='content', _search_services={'data': ['_text']}, order=0)
cell.save()
index_site()
request = RequestFactory().get('/')
request.user = AnonymousUser()
hits = search_site(request, 'foobar')
assert len(hits) == 0
cell = LinkListCell.objects.create(order=0, page=page, placeholder='content')
LinkCell.objects.create(
page=page,
placeholder=cell.link_placeholder,
title='foobar example',
url='http://example.net',
order=0,
)
LinkCell.objects.create(
page=page,
placeholder=cell.link_placeholder,
title='Example 2 Site',
url='http://example.org/',
order=1,
)
index_site()
hits = search_site(request, 'foobar')
assert len(hits) == 1
assert hits[0]['text'] == 'foobar example'
assert hits[0]['url'] == 'http://example.net'
hits = search_site(request, 'example')
assert len(hits) == 2
def test_manager_search_cell(settings, app, admin_user):
page = Page.objects.create(title='One', slug='one', template_name='standard')
cell = SearchCell.objects.create(page=page, placeholder='content', order=0)
app = login(app)
settings.KNOWN_SERVICES = {}
assert cell._search_services == {}
resp = app.get('/manage/pages/%s/' % page.pk)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/add/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/delete/' % (page.pk, cell.pk)
not in resp.text
)
settings.COMBO_SEARCH_SERVICES = SEARCH_SERVICES
resp = app.get('/manage/pages/%s/' % page.pk)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/add/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search1/add/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_tmpl/add/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_alternate_key/add/'
% (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/edit/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search1/edit/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_tmpl/edit/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_alternate_key/edit/'
% (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/delete/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search1/delete/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_tmpl/delete/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_alternate_key/delete/'
% (page.pk, cell.pk)
not in resp.text
)
# add engines
resp = resp.click(href='.*/search_searchcell-%s/engine/_text/add/' % cell.pk)
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
resp = app.get('/manage/pages/%s/' % page.pk)
resp = resp.click(href='.*/search_searchcell-%s/engine/search1/add/' % cell.pk)
resp = app.get('/manage/pages/%s/' % page.pk)
resp = resp.click(href='.*/search_searchcell-%s/engine/search_tmpl/add/' % cell.pk)
cell.refresh_from_db()
assert cell._search_services['data'] == ['_text', 'search1', 'search_tmpl']
resp = app.get('/manage/pages/%s/' % page.pk)
# '_text' is always available
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/add/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search1/add/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_tmpl/add/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_alternate_key/add/'
% (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/edit/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search1/edit/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_tmpl/edit/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_alternate_key/edit/'
% (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/delete/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search1/delete/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_tmpl/delete/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_alternate_key/delete/'
% (page.pk, cell.pk)
not in resp.text
)
# edit engines
resp = resp.click(href='.*/search_searchcell-%s/engine/_text/edit/' % cell.pk)
resp.form['title'] = 'Custom Title'
resp.form['with_description'] = True
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['options']['_text'] == {
'title': 'Custom Title',
'with_description': True,
}
# delete engines
resp = app.get('/manage/pages/%s/' % page.pk)
resp = resp.click(href='.*/search_searchcell-%s/engine/_text/delete/' % cell.pk)
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == ['search1', 'search_tmpl']
settings.COMBO_SEARCH_SERVICES = {}
# check there's no crash if search engines are removed from config
resp = app.get('/manage/pages/%s/' % page.pk)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search1/add/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_tmpl/add/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_alternate_key/add/'
% (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search1/delete/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_tmpl/delete/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/search_alternate_key/delete/'
% (page.pk, cell.pk)
not in resp.text
)
# add engines on page and sub pages
resp = resp.click(href='.*/search_searchcell-%s/engine/_text/add/' % cell.pk)
assert list(resp.context['form']['selected_page'].field.queryset) == [page]
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == ['search1', 'search_tmpl', '_text']
resp = app.get('/manage/pages/%s/' % page.pk)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/add/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/delete/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text_page_one/delete/' % (page.pk, cell.pk)
not in resp.text
)
resp = resp.click(href='.*/search_searchcell-%s/engine/_text/add/' % cell.pk)
resp.form['selected_page'] = page.pk
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == ['search1', 'search_tmpl', '_text', '_text_page_one']
resp = app.get('/manage/pages/%s/' % page.pk)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/add/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text/delete/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/_text_page_one/delete/' % (page.pk, cell.pk)
in resp.text
)
# remove engine
resp = resp.click(href='.*/search_searchcell-%s/engine/_text_page_one/delete/' % cell.pk)
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
# add engine on page and sub pages, with a custom title and description
resp = app.get('/manage/pages/%s/' % page.pk)
resp = resp.click(href='.*/search_searchcell-%s/engine/_text/add/' % cell.pk)
resp.form['selected_page'] = page.pk
resp.form['title'] = 'Custom Title'
resp.form['with_description'] = True
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == ['search1', 'search_tmpl', '_text', '_text_page_one']
assert cell._search_services['options']['_text_page_one'] == {
'title': 'Custom Title',
'with_description': True,
}
def test_manager_search_cell_order(settings, app, admin_user):
settings.COMBO_SEARCH_SERVICES = SEARCH_SERVICES
page = Page.objects.create(title='One', slug='one', template_name='standard')
cell = SearchCell.objects.create(
page=page,
placeholder='content',
order=0,
_search_services={'data': ['_text', 'search1', 'search_tmpl']},
)
params = []
new_order = [2, 3, 1]
for service_slug, new_pos in zip(cell._search_services['data'], new_order):
params.append(('pos_%s' % service_slug, str(new_pos)))
app = login(app)
resp = app.get(
'/manage/search/pages/%s/cell/%s/engine/order?%s' % (page.pk, cell.get_reference(), urlencode(params))
)
assert resp.status_code == 204
cell.refresh_from_db()
assert cell._search_services == {'data': ['search_tmpl', '_text', 'search1']}
def test_manager_search_cell_tabs(app, admin_user):
page = Page.objects.create(title='One', slug='one', template_name='standard')
SearchCell.objects.create(page=page, placeholder='content', order=0)
app = login(app)
resp = app.get('/manage/pages/%s/' % page.pk)
assert not resp.pyquery('[data-tab-slug="general"] input[name$="title"]')
assert resp.pyquery('[data-tab-slug="appearance"] input[name$="title"]')
def test_manager_waiting_index_message(app, admin_user):
page = Page.objects.create(title='One', slug='one', template_name='standard')
cell = SearchCell.objects.create(page=page, placeholder='content', order=0)
TextCell.objects.create(page=page, placeholder='content', text='<p>foobar</p>', order=0)
app = login(app)
resp = app.get('/manage/pages/%s/' % page.pk)
assert 'Content indexing has been scheduled' not in resp.text
resp = resp.click(href='.*/search_searchcell-%s/engine/_text/add/' % cell.pk)
resp = resp.form.submit('submit')
resp = app.get('/manage/pages/%s/' % page.pk)
assert 'Content indexing has been scheduled' in resp.text
index_site()
resp = app.get('/manage/pages/%s/' % page.pk)
assert 'Content indexing has been scheduled' not in resp.text
def test_wcs_search_engines(settings, app):
settings.KNOWN_SERVICES = {}
search_engines = engines.get_engines()
assert 'tracking-code' not in search_engines
assert len([x for x in search_engines if x.startswith('formdata:')]) == 0
assert len([x for x in search_engines if x.startswith('cards:')]) == 0
settings.KNOWN_SERVICES = {'wcs': {'default': {'title': 'test', 'url': 'http://127.0.0.1:8999/'}}}
settings.TEMPLATE_VARS['is_portal_agent'] = False
search_engines = engines.get_engines()
assert len([x for x in search_engines if x.startswith('formdata:')]) == 0
settings.TEMPLATE_VARS['is_portal_agent'] = True
search_engines = engines.get_engines()
assert len([x for x in search_engines if x.startswith('formdata:')]) == 1
for key, engine in search_engines.items():
if key.startswith('formdata:'):
assert '&include-anonymised=off' in engine['url']
# create a page with sub_slug to enable card engines
Page.objects.create(slug='foo', title='Foo', sub_slug='(?P<foo_id>[a-z0-9]+)')
with mock.patch('combo.apps.wcs.utils.get_wcs_json') as mock_wcs:
# no card model found
mock_wcs.return_value = {}
search_engines = engines.get_engines()
assert len([x for x in search_engines if x.startswith('cards:')]) == 0
assert mock_wcs.call_args_list[0][0][1] == 'api/cards/@list'
mock_wcs.return_value = {'data': []}
search_engines = engines.get_engines()
assert len([x for x in search_engines if x.startswith('cards:')]) == 0
# card model found, but related page does not exist
mock_wcs.return_value = {'data': [{'id': 'card-bar', 'text': 'Card Bar'}]}
search_engines = engines.get_engines()
assert len([x for x in search_engines if x.startswith('cards:')]) == 0
# related page exists
page = Page.objects.create(slug='bar', title='Bar')
for sub_slug in ['(?P<card-bar_id>[a-z0-9]+)', 'card-bar_id']:
page.sub_slug = sub_slug
page.save()
search_engines = engines.get_engines()
assert len([x for x in search_engines if x.startswith('cards:')]) == 1
assert 'cards:c21f969b:card-bar' in search_engines
card_engine = search_engines['cards:c21f969b:card-bar']
assert card_engine['url'] == (
'http://127.0.0.1:8999/api/cards/card-bar/list/'
'{% if search_service.selected_custom_view %}{{ search_service.selected_custom_view }}{% endif %}'
'?{% if not search_service.without_user %}NameID={{ user_nameid }}&{% endif %}q=%(q)s'
)
assert card_engine['hit_url_template'] == '/bar/{{ id }}'
def test_wcs_errors(settings, app):
settings.KNOWN_SERVICES = {'wcs': {'default': {'title': 'test', 'url': 'http://127.0.0.1:8999/'}}}
settings.TEMPLATE_VARS['is_portal_agent'] = True
Page.objects.create(slug='foo', title='Foo', sub_slug='(?P<foo_id>[a-z0-9]+)')
with mock.patch('combo.apps.wcs.utils.requests.get') as requests_get:
mock_resp = Response()
mock_resp.status_code = 500
requests_get.return_value = mock_resp
assert set(engines.get_engines().keys()) == {'tracking-code', 'formdata:c21f969b', '_text'}
with mock.patch('combo.apps.wcs.utils.requests.get') as requests_get:
requests_get.side_effect = ConnectionError()
assert set(engines.get_engines().keys()) == {'tracking-code', 'formdata:c21f969b', '_text'}
with mock.patch('combo.apps.wcs.utils.requests.get') as requests_get:
mock_resp = Response()
mock_resp.status_code = 404
requests_get.return_value = mock_resp
assert set(engines.get_engines().keys()) == {'tracking-code', 'formdata:c21f969b', '_text'}
@mock.patch('combo.apps.wcs.utils.get_wcs_json')
def test_wcs_add_search_engines(mock_wcs, settings, app, admin_user):
settings.KNOWN_SERVICES = {'wcs': {'default': {'title': 'test', 'url': 'http://127.0.0.1:8999/'}}}
settings.TEMPLATE_VARS['is_portal_agent'] = True
Page.objects.create(slug='bar', title='Bar', sub_slug='card-bar_id')
mock_wcs.return_value = {'data': [{'id': 'card-bar', 'text': 'Card Bar'}]}
page = Page.objects.create(title='One', slug='one', template_name='standard')
cell = SearchCell.objects.create(page=page, placeholder='content', order=0)
app = login(app)
resp = app.get('/manage/pages/%s/' % page.pk)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/cards:c21f969b:card-bar/add/'
% (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/cards:c21f969b:card-bar/edit/'
% (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/cards:c21f969b:card-bar/delete/'
% (page.pk, cell.pk)
not in resp.text
)
resp = resp.click(href='.*/search_searchcell-%s/engine/cards:c21f969b:card-bar/add/' % cell.pk)
assert resp.context['form'].fields['selected_view'].choices == [(None, '- All cards -')]
resp.form['title'] = 'Custom Title'
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == ['cards:c21f969b:card-bar']
assert cell._search_services['options']['cards:c21f969b:card-bar'] == {'title': 'Custom Title'}
mock_wcs.return_value = {
'data': [
{
'id': 'card-bar',
'text': 'Card Bar',
'custom_views': [{'id': 'foo', 'text': 'Foo'}, {'id': 'baz', 'text': 'Baz'}],
}
]
}
resp = resp.follow()
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/cards:c21f969b:card-bar/add/'
% (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/cards:c21f969b:card-bar/edit/'
% (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/cards:c21f969b:card-bar/delete/'
% (page.pk, cell.pk)
in resp.text
)
resp = resp.click(href='.*/search_searchcell-%s/engine/cards:c21f969b:card-bar/add/' % cell.pk)
assert resp.context['form'].fields['selected_view'].choices == [
(None, '- All cards -'),
('foo', 'Foo'),
('baz', 'Baz'),
]
resp.form['selected_view'] = 'foo'
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == ['cards:c21f969b:card-bar', 'cards:c21f969b:card-bar:foo']
# edit engine
resp = resp.follow()
resp = resp.click(href='.*/search_searchcell-%s/engine/cards:c21f969b:card-bar/edit/' % cell.pk)
resp.form['title'] = 'Custom Title Foo Bar'
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['options']['cards:c21f969b:card-bar'] == {'title': 'Custom Title Foo Bar'}
# remove engine
resp = resp.follow()
resp = resp.click(href='.*/search_searchcell-%s/engine/cards:c21f969b:card-bar:foo/delete/' % cell.pk)
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == ['cards:c21f969b:card-bar']
# without_user ?
resp = resp.follow()
resp = resp.click(href='.*/search_searchcell-%s/engine/cards:c21f969b:card-bar/add/' % cell.pk)
resp.form['without_user'] = True
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == [
'cards:c21f969b:card-bar',
'cards:c21f969b:card-bar__without-user__',
]
resp = resp.follow()
resp = resp.click(href='.*/search_searchcell-%s/engine/cards:c21f969b:card-bar/add/' % cell.pk)
resp.form['selected_view'] = 'foo'
resp.form['without_user'] = True
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == [
'cards:c21f969b:card-bar',
'cards:c21f969b:card-bar__without-user__',
'cards:c21f969b:card-bar__without-user__:foo',
]
@mock.patch('combo.apps.wcs.utils.get_wcs_json')
@mock.patch('combo.apps.search.models.requests.get')
def test_wcs_search_cell(requests_get, mock_wcs, settings, app):
settings.KNOWN_SERVICES = {'wcs': {'default': {'title': 'test', 'url': 'http://127.0.0.1:8999/'}}}
settings.TEMPLATE_VARS['is_portal_agent'] = True
Page.objects.create(slug='bar', title='Bar', sub_slug='(?P<card-bar_id>[a-z0-9]+)')
mock_wcs.return_value = {
'data': [{'id': 'card-bar', 'text': 'Card Bar', 'custom_views': [{'id': 'foo', 'text': 'Foo'}]}]
}
response = {'err': 0, 'data': []}
mock_json = mock.Mock()
mock_json.json.return_value = response
requests_get.return_value = mock_json
page = Page.objects.create(title='One', slug='one', template_name='standard')
cell = SearchCell.objects.create(page=page, placeholder='content', order=0)
cell._search_services = {
'data': [
'cards:c21f969b:card-bar',
'cards:c21f969b:card-bar:foo',
'cards:c21f969b:card-bar__without-user__',
'cards:c21f969b:card-bar__without-user__:foo',
]
}
cell.save()
assert cell.search_services[0]['custom_views'] == [{'id': 'foo', 'text': 'Foo'}]
assert cell.search_services[0]['label'] == 'Card Bar'
assert cell.search_services[0]['slug'] == 'cards:c21f969b:card-bar'
assert 'selected_custom_view' not in cell.search_services[0]
assert cell.search_services[1]['custom_views'] == [{'id': 'foo', 'text': 'Foo'}]
assert cell.search_services[1]['label'] == 'Card Bar - Foo'
assert cell.search_services[1]['slug'] == 'cards:c21f969b:card-bar:foo'
assert cell.search_services[1]['selected_custom_view'] == 'foo'
app.get('/ajax/search/%s/cards:c21f969b:card-bar/?q=foobar' % cell.pk)
assert (
requests_get.call_args_list[0][0][0]
== 'http://127.0.0.1:8999/api/cards/card-bar/list/?NameID=&q=foobar'
)
requests_get.reset_mock()
app.get('/ajax/search/%s/cards:c21f969b:card-bar:foo/?q=foobar' % cell.pk)
assert (
requests_get.call_args_list[0][0][0]
== 'http://127.0.0.1:8999/api/cards/card-bar/list/foo?NameID=&q=foobar'
)
requests_get.reset_mock()
app.get('/ajax/search/%s/cards:c21f969b:card-bar__without-user__/?q=foobar' % cell.pk)
assert requests_get.call_args_list[0][0][0] == 'http://127.0.0.1:8999/api/cards/card-bar/list/?q=foobar'
requests_get.reset_mock()
app.get('/ajax/search/%s/cards:c21f969b:card-bar__without-user__:foo/?q=foobar' % cell.pk)
assert (
requests_get.call_args_list[0][0][0] == 'http://127.0.0.1:8999/api/cards/card-bar/list/foo?q=foobar'
)
# test with unknown view selected
mock_wcs.return_value = {'data': [{'id': 'card-bar', 'text': 'Card Bar', 'custom_views': []}]}
del cell.search_services # clear cache
assert cell.search_services[0]['custom_views'] == []
assert cell.search_services[0]['label'] == 'Card Bar'
assert cell.search_services[0]['slug'] == 'cards:c21f969b:card-bar'
assert 'selected_custom_view' not in cell.search_services[0]
assert cell.search_services[1]['custom_views'] == []
assert cell.search_services[1]['label'] == 'Card Bar - None' # unknown view
assert cell.search_services[1]['slug'] == 'cards:c21f969b:card-bar:foo'
assert cell.search_services[1]['selected_custom_view'] == 'foo'
requests_get.reset_mock()
app.get('/ajax/search/%s/cards:c21f969b:card-bar:foo/?q=foobar' % cell.pk)
assert (
requests_get.call_args_list[0][0][0]
== 'http://127.0.0.1:8999/api/cards/card-bar/list/foo?NameID=&q=foobar'
)
# test with unknown card model selected
mock_wcs.return_value = {'data': []}
del cell.search_services # clear cache
assert cell.search_services == []
requests_get.reset_mock()
app.get('/ajax/search/%s/cards:c21f969b:card-bar/?q=foobar' % cell.pk)
assert requests_get.call_args_list == []
def test_profile_search_engines(settings, app):
settings.KNOWN_SERVICES = {}
search_engines = engines.get_engines()
assert 'users' not in search_engines
settings.KNOWN_SERVICES = {'authentic': {'default': {'title': 'authentic', 'url': 'https://authentic/'}}}
search_engines = engines.get_engines()
assert 'users' not in search_engines
page = Page.objects.create(slug='users', title='Users', sub_slug='(?P<name_id>[a-z0-9]+)')
search_engines = engines.get_engines()
assert 'users' in search_engines
page.sub_slug = 'name_id'
page.save()
search_engines = engines.get_engines()
assert 'users' in search_engines
def test_profile_add_search_engines(settings, app, admin_user):
settings.KNOWN_SERVICES = {'authentic': {'default': {'title': 'authentic', 'url': 'https://authentic/'}}}
Page.objects.create(slug='users', title='Users', sub_slug='(?P<name_id>[a-z0-9]+)')
page = Page.objects.create(title='One', slug='one', template_name='standard')
cell = SearchCell.objects.create(page=page, placeholder='content', order=0)
app = login(app)
resp = app.get('/manage/pages/%s/' % page.pk)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/users/add/' % (page.pk, cell.pk)
in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/users/edit/' % (page.pk, cell.pk)
not in resp.text
)
assert (
'/manage/search/pages/%s/cell/search_searchcell-%s/engine/users/delete/' % (page.pk, cell.pk)
not in resp.text
)
# check default value from settings is retained in configuration form
users_engine = engines.get('users').copy()
settings.COMBO_SEARCH_SERVICES = {'users': users_engine}
with mock.patch.dict(
users_engine, {'hit_description_template': users_engine['hit_description_template'] + ' xxx'}
):
form_resp = resp.click(href='.*/search_searchcell-%s/engine/users/add/' % cell.pk)
assert form_resp.form['description_template'].value.endswith(' xxx')
resp = resp.click(href='.*/search_searchcell-%s/engine/users/add/' % cell.pk)
resp.form['title'] = 'Custom Title'
assert not resp.form['description_template'].value.endswith(' xxx')
resp.form['description_template'] = 'Foo Bar'
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == ['users']
assert cell._search_services['options']['users'] == {
'title': 'Custom Title',
'description_template': 'Foo Bar',
}
# edit engine
resp = app.get('/manage/pages/%s/' % page.pk)
resp = resp.click(href='.*/search_searchcell-%s/engine/users/edit/' % cell.pk)
resp.form['title'] = 'Custom Title Foo Bar'
resp.form['description_template'] = ''
resp.form['description_template'] = 'Foo Bar Baz'
resp = resp.form.submit('submit')
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['options']['users'] == {
'title': 'Custom Title Foo Bar',
'description_template': 'Foo Bar Baz',
}
# remove engine
resp = resp.follow()
resp = resp.click(href='.*/search_searchcell-%s/engine/users/delete/' % cell.pk)
assert resp.status_int == 302
assert resp.location.endswith('/manage/pages/%s/#cell-%s' % (page.pk, cell.get_reference()))
cell.refresh_from_db()
assert cell._search_services['data'] == []
def test_search_profile(settings, app):
settings.KNOWN_SERVICES = {'authentic': {'default': {'title': 'authentic', 'url': 'https://authentic/'}}}
Page.objects.create(slug='users', title='Users', sub_slug='(?P<name_id>[a-z0-9]+)')
page = Page.objects.create(title='One', slug='one', template_name='standard')
cell = SearchCell.objects.create(page=page, placeholder='content', order=0)
cell._search_services = {'data': ['users']}
cell.save()
with mock.patch('combo.apps.search.models.requests.get') as requests_get:
response = {
'results': [
{
'id': '1',
'uuid': '982a7c65f6c14a2c80307afd9c4bb368',
'first_name': 'Foo',
'last_name': 'Bar',
'email': 'foo@bar.com',
'phone': '0505050505',
'mobile': '0606060606',
'address': 'There',
'zipcode': '75000',
'city': 'Paris',
}
],
}
mock_json = mock.Mock()
mock_json.json.return_value = response
requests_get.return_value = mock_json
resp = app.get('/ajax/search/%s/users/?q=foo' % cell.pk, status=200)
assert resp.text.count('<li>') == 1
assert '<li><a href="/users/982a7c65f6c14a2c80307afd9c4bb368/">Foo Bar</a>' in resp.text
assert '<div>foo@bar.com 📞 0505050505 📱 0606060606 📨 There 75000 Paris</div>' in resp.text.replace(
'\n', ''
)
cell._search_services['options'] = {'users': {'description_template': '{{ email }} Foo Bar'}}
cell.save()
resp = app.get('/ajax/search/%s/users/?q=foo' % cell.pk, status=200)
assert resp.text.count('<li>') == 1
assert '<li><a href="/users/982a7c65f6c14a2c80307afd9c4bb368/">Foo Bar</a>' in resp.text
assert '<div>foo@bar.com Foo Bar</div>' in resp.text
def test_private_search(app):
page = Page(title='example page', slug='example-page')
page.save()
TextCell(page=page, placeholder='content', text='<p>foobar</p>', order=0, public=False).save()
TextCell(page=page, placeholder='content', text='<p>barfoo</p>', order=0, public=True).save()
request = RequestFactory().get('/')
request.user = AnonymousUser()
index_site()
hits = search_site(request, 'foobar')
assert len(hits) == 0
hits = search_site(request, 'barfoo')
assert len(hits) == 1
request.user = User.objects.create_user(username='normal-user')
hits = search_site(request, 'foobar')
assert len(hits) == 1
hits = search_site(request, 'barfoo')
assert len(hits) == 1
def test_restricted_search(app):
group = Group(name='plop')
group.save()
page = Page(title='example page', slug='example-page')
page.save()
cell = TextCell(page=page, placeholder='content', text='<p>foobar</p>', order=0, public=False)
cell.save()
cell.groups.set([group])
TextCell(page=page, placeholder='content', text='<p>barfoo</p>', order=0, public=False).save()
index_site()
# first cell is restricted, it's not found
request = RequestFactory().get('/')
request.user = User.objects.create_user(username='normal-user')
hits = search_site(request, 'foobar')
assert len(hits) == 0
hits = search_site(request, 'barfoo')
assert len(hits) == 1
page.groups.set([group])
index_site()
# page is restricted, no cell is found
hits = search_site(request, 'foobar')
assert len(hits) == 0
hits = search_site(request, 'barfoo')
assert len(hits) == 0
# user is in group, gets a result
request.user.groups.set([group])
hits = search_site(request, 'foobar')
assert len(hits) == 1
hits = search_site(request, 'barfoo')
assert len(hits) == 1
# cell is excluded from group view
cell.restricted_to_unlogged = True
cell.save()
index_site()
hits = search_site(request, 'foobar')
assert len(hits) == 0
hits = search_site(request, 'barfoo')
assert len(hits) == 1
def test_no_sub_slug_search(app):
page = Page(title='example page', slug='example-page')
page.save()
TextCell(page=page, placeholder='content', text='<p>foobar</p>', order=0, public=True).save()
page = Page(title='example page with sub_slug', slug='sub-slugged-page', sub_slug=r'(?P<foo>\d+)')
page.save()
TextCell(page=page, placeholder='content', text='<p>barfoo</p>', order=0, public=True).save()
request = RequestFactory().get('/')
request.user = AnonymousUser()
index_site()
hits = search_site(request, 'foobar')
assert len(hits) == 1
hits = search_site(request, 'barfoo')
assert len(hits) == 0
def test_index_site_inactive_placeholder(app):
page = Page.objects.create(title='page', slug='example-page')
cell = TextCell.objects.create(page=page, placeholder='content', text='<p>foobar</p>', order=0)
assert cell.is_placeholder_active() is True
index_site()
assert IndexedCell.objects.count() == 1
cell.placeholder = ''
cell.save()
assert cell.is_placeholder_active() is False
index_site()
assert IndexedCell.objects.count() == 0
def test_index_site_invalid_cell(app):
request = RequestFactory().get('/')
page = Page.objects.create(title='page', slug='example-page')
cell = TextCell.objects.create(page=page, placeholder='content', text='<p>foobar</p>', order=0)
index_site()
assert IndexedCell.objects.count() == 1
# invalid cell since just now
cell.mark_as_invalid(reason_code='foobar')
assert cell.is_visible(request) is True
index_site()
assert IndexedCell.objects.count() == 1
# invalid cell since two days
validity_info = cell.get_validity_info()
validity_info.invalid_since = now() - datetime.timedelta(days=2)
validity_info.save()
assert cell.is_visible(request) is False
index_site()
assert IndexedCell.objects.count() == 0
def test_index_site_num_queries(settings, app):
group = Group.objects.create(name='plop')
for i in range(0, 10):
page = Page.objects.create(title='page %s' % i, slug='example-page-%s' % i)
page.groups.set([group])
for j in range(0, 5):
cell = TextCell.objects.create(
page=page, placeholder='content', text='<p>foobar %s</p>' % j, order=j, public=False
)
cell.groups.set([group])
index_site() # populate cache
assert IndexedCell.objects.count() == 50
with CaptureQueriesContext(connection) as ctx:
index_site()
assert len(ctx.captured_queries) == 175
SearchCell.objects.create(
page=page, placeholder='content', order=0, _search_services={'data': ['search1']}
)
# search cells are not indexed
index_site()
assert IndexedCell.objects.count() == 50
@mock.patch('combo.apps.search.engines.get_engines')
def test_index_site_search_engines_load(get_engines_mock, settings, app):
# be sure that get_engines is not called during page indexation
get_engines_mock.side_effect = Exception
settings.COMBO_SEARCH_SERVICES = {}
page = Page.objects.create(title='page', slug='example-page')
SearchCell.objects.create(
page=page, placeholder='content', order=0, _search_services={'data': ['search1']}
)
TextCell.objects.create(page=page, placeholder='content', text='<p>foobar</p>', order=0)
# no exception raised
index_site()
@pytest.mark.skipif(connection.vendor != 'postgresql', reason='only postgresql is supported')
def test_search_by_page_title(app):
query = 'nanoparticle electrochemistry'
# First page containing a cell whose text matches the query
page = Page(title='example page', slug='example-page')
page.save()
TextCell(
page=page,
placeholder='content',
text='<p>Some nanoparticle electrochemistry content here</p>',
order=0,
public=True,
).save()
# Second page whose title matches the search query
page_of_interest = Page(title='Nanoparticle electrochemistry', slug='page-of-interest')
page_of_interest.save()
TextCell(
page=page_of_interest,
placeholder='content',
text='<p>Some random text here</p>',
order=0,
public=True,
).save()
request = RequestFactory().get('/')
request.user = AnonymousUser()
index_site()
hits = search_site(request, query)
# Check that title matching gets precedence over content matching
assert len(hits) == 2
assert hits[0]['text'] == page_of_interest.title
assert hits[0]['url'] == f'/{page_of_interest.slug}/'
assert hits[0]['rank'] > hits[1]['rank']
def test_search_same_link_multiple_pages(settings, app):
settings.KNOWN_SERVICES = {}
page = Page.objects.create(title='first page', slug='one')
LinkCell.objects.create(
title='foobar', url='http://example.net', page=page, placeholder='content', order=0
)
second_page = Page.objects.create(title='second page', slug='two')
LinkCell.objects.create(
title='barfoo', url='http://example.net', page=second_page, placeholder='content', order=0
)
index_site()
cell = SearchCell.objects.create(
page=page, placeholder='content', _search_services={'data': ['_text_page_one']}, order=1
)
resp = app.get('/ajax/search/%s/_text_page_one/?q=foobar' % cell.pk, status=200)
assert resp.text.count('<li') == 1
resp = app.get('/ajax/search/%s/_text_page_one/?q=barfoo' % cell.pk, status=200)
assert resp.text.count('<li') == 0
cell._search_services = {'data': ['_text_page_two']}
cell.save()
resp = app.get('/ajax/search/%s/_text_page_two/?q=foobar' % cell.pk, status=200)
assert resp.text.count('<li') == 0
resp = app.get('/ajax/search/%s/_text_page_two/?q=barfoo' % cell.pk, status=200)
assert resp.text.count('<li') == 1