##// END OF EJS Templates
goto-switcher: optimized performance and query capabilities....
goto-switcher: optimized performance and query capabilities. - Previous implementation had on significant bug. The use of LIMIT 20 was limiting results BEFORE auth checks. In case of large ammount of similarly named repositories user didn't had access too, the result goto search was empty. This was becuase first 20 items fetched didn't pass permission checks and final auth list was empty. To fix this we now use proper filtering for auth using SQL. It means we first check user allowed repositories, and add this as a filter so end result from database is already to only the accessible repositories.

File last commit:

r1957:00f3a509 default
r2038:2bdf9d4d default
Show More
test_repo_compare.py
697 lines | 25.9 KiB | text/x-python | PythonLexer
# -*- 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 <http://www.gnu.org/licenses/>.
#
# 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 mock
import pytest
import lxml.html
from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
from rhodecode.tests import assert_session_flash
from rhodecode.tests.utils import AssertResponse, commit_change
def route_path(name, params=None, **kwargs):
import urllib
base_url = {
'repo_compare_select': '/{repo_name}/compare',
'repo_compare': '/{repo_name}/compare/{source_ref_type}@{source_ref}...{target_ref_type}@{target_ref}',
}[name].format(**kwargs)
if params:
base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
return base_url
@pytest.mark.usefixtures("autologin_user", "app")
class TestCompareView(object):
def test_compare_index_is_reached_at_least_once(self, backend):
repo = backend.repo
self.app.get(
route_path('repo_compare_select', repo_name=repo.repo_name))
@pytest.mark.xfail_backends("svn", reason="Requires pull")
def test_compare_remote_with_different_commit_indexes(self, backend):
# Preparing the following repository structure:
#
# Origin repository has two commits:
#
# 0 1
# A -- D
#
# The fork of it has a few more commits and "D" has a commit index
# which does not exist in origin.
#
# 0 1 2 3 4
# A -- -- -- D -- E
# \- B -- C
#
fork = backend.create_repo()
# prepare fork
commit0 = commit_change(
fork.repo_name, filename='file1', content='A',
message='A', vcs_type=backend.alias, parent=None, newfile=True)
commit1 = commit_change(
fork.repo_name, filename='file1', content='B',
message='B, child of A', vcs_type=backend.alias, parent=commit0)
commit_change( # commit 2
fork.repo_name, filename='file1', content='C',
message='C, child of B', vcs_type=backend.alias, parent=commit1)
commit3 = commit_change(
fork.repo_name, filename='file1', content='D',
message='D, child of A', vcs_type=backend.alias, parent=commit0)
commit4 = commit_change(
fork.repo_name, filename='file1', content='E',
message='E, child of D', vcs_type=backend.alias, parent=commit3)
# prepare origin repository, taking just the history up to D
origin = backend.create_repo()
origin_repo = origin.scm_instance(cache=False)
origin_repo.config.clear_section('hooks')
origin_repo.pull(fork.repo_full_path, commit_ids=[commit3.raw_id])
# Verify test fixture setup
# This does not work for git
if backend.alias != 'git':
assert 5 == len(fork.scm_instance().commit_ids)
assert 2 == len(origin_repo.commit_ids)
# Comparing the revisions
response = self.app.get(
route_path('repo_compare',
repo_name=origin.repo_name,
source_ref_type="rev",
source_ref=commit3.raw_id,
target_ref_type="rev",
target_ref=commit4.raw_id,
params=dict(merge='1', target_repo=fork.repo_name)
))
compare_page = ComparePage(response)
compare_page.contains_commits([commit4])
@pytest.mark.xfail_backends("svn", reason="Depends on branch support")
def test_compare_forks_on_branch_extra_commits(self, backend):
repo1 = backend.create_repo()
# commit something !
commit0 = commit_change(
repo1.repo_name, filename='file1', content='line1\n',
message='commit1', vcs_type=backend.alias, parent=None,
newfile=True)
# fork this repo
repo2 = backend.create_fork()
# add two extra commit into fork
commit1 = commit_change(
repo2.repo_name, filename='file1', content='line1\nline2\n',
message='commit2', vcs_type=backend.alias, parent=commit0)
commit2 = commit_change(
repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
message='commit3', vcs_type=backend.alias, parent=commit1)
commit_id1 = repo1.scm_instance().DEFAULT_BRANCH_NAME
commit_id2 = repo2.scm_instance().DEFAULT_BRANCH_NAME
response = self.app.get(
route_path('repo_compare',
repo_name=repo1.repo_name,
source_ref_type="branch",
source_ref=commit_id2,
target_ref_type="branch",
target_ref=commit_id1,
params=dict(merge='1', target_repo=repo2.repo_name)
))
response.mustcontain('%s@%s' % (repo1.repo_name, commit_id2))
response.mustcontain('%s@%s' % (repo2.repo_name, commit_id1))
compare_page = ComparePage(response)
compare_page.contains_change_summary(1, 2, 0)
compare_page.contains_commits([commit1, commit2])
compare_page.contains_file_links_and_anchors([
('file1', 'a_c--826e8142e6ba'),
])
# Swap is removed when comparing branches since it's a PR feature and
# it is then a preview mode
compare_page.swap_is_hidden()
compare_page.target_source_are_disabled()
@pytest.mark.xfail_backends("svn", reason="Depends on branch support")
def test_compare_forks_on_branch_extra_commits_origin_has_incomming(
self, backend):
repo1 = backend.create_repo()
# commit something !
commit0 = commit_change(
repo1.repo_name, filename='file1', content='line1\n',
message='commit1', vcs_type=backend.alias, parent=None,
newfile=True)
# fork this repo
repo2 = backend.create_fork()
# now commit something to origin repo
commit_change(
repo1.repo_name, filename='file2', content='line1file2\n',
message='commit2', vcs_type=backend.alias, parent=commit0,
newfile=True)
# add two extra commit into fork
commit1 = commit_change(
repo2.repo_name, filename='file1', content='line1\nline2\n',
message='commit2', vcs_type=backend.alias, parent=commit0)
commit2 = commit_change(
repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
message='commit3', vcs_type=backend.alias, parent=commit1)
commit_id1 = repo1.scm_instance().DEFAULT_BRANCH_NAME
commit_id2 = repo2.scm_instance().DEFAULT_BRANCH_NAME
response = self.app.get(
route_path('repo_compare',
repo_name=repo1.repo_name,
source_ref_type="branch",
source_ref=commit_id2,
target_ref_type="branch",
target_ref=commit_id1,
params=dict(merge='1', target_repo=repo2.repo_name),
))
response.mustcontain('%s@%s' % (repo1.repo_name, commit_id2))
response.mustcontain('%s@%s' % (repo2.repo_name, commit_id1))
compare_page = ComparePage(response)
compare_page.contains_change_summary(1, 2, 0)
compare_page.contains_commits([commit1, commit2])
compare_page.contains_file_links_and_anchors([
('file1', 'a_c--826e8142e6ba'),
])
# Swap is removed when comparing branches since it's a PR feature and
# it is then a preview mode
compare_page.swap_is_hidden()
compare_page.target_source_are_disabled()
@pytest.mark.xfail_backends("svn")
# TODO(marcink): no svn support for compare two seperate repos
def test_compare_of_unrelated_forks(self, backend):
orig = backend.create_repo(number_of_commits=1)
fork = backend.create_repo(number_of_commits=1)
response = self.app.get(
route_path('repo_compare',
repo_name=orig.repo_name,
source_ref_type="rev",
source_ref="tip",
target_ref_type="rev",
target_ref="tip",
params=dict(merge='1', target_repo=fork.repo_name),
),
status=302)
response = response.follow()
response.mustcontain("Repositories unrelated.")
@pytest.mark.xfail_backends("svn")
def test_compare_cherry_pick_commits_from_bottom(self, backend):
# repo1:
# commit0:
# commit1:
# repo1-fork- in which we will cherry pick bottom commits
# commit0:
# commit1:
# commit2: x
# commit3: x
# commit4: x
# commit5:
# make repo1, and commit1+commit2
repo1 = backend.create_repo()
# commit something !
commit0 = commit_change(
repo1.repo_name, filename='file1', content='line1\n',
message='commit1', vcs_type=backend.alias, parent=None,
newfile=True)
commit1 = commit_change(
repo1.repo_name, filename='file1', content='line1\nline2\n',
message='commit2', vcs_type=backend.alias, parent=commit0)
# fork this repo
repo2 = backend.create_fork()
# now make commit3-6
commit2 = commit_change(
repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
message='commit3', vcs_type=backend.alias, parent=commit1)
commit3 = commit_change(
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\n', message='commit4',
vcs_type=backend.alias, parent=commit2)
commit4 = commit_change(
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
vcs_type=backend.alias, parent=commit3)
commit_change( # commit 5
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\nline5\nline6\n',
message='commit6', vcs_type=backend.alias, parent=commit4)
response = self.app.get(
route_path('repo_compare',
repo_name=repo2.repo_name,
source_ref_type="rev",
# parent of commit2, in target repo2
source_ref=commit1.raw_id,
target_ref_type="rev",
target_ref=commit4.raw_id,
params=dict(merge='1', target_repo=repo1.repo_name),
))
response.mustcontain('%s@%s' % (repo2.repo_name, commit1.short_id))
response.mustcontain('%s@%s' % (repo1.repo_name, commit4.short_id))
# files
compare_page = ComparePage(response)
compare_page.contains_change_summary(1, 3, 0)
compare_page.contains_commits([commit2, commit3, commit4])
compare_page.contains_file_links_and_anchors([
('file1', 'a_c--826e8142e6ba'),
])
@pytest.mark.xfail_backends("svn")
def test_compare_cherry_pick_commits_from_top(self, backend):
# repo1:
# commit0:
# commit1:
# repo1-fork- in which we will cherry pick bottom commits
# commit0:
# commit1:
# commit2:
# commit3: x
# commit4: x
# commit5: x
# make repo1, and commit1+commit2
repo1 = backend.create_repo()
# commit something !
commit0 = commit_change(
repo1.repo_name, filename='file1', content='line1\n',
message='commit1', vcs_type=backend.alias, parent=None,
newfile=True)
commit1 = commit_change(
repo1.repo_name, filename='file1', content='line1\nline2\n',
message='commit2', vcs_type=backend.alias, parent=commit0)
# fork this repo
backend.create_fork()
# now make commit3-6
commit2 = commit_change(
repo1.repo_name, filename='file1', content='line1\nline2\nline3\n',
message='commit3', vcs_type=backend.alias, parent=commit1)
commit3 = commit_change(
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\n', message='commit4',
vcs_type=backend.alias, parent=commit2)
commit4 = commit_change(
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\nline5\n', message='commit5',
vcs_type=backend.alias, parent=commit3)
commit5 = commit_change(
repo1.repo_name, filename='file1',
content='line1\nline2\nline3\nline4\nline5\nline6\n',
message='commit6', vcs_type=backend.alias, parent=commit4)
response = self.app.get(
route_path('repo_compare',
repo_name=repo1.repo_name,
source_ref_type="rev",
# parent of commit3, not in source repo2
source_ref=commit2.raw_id,
target_ref_type="rev",
target_ref=commit5.raw_id,
params=dict(merge='1'),
))
response.mustcontain('%s@%s' % (repo1.repo_name, commit2.short_id))
response.mustcontain('%s@%s' % (repo1.repo_name, commit5.short_id))
compare_page = ComparePage(response)
compare_page.contains_change_summary(1, 3, 0)
compare_page.contains_commits([commit3, commit4, commit5])
# files
compare_page.contains_file_links_and_anchors([
('file1', 'a_c--826e8142e6ba'),
])
@pytest.mark.xfail_backends("svn")
def test_compare_remote_branches(self, backend):
repo1 = backend.repo
repo2 = backend.create_fork()
commit_id1 = repo1.get_commit(commit_idx=3).raw_id
commit_id2 = repo1.get_commit(commit_idx=6).raw_id
response = self.app.get(
route_path('repo_compare',
repo_name=repo1.repo_name,
source_ref_type="rev",
source_ref=commit_id1,
target_ref_type="rev",
target_ref=commit_id2,
params=dict(merge='1', target_repo=repo2.repo_name),
))
response.mustcontain('%s@%s' % (repo1.repo_name, commit_id1))
response.mustcontain('%s@%s' % (repo2.repo_name, commit_id2))
compare_page = ComparePage(response)
# outgoing commits between those commits
compare_page.contains_commits(
[repo2.get_commit(commit_idx=x) for x in [4, 5, 6]])
# files
compare_page.contains_file_links_and_anchors([
('vcs/backends/hg.py', 'a_c--9c390eb52cd6'),
('vcs/backends/__init__.py', 'a_c--41b41c1f2796'),
('vcs/backends/base.py', 'a_c--2f574d260608'),
])
@pytest.mark.xfail_backends("svn")
def test_source_repo_new_commits_after_forking_simple_diff(self, backend):
repo1 = backend.create_repo()
r1_name = repo1.repo_name
commit0 = commit_change(
repo=r1_name, filename='file1',
content='line1', message='commit1', vcs_type=backend.alias,
newfile=True)
assert repo1.scm_instance().commit_ids == [commit0.raw_id]
# fork the repo1
repo2 = backend.create_fork()
assert repo2.scm_instance().commit_ids == [commit0.raw_id]
self.r2_id = repo2.repo_id
r2_name = repo2.repo_name
commit1 = commit_change(
repo=r2_name, filename='file1-fork',
content='file1-line1-from-fork', message='commit1-fork',
vcs_type=backend.alias, parent=repo2.scm_instance()[-1],
newfile=True)
commit2 = commit_change(
repo=r2_name, filename='file2-fork',
content='file2-line1-from-fork', message='commit2-fork',
vcs_type=backend.alias, parent=commit1,
newfile=True)
commit_change( # commit 3
repo=r2_name, filename='file3-fork',
content='file3-line1-from-fork', message='commit3-fork',
vcs_type=backend.alias, parent=commit2, newfile=True)
# compare !
commit_id1 = repo1.scm_instance().DEFAULT_BRANCH_NAME
commit_id2 = repo2.scm_instance().DEFAULT_BRANCH_NAME
response = self.app.get(
route_path('repo_compare',
repo_name=r2_name,
source_ref_type="branch",
source_ref=commit_id1,
target_ref_type="branch",
target_ref=commit_id2,
params=dict(merge='1', target_repo=r1_name),
))
response.mustcontain('%s@%s' % (r2_name, commit_id1))
response.mustcontain('%s@%s' % (r1_name, commit_id2))
response.mustcontain('No files')
response.mustcontain('No commits in this compare')
commit0 = commit_change(
repo=r1_name, filename='file2',
content='line1-added-after-fork', message='commit2-parent',
vcs_type=backend.alias, parent=None, newfile=True)
# compare !
response = self.app.get(
route_path('repo_compare',
repo_name=r2_name,
source_ref_type="branch",
source_ref=commit_id1,
target_ref_type="branch",
target_ref=commit_id2,
params=dict(merge='1', target_repo=r1_name),
))
response.mustcontain('%s@%s' % (r2_name, commit_id1))
response.mustcontain('%s@%s' % (r1_name, commit_id2))
response.mustcontain("""commit2-parent""")
response.mustcontain("""line1-added-after-fork""")
compare_page = ComparePage(response)
compare_page.contains_change_summary(1, 1, 0)
@pytest.mark.xfail_backends("svn")
def test_compare_commits(self, backend, xhr_header):
commit0 = backend.repo.get_commit(commit_idx=0)
commit1 = backend.repo.get_commit(commit_idx=1)
response = self.app.get(
route_path('repo_compare',
repo_name=backend.repo_name,
source_ref_type="rev",
source_ref=commit0.raw_id,
target_ref_type="rev",
target_ref=commit1.raw_id,
params=dict(merge='1')
),
extra_environ=xhr_header,)
# outgoing commits between those commits
compare_page = ComparePage(response)
compare_page.contains_commits(commits=[commit1], ancestors=[commit0])
def test_errors_when_comparing_unknown_source_repo(self, backend):
repo = backend.repo
badrepo = 'badrepo'
response = self.app.get(
route_path('repo_compare',
repo_name=badrepo,
source_ref_type="rev",
source_ref='tip',
target_ref_type="rev",
target_ref='tip',
params=dict(merge='1', target_repo=repo.repo_name)
),
status=404)
def test_errors_when_comparing_unknown_target_repo(self, backend):
repo = backend.repo
badrepo = 'badrepo'
response = self.app.get(
route_path('repo_compare',
repo_name=repo.repo_name,
source_ref_type="rev",
source_ref='tip',
target_ref_type="rev",
target_ref='tip',
params=dict(merge='1', target_repo=badrepo),
),
status=302)
redirected = response.follow()
redirected.mustcontain(
'Could not find the target repo: `{}`'.format(badrepo))
def test_compare_not_in_preview_mode(self, backend_stub):
commit0 = backend_stub.repo.get_commit(commit_idx=0)
commit1 = backend_stub.repo.get_commit(commit_idx=1)
response = self.app.get(
route_path('repo_compare',
repo_name=backend_stub.repo_name,
source_ref_type="rev",
source_ref=commit0.raw_id,
target_ref_type="rev",
target_ref=commit1.raw_id,
))
# outgoing commits between those commits
compare_page = ComparePage(response)
compare_page.swap_is_visible()
compare_page.target_source_are_enabled()
def test_compare_of_fork_with_largefiles(self, backend_hg, settings_util):
orig = backend_hg.create_repo(number_of_commits=1)
fork = backend_hg.create_fork()
settings_util.create_repo_rhodecode_ui(
orig, 'extensions', value='', key='largefiles', active=False)
settings_util.create_repo_rhodecode_ui(
fork, 'extensions', value='', key='largefiles', active=True)
compare_module = ('rhodecode.lib.vcs.backends.hg.repository.'
'MercurialRepository.compare')
with mock.patch(compare_module) as compare_mock:
compare_mock.side_effect = RepositoryRequirementError()
response = self.app.get(
route_path('repo_compare',
repo_name=orig.repo_name,
source_ref_type="rev",
source_ref="tip",
target_ref_type="rev",
target_ref="tip",
params=dict(merge='1', target_repo=fork.repo_name),
),
status=302)
assert_session_flash(
response,
'Could not compare repos with different large file settings')
@pytest.mark.usefixtures("autologin_user")
class TestCompareControllerSvn(object):
def test_supports_references_with_path(self, app, backend_svn):
repo = backend_svn['svn-simple-layout']
commit_id = repo.get_commit(commit_idx=-1).raw_id
response = app.get(
route_path('repo_compare',
repo_name=repo.repo_name,
source_ref_type="tag",
source_ref="%s@%s" % ('tags/v0.1', commit_id),
target_ref_type="tag",
target_ref="%s@%s" % ('tags/v0.2', commit_id),
params=dict(merge='1'),
),
status=200)
# Expecting no commits, since both paths are at the same revision
response.mustcontain('No commits in this compare')
# Should find only one file changed when comparing those two tags
response.mustcontain('example.py')
compare_page = ComparePage(response)
compare_page.contains_change_summary(1, 5, 1)
def test_shows_commits_if_different_ids(self, app, backend_svn):
repo = backend_svn['svn-simple-layout']
source_id = repo.get_commit(commit_idx=-6).raw_id
target_id = repo.get_commit(commit_idx=-1).raw_id
response = app.get(
route_path('repo_compare',
repo_name=repo.repo_name,
source_ref_type="tag",
source_ref="%s@%s" % ('tags/v0.1', source_id),
target_ref_type="tag",
target_ref="%s@%s" % ('tags/v0.2', target_id),
params=dict(merge='1')
),
status=200)
# It should show commits
assert 'No commits in this compare' not in response.body
# Should find only one file changed when comparing those two tags
response.mustcontain('example.py')
compare_page = ComparePage(response)
compare_page.contains_change_summary(1, 5, 1)
class ComparePage(AssertResponse):
"""
Abstracts the page template from the tests
"""
def contains_file_links_and_anchors(self, files):
doc = lxml.html.fromstring(self.response.body)
for filename, file_id in files:
self.contains_one_anchor(file_id)
diffblock = doc.cssselect('[data-f-path="%s"]' % filename)
assert len(diffblock) == 1
assert len(diffblock[0].cssselect('a[href="#%s"]' % file_id)) == 1
def contains_change_summary(self, files_changed, inserted, deleted):
template = (
"{files_changed} file{plural} changed: "
"{inserted} inserted, {deleted} deleted")
self.response.mustcontain(template.format(
files_changed=files_changed,
plural="s" if files_changed > 1 else "",
inserted=inserted,
deleted=deleted))
def contains_commits(self, commits, ancestors=None):
response = self.response
for commit in commits:
# Expecting to see the commit message in an element which
# has the ID "c-{commit.raw_id}"
self.element_contains('#c-' + commit.raw_id, commit.message)
self.contains_one_link(
'r%s:%s' % (commit.idx, commit.short_id),
self._commit_url(commit))
if ancestors:
response.mustcontain('Ancestor')
for ancestor in ancestors:
self.contains_one_link(
ancestor.short_id, self._commit_url(ancestor))
def _commit_url(self, commit):
return '/%s/changeset/%s' % (commit.repository.name, commit.raw_id)
def swap_is_hidden(self):
assert '<a id="btn-swap"' not in self.response.text
def swap_is_visible(self):
assert '<a id="btn-swap"' in self.response.text
def target_source_are_disabled(self):
response = self.response
response.mustcontain("var enable_fields = false;")
response.mustcontain('.select2("enable", enable_fields)')
def target_source_are_enabled(self):
response = self.response
response.mustcontain("var enable_fields = true;")