816 lines
34 KiB
Python
816 lines
34 KiB
Python
import json
|
|
import os
|
|
import pytest
|
|
import mock
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import AnonymousUser, User, Group
|
|
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 combo.apps.search.engines import engines
|
|
from combo.apps.search.models import SearchCell, IndexedCell
|
|
from combo.apps.search.utils import index_site, search_site
|
|
from combo.data.models import Page, JsonCell, TextCell, MenuCell, LinkCell, PageSnapshot
|
|
|
|
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(object):
|
|
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(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.input_placeholder = 'my placeholder'
|
|
cell.save()
|
|
|
|
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 'autofocus' in resp
|
|
|
|
cell.slug = 'var-name'
|
|
context = {'request': RequestFactory().get('/?q_var_name=searchme')}
|
|
resp = cell.render(context)
|
|
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
|
|
|
|
with override_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)
|
|
resp = 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):
|
|
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()
|
|
|
|
cell._search_services = {'data': ['_text']}
|
|
assert cell.is_visible()
|
|
|
|
|
|
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(title='example page', slug='example-page')
|
|
page.save()
|
|
|
|
cell = TextCell(page=page, placeholder='content', text='<p>foobar baz</p>', order=0)
|
|
cell.save()
|
|
|
|
second_page = Page(title='second page', slug='second-page')
|
|
second_page.save()
|
|
|
|
cell = TextCell(page=second_page, placeholder='content', text='<p>other baz</p>', order=0)
|
|
cell.save()
|
|
index_site()
|
|
|
|
cell = SearchCell(page=page, placeholder='content', _search_services={'data': ['_text']}, order=0)
|
|
cell.save()
|
|
|
|
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
|
|
assert 'second page' in resp.text
|
|
|
|
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
|
|
|
|
|
|
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'
|
|
|
|
|
|
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/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/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
|
|
|
|
# delete engines
|
|
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
|
|
|
|
|
|
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 i, (service_slug, new_pos) in enumerate(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_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.keys()
|
|
assert len([x for x in search_engines.keys() if x.startswith('formdata:')]) == 0
|
|
assert len([x for x in search_engines.keys() 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.keys() 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.keys() 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.keys() 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.keys() if x.startswith('cards:')]) == 0
|
|
|
|
# card model found, but related page does not exist
|
|
mock_wcs.return_value = {'data': [{'id': 'bar', 'text': 'Bar'}]}
|
|
search_engines = engines.get_engines()
|
|
assert len([x for x in search_engines.keys() if x.startswith('cards:')]) == 0
|
|
|
|
# related page exists
|
|
Page.objects.create(slug='bar', title='Bar', sub_slug='(?P<bar_id>[a-z0-9]+')
|
|
search_engines = engines.get_engines()
|
|
assert len([x for x in search_engines.keys() if x.startswith('cards:')]) == 1
|
|
assert 'cards:c21f969b:bar' in search_engines.keys()
|
|
card_engine = search_engines['cards:c21f969b:bar']
|
|
assert card_engine['url'] == 'http://127.0.0.1:8999/api/cards/bar/list?NameID={{ user_nameid }}&q=%(q)s'
|
|
assert card_engine['hit_url_template'] == '/bar/{{ id }}'
|
|
|
|
|
|
def test_profile_search_engines(settings, app):
|
|
settings.KNOWN_SERVICES = {}
|
|
search_engines = engines.get_engines()
|
|
assert 'users' not in search_engines.keys()
|
|
|
|
settings.KNOWN_SERVICES = {'authentic': {'default': {'title': 'authentic', 'url': 'https://authentic/'}}}
|
|
search_engines = engines.get_engines()
|
|
assert 'users' not in search_engines.keys()
|
|
|
|
page = Page(slug='users', title='Users', sub_slug='(?P<name_id>[a-z0-9]+)')
|
|
page.save()
|
|
search_engines = engines.get_engines()
|
|
assert 'users' in search_engines.keys()
|
|
|
|
|
|
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='(?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_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) == 221
|
|
|
|
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()
|