test_repo_files.py
1071 lines
| 40.9 KiB
| text/x-python
|
PythonLexer
r5608 | # Copyright (C) 2010-2024 RhodeCode GmbH | |||
r1927 | # | |||
# 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 os | ||||
import mock | ||||
import pytest | ||||
r5150 | from collections import OrderedDict | |||
r1927 | ||||
r3885 | from rhodecode.apps.repository.tests.test_repo_compare import ComparePage | |||
r5087 | from rhodecode.apps.repository.views.repo_files import RepoFilesView, get_archive_name, get_path_sha | |||
r1927 | from rhodecode.lib import helpers as h | |||
from rhodecode.lib.ext_json import json | ||||
r5087 | from rhodecode.lib.str_utils import safe_str | |||
r1927 | from rhodecode.lib.vcs import nodes | |||
r5150 | from rhodecode.lib.vcs.conf import settings | |||
from rhodecode.model.db import Session, Repository | ||||
r1927 | ||||
from rhodecode.tests import assert_session_flash | ||||
r5607 | from rhodecode.tests.fixtures.rc_fixture import Fixture | |||
r5173 | from rhodecode.tests.routes import route_path | |||
r1927 | ||||
fixture = Fixture() | ||||
r1932 | ||||
def get_node_history(backend_type): | ||||
return { | ||||
r5649 | "hg": json.loads(fixture.load_resource("hg_node_history_response.json")), | |||
"git": json.loads(fixture.load_resource("git_node_history_response.json")), | ||||
"svn": json.loads(fixture.load_resource("svn_node_history_response.json")), | ||||
r1932 | }[backend_type] | |||
r1927 | ||||
def assert_files_in_response(response, files, params): | ||||
r5649 | template = 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"' | |||
r1927 | _assert_items_in_response(response, files, template, params) | |||
def assert_dirs_in_response(response, dirs, params): | ||||
r5649 | template = 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"' | |||
r1927 | _assert_items_in_response(response, dirs, template, params) | |||
def _assert_items_in_response(response, items, template, params): | ||||
for item in items: | ||||
r5649 | item_params = {"name": item} | |||
r1927 | item_params.update(params) | |||
response.mustcontain(template % item_params) | ||||
def assert_timeago_in_response(response, items, params): | ||||
for item in items: | ||||
r5649 | response.mustcontain(h.age_component(params["date"])) | |||
r1927 | ||||
@pytest.mark.usefixtures("app") | ||||
class TestFilesViews(object): | ||||
def test_show_files(self, backend): | ||||
r5649 | response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path="")) | |||
r1927 | commit = backend.repo.get_commit() | |||
r5649 | params = {"repo_name": backend.repo_name, "commit_id": commit.raw_id, "date": commit.date} | |||
assert_dirs_in_response(response, ["docs", "vcs"], params) | ||||
r1927 | files = [ | |||
r5649 | ".gitignore", | |||
".hgignore", | ||||
".hgtags", | ||||
r1927 | # TODO: missing in Git | |||
# '.travis.yml', | ||||
r5649 | "MANIFEST.in", | |||
"README.rst", | ||||
r1927 | # TODO: File is missing in svn repository | |||
# 'run_test_and_report.sh', | ||||
r5649 | "setup.cfg", | |||
"setup.py", | ||||
"test_and_report.sh", | ||||
"tox.ini", | ||||
r1927 | ] | |||
assert_files_in_response(response, files, params) | ||||
assert_timeago_in_response(response, files, params) | ||||
def test_show_files_links_submodules_with_absolute_url(self, backend_hg): | ||||
r5649 | repo = backend_hg["subrepos"] | |||
response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path="")) | ||||
r1927 | assert_response = response.assert_response() | |||
r5649 | assert_response.contains_one_link("absolute-path @ 000000000000", "http://example.com/absolute-path") | |||
r1927 | ||||
r5649 | def test_show_files_links_submodules_with_absolute_url_subpaths(self, backend_hg): | |||
repo = backend_hg["subrepos"] | ||||
response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path="")) | ||||
r1927 | assert_response = response.assert_response() | |||
r5649 | assert_response.contains_one_link("subpaths-path @ 000000000000", "http://sub-base.example.com/subpaths-path") | |||
r1927 | ||||
@pytest.mark.xfail_backends("svn", reason="Depends on branch support") | ||||
def test_files_menu(self, backend): | ||||
new_branch = "temp_branch_name" | ||||
r5649 | commits = [{"message": "a"}, {"message": "b", "branch": new_branch}] | |||
r1927 | backend.create_repo(commits) | |||
r5150 | backend.repo.landing_rev = f"branch:{new_branch}" | |||
r2929 | Session().commit() | |||
r1927 | ||||
# get response based on tip and not new commit | ||||
r5649 | response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path="")) | |||
r1927 | ||||
# make sure Files menu url is not tip but new commit | ||||
r4370 | landing_rev = backend.repo.landing_ref_name | |||
r5649 | files_url = route_path( | |||
"repo_files:default_path", repo_name=backend.repo_name, commit_id=landing_rev, params={"at": landing_rev} | ||||
) | ||||
r1927 | ||||
r5649 | assert landing_rev != "tip" | |||
r5150 | response.mustcontain(f'<li class="active"><a class="menulink" href="{files_url}">') | |||
r1927 | ||||
def test_show_files_commit(self, backend): | ||||
commit = backend.repo.get_commit(commit_idx=32) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="") | |||
) | ||||
r1927 | ||||
r5649 | dirs = ["docs", "tests"] | |||
files = ["README.rst"] | ||||
r1927 | params = { | |||
r5649 | "repo_name": backend.repo_name, | |||
"commit_id": commit.raw_id, | ||||
r1927 | } | |||
assert_dirs_in_response(response, dirs, params) | ||||
assert_files_in_response(response, files, params) | ||||
def test_show_files_different_branch(self, backend): | ||||
branches = dict( | ||||
r5649 | hg=(150, ["git"]), | |||
r1927 | # TODO: Git test repository does not contain other branches | |||
r5649 | git=(633, ["master"]), | |||
r1927 | # TODO: Branch support in Subversion | |||
r5649 | svn=(150, []), | |||
r1927 | ) | |||
idx, branches = branches[backend.alias] | ||||
commit = backend.repo.get_commit(commit_idx=idx) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="") | |||
) | ||||
r1927 | ||||
assert_response = response.assert_response() | ||||
for branch in branches: | ||||
r5649 | assert_response.element_contains(".tags .branchtag", branch) | |||
r1927 | ||||
def test_show_files_paging(self, backend): | ||||
repo = backend.repo | ||||
indexes = [73, 92, 109, 1, 0] | ||||
r5649 | idx_map = [(rev, repo.get_commit(commit_idx=rev).raw_id) for rev in indexes] | |||
r1927 | ||||
for idx in idx_map: | ||||
r5649 | response = self.app.get(route_path("repo_files", repo_name=backend.repo_name, commit_id=idx[1], f_path="")) | |||
r1927 | ||||
response.mustcontain("""r%s:%s""" % (idx[0], idx[1][:8])) | ||||
def test_file_source(self, backend): | ||||
commit = backend.repo.get_commit(commit_idx=167) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py") | |||
) | ||||
r1927 | ||||
r3776 | msgbox = """<div class="commit">%s</div>""" | |||
r5649 | response.mustcontain(msgbox % (commit.message,)) | |||
r1927 | ||||
assert_response = response.assert_response() | ||||
if commit.branch: | ||||
r5649 | assert_response.element_contains(".tags.tags-main .branchtag", commit.branch) | |||
r1927 | if commit.tags: | |||
for tag in commit.tags: | ||||
r5649 | assert_response.element_contains(".tags.tags-main .tagtag", tag) | |||
r1927 | ||||
def test_file_source_annotated(self, backend): | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files:annotated", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py") | |||
) | ||||
r1927 | expected_commits = { | |||
r5649 | "hg": "r356", | |||
"git": "r345", | ||||
"svn": "r208", | ||||
r1927 | } | |||
response.mustcontain(expected_commits[backend.alias]) | ||||
def test_file_source_authors(self, backend): | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_file_authors", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py") | |||
) | ||||
r1927 | expected_authors = { | |||
r5649 | "hg": ("Marcin Kuzminski", "Lukasz Balcerzak"), | |||
"git": ("Marcin Kuzminski", "Lukasz Balcerzak"), | ||||
"svn": ("marcin", "lukasz"), | ||||
r1927 | } | |||
for author in expected_authors[backend.alias]: | ||||
response.mustcontain(author) | ||||
def test_file_source_authors_with_annotation(self, backend): | ||||
response = self.app.get( | ||||
r5649 | route_path( | |||
"repo_file_authors", | ||||
repo_name=backend.repo_name, | ||||
commit_id="tip", | ||||
f_path="vcs/nodes.py", | ||||
params=dict(annotate=1), | ||||
) | ||||
) | ||||
r1927 | expected_authors = { | |||
r5649 | "hg": ("Marcin Kuzminski", "Lukasz Balcerzak"), | |||
"git": ("Marcin Kuzminski", "Lukasz Balcerzak"), | ||||
"svn": ("marcin", "lukasz"), | ||||
r1927 | } | |||
for author in expected_authors[backend.alias]: | ||||
response.mustcontain(author) | ||||
def test_file_source_history(self, backend, xhr_header): | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_file_history", repo_name=backend.repo_name, commit_id="tip", f_path="vcs/nodes.py"), | |||
extra_environ=xhr_header, | ||||
) | ||||
r1932 | assert get_node_history(backend.alias) == json.loads(response.body) | |||
r1927 | ||||
def test_file_source_history_svn(self, backend_svn, xhr_header): | ||||
r5649 | simple_repo = backend_svn["svn-simple-layout"] | |||
r1927 | response = self.app.get( | |||
r5649 | route_path( | |||
"repo_file_history", repo_name=simple_repo.repo_name, commit_id="tip", f_path="trunk/example.py" | ||||
), | ||||
extra_environ=xhr_header, | ||||
) | ||||
r1927 | ||||
r5649 | expected_data = json.loads(fixture.load_resource("svn_node_history_branches.json")) | |||
r3776 | ||||
r1927 | assert expected_data == response.json | |||
def test_file_source_history_with_annotation(self, backend, xhr_header): | ||||
response = self.app.get( | ||||
r5649 | route_path( | |||
"repo_file_history", | ||||
repo_name=backend.repo_name, | ||||
commit_id="tip", | ||||
f_path="vcs/nodes.py", | ||||
params=dict(annotate=1), | ||||
), | ||||
extra_environ=xhr_header, | ||||
) | ||||
r1932 | assert get_node_history(backend.alias) == json.loads(response.body) | |||
r1927 | ||||
def test_tree_search_top_level(self, backend, xhr_header): | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=""), | |||
extra_environ=xhr_header, | ||||
) | ||||
assert "nodes" in response.json | ||||
assert {"name": "docs", "type": "dir"} in response.json["nodes"] | ||||
r1927 | ||||
def test_tree_search_missing_xhr(self, backend): | ||||
self.app.get( | ||||
r5649 | route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id="tip", f_path=""), status=404 | |||
) | ||||
r1927 | ||||
def test_tree_search_at_path(self, backend, xhr_header): | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/docs"), | |||
extra_environ=xhr_header, | ||||
) | ||||
assert "nodes" in response.json | ||||
nodes = response.json["nodes"] | ||||
assert {"name": "docs/api", "type": "dir"} in nodes | ||||
assert {"name": "docs/index.rst", "type": "file"} in nodes | ||||
r1927 | ||||
def test_tree_search_at_path_2nd_level(self, backend, xhr_header): | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/docs/api"), | |||
extra_environ=xhr_header, | ||||
) | ||||
assert "nodes" in response.json | ||||
nodes = response.json["nodes"] | ||||
assert {"name": "docs/api/index.rst", "type": "file"} in nodes | ||||
r1927 | ||||
def test_tree_search_at_path_missing_xhr(self, backend): | ||||
self.app.get( | ||||
r5649 | route_path("repo_files_nodelist", repo_name=backend.repo_name, commit_id="tip", f_path="/docs"), status=404 | |||
) | ||||
r1927 | ||||
def test_nodetree(self, backend, xhr_header): | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=""), | |||
extra_environ=xhr_header, | ||||
) | ||||
r1927 | ||||
assert_response = response.assert_response() | ||||
r5649 | for attr in ["data-commit-id", "data-date", "data-author"]: | |||
elements = assert_response.get_elements("[{}]".format(attr)) | ||||
r1927 | assert len(elements) > 1 | |||
for element in elements: | ||||
assert element.get(attr) | ||||
def test_nodetree_if_file(self, backend, xhr_header): | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="README.rst"), | |||
extra_environ=xhr_header, | ||||
) | ||||
assert response.text == "" | ||||
r1927 | ||||
def test_nodetree_wrong_path(self, backend, xhr_header): | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
response = self.app.get( | ||||
r5649 | route_path( | |||
"repo_nodetree_full", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="/dont-exist" | ||||
), | ||||
extra_environ=xhr_header, | ||||
) | ||||
r1927 | ||||
r5649 | err = "error: There is no file nor " "directory at the given path" | |||
r4994 | assert err in response.text | |||
r1927 | ||||
def test_nodetree_missing_xhr(self, backend): | ||||
self.app.get( | ||||
r5649 | route_path("repo_nodetree_full", repo_name=backend.repo_name, commit_id="tip", f_path=""), status=404 | |||
) | ||||
r1927 | ||||
@pytest.mark.usefixtures("app", "autologin_user") | ||||
class TestRawFileHandling(object): | ||||
def test_download_file(self, backend): | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
response = self.app.get( | ||||
r5649 | route_path( | |||
"repo_file_download", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py" | ||||
), | ||||
) | ||||
r1927 | ||||
r5649 | assert response.content_disposition == "attachment; filename=\"nodes.py\"; filename*=UTF-8''nodes.py" | |||
r1927 | assert response.content_type == "text/x-python" | |||
def test_download_file_wrong_cs(self, backend): | ||||
r5649 | raw_id = "ERRORce30c96924232dffcd24178a07ffeb5dfc" | |||
r1927 | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_file_download", repo_name=backend.repo_name, commit_id=raw_id, f_path="vcs/nodes.svg"), | |||
status=404, | ||||
) | ||||
r1927 | ||||
msg = """No such commit exists for this repository""" | ||||
response.mustcontain(msg) | ||||
def test_download_file_wrong_f_path(self, backend): | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
r5649 | f_path = "vcs/ERRORnodes.py" | |||
r1927 | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_file_download", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=f_path), | |||
status=404, | ||||
) | ||||
r1927 | ||||
r5649 | msg = "There is no file nor directory at the given path: " "`%s` at commit %s" % (f_path, commit.short_id) | |||
r1927 | response.mustcontain(msg) | |||
def test_file_raw(self, backend): | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path="vcs/nodes.py"), | |||
) | ||||
r1927 | ||||
assert response.content_type == "text/plain" | ||||
def test_file_raw_binary(self, backend): | ||||
commit = backend.repo.get_commit() | ||||
response = self.app.get( | ||||
r5649 | route_path( | |||
"repo_file_raw", | ||||
repo_name=backend.repo_name, | ||||
commit_id=commit.raw_id, | ||||
f_path="docs/theme/ADC/static/breadcrumb_background.png", | ||||
), | ||||
) | ||||
r1927 | ||||
r5649 | assert response.content_disposition == "inline" | |||
r1927 | ||||
def test_raw_file_wrong_cs(self, backend): | ||||
r5649 | raw_id = "ERRORcce30c96924232dffcd24178a07ffeb5dfc" | |||
r1927 | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=raw_id, f_path="vcs/nodes.svg"), | |||
status=404, | ||||
) | ||||
r1927 | ||||
msg = """No such commit exists for this repository""" | ||||
response.mustcontain(msg) | ||||
def test_raw_wrong_f_path(self, backend): | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
r5649 | f_path = "vcs/ERRORnodes.py" | |||
r1927 | response = self.app.get( | |||
r5649 | route_path("repo_file_raw", repo_name=backend.repo_name, commit_id=commit.raw_id, f_path=f_path), status=404 | |||
) | ||||
r1927 | ||||
r5649 | msg = "There is no file nor directory at the given path: " "`%s` at commit %s" % (f_path, commit.short_id) | |||
r1927 | response.mustcontain(msg) | |||
def test_raw_svg_should_not_be_rendered(self, backend): | ||||
backend.create_repo() | ||||
r5087 | backend.ensure_file(b"xss.svg") | |||
r1927 | response = self.app.get( | |||
r5649 | route_path("repo_file_raw", repo_name=backend.repo_name, commit_id="tip", f_path="xss.svg"), | |||
) | ||||
r1927 | # If the content type is image/svg+xml then it allows to render HTML | |||
# and malicious SVG. | ||||
assert response.content_type == "text/plain" | ||||
@pytest.mark.usefixtures("app") | ||||
class TestRepositoryArchival(object): | ||||
def test_archival(self, backend): | ||||
backend.enable_downloads() | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
r5150 | ||||
r3736 | for a_type, content_type, extension in settings.ARCHIVE_SPECS: | |||
r5649 | path_sha = get_path_sha("/") | |||
filename = get_archive_name( | ||||
backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha | ||||
) | ||||
r3736 | ||||
fname = commit.raw_id + extension | ||||
r5649 | response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname)) | |||
r1927 | ||||
r5649 | assert response.status == "200 OK" | |||
r1927 | headers = [ | |||
r5649 | ("Content-Disposition", f"attachment; filename={filename}"), | |||
("Content-Type", content_type), | ||||
r1927 | ] | |||
for header in headers: | ||||
r5150 | assert header in list(response.headers.items()) | |||
r1927 | ||||
r4647 | def test_archival_no_hash(self, backend): | |||
backend.enable_downloads() | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
for a_type, content_type, extension in settings.ARCHIVE_SPECS: | ||||
r5649 | path_sha = get_path_sha("/") | |||
filename = get_archive_name( | ||||
backend.repo_id, | ||||
backend.repo_name, | ||||
commit_sha=commit.short_id, | ||||
ext=extension, | ||||
path_sha=path_sha, | ||||
with_hash=False, | ||||
) | ||||
r4647 | ||||
fname = commit.raw_id + extension | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname, params={"with_hash": 0}) | |||
) | ||||
r4647 | ||||
r5649 | assert response.status == "200 OK" | |||
r4647 | headers = [ | |||
r5649 | ("Content-Disposition", f"attachment; filename={filename}"), | |||
("Content-Type", content_type), | ||||
r4647 | ] | |||
for header in headers: | ||||
r5112 | assert header in list(response.headers.items()) | |||
def test_archival_at_path(self, backend): | ||||
backend.enable_downloads() | ||||
commit = backend.repo.get_commit(commit_idx=190) | ||||
r5649 | at_path = "vcs" | |||
r5112 | ||||
for a_type, content_type, extension in settings.ARCHIVE_SPECS: | ||||
path_sha = get_path_sha(at_path) | ||||
r5649 | filename = get_archive_name( | |||
backend.repo_id, backend.repo_name, commit_sha=commit.short_id, ext=extension, path_sha=path_sha | ||||
) | ||||
r5112 | ||||
fname = commit.raw_id + extension | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname, params={"at_path": at_path}) | |||
) | ||||
r5112 | ||||
r5649 | assert response.status == "200 OK" | |||
r5112 | headers = [ | |||
r5649 | ("Content-Disposition", f"attachment; filename={filename}"), | |||
("Content-Type", content_type), | ||||
r5112 | ] | |||
for header in headers: | ||||
assert header in list(response.headers.items()) | ||||
r4647 | ||||
r5649 | @pytest.mark.parametrize("arch_ext", ["tar", "rar", "x", "..ax", ".zipz", "tar.gz.tar"]) | |||
r1927 | def test_archival_wrong_ext(self, backend, arch_ext): | |||
backend.enable_downloads() | ||||
commit = backend.repo.get_commit(commit_idx=173) | ||||
r5649 | fname = commit.raw_id + "." + arch_ext | |||
r1927 | ||||
r5649 | response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname)) | |||
response.mustcontain("Unknown archive type for: `{}`".format(fname)) | ||||
r1927 | ||||
r5649 | @pytest.mark.parametrize("commit_id", ["00x000000", "tar", "wrong", "@$@$42413232", "232dffcd"]) | |||
r1927 | def test_archival_wrong_commit_id(self, backend, commit_id): | |||
backend.enable_downloads() | ||||
r5649 | fname = f"{commit_id}.zip" | |||
r1927 | ||||
r5649 | response = self.app.get(route_path("repo_archivefile", repo_name=backend.repo_name, fname=fname)) | |||
response.mustcontain("Unknown commit_id") | ||||
r1927 | ||||
@pytest.mark.usefixtures("app") | ||||
class TestFilesDiff(object): | ||||
r5649 | @pytest.mark.parametrize("diff", ["diff", "download", "raw"]) | |||
r1927 | def test_file_full_diff(self, backend, diff): | |||
commit1 = backend.repo.get_commit(commit_idx=-1) | ||||
commit2 = backend.repo.get_commit(commit_idx=-2) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_diff", repo_name=backend.repo_name, f_path="README"), | |||
r1927 | params={ | |||
r5649 | "diff1": commit2.raw_id, | |||
"diff2": commit1.raw_id, | ||||
"fulldiff": "1", | ||||
"diff": diff, | ||||
}, | ||||
) | ||||
r1927 | ||||
r5649 | if diff == "diff": | |||
r1927 | # use redirect since this is OLD view redirecting to compare page | |||
response = response.follow() | ||||
# It's a symlink to README.rst | ||||
r5649 | response.mustcontain("README.rst") | |||
response.mustcontain("No newline at end of file") | ||||
r1927 | ||||
def test_file_binary_diff(self, backend): | ||||
commits = [ | ||||
r5649 | {"message": "First commit"}, | |||
{"message": "Commit with binary", "added": [nodes.FileNode(b"file.bin", content="\0BINARY\0")]}, | ||||
r1927 | ] | |||
repo = backend.create_repo(commits=commits) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_diff", repo_name=backend.repo_name, f_path="file.bin"), | |||
r1927 | params={ | |||
r5649 | "diff1": repo.get_commit(commit_idx=0).raw_id, | |||
"diff2": repo.get_commit(commit_idx=1).raw_id, | ||||
"fulldiff": "1", | ||||
"diff": "diff", | ||||
}, | ||||
) | ||||
r1927 | # use redirect since this is OLD view redirecting to compare page | |||
response = response.follow() | ||||
r5649 | response.mustcontain("Collapse 1 commit") | |||
r3885 | file_changes = (1, 0, 0) | |||
compare_page = ComparePage(response) | ||||
compare_page.contains_change_summary(*file_changes) | ||||
r1927 | ||||
r5649 | if backend.alias == "svn": | |||
response.mustcontain("new file 10644") | ||||
r1927 | # TODO(marcink): SVN doesn't yet detect binary changes | |||
else: | ||||
r5649 | response.mustcontain("new file 100644") | |||
response.mustcontain("binary diff hidden") | ||||
r1927 | ||||
def test_diff_2way(self, backend): | ||||
commit1 = backend.repo.get_commit(commit_idx=-1) | ||||
commit2 = backend.repo.get_commit(commit_idx=-2) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_diff_2way_redirect", repo_name=backend.repo_name, f_path="README"), | |||
r1927 | params={ | |||
r5649 | "diff1": commit2.raw_id, | |||
"diff2": commit1.raw_id, | ||||
}, | ||||
) | ||||
r1927 | # use redirect since this is OLD view redirecting to compare page | |||
response = response.follow() | ||||
# It's a symlink to README.rst | ||||
r5649 | response.mustcontain("README.rst") | |||
response.mustcontain("No newline at end of file") | ||||
r1927 | ||||
def test_requires_one_commit_id(self, backend, autologin_user): | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_diff", repo_name=backend.repo_name, f_path="README.rst"), status=400 | |||
) | ||||
response.mustcontain("Need query parameter", "diff1", "diff2", "to generate a diff.") | ||||
r1927 | ||||
def test_returns_no_files_if_file_does_not_exist(self, vcsbackend): | ||||
repo = vcsbackend.repo | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_diff", repo_name=repo.name, f_path="does-not-exist-in-any-commit"), | |||
params={"diff1": repo[0].raw_id, "diff2": repo[1].raw_id}, | ||||
) | ||||
r1927 | ||||
response = response.follow() | ||||
r5649 | response.mustcontain("No files") | |||
r1927 | ||||
def test_returns_redirect_if_file_not_changed(self, backend): | ||||
commit = backend.repo.get_commit(commit_idx=-1) | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_diff_2way_redirect", repo_name=backend.repo_name, f_path="README"), | |||
r1927 | params={ | |||
r5649 | "diff1": commit.raw_id, | |||
"diff2": commit.raw_id, | ||||
}, | ||||
) | ||||
r1927 | ||||
response = response.follow() | ||||
r5649 | response.mustcontain("No files") | |||
response.mustcontain("No commits in this compare") | ||||
r1927 | ||||
def test_supports_diff_to_different_path_svn(self, backend_svn): | ||||
r5649 | # TODO: check this case | |||
r1927 | return | |||
r5649 | repo = backend_svn["svn-simple-layout"].scm_instance() | |||
commit_id_1 = "24" | ||||
commit_id_2 = "26" | ||||
r1927 | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"), | |||
r1927 | params={ | |||
r5649 | "diff1": "tags/v0.2/example.py@" + commit_id_1, | |||
"diff2": commit_id_2, | ||||
}, | ||||
) | ||||
r1927 | ||||
response = response.follow() | ||||
response.mustcontain( | ||||
# diff contains this | ||||
r5649 | "Will print out a useful message on invocation." | |||
) | ||||
r1927 | ||||
# Note: Expecting that we indicate the user what's being compared | ||||
response.mustcontain("trunk/example.py") | ||||
response.mustcontain("tags/v0.2/example.py") | ||||
def test_show_rev_redirects_to_svn_path(self, backend_svn): | ||||
r5649 | # TODO: check this case | |||
r1927 | return | |||
r5649 | repo = backend_svn["svn-simple-layout"].scm_instance() | |||
r1927 | commit_id = repo[-1].raw_id | |||
response = self.app.get( | ||||
r5649 | route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"), | |||
r1927 | params={ | |||
r5649 | "diff1": "branches/argparse/example.py@" + commit_id, | |||
"diff2": commit_id, | ||||
r1927 | }, | |||
r5649 | status=302, | |||
) | ||||
r1927 | response = response.follow() | |||
r5649 | assert response.headers["Location"].endswith("svn-svn-simple-layout/files/26/branches/argparse/example.py") | |||
r1927 | ||||
def test_show_rev_and_annotate_redirects_to_svn_path(self, backend_svn): | ||||
r5649 | # TODO: check this case | |||
r1927 | return | |||
r5649 | repo = backend_svn["svn-simple-layout"].scm_instance() | |||
r1927 | commit_id = repo[-1].raw_id | |||
response = self.app.get( | ||||
r5649 | route_path("repo_files_diff", repo_name=backend_svn.repo_name, f_path="trunk/example.py"), | |||
r1927 | params={ | |||
r5649 | "diff1": "branches/argparse/example.py@" + commit_id, | |||
"diff2": commit_id, | ||||
"show_rev": "Show at Revision", | ||||
"annotate": "true", | ||||
r1927 | }, | |||
r5649 | status=302, | |||
) | ||||
r1927 | response = response.follow() | |||
r5649 | assert response.headers["Location"].endswith("svn-svn-simple-layout/annotate/26/branches/argparse/example.py") | |||
r1927 | ||||
@pytest.mark.usefixtures("app", "autologin_user") | ||||
class TestModifyFilesWithWebInterface(object): | ||||
def test_add_file_view(self, backend): | ||||
r5649 | self.app.get(route_path("repo_files_add_file", repo_name=backend.repo_name, commit_id="tip", f_path="")) | |||
r1927 | ||||
@pytest.mark.xfail_backends("svn", reason="Depends on online editing") | ||||
def test_add_file_into_repo_missing_content(self, backend, csrf_token): | ||||
r3776 | backend.create_repo() | |||
r5649 | filename = "init.py" | |||
r1927 | response = self.app.post( | |||
r5649 | route_path("repo_files_create_file", repo_name=backend.repo_name, commit_id="tip", f_path=""), | |||
r1927 | params={ | |||
r5649 | "content": "", | |||
"filename": filename, | ||||
"csrf_token": csrf_token, | ||||
r1927 | }, | |||
r5649 | status=302, | |||
) | ||||
expected_msg = "Successfully committed new file `{}`".format(os.path.join(filename)) | ||||
r3776 | assert_session_flash(response, expected_msg) | |||
r1927 | ||||
def test_add_file_into_repo_missing_filename(self, backend, csrf_token): | ||||
r3776 | commit_id = backend.repo.get_commit().raw_id | |||
r1927 | response = self.app.post( | |||
r5649 | route_path("repo_files_create_file", repo_name=backend.repo_name, commit_id=commit_id, f_path=""), | |||
r1927 | params={ | |||
r5649 | "content": "foo", | |||
"csrf_token": csrf_token, | ||||
r1927 | }, | |||
r5649 | status=302, | |||
) | ||||
r1927 | ||||
r5649 | assert_session_flash(response, "No filename specified") | |||
r1927 | ||||
r5649 | def test_add_file_into_repo_errors_and_no_commits(self, backend, csrf_token): | |||
r1927 | repo = backend.create_repo() | |||
# Create a file with no filename, it will display an error but | ||||
# the repo has no commits yet | ||||
response = self.app.post( | ||||
r5649 | route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id="tip", f_path=""), | |||
r1927 | params={ | |||
r5649 | "content": "foo", | |||
"csrf_token": csrf_token, | ||||
r1927 | }, | |||
r5649 | status=302, | |||
) | ||||
r1927 | ||||
r5649 | assert_session_flash(response, "No filename specified") | |||
r1927 | ||||
# Not allowed, redirect to the summary | ||||
redirected = response.follow() | ||||
r5649 | summary_url = h.route_path("repo_summary", repo_name=repo.repo_name) | |||
r1927 | ||||
# As there are no commits, displays the summary page with the error of | ||||
# creating a file with no filename | ||||
assert redirected.request.path == summary_url | ||||
r5649 | @pytest.mark.parametrize( | |||
"filename, clean_filename", | ||||
[ | ||||
("/abs/foo", "abs/foo"), | ||||
("../rel/foo", "rel/foo"), | ||||
("file/../foo/foo", "file/foo/foo"), | ||||
], | ||||
) | ||||
r3776 | def test_add_file_into_repo_bad_filenames(self, filename, clean_filename, backend, csrf_token): | |||
repo = backend.create_repo() | ||||
commit_id = repo.get_commit().raw_id | ||||
r1927 | response = self.app.post( | |||
r5649 | route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id=commit_id, f_path=""), | |||
r1927 | params={ | |||
r5649 | "content": "foo", | |||
"filename": filename, | ||||
"csrf_token": csrf_token, | ||||
r1927 | }, | |||
r5649 | status=302, | |||
) | ||||
r1927 | ||||
r5649 | expected_msg = "Successfully committed new file `{}`".format(clean_filename) | |||
r3776 | assert_session_flash(response, expected_msg) | |||
r1927 | ||||
r5649 | @pytest.mark.parametrize( | |||
"cnt, filename, content", | ||||
[ | ||||
(1, "foo.txt", "Content"), | ||||
(2, "dir/foo.rst", "Content"), | ||||
(3, "dir/foo-second.rst", "Content"), | ||||
(4, "rel/dir/foo.bar", "Content"), | ||||
], | ||||
) | ||||
r3776 | def test_add_file_into_empty_repo(self, cnt, filename, content, backend, csrf_token): | |||
r1927 | repo = backend.create_repo() | |||
r3776 | commit_id = repo.get_commit().raw_id | |||
r1927 | response = self.app.post( | |||
r5649 | route_path("repo_files_create_file", repo_name=repo.repo_name, commit_id=commit_id, f_path=""), | |||
r1927 | params={ | |||
r5649 | "content": content, | |||
"filename": filename, | ||||
"csrf_token": csrf_token, | ||||
r1927 | }, | |||
r5649 | status=302, | |||
) | ||||
r3776 | ||||
r5649 | expected_msg = "Successfully committed new file `{}`".format(filename) | |||
r3776 | assert_session_flash(response, expected_msg) | |||
r1927 | ||||
def test_edit_file_view(self, backend): | ||||
response = self.app.get( | ||||
r5649 | route_path( | |||
"repo_files_edit_file", | ||||
repo_name=backend.repo_name, | ||||
commit_id=backend.default_head_id, | ||||
f_path="vcs/nodes.py", | ||||
), | ||||
status=200, | ||||
) | ||||
r1927 | response.mustcontain("Module holding everything related to vcs nodes.") | |||
def test_edit_file_view_not_on_branch(self, backend): | ||||
repo = backend.create_repo() | ||||
r5087 | backend.ensure_file(b"vcs/nodes.py") | |||
r1927 | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_edit_file", repo_name=repo.repo_name, commit_id="tip", f_path="vcs/nodes.py"), | |||
status=302, | ||||
) | ||||
assert_session_flash(response, "Cannot modify file. Given commit `tip` is not head of a branch.") | ||||
r1927 | ||||
def test_edit_file_view_commit_changes(self, backend, csrf_token): | ||||
repo = backend.create_repo() | ||||
r5087 | backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'") | |||
r1927 | ||||
response = self.app.post( | ||||
r5649 | route_path( | |||
"repo_files_update_file", | ||||
repo_name=repo.repo_name, | ||||
commit_id=backend.default_head_id, | ||||
f_path="vcs/nodes.py", | ||||
), | ||||
r1927 | params={ | |||
r5649 | "content": "print 'hello world'", | |||
"message": "I committed", | ||||
"filename": "vcs/nodes.py", | ||||
"csrf_token": csrf_token, | ||||
r1927 | }, | |||
r5649 | status=302, | |||
) | ||||
assert_session_flash(response, "Successfully committed changes to file `vcs/nodes.py`") | ||||
r1927 | tip = repo.get_commit(commit_idx=-1) | |||
r5649 | assert tip.message == "I committed" | |||
r1927 | ||||
r5274 | def test_replace_binary_file_view_commit_changes(self, backend, csrf_token): | |||
repo = backend.create_repo() | ||||
backend.ensure_file(b"vcs/nodes.docx", content=b"PREVIOUS CONTENT'") | ||||
response = self.app.post( | ||||
r5649 | route_path( | |||
"repo_files_replace_binary", | ||||
repo_name=repo.repo_name, | ||||
commit_id=backend.default_head_id, | ||||
f_path="vcs/nodes.docx", | ||||
), | ||||
r5274 | params={ | |||
r5649 | "message": "I committed", | |||
"csrf_token": csrf_token, | ||||
r5274 | }, | |||
r5649 | upload_files=[("files_upload", "vcs/nodes.docx", b"SOME CONTENT")], | |||
status=200, | ||||
) | ||||
assert_session_flash(response, "Successfully committed 1 new file") | ||||
r5274 | tip = repo.get_commit(commit_idx=-1) | |||
r5649 | assert tip.message == "I committed" | |||
r5274 | ||||
r5649 | def test_edit_file_view_commit_changes_default_message(self, backend, csrf_token): | |||
r1927 | repo = backend.create_repo() | |||
r5087 | backend.ensure_file(b"vcs/nodes.py", content=b"print 'hello'") | |||
r1927 | ||||
r5649 | commit_id = backend.default_branch_name or backend.repo.scm_instance().commit_ids[-1] | |||
r1927 | ||||
response = self.app.post( | ||||
r5649 | route_path("repo_files_update_file", repo_name=repo.repo_name, commit_id=commit_id, f_path="vcs/nodes.py"), | |||
r1927 | params={ | |||
r5649 | "content": "print 'hello world'", | |||
"message": "", | ||||
"filename": "vcs/nodes.py", | ||||
"csrf_token": csrf_token, | ||||
r1927 | }, | |||
r5649 | status=302, | |||
) | ||||
assert_session_flash(response, "Successfully committed changes to file `vcs/nodes.py`") | ||||
r1927 | tip = repo.get_commit(commit_idx=-1) | |||
r5649 | assert tip.message == "Edited file vcs/nodes.py via RhodeCode Enterprise" | |||
r1927 | ||||
r5274 | def test_replace_binary_file_content_with_content_that_not_belong_to_original_type(self, backend, csrf_token): | |||
repo = backend.create_repo() | ||||
backend.ensure_file(b"vcs/sheet.xlsx", content=b"PREVIOUS CONTENT'") | ||||
response = self.app.post( | ||||
r5649 | route_path( | |||
"repo_files_replace_binary", | ||||
repo_name=repo.repo_name, | ||||
commit_id=backend.default_head_id, | ||||
f_path="vcs/sheet.xlsx", | ||||
), | ||||
r5274 | params={ | |||
r5649 | "message": "I committed", | |||
"csrf_token": csrf_token, | ||||
r5274 | }, | |||
r5649 | upload_files=[("files_upload", "vcs/sheet.docx", b"SOME CONTENT")], | |||
status=200, | ||||
) | ||||
assert response.json["error"] == "file extension of uploaded file doesn't match an original file's extension" | ||||
r5274 | ||||
r5649 | @pytest.mark.parametrize( | |||
"replacement_files, expected_error", | ||||
[ | ||||
([], "missing files"), | ||||
( | ||||
[ | ||||
("files_upload", "vcs/node1.docx", b"SOME CONTENT"), | ||||
("files_upload", "vcs/node2.docx", b"SOME CONTENT"), | ||||
], | ||||
"too many files for replacement", | ||||
), | ||||
], | ||||
) | ||||
def test_replace_binary_with_wrong_amount_of_content_sources( | ||||
self, replacement_files, expected_error, backend, csrf_token | ||||
): | ||||
r5274 | repo = backend.create_repo() | |||
backend.ensure_file(b"vcs/node.docx", content=b"PREVIOUS CONTENT'") | ||||
response = self.app.post( | ||||
r5649 | route_path( | |||
"repo_files_replace_binary", | ||||
repo_name=repo.repo_name, | ||||
commit_id=backend.default_head_id, | ||||
f_path="vcs/node.docx", | ||||
), | ||||
r5274 | params={ | |||
r5649 | "message": "I committed", | |||
"csrf_token": csrf_token, | ||||
r5274 | }, | |||
upload_files=replacement_files, | ||||
r5649 | status=200, | |||
) | ||||
assert response.json["error"] == expected_error | ||||
r5274 | ||||
r1927 | def test_delete_file_view(self, backend): | |||
self.app.get( | ||||
r5649 | route_path( | |||
"repo_files_remove_file", | ||||
repo_name=backend.repo_name, | ||||
commit_id=backend.default_head_id, | ||||
f_path="vcs/nodes.py", | ||||
), | ||||
status=200, | ||||
) | ||||
r1927 | ||||
def test_delete_file_view_not_on_branch(self, backend): | ||||
repo = backend.create_repo() | ||||
r5649 | backend.ensure_file(b"vcs/nodes.py") | |||
r1927 | ||||
response = self.app.get( | ||||
r5649 | route_path("repo_files_remove_file", repo_name=repo.repo_name, commit_id="tip", f_path="vcs/nodes.py"), | |||
status=302, | ||||
) | ||||
assert_session_flash(response, "Cannot modify file. Given commit `tip` is not head of a branch.") | ||||
r1927 | ||||
def test_delete_file_view_commit_changes(self, backend, csrf_token): | ||||
repo = backend.create_repo() | ||||
r5087 | backend.ensure_file(b"vcs/nodes.py") | |||
r1927 | ||||
response = self.app.post( | ||||
r5649 | route_path( | |||
"repo_files_delete_file", | ||||
repo_name=repo.repo_name, | ||||
commit_id=backend.default_head_id, | ||||
f_path="vcs/nodes.py", | ||||
), | ||||
r1927 | params={ | |||
r5649 | "message": "i committed", | |||
"csrf_token": csrf_token, | ||||
r1927 | }, | |||
r5649 | status=302, | |||
) | ||||
assert_session_flash(response, "Successfully deleted file `vcs/nodes.py`") | ||||
r1927 | ||||
@pytest.mark.usefixtures("app") | ||||
class TestFilesViewOtherCases(object): | ||||
def test_access_empty_repo_redirect_to_summary_with_alert_write_perms( | ||||
r5649 | self, backend_stub, autologin_regular_user, user_regular, user_util | |||
): | ||||
r1927 | repo = backend_stub.create_repo() | |||
r5649 | user_util.grant_user_permission_to_repo(repo, user_regular, "repository.write") | |||
response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path="")) | ||||
r1927 | ||||
r5649 | repo_file_add_url = route_path("repo_files_add_file", repo_name=repo.repo_name, commit_id=0, f_path="") | |||
r5087 | add_new = f'<a class="alert-link" href="{repo_file_add_url}">add a new file</a>' | |||
r5649 | repo_file_upload_url = route_path("repo_files_upload_file", repo_name=repo.repo_name, commit_id=0, f_path="") | |||
r5087 | upload_new = f'<a class="alert-link" href="{repo_file_upload_url}">upload a new file</a>' | |||
r1927 | ||||
r5649 | assert_session_flash(response, "There are no files yet. Click here to %s or %s." % (add_new, upload_new)) | |||
r1927 | ||||
def test_access_empty_repo_redirect_to_summary_with_alert_no_write_perms( | ||||
r5649 | self, backend_stub, autologin_regular_user | |||
): | ||||
r1927 | repo = backend_stub.create_repo() | |||
r2358 | # init session for anon user | |||
r5649 | route_path("repo_summary", repo_name=repo.repo_name) | |||
r2358 | ||||
r5649 | repo_file_add_url = route_path("repo_files_add_file", repo_name=repo.repo_name, commit_id=0, f_path="") | |||
r1927 | ||||
r5649 | response = self.app.get(route_path("repo_files", repo_name=repo.repo_name, commit_id="tip", f_path="")) | |||
r1927 | ||||
assert_session_flash(response, no_=repo_file_add_url) | ||||
r5649 | @pytest.mark.parametrize( | |||
"file_node", | ||||
[ | ||||
b"archive/file.zip", | ||||
b"diff/my-file.txt", | ||||
b"render.py", | ||||
b"render", | ||||
b"remove_file", | ||||
b"remove_file/to-delete.txt", | ||||
], | ||||
) | ||||
r1927 | def test_file_names_equal_to_routes_parts(self, backend, file_node): | |||
backend.create_repo() | ||||
backend.ensure_file(file_node) | ||||
self.app.get( | ||||
r5649 | route_path("repo_files", repo_name=backend.repo_name, commit_id="tip", f_path=safe_str(file_node)), | |||
status=200, | ||||
) | ||||
r1927 | ||||
class TestAdjustFilePathForSvn(object): | ||||
""" | ||||
SVN specific adjustments of node history in RepoFilesView. | ||||
""" | ||||
def test_returns_path_relative_to_matched_reference(self): | ||||
r5649 | repo = self._repo(branches=["trunk"]) | |||
self.assert_file_adjustment("trunk/file", "file", repo) | ||||
r1927 | ||||
def test_does_not_modify_file_if_no_reference_matches(self): | ||||
r5649 | repo = self._repo(branches=["trunk"]) | |||
self.assert_file_adjustment("notes/file", "notes/file", repo) | ||||
r1927 | ||||
def test_does_not_adjust_partial_directory_names(self): | ||||
r5649 | repo = self._repo(branches=["trun"]) | |||
self.assert_file_adjustment("trunk/file", "trunk/file", repo) | ||||
r1927 | ||||
def test_is_robust_to_patterns_which_prefix_other_patterns(self): | ||||
r5649 | repo = self._repo(branches=["trunk", "trunk/new", "trunk/old"]) | |||
self.assert_file_adjustment("trunk/new/file", "file", repo) | ||||
r1927 | ||||
def assert_file_adjustment(self, f_path, expected, repo): | ||||
result = RepoFilesView.adjust_file_path_for_svn(f_path, repo) | ||||
assert result == expected | ||||
def _repo(self, branches=None): | ||||
repo = mock.Mock() | ||||
r5649 | repo.branches = OrderedDict((name, "0") for name in branches or []) | |||
r1927 | repo.tags = {} | |||
return repo | ||||