diff --git a/rhodecode/apps/search/__init__.py b/rhodecode/apps/search/__init__.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/apps/search/__init__.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2016-2017 RhodeCode GmbH
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License, version 3
+# (only), as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+# This program is dual-licensed. If you wish to learn more about the
+# RhodeCode Enterprise Edition, including its added features, Support services,
+# and proprietary license terms, please see https://rhodecode.com/licenses/
+from rhodecode.apps._base import ADMIN_PREFIX
+
+
+def includeme(config):
+
+ config.add_route(
+ name='search',
+ pattern=ADMIN_PREFIX + '/search')
+
+ config.add_route(
+ name='search_repo',
+ pattern='/{repo_name:.*?[^/]}/search', repo_route=True)
+
+ # Scan module for configuration decorators.
+ config.scan()
+
+
+ # # FULL TEXT SEARCH
+ # rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
+ # controller='search')
+ # rmap.connect('search_repo_home', '/{repo_name}/search',
+ # controller='search',
+ # action='index',
+ # conditions={'function': check_repo},
+ # requirements=URL_NAME_REQUIREMENTS)
\ No newline at end of file
diff --git a/rhodecode/apps/search/tests/__init__.py b/rhodecode/apps/search/tests/__init__.py
new file mode 100644
diff --git a/rhodecode/apps/search/tests/test_search.py b/rhodecode/apps/search/tests/test_search.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/apps/search/tests/test_search.py
@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2010-2017 RhodeCode GmbH
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License, version 3
+# (only), as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+# This program is dual-licensed. If you wish to learn more about the
+# RhodeCode Enterprise Edition, including its added features, Support services,
+# and proprietary license terms, please see https://rhodecode.com/licenses/
+
+import os
+
+import mock
+import pytest
+from whoosh import query
+
+from rhodecode.tests import (
+ TestController, SkipTest, HG_REPO,
+ TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
+from rhodecode.tests.utils import AssertResponse
+
+
+def route_path(name, **kwargs):
+ from rhodecode.apps._base import ADMIN_PREFIX
+ return {
+ 'search':
+ ADMIN_PREFIX + '/search',
+ 'search_repo':
+ '/{repo_name}/search',
+
+ }[name].format(**kwargs)
+
+
+class TestSearchController(TestController):
+
+ def test_index(self):
+ self.log_user()
+ response = self.app.get(route_path('search'))
+ assert_response = AssertResponse(response)
+ assert_response.one_element_exists('input#q')
+
+ def test_search_files_empty_search(self):
+ if os.path.isdir(self.index_location):
+ raise SkipTest('skipped due to existing index')
+ else:
+ self.log_user()
+ response = self.app.get(route_path('search'),
+ {'q': HG_REPO})
+ response.mustcontain('There is no index to search in. '
+ 'Please run whoosh indexer')
+
+ def test_search_validation(self):
+ self.log_user()
+ response = self.app.get(route_path('search'),
+ {'q': query, 'type': 'content', 'page_limit': 1000})
+
+ response.mustcontain(
+ 'page_limit - 1000 is greater than maximum value 500')
+
+ @pytest.mark.parametrize("query, expected_hits, expected_paths", [
+ ('todo', 23, [
+ 'vcs/backends/hg/inmemory.py',
+ 'vcs/tests/test_git.py']),
+ ('extension:rst installation', 6, [
+ 'docs/index.rst',
+ 'docs/installation.rst']),
+ ('def repo', 87, [
+ 'vcs/tests/test_git.py',
+ 'vcs/tests/test_changesets.py']),
+ ('repository:%s def test' % HG_REPO, 18, [
+ 'vcs/tests/test_git.py',
+ 'vcs/tests/test_changesets.py']),
+ ('"def main"', 9, [
+ 'vcs/__init__.py',
+ 'vcs/tests/__init__.py',
+ 'vcs/utils/progressbar.py']),
+ ('owner:test_admin', 358, [
+ 'vcs/tests/base.py',
+ 'MANIFEST.in',
+ 'vcs/utils/termcolors.py',
+ 'docs/theme/ADC/static/documentation.png']),
+ ('owner:test_admin def main', 72, [
+ 'vcs/__init__.py',
+ 'vcs/tests/test_utils_filesize.py',
+ 'vcs/tests/test_cli.py']),
+ ('owner:michał test', 0, []),
+ ])
+ def test_search_files(self, query, expected_hits, expected_paths):
+ self.log_user()
+ response = self.app.get(route_path('search'),
+ {'q': query, 'type': 'content', 'page_limit': 500})
+
+ response.mustcontain('%s results' % expected_hits)
+ for path in expected_paths:
+ response.mustcontain(path)
+
+ @pytest.mark.parametrize("query, expected_hits, expected_commits", [
+ ('bother to ask where to fetch repo during tests', 3, [
+ ('hg', 'a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1'),
+ ('git', 'c6eb379775c578a95dad8ddab53f963b80894850'),
+ ('svn', '98')]),
+ ('michał', 0, []),
+ ('changed:tests/utils.py', 36, [
+ ('hg', 'a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1')]),
+ ('changed:vcs/utils/archivers.py', 11, [
+ ('hg', '25213a5fbb048dff8ba65d21e466a835536e5b70'),
+ ('hg', '47aedd538bf616eedcb0e7d630ea476df0e159c7'),
+ ('hg', 'f5d23247fad4856a1dabd5838afade1e0eed24fb'),
+ ('hg', '04ad456aefd6461aea24f90b63954b6b1ce07b3e'),
+ ('git', 'c994f0de03b2a0aa848a04fc2c0d7e737dba31fc'),
+ ('git', 'd1f898326327e20524fe22417c22d71064fe54a1'),
+ ('git', 'fe568b4081755c12abf6ba673ba777fc02a415f3'),
+ ('git', 'bafe786f0d8c2ff7da5c1dcfcfa577de0b5e92f1')]),
+ ('added:README.rst', 3, [
+ ('hg', '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'),
+ ('git', 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'),
+ ('svn', '8')]),
+ ('changed:lazy.py', 15, [
+ ('hg', 'eaa291c5e6ae6126a203059de9854ccf7b5baa12'),
+ ('git', '17438a11f72b93f56d0e08e7d1fa79a378578a82'),
+ ('svn', '82'),
+ ('svn', '262'),
+ ('hg', 'f5d23247fad4856a1dabd5838afade1e0eed24fb'),
+ ('git', '33fa3223355104431402a888fa77a4e9956feb3e')
+ ]),
+ ('author:marcin@python-blog.com '
+ 'commit_id:b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
+ ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
+ ('b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
+ ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
+ ('b986218b', 1, [
+ ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
+ ])
+ def test_search_commit_messages(
+ self, query, expected_hits, expected_commits, enabled_backends):
+ self.log_user()
+ response = self.app.get(route_path('search'),
+ {'q': query, 'type': 'commit', 'page_limit': 500})
+
+ response.mustcontain('%s results' % expected_hits)
+ for backend, commit_id in expected_commits:
+ if backend in enabled_backends:
+ response.mustcontain(commit_id)
+
+ @pytest.mark.parametrize("query, expected_hits, expected_paths", [
+ ('readme.rst', 3, []),
+ ('test*', 75, []),
+ ('*model*', 1, []),
+ ('extension:rst', 48, []),
+ ('extension:rst api', 24, []),
+ ])
+ def test_search_file_paths(self, query, expected_hits, expected_paths):
+ self.log_user()
+ response = self.app.get(route_path('search'),
+ {'q': query, 'type': 'path', 'page_limit': 500})
+
+ response.mustcontain('%s results' % expected_hits)
+ for path in expected_paths:
+ response.mustcontain(path)
+
+ def test_search_commit_message_specific_repo(self, backend):
+ self.log_user()
+ response = self.app.get(
+ route_path('search_repo',repo_name=backend.repo_name),
+ {'q': 'bother to ask where to fetch repo during tests',
+ 'type': 'commit'})
+
+ response.mustcontain('1 results')
+
+ def test_filters_are_not_applied_for_admin_user(self):
+ self.log_user()
+ with mock.patch('whoosh.searching.Searcher.search') as search_mock:
+ self.app.get(route_path('search'),
+ {'q': 'test query', 'type': 'commit'})
+ assert search_mock.call_count == 1
+ _, kwargs = search_mock.call_args
+ assert kwargs['filter'] is None
+
+ def test_filters_are_applied_for_normal_user(self, enabled_backends):
+ self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
+ with mock.patch('whoosh.searching.Searcher.search') as search_mock:
+ self.app.get(route_path('search'),
+ {'q': 'test query', 'type': 'commit'})
+ assert search_mock.call_count == 1
+ _, kwargs = search_mock.call_args
+ assert isinstance(kwargs['filter'], query.Or)
+ expected_repositories = [
+ 'vcs_test_{}'.format(b) for b in enabled_backends]
+ queried_repositories = [
+ name for type_, name in kwargs['filter'].all_terms()]
+ for repository in expected_repositories:
+ assert repository in queried_repositories
diff --git a/rhodecode/apps/search/views.py b/rhodecode/apps/search/views.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/apps/search/views.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2011-2017 RhodeCode GmbH
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License, version 3
+# (only), as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+# This program is dual-licensed. If you wish to learn more about the
+# RhodeCode Enterprise Edition, including its added features, Support services,
+# and proprietary license terms, please see https://rhodecode.com/licenses/
+
+import logging
+import urllib
+from pyramid.view import view_config
+from webhelpers.util import update_params
+
+from rhodecode.apps._base import BaseAppView, RepoAppView
+from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator)
+from rhodecode.lib.helpers import Page
+from rhodecode.lib.utils2 import safe_str, safe_int
+from rhodecode.lib.index import searcher_from_config
+from rhodecode.model import validation_schema
+from rhodecode.model.validation_schema.schemas import search_schema
+
+log = logging.getLogger(__name__)
+
+
+def search(request, tmpl_context, repo_name):
+ searcher = searcher_from_config(request.registry.settings)
+ formatted_results = []
+ execution_time = ''
+
+ schema = search_schema.SearchParamsSchema()
+
+ search_params = {}
+ errors = []
+ try:
+ search_params = schema.deserialize(
+ dict(search_query=request.GET.get('q'),
+ search_type=request.GET.get('type'),
+ search_sort=request.GET.get('sort'),
+ page_limit=request.GET.get('page_limit'),
+ requested_page=request.GET.get('page'))
+ )
+ except validation_schema.Invalid as e:
+ errors = e.children
+
+ def url_generator(**kw):
+ q = urllib.quote(safe_str(search_query))
+ return update_params(
+ "?q=%s&type=%s" % (q, safe_str(search_type)), **kw)
+
+ c = tmpl_context
+ search_query = search_params.get('search_query')
+ search_type = search_params.get('search_type')
+ search_sort = search_params.get('search_sort')
+ if search_params.get('search_query'):
+ page_limit = search_params['page_limit']
+ requested_page = search_params['requested_page']
+
+ try:
+ search_result = searcher.search(
+ search_query, search_type, c.auth_user, repo_name,
+ requested_page, page_limit, search_sort)
+
+ formatted_results = Page(
+ search_result['results'], page=requested_page,
+ item_count=search_result['count'],
+ items_per_page=page_limit, url=url_generator)
+ finally:
+ searcher.cleanup()
+
+ if not search_result['error']:
+ execution_time = '%s results (%.3f seconds)' % (
+ search_result['count'],
+ search_result['runtime'])
+ elif not errors:
+ node = schema['search_query']
+ errors = [
+ validation_schema.Invalid(node, search_result['error'])]
+
+ c.perm_user = c.auth_user
+ c.repo_name = repo_name
+ c.sort = search_sort
+ c.url_generator = url_generator
+ c.errors = errors
+ c.formatted_results = formatted_results
+ c.runtime = execution_time
+ c.cur_query = search_query
+ c.search_type = search_type
+ c.searcher = searcher
+
+
+class SearchView(BaseAppView):
+ def load_default_context(self):
+ c = self._get_local_tmpl_context()
+ self._register_global_c(c)
+ return c
+
+ @LoginRequired()
+ @view_config(
+ route_name='search', request_method='GET',
+ renderer='rhodecode:templates/search/search.mako')
+ def search(self):
+ c = self.load_default_context()
+ search(self.request, c, repo_name=None)
+ return self._get_template_context(c)
+
+
+class SearchRepoView(RepoAppView):
+ def load_default_context(self):
+ c = self._get_local_tmpl_context()
+ self._register_global_c(c)
+ return c
+
+ @LoginRequired()
+ @HasRepoPermissionAnyDecorator('repository.admin')
+ @view_config(
+ route_name='search_repo', request_method='GET',
+ renderer='rhodecode:templates/search/search.mako')
+ def search_repo(self):
+ c = self.load_default_context()
+ search(self.request, c, repo_name=self.db_repo_name)
+ return self._get_template_context(c)
diff --git a/rhodecode/config/middleware.py b/rhodecode/config/middleware.py
--- a/rhodecode/config/middleware.py
+++ b/rhodecode/config/middleware.py
@@ -291,6 +291,7 @@ def includeme(config):
config.include('rhodecode.apps.login')
config.include('rhodecode.apps.home')
config.include('rhodecode.apps.repository')
+ config.include('rhodecode.apps.search')
config.include('rhodecode.apps.user_profile')
config.include('rhodecode.apps.my_account')
config.include('rhodecode.apps.svn_support')
diff --git a/rhodecode/config/routing.py b/rhodecode/config/routing.py
--- a/rhodecode/config/routing.py
+++ b/rhodecode/config/routing.py
@@ -614,15 +614,6 @@ def make_map(config):
controller='journal', action='toggle_following', jsroute=True,
conditions={'method': ['POST']})
- # FULL TEXT SEARCH
- rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
- controller='search')
- rmap.connect('search_repo_home', '/{repo_name}/search',
- controller='search',
- action='index',
- conditions={'function': check_repo},
- requirements=URL_NAME_REQUIREMENTS)
-
# FEEDS
rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
controller='feed', action='rss',
diff --git a/rhodecode/controllers/search.py b/rhodecode/controllers/search.py
deleted file mode 100644
--- a/rhodecode/controllers/search.py
+++ /dev/null
@@ -1,112 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2010-2017 RhodeCode GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License, version 3
-# (only), as published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-# This program is dual-licensed. If you wish to learn more about the
-# RhodeCode Enterprise Edition, including its added features, Support services,
-# and proprietary license terms, please see https://rhodecode.com/licenses/
-
-"""
-Search controller for RhodeCode
-"""
-
-import logging
-import urllib
-
-from pylons import request, config, tmpl_context as c
-
-from webhelpers.util import update_params
-
-from rhodecode.lib.auth import LoginRequired, AuthUser
-from rhodecode.lib.base import BaseRepoController, render
-from rhodecode.lib.helpers import Page
-from rhodecode.lib.utils2 import safe_str, safe_int
-from rhodecode.lib.index import searcher_from_config
-from rhodecode.model import validation_schema
-from rhodecode.model.validation_schema.schemas import search_schema
-
-log = logging.getLogger(__name__)
-
-
-class SearchController(BaseRepoController):
-
- @LoginRequired()
- def index(self, repo_name=None):
-
- searcher = searcher_from_config(config)
- formatted_results = []
- execution_time = ''
-
- schema = search_schema.SearchParamsSchema()
-
- search_params = {}
- errors = []
- try:
- search_params = schema.deserialize(
- dict(search_query=request.GET.get('q'),
- search_type=request.GET.get('type'),
- search_sort=request.GET.get('sort'),
- page_limit=request.GET.get('page_limit'),
- requested_page=request.GET.get('page'))
- )
- except validation_schema.Invalid as e:
- errors = e.children
-
- def url_generator(**kw):
- q = urllib.quote(safe_str(search_query))
- return update_params(
- "?q=%s&type=%s" % (q, safe_str(search_type)), **kw)
-
- search_query = search_params.get('search_query')
- search_type = search_params.get('search_type')
- search_sort = search_params.get('search_sort')
- if search_params.get('search_query'):
- page_limit = search_params['page_limit']
- requested_page = search_params['requested_page']
-
- c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
- ip_addr=self.ip_addr)
-
- try:
- search_result = searcher.search(
- search_query, search_type, c.perm_user, repo_name,
- requested_page, page_limit, search_sort)
-
- formatted_results = Page(
- search_result['results'], page=requested_page,
- item_count=search_result['count'],
- items_per_page=page_limit, url=url_generator)
- finally:
- searcher.cleanup()
-
- if not search_result['error']:
- execution_time = '%s results (%.3f seconds)' % (
- search_result['count'],
- search_result['runtime'])
- elif not errors:
- node = schema['search_query']
- errors = [
- validation_schema.Invalid(node, search_result['error'])]
-
- c.sort = search_sort
- c.url_generator = url_generator
- c.errors = errors
- c.formatted_results = formatted_results
- c.runtime = execution_time
- c.cur_query = search_query
- c.search_type = search_type
- c.searcher = searcher
- # Return a rendered template
- return render('/search/search.mako')
diff --git a/rhodecode/templates/base/base.mako b/rhodecode/templates/base/base.mako
--- a/rhodecode/templates/base/base.mako
+++ b/rhodecode/templates/base/base.mako
@@ -253,7 +253,7 @@
${_('Compare fork')}
%endif
-
${_('Search')}
+ ${_('Search')}
%if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
%if c.rhodecode_db_repo.locked[0]:
@@ -399,7 +399,7 @@
-
diff --git a/rhodecode/templates/search/search.mako b/rhodecode/templates/search/search.mako
--- a/rhodecode/templates/search/search.mako
+++ b/rhodecode/templates/search/search.mako
@@ -45,7 +45,7 @@
${self.repo_page_title(c.rhodecode_db_repo)}
- ${h.form(h.url('search_repo_home',repo_name=c.repo_name),method='get')}
+ ${h.form(h.route_path('search_repo',repo_name=c.repo_name),method='get')}
%else:
- ${h.form(h.url('search'),method='get')}
+ ${h.form(h.route_path('search'), method='get')}
%endif
diff --git a/rhodecode/templates/search/search_content.mako b/rhodecode/templates/search/search_content.mako
--- a/rhodecode/templates/search/search_content.mako
+++ b/rhodecode/templates/search/search_content.mako
@@ -54,7 +54,7 @@ for line_number in matching_lines:
${h.link_to(h.literal(entry['f_path']), h.url('files_home',repo_name=entry['repository'],revision=entry.get('commit_id', 'tip'),f_path=entry['f_path']))}
%if entry.get('lines'):
- | ${entry.get('lines', 0.)} ${ungettext('line', 'lines', entry.get('lines', 0.))}
+ | ${entry.get('lines', 0.)} ${_ungettext('line', 'lines', entry.get('lines', 0.))}
%endif
%if entry.get('size'):
| ${h.format_byte_size_binary(entry['size'])}
diff --git a/rhodecode/tests/functional/test_search.py b/rhodecode/tests/functional/test_search.py
deleted file mode 100644
--- a/rhodecode/tests/functional/test_search.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2010-2017 RhodeCode GmbH
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License, version 3
-# (only), as published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-# This program is dual-licensed. If you wish to learn more about the
-# RhodeCode Enterprise Edition, including its added features, Support services,
-# and proprietary license terms, please see https://rhodecode.com/licenses/
-
-import os
-
-import mock
-import pytest
-from whoosh import query
-
-from rhodecode.tests import (
- TestController, url, SkipTest, HG_REPO,
- TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
-from rhodecode.tests.utils import AssertResponse
-
-
-class TestSearchController(TestController):
-
- def test_index(self):
- self.log_user()
- response = self.app.get(url(controller='search', action='index'))
- assert_response = AssertResponse(response)
- assert_response.one_element_exists('input#q')
-
- def test_search_files_empty_search(self):
- if os.path.isdir(self.index_location):
- raise SkipTest('skipped due to existing index')
- else:
- self.log_user()
- response = self.app.get(url(controller='search', action='index'),
- {'q': HG_REPO})
- response.mustcontain('There is no index to search in. '
- 'Please run whoosh indexer')
-
- def test_search_validation(self):
- self.log_user()
- response = self.app.get(
- url(controller='search', action='index'), {'q': query,
- 'type': 'content',
- 'page_limit': 1000})
-
- response.mustcontain(
- 'page_limit - 1000 is greater than maximum value 500')
-
- @pytest.mark.parametrize("query, expected_hits, expected_paths", [
- ('todo', 23, [
- 'vcs/backends/hg/inmemory.py',
- 'vcs/tests/test_git.py']),
- ('extension:rst installation', 6, [
- 'docs/index.rst',
- 'docs/installation.rst']),
- ('def repo', 87, [
- 'vcs/tests/test_git.py',
- 'vcs/tests/test_changesets.py']),
- ('repository:%s def test' % HG_REPO, 18, [
- 'vcs/tests/test_git.py',
- 'vcs/tests/test_changesets.py']),
- ('"def main"', 9, [
- 'vcs/__init__.py',
- 'vcs/tests/__init__.py',
- 'vcs/utils/progressbar.py']),
- ('owner:test_admin', 358, [
- 'vcs/tests/base.py',
- 'MANIFEST.in',
- 'vcs/utils/termcolors.py',
- 'docs/theme/ADC/static/documentation.png']),
- ('owner:test_admin def main', 72, [
- 'vcs/__init__.py',
- 'vcs/tests/test_utils_filesize.py',
- 'vcs/tests/test_cli.py']),
- ('owner:michał test', 0, []),
- ])
- def test_search_files(self, query, expected_hits, expected_paths):
- self.log_user()
- response = self.app.get(
- url(controller='search', action='index'), {'q': query,
- 'type': 'content',
- 'page_limit': 500})
-
- response.mustcontain('%s results' % expected_hits)
- for path in expected_paths:
- response.mustcontain(path)
-
- @pytest.mark.parametrize("query, expected_hits, expected_commits", [
- ('bother to ask where to fetch repo during tests', 3, [
- ('hg', 'a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1'),
- ('git', 'c6eb379775c578a95dad8ddab53f963b80894850'),
- ('svn', '98')]),
- ('michał', 0, []),
- ('changed:tests/utils.py', 36, [
- ('hg', 'a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1')]),
- ('changed:vcs/utils/archivers.py', 11, [
- ('hg', '25213a5fbb048dff8ba65d21e466a835536e5b70'),
- ('hg', '47aedd538bf616eedcb0e7d630ea476df0e159c7'),
- ('hg', 'f5d23247fad4856a1dabd5838afade1e0eed24fb'),
- ('hg', '04ad456aefd6461aea24f90b63954b6b1ce07b3e'),
- ('git', 'c994f0de03b2a0aa848a04fc2c0d7e737dba31fc'),
- ('git', 'd1f898326327e20524fe22417c22d71064fe54a1'),
- ('git', 'fe568b4081755c12abf6ba673ba777fc02a415f3'),
- ('git', 'bafe786f0d8c2ff7da5c1dcfcfa577de0b5e92f1')]),
- ('added:README.rst', 3, [
- ('hg', '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'),
- ('git', 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'),
- ('svn', '8')]),
- ('changed:lazy.py', 15, [
- ('hg', 'eaa291c5e6ae6126a203059de9854ccf7b5baa12'),
- ('git', '17438a11f72b93f56d0e08e7d1fa79a378578a82'),
- ('svn', '82'),
- ('svn', '262'),
- ('hg', 'f5d23247fad4856a1dabd5838afade1e0eed24fb'),
- ('git', '33fa3223355104431402a888fa77a4e9956feb3e')
- ]),
- ('author:marcin@python-blog.com '
- 'commit_id:b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
- ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
- ('b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
- ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
- ('b986218b', 1, [
- ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
- ])
- def test_search_commit_messages(
- self, query, expected_hits, expected_commits, enabled_backends):
- self.log_user()
- response = self.app.get(
- url(controller='search', action='index'), {'q': query,
- 'type': 'commit',
- 'page_limit': 500})
-
- response.mustcontain('%s results' % expected_hits)
- for backend, commit_id in expected_commits:
- if backend in enabled_backends:
- response.mustcontain(commit_id)
-
- @pytest.mark.parametrize("query, expected_hits, expected_paths", [
- ('readme.rst', 3, []),
- ('test*', 75, []),
- ('*model*', 1, []),
- ('extension:rst', 48, []),
- ('extension:rst api', 24, []),
- ])
- def test_search_file_paths(self, query, expected_hits, expected_paths):
- self.log_user()
- response = self.app.get(
- url(controller='search', action='index'), {'q': query,
- 'type': 'path',
- 'page_limit': 500})
-
- response.mustcontain('%s results' % expected_hits)
- for path in expected_paths:
- response.mustcontain(path)
-
- def test_search_commit_message_specific_repo(self, backend):
- self.log_user()
- response = self.app.get(
- url(controller='search', action='index',
- repo_name=backend.repo_name),
- {'q': 'bother to ask where to fetch repo during tests',
- 'type': 'commit'})
-
- response.mustcontain('1 results')
-
- def test_filters_are_not_applied_for_admin_user(self):
- self.log_user()
- with mock.patch('whoosh.searching.Searcher.search') as search_mock:
- self.app.get(
- url(controller='search', action='index'),
- {'q': 'test query', 'type': 'commit'})
- assert search_mock.call_count == 1
- _, kwargs = search_mock.call_args
- assert kwargs['filter'] is None
-
- def test_filters_are_applied_for_normal_user(self, enabled_backends):
- self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
- with mock.patch('whoosh.searching.Searcher.search') as search_mock:
- self.app.get(
- url(controller='search', action='index'),
- {'q': 'test query', 'type': 'commit'})
- assert search_mock.call_count == 1
- _, kwargs = search_mock.call_args
- assert isinstance(kwargs['filter'], query.Or)
- expected_repositories = [
- 'vcs_test_{}'.format(b) for b in enabled_backends]
- queried_repositories = [
- name for type_, name in kwargs['filter'].all_terms()]
- for repository in expected_repositories:
- assert repository in queried_repositories
diff --git a/rhodecode/tests/controllers/test_search.py b/rhodecode/tests/lib/test_search.py
rename from rhodecode/tests/controllers/test_search.py
rename to rhodecode/tests/lib/test_search.py