##// END OF EJS Templates
tests: functional tests for diff 2 way
lisaq -
r622:79a1b51c default
parent child Browse files
Show More
@@ -1,942 +1,1078 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22
22
23 import mock
23 import mock
24 import pytest
24 import pytest
25
25
26 from rhodecode.controllers.files import FilesController
26 from rhodecode.controllers.files import FilesController
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.compat import OrderedDict
28 from rhodecode.lib.compat import OrderedDict
29 from rhodecode.lib.ext_json import json
29 from rhodecode.lib.ext_json import json
30 from rhodecode.lib.vcs import nodes
30 from rhodecode.lib.vcs import nodes
31 from rhodecode.lib.vcs.backends.base import EmptyCommit
31 from rhodecode.lib.vcs.conf import settings
32 from rhodecode.lib.vcs.conf import settings
33 from rhodecode.lib.vcs.nodes import FileNode
34 from rhodecode.model.db import Repository
35 from rhodecode.model.scm import ScmModel
32 from rhodecode.tests import (
36 from rhodecode.tests import (
33 url, assert_session_flash, assert_not_in_session_flash)
37 url, TEST_USER_ADMIN_LOGIN, assert_session_flash, assert_not_in_session_flash)
34 from rhodecode.tests.fixture import Fixture
38 from rhodecode.tests.fixture import Fixture
35 from rhodecode.tests.utils import AssertResponse
39 from rhodecode.tests.utils import AssertResponse
36
40
37 fixture = Fixture()
41 fixture = Fixture()
38
42
39 NODE_HISTORY = {
43 NODE_HISTORY = {
40 'hg': json.loads(fixture.load_resource('hg_node_history_response.json')),
44 'hg': json.loads(fixture.load_resource('hg_node_history_response.json')),
41 'git': json.loads(fixture.load_resource('git_node_history_response.json')),
45 'git': json.loads(fixture.load_resource('git_node_history_response.json')),
42 'svn': json.loads(fixture.load_resource('svn_node_history_response.json')),
46 'svn': json.loads(fixture.load_resource('svn_node_history_response.json')),
43 }
47 }
44
48
45
49
50
51 def _commit_change(
52 repo, filename, content, message, vcs_type, parent=None,
53 newfile=False):
54 repo = Repository.get_by_repo_name(repo)
55 _commit = parent
56 if not parent:
57 _commit = EmptyCommit(alias=vcs_type)
58
59 if newfile:
60 nodes = {
61 filename: {
62 'content': content
63 }
64 }
65 commit = ScmModel().create_nodes(
66 user=TEST_USER_ADMIN_LOGIN, repo=repo,
67 message=message,
68 nodes=nodes,
69 parent_commit=_commit,
70 author=TEST_USER_ADMIN_LOGIN,
71 )
72 else:
73 commit = ScmModel().commit_change(
74 repo=repo.scm_instance(), repo_name=repo.repo_name,
75 commit=parent, user=TEST_USER_ADMIN_LOGIN,
76 author=TEST_USER_ADMIN_LOGIN,
77 message=message,
78 content=content,
79 f_path=filename
80 )
81 return commit
82
83
84
46 @pytest.mark.usefixtures("app")
85 @pytest.mark.usefixtures("app")
47 class TestFilesController:
86 class TestFilesController:
48
87
49 def test_index(self, backend):
88 def test_index(self, backend):
50 response = self.app.get(url(
89 response = self.app.get(url(
51 controller='files', action='index',
90 controller='files', action='index',
52 repo_name=backend.repo_name, revision='tip', f_path='/'))
91 repo_name=backend.repo_name, revision='tip', f_path='/'))
53 commit = backend.repo.get_commit()
92 commit = backend.repo.get_commit()
54
93
55 params = {
94 params = {
56 'repo_name': backend.repo_name,
95 'repo_name': backend.repo_name,
57 'commit_id': commit.raw_id,
96 'commit_id': commit.raw_id,
58 'date': commit.date
97 'date': commit.date
59 }
98 }
60 assert_dirs_in_response(response, ['docs', 'vcs'], params)
99 assert_dirs_in_response(response, ['docs', 'vcs'], params)
61 files = [
100 files = [
62 '.gitignore',
101 '.gitignore',
63 '.hgignore',
102 '.hgignore',
64 '.hgtags',
103 '.hgtags',
65 # TODO: missing in Git
104 # TODO: missing in Git
66 # '.travis.yml',
105 # '.travis.yml',
67 'MANIFEST.in',
106 'MANIFEST.in',
68 'README.rst',
107 'README.rst',
69 # TODO: File is missing in svn repository
108 # TODO: File is missing in svn repository
70 # 'run_test_and_report.sh',
109 # 'run_test_and_report.sh',
71 'setup.cfg',
110 'setup.cfg',
72 'setup.py',
111 'setup.py',
73 'test_and_report.sh',
112 'test_and_report.sh',
74 'tox.ini',
113 'tox.ini',
75 ]
114 ]
76 assert_files_in_response(response, files, params)
115 assert_files_in_response(response, files, params)
77 assert_timeago_in_response(response, files, params)
116 assert_timeago_in_response(response, files, params)
78
117
79 def test_index_links_submodules_with_absolute_url(self, backend_hg):
118 def test_index_links_submodules_with_absolute_url(self, backend_hg):
80 repo = backend_hg['subrepos']
119 repo = backend_hg['subrepos']
81 response = self.app.get(url(
120 response = self.app.get(url(
82 controller='files', action='index',
121 controller='files', action='index',
83 repo_name=repo.repo_name, revision='tip', f_path='/'))
122 repo_name=repo.repo_name, revision='tip', f_path='/'))
84 assert_response = AssertResponse(response)
123 assert_response = AssertResponse(response)
85 assert_response.contains_one_link(
124 assert_response.contains_one_link(
86 'absolute-path @ 000000000000', 'http://example.com/absolute-path')
125 'absolute-path @ 000000000000', 'http://example.com/absolute-path')
87
126
88 def test_index_links_submodules_with_absolute_url_subpaths(
127 def test_index_links_submodules_with_absolute_url_subpaths(
89 self, backend_hg):
128 self, backend_hg):
90 repo = backend_hg['subrepos']
129 repo = backend_hg['subrepos']
91 response = self.app.get(url(
130 response = self.app.get(url(
92 controller='files', action='index',
131 controller='files', action='index',
93 repo_name=repo.repo_name, revision='tip', f_path='/'))
132 repo_name=repo.repo_name, revision='tip', f_path='/'))
94 assert_response = AssertResponse(response)
133 assert_response = AssertResponse(response)
95 assert_response.contains_one_link(
134 assert_response.contains_one_link(
96 'subpaths-path @ 000000000000',
135 'subpaths-path @ 000000000000',
97 'http://sub-base.example.com/subpaths-path')
136 'http://sub-base.example.com/subpaths-path')
98
137
99 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
138 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
100 def test_files_menu(self, backend):
139 def test_files_menu(self, backend):
101 new_branch = "temp_branch_name"
140 new_branch = "temp_branch_name"
102 commits = [
141 commits = [
103 {'message': 'a'},
142 {'message': 'a'},
104 {'message': 'b', 'branch': new_branch}
143 {'message': 'b', 'branch': new_branch}
105 ]
144 ]
106 backend.create_repo(commits)
145 backend.create_repo(commits)
107
146
108 backend.repo.landing_rev = "branch:%s" % new_branch
147 backend.repo.landing_rev = "branch:%s" % new_branch
109
148
110 # get response based on tip and not new revision
149 # get response based on tip and not new revision
111 response = self.app.get(url(
150 response = self.app.get(url(
112 controller='files', action='index',
151 controller='files', action='index',
113 repo_name=backend.repo_name, revision='tip', f_path='/'),
152 repo_name=backend.repo_name, revision='tip', f_path='/'),
114 status=200)
153 status=200)
115
154
116 # make sure Files menu url is not tip but new revision
155 # make sure Files menu url is not tip but new revision
117 landing_rev = backend.repo.landing_rev[1]
156 landing_rev = backend.repo.landing_rev[1]
118 files_url = url('files_home', repo_name=backend.repo_name,
157 files_url = url('files_home', repo_name=backend.repo_name,
119 revision=landing_rev)
158 revision=landing_rev)
120
159
121 assert landing_rev != 'tip'
160 assert landing_rev != 'tip'
122 response.mustcontain('<li class="active"><a class="menulink" href="%s">' % files_url)
161 response.mustcontain('<li class="active"><a class="menulink" href="%s">' % files_url)
123
162
124 def test_index_commit(self, backend):
163 def test_index_commit(self, backend):
125 commit = backend.repo.get_commit(commit_idx=32)
164 commit = backend.repo.get_commit(commit_idx=32)
126
165
127 response = self.app.get(url(
166 response = self.app.get(url(
128 controller='files', action='index',
167 controller='files', action='index',
129 repo_name=backend.repo_name,
168 repo_name=backend.repo_name,
130 revision=commit.raw_id,
169 revision=commit.raw_id,
131 f_path='/')
170 f_path='/')
132 )
171 )
133
172
134 dirs = ['docs', 'tests']
173 dirs = ['docs', 'tests']
135 files = ['README.rst']
174 files = ['README.rst']
136 params = {
175 params = {
137 'repo_name': backend.repo_name,
176 'repo_name': backend.repo_name,
138 'commit_id': commit.raw_id,
177 'commit_id': commit.raw_id,
139 }
178 }
140 assert_dirs_in_response(response, dirs, params)
179 assert_dirs_in_response(response, dirs, params)
141 assert_files_in_response(response, files, params)
180 assert_files_in_response(response, files, params)
142
181
143 @pytest.mark.xfail_backends("git", reason="Missing branches in git repo")
182 @pytest.mark.xfail_backends("git", reason="Missing branches in git repo")
144 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
183 @pytest.mark.xfail_backends("svn", reason="Depends on branch support")
145 def test_index_different_branch(self, backend):
184 def test_index_different_branch(self, backend):
146 # TODO: Git test repository does not contain branches
185 # TODO: Git test repository does not contain branches
147 # TODO: Branch support in Subversion
186 # TODO: Branch support in Subversion
148
187
149 commit = backend.repo.get_commit(commit_idx=150)
188 commit = backend.repo.get_commit(commit_idx=150)
150 response = self.app.get(url(
189 response = self.app.get(url(
151 controller='files', action='index',
190 controller='files', action='index',
152 repo_name=backend.repo_name,
191 repo_name=backend.repo_name,
153 revision=commit.raw_id,
192 revision=commit.raw_id,
154 f_path='/'))
193 f_path='/'))
155 assert_response = AssertResponse(response)
194 assert_response = AssertResponse(response)
156 assert_response.element_contains(
195 assert_response.element_contains(
157 '.tags .branchtag', 'git')
196 '.tags .branchtag', 'git')
158
197
159 def test_index_paging(self, backend):
198 def test_index_paging(self, backend):
160 repo = backend.repo
199 repo = backend.repo
161 indexes = [73, 92, 109, 1, 0]
200 indexes = [73, 92, 109, 1, 0]
162 idx_map = [(rev, repo.get_commit(commit_idx=rev).raw_id)
201 idx_map = [(rev, repo.get_commit(commit_idx=rev).raw_id)
163 for rev in indexes]
202 for rev in indexes]
164
203
165 for idx in idx_map:
204 for idx in idx_map:
166 response = self.app.get(url(
205 response = self.app.get(url(
167 controller='files', action='index',
206 controller='files', action='index',
168 repo_name=backend.repo_name,
207 repo_name=backend.repo_name,
169 revision=idx[1],
208 revision=idx[1],
170 f_path='/'))
209 f_path='/'))
171
210
172 response.mustcontain("""r%s:%s""" % (idx[0], idx[1][:8]))
211 response.mustcontain("""r%s:%s""" % (idx[0], idx[1][:8]))
173
212
174 def test_file_source(self, backend):
213 def test_file_source(self, backend):
175 commit = backend.repo.get_commit(commit_idx=167)
214 commit = backend.repo.get_commit(commit_idx=167)
176 response = self.app.get(url(
215 response = self.app.get(url(
177 controller='files', action='index',
216 controller='files', action='index',
178 repo_name=backend.repo_name,
217 repo_name=backend.repo_name,
179 revision=commit.raw_id,
218 revision=commit.raw_id,
180 f_path='vcs/nodes.py'))
219 f_path='vcs/nodes.py'))
181
220
182 msgbox = """<div class="commit right-content">%s</div>"""
221 msgbox = """<div class="commit right-content">%s</div>"""
183 response.mustcontain(msgbox % (commit.message, ))
222 response.mustcontain(msgbox % (commit.message, ))
184
223
185 assert_response = AssertResponse(response)
224 assert_response = AssertResponse(response)
186 if commit.branch:
225 if commit.branch:
187 assert_response.element_contains('.tags.tags-main .branchtag', commit.branch)
226 assert_response.element_contains('.tags.tags-main .branchtag', commit.branch)
188 if commit.tags:
227 if commit.tags:
189 for tag in commit.tags:
228 for tag in commit.tags:
190 assert_response.element_contains('.tags.tags-main .tagtag', tag)
229 assert_response.element_contains('.tags.tags-main .tagtag', tag)
191
230
192 def test_file_source_history(self, backend):
231 def test_file_source_history(self, backend):
193 response = self.app.get(
232 response = self.app.get(
194 url(
233 url(
195 controller='files', action='history',
234 controller='files', action='history',
196 repo_name=backend.repo_name,
235 repo_name=backend.repo_name,
197 revision='tip',
236 revision='tip',
198 f_path='vcs/nodes.py'),
237 f_path='vcs/nodes.py'),
199 extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
238 extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
200 assert NODE_HISTORY[backend.alias] == json.loads(response.body)
239 assert NODE_HISTORY[backend.alias] == json.loads(response.body)
201
240
202 def test_file_source_history_svn(self, backend_svn):
241 def test_file_source_history_svn(self, backend_svn):
203 simple_repo = backend_svn['svn-simple-layout']
242 simple_repo = backend_svn['svn-simple-layout']
204 response = self.app.get(
243 response = self.app.get(
205 url(
244 url(
206 controller='files', action='history',
245 controller='files', action='history',
207 repo_name=simple_repo.repo_name,
246 repo_name=simple_repo.repo_name,
208 revision='tip',
247 revision='tip',
209 f_path='trunk/example.py'),
248 f_path='trunk/example.py'),
210 extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
249 extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
211
250
212 expected_data = json.loads(
251 expected_data = json.loads(
213 fixture.load_resource('svn_node_history_branches.json'))
252 fixture.load_resource('svn_node_history_branches.json'))
214 assert expected_data == response.json
253 assert expected_data == response.json
215
254
216 def test_file_annotation_history(self, backend):
255 def test_file_annotation_history(self, backend):
217 response = self.app.get(
256 response = self.app.get(
218 url(
257 url(
219 controller='files', action='history',
258 controller='files', action='history',
220 repo_name=backend.repo_name,
259 repo_name=backend.repo_name,
221 revision='tip',
260 revision='tip',
222 f_path='vcs/nodes.py',
261 f_path='vcs/nodes.py',
223 annotate=True),
262 annotate=True),
224 extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
263 extra_environ={'HTTP_X_PARTIAL_XHR': '1'})
225 assert NODE_HISTORY[backend.alias] == json.loads(response.body)
264 assert NODE_HISTORY[backend.alias] == json.loads(response.body)
226
265
227 def test_file_annotation(self, backend):
266 def test_file_annotation(self, backend):
228 response = self.app.get(url(
267 response = self.app.get(url(
229 controller='files', action='index',
268 controller='files', action='index',
230 repo_name=backend.repo_name, revision='tip', f_path='vcs/nodes.py',
269 repo_name=backend.repo_name, revision='tip', f_path='vcs/nodes.py',
231 annotate=True))
270 annotate=True))
232
271
233 expected_revisions = {
272 expected_revisions = {
234 'hg': 'r356:25213a5fbb04',
273 'hg': 'r356:25213a5fbb04',
235 'git': 'r345:c994f0de03b2',
274 'git': 'r345:c994f0de03b2',
236 'svn': 'r208:209',
275 'svn': 'r208:209',
237 }
276 }
238 response.mustcontain(expected_revisions[backend.alias])
277 response.mustcontain(expected_revisions[backend.alias])
239
278
240 def test_file_authors(self, backend):
279 def test_file_authors(self, backend):
241 response = self.app.get(url(
280 response = self.app.get(url(
242 controller='files', action='authors',
281 controller='files', action='authors',
243 repo_name=backend.repo_name,
282 repo_name=backend.repo_name,
244 revision='tip',
283 revision='tip',
245 f_path='vcs/nodes.py',
284 f_path='vcs/nodes.py',
246 annotate=True))
285 annotate=True))
247
286
248 expected_authors = {
287 expected_authors = {
249 'hg': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
288 'hg': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
250 'git': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
289 'git': ('Marcin Kuzminski', 'Lukasz Balcerzak'),
251 'svn': ('marcin', 'lukasz'),
290 'svn': ('marcin', 'lukasz'),
252 }
291 }
253
292
254 for author in expected_authors[backend.alias]:
293 for author in expected_authors[backend.alias]:
255 response.mustcontain(author)
294 response.mustcontain(author)
256
295
257 def test_tree_search_top_level(self, backend, xhr_header):
296 def test_tree_search_top_level(self, backend, xhr_header):
258 commit = backend.repo.get_commit(commit_idx=173)
297 commit = backend.repo.get_commit(commit_idx=173)
259 response = self.app.get(
298 response = self.app.get(
260 url('files_nodelist_home', repo_name=backend.repo_name,
299 url('files_nodelist_home', repo_name=backend.repo_name,
261 revision=commit.raw_id, f_path='/'),
300 revision=commit.raw_id, f_path='/'),
262 extra_environ=xhr_header)
301 extra_environ=xhr_header)
263 assert 'nodes' in response.json
302 assert 'nodes' in response.json
264 assert {'name': 'docs', 'type': 'dir'} in response.json['nodes']
303 assert {'name': 'docs', 'type': 'dir'} in response.json['nodes']
265
304
266 def test_tree_search_at_path(self, backend, xhr_header):
305 def test_tree_search_at_path(self, backend, xhr_header):
267 commit = backend.repo.get_commit(commit_idx=173)
306 commit = backend.repo.get_commit(commit_idx=173)
268 response = self.app.get(
307 response = self.app.get(
269 url('files_nodelist_home', repo_name=backend.repo_name,
308 url('files_nodelist_home', repo_name=backend.repo_name,
270 revision=commit.raw_id, f_path='/docs'),
309 revision=commit.raw_id, f_path='/docs'),
271 extra_environ=xhr_header)
310 extra_environ=xhr_header)
272 assert 'nodes' in response.json
311 assert 'nodes' in response.json
273 nodes = response.json['nodes']
312 nodes = response.json['nodes']
274 assert {'name': 'docs/api', 'type': 'dir'} in nodes
313 assert {'name': 'docs/api', 'type': 'dir'} in nodes
275 assert {'name': 'docs/index.rst', 'type': 'file'} in nodes
314 assert {'name': 'docs/index.rst', 'type': 'file'} in nodes
276
315
277 def test_tree_search_at_path_missing_xhr(self, backend):
316 def test_tree_search_at_path_missing_xhr(self, backend):
278 self.app.get(
317 self.app.get(
279 url('files_nodelist_home', repo_name=backend.repo_name,
318 url('files_nodelist_home', repo_name=backend.repo_name,
280 revision='tip', f_path=''), status=400)
319 revision='tip', f_path=''), status=400)
281
320
282 def test_tree_view_list(self, backend, xhr_header):
321 def test_tree_view_list(self, backend, xhr_header):
283 commit = backend.repo.get_commit(commit_idx=173)
322 commit = backend.repo.get_commit(commit_idx=173)
284 response = self.app.get(
323 response = self.app.get(
285 url('files_nodelist_home', repo_name=backend.repo_name,
324 url('files_nodelist_home', repo_name=backend.repo_name,
286 f_path='/', revision=commit.raw_id),
325 f_path='/', revision=commit.raw_id),
287 extra_environ=xhr_header,
326 extra_environ=xhr_header,
288 )
327 )
289 response.mustcontain("vcs/web/simplevcs/views/repository.py")
328 response.mustcontain("vcs/web/simplevcs/views/repository.py")
290
329
291 def test_tree_view_list_at_path(self, backend, xhr_header):
330 def test_tree_view_list_at_path(self, backend, xhr_header):
292 commit = backend.repo.get_commit(commit_idx=173)
331 commit = backend.repo.get_commit(commit_idx=173)
293 response = self.app.get(
332 response = self.app.get(
294 url('files_nodelist_home', repo_name=backend.repo_name,
333 url('files_nodelist_home', repo_name=backend.repo_name,
295 f_path='/docs', revision=commit.raw_id),
334 f_path='/docs', revision=commit.raw_id),
296 extra_environ=xhr_header,
335 extra_environ=xhr_header,
297 )
336 )
298 response.mustcontain("docs/index.rst")
337 response.mustcontain("docs/index.rst")
299
338
300 def test_tree_view_list_missing_xhr(self, backend):
339 def test_tree_view_list_missing_xhr(self, backend):
301 self.app.get(
340 self.app.get(
302 url('files_nodelist_home', repo_name=backend.repo_name,
341 url('files_nodelist_home', repo_name=backend.repo_name,
303 f_path='/', revision='tip'), status=400)
342 f_path='/', revision='tip'), status=400)
304
343
305 def test_nodetree_full_success(self, backend, xhr_header):
344 def test_nodetree_full_success(self, backend, xhr_header):
306 commit = backend.repo.get_commit(commit_idx=173)
345 commit = backend.repo.get_commit(commit_idx=173)
307 response = self.app.get(
346 response = self.app.get(
308 url('files_nodetree_full', repo_name=backend.repo_name,
347 url('files_nodetree_full', repo_name=backend.repo_name,
309 f_path='/', commit_id=commit.raw_id),
348 f_path='/', commit_id=commit.raw_id),
310 extra_environ=xhr_header)
349 extra_environ=xhr_header)
311
350
312 assert_response = AssertResponse(response)
351 assert_response = AssertResponse(response)
313
352
314 for attr in ['data-commit-id', 'data-date', 'data-author']:
353 for attr in ['data-commit-id', 'data-date', 'data-author']:
315 elements = assert_response.get_elements('[{}]'.format(attr))
354 elements = assert_response.get_elements('[{}]'.format(attr))
316 assert len(elements) > 1
355 assert len(elements) > 1
317
356
318 for element in elements:
357 for element in elements:
319 assert element.get(attr)
358 assert element.get(attr)
320
359
321 def test_nodetree_full_if_file(self, backend, xhr_header):
360 def test_nodetree_full_if_file(self, backend, xhr_header):
322 commit = backend.repo.get_commit(commit_idx=173)
361 commit = backend.repo.get_commit(commit_idx=173)
323 response = self.app.get(
362 response = self.app.get(
324 url('files_nodetree_full', repo_name=backend.repo_name,
363 url('files_nodetree_full', repo_name=backend.repo_name,
325 f_path='README.rst', commit_id=commit.raw_id),
364 f_path='README.rst', commit_id=commit.raw_id),
326 extra_environ=xhr_header)
365 extra_environ=xhr_header)
327 assert response.body == ''
366 assert response.body == ''
328
367
329 def test_tree_metadata_list_missing_xhr(self, backend):
368 def test_tree_metadata_list_missing_xhr(self, backend):
330 self.app.get(
369 self.app.get(
331 url('files_nodetree_full', repo_name=backend.repo_name,
370 url('files_nodetree_full', repo_name=backend.repo_name,
332 f_path='/', commit_id='tip'), status=400)
371 f_path='/', commit_id='tip'), status=400)
333
372
334 def test_access_empty_repo_redirect_to_summary_with_alert_write_perms(
373 def test_access_empty_repo_redirect_to_summary_with_alert_write_perms(
335 self, app, backend_stub, autologin_regular_user, user_regular,
374 self, app, backend_stub, autologin_regular_user, user_regular,
336 user_util):
375 user_util):
337 repo = backend_stub.create_repo()
376 repo = backend_stub.create_repo()
338 user_util.grant_user_permission_to_repo(
377 user_util.grant_user_permission_to_repo(
339 repo, user_regular, 'repository.write')
378 repo, user_regular, 'repository.write')
340 response = self.app.get(url(
379 response = self.app.get(url(
341 controller='files', action='index',
380 controller='files', action='index',
342 repo_name=repo.repo_name, revision='tip', f_path='/'))
381 repo_name=repo.repo_name, revision='tip', f_path='/'))
343 assert_session_flash(
382 assert_session_flash(
344 response,
383 response,
345 'There are no files yet. <a class="alert-link" '
384 'There are no files yet. <a class="alert-link" '
346 'href="/%s/add/0/#edit">Click here to add a new file.</a>'
385 'href="/%s/add/0/#edit">Click here to add a new file.</a>'
347 % (repo.repo_name))
386 % (repo.repo_name))
348
387
349 def test_access_empty_repo_redirect_to_summary_with_alert_no_write_perms(
388 def test_access_empty_repo_redirect_to_summary_with_alert_no_write_perms(
350 self, backend_stub, user_util):
389 self, backend_stub, user_util):
351 repo = backend_stub.create_repo()
390 repo = backend_stub.create_repo()
352 repo_file_url = url(
391 repo_file_url = url(
353 'files_add_home',
392 'files_add_home',
354 repo_name=repo.repo_name,
393 repo_name=repo.repo_name,
355 revision=0, f_path='', anchor='edit')
394 revision=0, f_path='', anchor='edit')
356 response = self.app.get(url(
395 response = self.app.get(url(
357 controller='files', action='index',
396 controller='files', action='index',
358 repo_name=repo.repo_name, revision='tip', f_path='/'))
397 repo_name=repo.repo_name, revision='tip', f_path='/'))
359 assert_not_in_session_flash(response, repo_file_url)
398 assert_not_in_session_flash(response, repo_file_url)
360
399
361
400
362 # TODO: johbo: Think about a better place for these tests. Either controller
401 # TODO: johbo: Think about a better place for these tests. Either controller
363 # specific unit tests or we move down the whole logic further towards the vcs
402 # specific unit tests or we move down the whole logic further towards the vcs
364 # layer
403 # layer
365 class TestAdjustFilePathForSvn:
404 class TestAdjustFilePathForSvn:
366 """SVN specific adjustments of node history in FileController."""
405 """SVN specific adjustments of node history in FileController."""
367
406
368 def test_returns_path_relative_to_matched_reference(self):
407 def test_returns_path_relative_to_matched_reference(self):
369 repo = self._repo(branches=['trunk'])
408 repo = self._repo(branches=['trunk'])
370 self.assert_file_adjustment('trunk/file', 'file', repo)
409 self.assert_file_adjustment('trunk/file', 'file', repo)
371
410
372 def test_does_not_modify_file_if_no_reference_matches(self):
411 def test_does_not_modify_file_if_no_reference_matches(self):
373 repo = self._repo(branches=['trunk'])
412 repo = self._repo(branches=['trunk'])
374 self.assert_file_adjustment('notes/file', 'notes/file', repo)
413 self.assert_file_adjustment('notes/file', 'notes/file', repo)
375
414
376 def test_does_not_adjust_partial_directory_names(self):
415 def test_does_not_adjust_partial_directory_names(self):
377 repo = self._repo(branches=['trun'])
416 repo = self._repo(branches=['trun'])
378 self.assert_file_adjustment('trunk/file', 'trunk/file', repo)
417 self.assert_file_adjustment('trunk/file', 'trunk/file', repo)
379
418
380 def test_is_robust_to_patterns_which_prefix_other_patterns(self):
419 def test_is_robust_to_patterns_which_prefix_other_patterns(self):
381 repo = self._repo(branches=['trunk', 'trunk/new', 'trunk/old'])
420 repo = self._repo(branches=['trunk', 'trunk/new', 'trunk/old'])
382 self.assert_file_adjustment('trunk/new/file', 'file', repo)
421 self.assert_file_adjustment('trunk/new/file', 'file', repo)
383
422
384 def assert_file_adjustment(self, f_path, expected, repo):
423 def assert_file_adjustment(self, f_path, expected, repo):
385 controller = FilesController()
424 controller = FilesController()
386 result = controller._adjust_file_path_for_svn(f_path, repo)
425 result = controller._adjust_file_path_for_svn(f_path, repo)
387 assert result == expected
426 assert result == expected
388
427
389 def _repo(self, branches=None):
428 def _repo(self, branches=None):
390 repo = mock.Mock()
429 repo = mock.Mock()
391 repo.branches = OrderedDict((name, '0') for name in branches or [])
430 repo.branches = OrderedDict((name, '0') for name in branches or [])
392 repo.tags = {}
431 repo.tags = {}
393 return repo
432 return repo
394
433
395
434
396 @pytest.mark.usefixtures("app")
435 @pytest.mark.usefixtures("app")
397 class TestRepositoryArchival:
436 class TestRepositoryArchival:
398
437
399 def test_archival(self, backend):
438 def test_archival(self, backend):
400 backend.enable_downloads()
439 backend.enable_downloads()
401 commit = backend.repo.get_commit(commit_idx=173)
440 commit = backend.repo.get_commit(commit_idx=173)
402 for archive, info in settings.ARCHIVE_SPECS.items():
441 for archive, info in settings.ARCHIVE_SPECS.items():
403 mime_type, arch_ext = info
442 mime_type, arch_ext = info
404 short = commit.short_id + arch_ext
443 short = commit.short_id + arch_ext
405 fname = commit.raw_id + arch_ext
444 fname = commit.raw_id + arch_ext
406 filename = '%s-%s' % (backend.repo_name, short)
445 filename = '%s-%s' % (backend.repo_name, short)
407 response = self.app.get(url(controller='files',
446 response = self.app.get(url(controller='files',
408 action='archivefile',
447 action='archivefile',
409 repo_name=backend.repo_name,
448 repo_name=backend.repo_name,
410 fname=fname))
449 fname=fname))
411
450
412 assert response.status == '200 OK'
451 assert response.status == '200 OK'
413 headers = {
452 headers = {
414 'Pragma': 'no-cache',
453 'Pragma': 'no-cache',
415 'Cache-Control': 'no-cache',
454 'Cache-Control': 'no-cache',
416 'Content-Disposition': 'attachment; filename=%s' % filename,
455 'Content-Disposition': 'attachment; filename=%s' % filename,
417 'Content-Type': '%s; charset=utf-8' % mime_type,
456 'Content-Type': '%s; charset=utf-8' % mime_type,
418 }
457 }
419 if 'Set-Cookie' in response.response.headers:
458 if 'Set-Cookie' in response.response.headers:
420 del response.response.headers['Set-Cookie']
459 del response.response.headers['Set-Cookie']
421 assert response.response.headers == headers
460 assert response.response.headers == headers
422
461
423 def test_archival_wrong_ext(self, backend):
462 def test_archival_wrong_ext(self, backend):
424 backend.enable_downloads()
463 backend.enable_downloads()
425 commit = backend.repo.get_commit(commit_idx=173)
464 commit = backend.repo.get_commit(commit_idx=173)
426 for arch_ext in ['tar', 'rar', 'x', '..ax', '.zipz']:
465 for arch_ext in ['tar', 'rar', 'x', '..ax', '.zipz']:
427 fname = commit.raw_id + arch_ext
466 fname = commit.raw_id + arch_ext
428
467
429 response = self.app.get(url(controller='files',
468 response = self.app.get(url(controller='files',
430 action='archivefile',
469 action='archivefile',
431 repo_name=backend.repo_name,
470 repo_name=backend.repo_name,
432 fname=fname))
471 fname=fname))
433 response.mustcontain('Unknown archive type')
472 response.mustcontain('Unknown archive type')
434
473
435 def test_archival_wrong_commit_id(self, backend):
474 def test_archival_wrong_commit_id(self, backend):
436 backend.enable_downloads()
475 backend.enable_downloads()
437 for commit_id in ['00x000000', 'tar', 'wrong', '@##$@$42413232',
476 for commit_id in ['00x000000', 'tar', 'wrong', '@##$@$42413232',
438 '232dffcd']:
477 '232dffcd']:
439 fname = '%s.zip' % commit_id
478 fname = '%s.zip' % commit_id
440
479
441 response = self.app.get(url(controller='files',
480 response = self.app.get(url(controller='files',
442 action='archivefile',
481 action='archivefile',
443 repo_name=backend.repo_name,
482 repo_name=backend.repo_name,
444 fname=fname))
483 fname=fname))
445 response.mustcontain('Unknown revision')
484 response.mustcontain('Unknown revision')
446
485
447
486
448 @pytest.mark.usefixtures("app", "autologin_user")
487 @pytest.mark.usefixtures("app", "autologin_user")
449 class TestRawFileHandling:
488 class TestRawFileHandling:
450
489
451 def test_raw_file_ok(self, backend):
490 def test_raw_file_ok(self, backend):
452 commit = backend.repo.get_commit(commit_idx=173)
491 commit = backend.repo.get_commit(commit_idx=173)
453 response = self.app.get(url(controller='files', action='rawfile',
492 response = self.app.get(url(controller='files', action='rawfile',
454 repo_name=backend.repo_name,
493 repo_name=backend.repo_name,
455 revision=commit.raw_id,
494 revision=commit.raw_id,
456 f_path='vcs/nodes.py'))
495 f_path='vcs/nodes.py'))
457
496
458 assert response.content_disposition == "attachment; filename=nodes.py"
497 assert response.content_disposition == "attachment; filename=nodes.py"
459 assert response.content_type == "text/x-python"
498 assert response.content_type == "text/x-python"
460
499
461 def test_raw_file_wrong_cs(self, backend):
500 def test_raw_file_wrong_cs(self, backend):
462 commit_id = u'ERRORce30c96924232dffcd24178a07ffeb5dfc'
501 commit_id = u'ERRORce30c96924232dffcd24178a07ffeb5dfc'
463 f_path = 'vcs/nodes.py'
502 f_path = 'vcs/nodes.py'
464
503
465 response = self.app.get(url(controller='files', action='rawfile',
504 response = self.app.get(url(controller='files', action='rawfile',
466 repo_name=backend.repo_name,
505 repo_name=backend.repo_name,
467 revision=commit_id,
506 revision=commit_id,
468 f_path=f_path), status=404)
507 f_path=f_path), status=404)
469
508
470 msg = """No such commit exists for this repository"""
509 msg = """No such commit exists for this repository"""
471 response.mustcontain(msg)
510 response.mustcontain(msg)
472
511
473 def test_raw_file_wrong_f_path(self, backend):
512 def test_raw_file_wrong_f_path(self, backend):
474 commit = backend.repo.get_commit(commit_idx=173)
513 commit = backend.repo.get_commit(commit_idx=173)
475 f_path = 'vcs/ERRORnodes.py'
514 f_path = 'vcs/ERRORnodes.py'
476 response = self.app.get(url(controller='files', action='rawfile',
515 response = self.app.get(url(controller='files', action='rawfile',
477 repo_name=backend.repo_name,
516 repo_name=backend.repo_name,
478 revision=commit.raw_id,
517 revision=commit.raw_id,
479 f_path=f_path), status=404)
518 f_path=f_path), status=404)
480
519
481 msg = (
520 msg = (
482 "There is no file nor directory at the given path: "
521 "There is no file nor directory at the given path: "
483 "&#39;%s&#39; at commit %s" % (f_path, commit.short_id))
522 "&#39;%s&#39; at commit %s" % (f_path, commit.short_id))
484 response.mustcontain(msg)
523 response.mustcontain(msg)
485
524
486 def test_raw_ok(self, backend):
525 def test_raw_ok(self, backend):
487 commit = backend.repo.get_commit(commit_idx=173)
526 commit = backend.repo.get_commit(commit_idx=173)
488 response = self.app.get(url(controller='files', action='raw',
527 response = self.app.get(url(controller='files', action='raw',
489 repo_name=backend.repo_name,
528 repo_name=backend.repo_name,
490 revision=commit.raw_id,
529 revision=commit.raw_id,
491 f_path='vcs/nodes.py'))
530 f_path='vcs/nodes.py'))
492
531
493 assert response.content_type == "text/plain"
532 assert response.content_type == "text/plain"
494
533
495 def test_raw_wrong_cs(self, backend):
534 def test_raw_wrong_cs(self, backend):
496 commit_id = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc'
535 commit_id = u'ERRORcce30c96924232dffcd24178a07ffeb5dfc'
497 f_path = 'vcs/nodes.py'
536 f_path = 'vcs/nodes.py'
498
537
499 response = self.app.get(url(controller='files', action='raw',
538 response = self.app.get(url(controller='files', action='raw',
500 repo_name=backend.repo_name,
539 repo_name=backend.repo_name,
501 revision=commit_id,
540 revision=commit_id,
502 f_path=f_path), status=404)
541 f_path=f_path), status=404)
503
542
504 msg = """No such commit exists for this repository"""
543 msg = """No such commit exists for this repository"""
505 response.mustcontain(msg)
544 response.mustcontain(msg)
506
545
507 def test_raw_wrong_f_path(self, backend):
546 def test_raw_wrong_f_path(self, backend):
508 commit = backend.repo.get_commit(commit_idx=173)
547 commit = backend.repo.get_commit(commit_idx=173)
509 f_path = 'vcs/ERRORnodes.py'
548 f_path = 'vcs/ERRORnodes.py'
510 response = self.app.get(url(controller='files', action='raw',
549 response = self.app.get(url(controller='files', action='raw',
511 repo_name=backend.repo_name,
550 repo_name=backend.repo_name,
512 revision=commit.raw_id,
551 revision=commit.raw_id,
513 f_path=f_path), status=404)
552 f_path=f_path), status=404)
514 msg = (
553 msg = (
515 "There is no file nor directory at the given path: "
554 "There is no file nor directory at the given path: "
516 "&#39;%s&#39; at commit %s" % (f_path, commit.short_id))
555 "&#39;%s&#39; at commit %s" % (f_path, commit.short_id))
517 response.mustcontain(msg)
556 response.mustcontain(msg)
518
557
519 def test_raw_svg_should_not_be_rendered(self, backend):
558 def test_raw_svg_should_not_be_rendered(self, backend):
520 backend.create_repo()
559 backend.create_repo()
521 backend.ensure_file("xss.svg")
560 backend.ensure_file("xss.svg")
522 response = self.app.get(url(controller='files', action='raw',
561 response = self.app.get(url(controller='files', action='raw',
523 repo_name=backend.repo_name,
562 repo_name=backend.repo_name,
524 revision='tip',
563 revision='tip',
525 f_path='xss.svg'))
564 f_path='xss.svg'))
526
565
527 # If the content type is image/svg+xml then it allows to render HTML
566 # If the content type is image/svg+xml then it allows to render HTML
528 # and malicious SVG.
567 # and malicious SVG.
529 assert response.content_type == "text/plain"
568 assert response.content_type == "text/plain"
530
569
531
570
532 @pytest.mark.usefixtures("app")
571 @pytest.mark.usefixtures("app")
533 class TestFilesDiff:
572 class TestFilesDiff:
534
573
535 @pytest.mark.parametrize("diff", ['diff', 'download', 'raw'])
574 @pytest.mark.parametrize("diff", ['diff', 'download', 'raw'])
536 def test_file_full_diff(self, backend, diff):
575 def test_file_full_diff(self, backend, diff):
537 commit1 = backend.repo.get_commit(commit_idx=-1)
576 commit1 = backend.repo.get_commit(commit_idx=-1)
538 commit2 = backend.repo.get_commit(commit_idx=-2)
577 commit2 = backend.repo.get_commit(commit_idx=-2)
539 response = self.app.get(
578 response = self.app.get(
540 url(
579 url(
541 controller='files',
580 controller='files',
542 action='diff',
581 action='diff',
543 repo_name=backend.repo_name,
582 repo_name=backend.repo_name,
544 f_path='README'),
583 f_path='README'),
545 params={
584 params={
546 'diff1': commit1.raw_id,
585 'diff1': commit1.raw_id,
547 'diff2': commit2.raw_id,
586 'diff2': commit2.raw_id,
548 'fulldiff': '1',
587 'fulldiff': '1',
549 'diff': diff,
588 'diff': diff,
550 })
589 })
551 response.mustcontain('README.rst')
590 response.mustcontain('README.rst')
552 response.mustcontain('No newline at end of file')
591 response.mustcontain('No newline at end of file')
553
592
554 def test_file_binary_diff(self, backend):
593 def test_file_binary_diff(self, backend):
555 commits = [
594 commits = [
556 {'message': 'First commit'},
595 {'message': 'First commit'},
557 {'message': 'Commit with binary',
596 {'message': 'Commit with binary',
558 'added': [nodes.FileNode('file.bin', content='\0BINARY\0')]},
597 'added': [nodes.FileNode('file.bin', content='\0BINARY\0')]},
559 ]
598 ]
560 repo = backend.create_repo(commits=commits)
599 repo = backend.create_repo(commits=commits)
561
600
562 response = self.app.get(
601 response = self.app.get(
563 url(
602 url(
564 controller='files',
603 controller='files',
565 action='diff',
604 action='diff',
566 repo_name=backend.repo_name,
605 repo_name=backend.repo_name,
567 f_path='file.bin'),
606 f_path='file.bin'),
568 params={
607 params={
569 'diff1': repo.get_commit(commit_idx=0).raw_id,
608 'diff1': repo.get_commit(commit_idx=0).raw_id,
570 'diff2': repo.get_commit(commit_idx=1).raw_id,
609 'diff2': repo.get_commit(commit_idx=1).raw_id,
571 'fulldiff': '1',
610 'fulldiff': '1',
572 'diff': 'diff',
611 'diff': 'diff',
573 })
612 })
574 response.mustcontain('Cannot diff binary files')
613 response.mustcontain('Cannot diff binary files')
575
614
576 def test_diff_2way(self, backend):
615 def test_diff_2way(self, backend):
577 commit1 = backend.repo.get_commit(commit_idx=-1)
616 commit1 = backend.repo.get_commit(commit_idx=-1)
578 commit2 = backend.repo.get_commit(commit_idx=-2)
617 commit2 = backend.repo.get_commit(commit_idx=-2)
579 response = self.app.get(
618 response = self.app.get(
580 url(
619 url(
581 controller='files',
620 controller='files',
582 action='diff_2way',
621 action='diff_2way',
583 repo_name=backend.repo_name,
622 repo_name=backend.repo_name,
584 f_path='README'),
623 f_path='README'),
585 params={
624 params={
586 'diff1': commit1.raw_id,
625 'diff1': commit1.raw_id,
587 'diff2': commit2.raw_id,
626 'diff2': commit2.raw_id,
588 })
627 })
589
628
590 # Expecting links to both variants of the file. Links are used
629 # Expecting links to both variants of the file. Links are used
591 # to load the content dynamically.
630 # to load the content dynamically.
592 response.mustcontain('/%s/README' % commit1.raw_id)
631 response.mustcontain('/%s/README' % commit1.raw_id)
593 response.mustcontain('/%s/README' % commit2.raw_id)
632 response.mustcontain('/%s/README' % commit2.raw_id)
594
633
595 def test_requires_one_commit_id(self, backend, autologin_user):
634 def test_requires_one_commit_id(self, backend, autologin_user):
596 response = self.app.get(
635 response = self.app.get(
597 url(
636 url(
598 controller='files',
637 controller='files',
599 action='diff',
638 action='diff',
600 repo_name=backend.repo_name,
639 repo_name=backend.repo_name,
601 f_path='README.rst'),
640 f_path='README.rst'),
602 status=400)
641 status=400)
603 response.mustcontain(
642 response.mustcontain(
604 'Need query parameter', 'diff1', 'diff2', 'to generate a diff.')
643 'Need query parameter', 'diff1', 'diff2', 'to generate a diff.')
605
644
606 def test_returns_not_found_if_file_does_not_exist(self, vcsbackend):
645 def test_returns_not_found_if_file_does_not_exist(self, vcsbackend):
607 repo = vcsbackend.repo
646 repo = vcsbackend.repo
608 self.app.get(
647 self.app.get(
609 url(
648 url(
610 controller='files',
649 controller='files',
611 action='diff',
650 action='diff',
612 repo_name=repo.name,
651 repo_name=repo.name,
613 f_path='does-not-exist-in-any-commit',
652 f_path='does-not-exist-in-any-commit',
614 diff1=repo[0].raw_id,
653 diff1=repo[0].raw_id,
615 diff2=repo[1].raw_id),
654 diff2=repo[1].raw_id),
616 status=404)
655 status=404)
617
656
618 def test_returns_redirect_if_file_not_changed(self, backend):
657 def test_returns_redirect_if_file_not_changed(self, backend):
619 commit = backend.repo.get_commit(commit_idx=-1)
658 commit = backend.repo.get_commit(commit_idx=-1)
620 f_path= 'README'
659 f_path= 'README'
621 response = self.app.get(
660 response = self.app.get(
622 url(
661 url(
623 controller='files',
662 controller='files',
624 action='diff_2way',
663 action='diff_2way',
625 repo_name=backend.repo_name,
664 repo_name=backend.repo_name,
626 f_path=f_path,
665 f_path=f_path,
627 diff1=commit.raw_id,
666 diff1=commit.raw_id,
628 diff2=commit.raw_id,
667 diff2=commit.raw_id,
629 ),
668 ),
630 status=302
669 status=302
631 )
670 )
632 assert response.headers['Location'].endswith(f_path)
671 assert response.headers['Location'].endswith(f_path)
633 redirected = response.follow()
672 redirected = response.follow()
634 redirected.mustcontain('has not changed between')
673 redirected.mustcontain('has not changed between')
635
674
636 def test_supports_diff_to_different_path_svn(self, backend_svn):
675 def test_supports_diff_to_different_path_svn(self, backend_svn):
637 repo = backend_svn['svn-simple-layout'].scm_instance()
676 repo = backend_svn['svn-simple-layout'].scm_instance()
638 commit_id = repo[-1].raw_id
677 commit_id = repo[-1].raw_id
639 response = self.app.get(
678 response = self.app.get(
640 url(
679 url(
641 controller='files',
680 controller='files',
642 action='diff',
681 action='diff',
643 repo_name=repo.name,
682 repo_name=repo.name,
644 f_path='trunk/example.py',
683 f_path='trunk/example.py',
645 diff1='tags/v0.2/example.py@' + commit_id,
684 diff1='tags/v0.2/example.py@' + commit_id,
646 diff2=commit_id),
685 diff2=commit_id),
647 status=200)
686 status=200)
648 response.mustcontain(
687 response.mustcontain(
649 "Will print out a useful message on invocation.")
688 "Will print out a useful message on invocation.")
650
689
651 # Note: Expecting that we indicate the user what's being compared
690 # Note: Expecting that we indicate the user what's being compared
652 response.mustcontain("trunk/example.py")
691 response.mustcontain("trunk/example.py")
653 response.mustcontain("tags/v0.2/example.py")
692 response.mustcontain("tags/v0.2/example.py")
654
693
655 def test_show_rev_redirects_to_svn_path(self, backend_svn):
694 def test_show_rev_redirects_to_svn_path(self, backend_svn):
656 repo = backend_svn['svn-simple-layout'].scm_instance()
695 repo = backend_svn['svn-simple-layout'].scm_instance()
657 commit_id = repo[-1].raw_id
696 commit_id = repo[-1].raw_id
658 response = self.app.get(
697 response = self.app.get(
659 url(
698 url(
660 controller='files',
699 controller='files',
661 action='diff',
700 action='diff',
662 repo_name=repo.name,
701 repo_name=repo.name,
663 f_path='trunk/example.py',
702 f_path='trunk/example.py',
664 diff1='branches/argparse/example.py@' + commit_id,
703 diff1='branches/argparse/example.py@' + commit_id,
665 diff2=commit_id),
704 diff2=commit_id),
666 params={'show_rev': 'Show at Revision'},
705 params={'show_rev': 'Show at Revision'},
667 status=302)
706 status=302)
668 assert response.headers['Location'].endswith(
707 assert response.headers['Location'].endswith(
669 'svn-svn-simple-layout/files/26/branches/argparse/example.py')
708 'svn-svn-simple-layout/files/26/branches/argparse/example.py')
670
709
671 def test_show_rev_and_annotate_redirects_to_svn_path(self, backend_svn):
710 def test_show_rev_and_annotate_redirects_to_svn_path(self, backend_svn):
672 repo = backend_svn['svn-simple-layout'].scm_instance()
711 repo = backend_svn['svn-simple-layout'].scm_instance()
673 commit_id = repo[-1].raw_id
712 commit_id = repo[-1].raw_id
674 response = self.app.get(
713 response = self.app.get(
675 url(
714 url(
676 controller='files',
715 controller='files',
677 action='diff',
716 action='diff',
678 repo_name=repo.name,
717 repo_name=repo.name,
679 f_path='trunk/example.py',
718 f_path='trunk/example.py',
680 diff1='branches/argparse/example.py@' + commit_id,
719 diff1='branches/argparse/example.py@' + commit_id,
681 diff2=commit_id),
720 diff2=commit_id),
682 params={
721 params={
683 'show_rev': 'Show at Revision',
722 'show_rev': 'Show at Revision',
684 'annotate': 'true',
723 'annotate': 'true',
685 },
724 },
686 status=302)
725 status=302)
687 assert response.headers['Location'].endswith(
726 assert response.headers['Location'].endswith(
688 'svn-svn-simple-layout/annotate/26/branches/argparse/example.py')
727 'svn-svn-simple-layout/annotate/26/branches/argparse/example.py')
689
728
690
729
691 @pytest.mark.usefixtures("app", "autologin_user")
730 @pytest.mark.usefixtures("app", "autologin_user")
692 class TestChangingFiles:
731 class TestChangingFiles:
693
732
694 def test_add_file_view(self, backend):
733 def test_add_file_view(self, backend):
695 self.app.get(url(
734 self.app.get(url(
696 'files_add_home',
735 'files_add_home',
697 repo_name=backend.repo_name,
736 repo_name=backend.repo_name,
698 revision='tip', f_path='/'))
737 revision='tip', f_path='/'))
699
738
700 @pytest.mark.xfail_backends("svn", reason="Depends on online editing")
739 @pytest.mark.xfail_backends("svn", reason="Depends on online editing")
701 def test_add_file_into_repo_missing_content(self, backend, csrf_token):
740 def test_add_file_into_repo_missing_content(self, backend, csrf_token):
702 repo = backend.create_repo()
741 repo = backend.create_repo()
703 filename = 'init.py'
742 filename = 'init.py'
704 response = self.app.post(
743 response = self.app.post(
705 url(
744 url(
706 'files_add',
745 'files_add',
707 repo_name=repo.repo_name,
746 repo_name=repo.repo_name,
708 revision='tip', f_path='/'),
747 revision='tip', f_path='/'),
709 params={
748 params={
710 'content': "",
749 'content': "",
711 'filename': filename,
750 'filename': filename,
712 'location': "",
751 'location': "",
713 'csrf_token': csrf_token,
752 'csrf_token': csrf_token,
714 },
753 },
715 status=302)
754 status=302)
716 assert_session_flash(
755 assert_session_flash(
717 response, 'Successfully committed to %s'
756 response, 'Successfully committed to %s'
718 % os.path.join(filename))
757 % os.path.join(filename))
719
758
720 def test_add_file_into_repo_missing_filename(self, backend, csrf_token):
759 def test_add_file_into_repo_missing_filename(self, backend, csrf_token):
721 response = self.app.post(
760 response = self.app.post(
722 url(
761 url(
723 'files_add',
762 'files_add',
724 repo_name=backend.repo_name,
763 repo_name=backend.repo_name,
725 revision='tip', f_path='/'),
764 revision='tip', f_path='/'),
726 params={
765 params={
727 'content': "foo",
766 'content': "foo",
728 'csrf_token': csrf_token,
767 'csrf_token': csrf_token,
729 },
768 },
730 status=302)
769 status=302)
731
770
732 assert_session_flash(response, 'No filename')
771 assert_session_flash(response, 'No filename')
733
772
734 def test_add_file_into_repo_errors_and_no_commits(
773 def test_add_file_into_repo_errors_and_no_commits(
735 self, backend, csrf_token):
774 self, backend, csrf_token):
736 repo = backend.create_repo()
775 repo = backend.create_repo()
737 # Create a file with no filename, it will display an error but
776 # Create a file with no filename, it will display an error but
738 # the repo has no commits yet
777 # the repo has no commits yet
739 response = self.app.post(
778 response = self.app.post(
740 url(
779 url(
741 'files_add',
780 'files_add',
742 repo_name=repo.repo_name,
781 repo_name=repo.repo_name,
743 revision='tip', f_path='/'),
782 revision='tip', f_path='/'),
744 params={
783 params={
745 'content': "foo",
784 'content': "foo",
746 'csrf_token': csrf_token,
785 'csrf_token': csrf_token,
747 },
786 },
748 status=302)
787 status=302)
749
788
750 assert_session_flash(response, 'No filename')
789 assert_session_flash(response, 'No filename')
751
790
752 # Not allowed, redirect to the summary
791 # Not allowed, redirect to the summary
753 redirected = response.follow()
792 redirected = response.follow()
754 summary_url = url('summary_home', repo_name=repo.repo_name)
793 summary_url = url('summary_home', repo_name=repo.repo_name)
755
794
756 # As there are no commits, displays the summary page with the error of
795 # As there are no commits, displays the summary page with the error of
757 # creating a file with no filename
796 # creating a file with no filename
758 assert redirected.req.path == summary_url
797 assert redirected.req.path == summary_url
759
798
760 @pytest.mark.parametrize("location, filename", [
799 @pytest.mark.parametrize("location, filename", [
761 ('/abs', 'foo'),
800 ('/abs', 'foo'),
762 ('../rel', 'foo'),
801 ('../rel', 'foo'),
763 ('file/../foo', 'foo'),
802 ('file/../foo', 'foo'),
764 ])
803 ])
765 def test_add_file_into_repo_bad_filenames(
804 def test_add_file_into_repo_bad_filenames(
766 self, location, filename, backend, csrf_token):
805 self, location, filename, backend, csrf_token):
767 response = self.app.post(
806 response = self.app.post(
768 url(
807 url(
769 'files_add',
808 'files_add',
770 repo_name=backend.repo_name,
809 repo_name=backend.repo_name,
771 revision='tip', f_path='/'),
810 revision='tip', f_path='/'),
772 params={
811 params={
773 'content': "foo",
812 'content': "foo",
774 'filename': filename,
813 'filename': filename,
775 'location': location,
814 'location': location,
776 'csrf_token': csrf_token,
815 'csrf_token': csrf_token,
777 },
816 },
778 status=302)
817 status=302)
779
818
780 assert_session_flash(
819 assert_session_flash(
781 response,
820 response,
782 'The location specified must be a relative path and must not '
821 'The location specified must be a relative path and must not '
783 'contain .. in the path')
822 'contain .. in the path')
784
823
785 @pytest.mark.parametrize("cnt, location, filename", [
824 @pytest.mark.parametrize("cnt, location, filename", [
786 (1, '', 'foo.txt'),
825 (1, '', 'foo.txt'),
787 (2, 'dir', 'foo.rst'),
826 (2, 'dir', 'foo.rst'),
788 (3, 'rel/dir', 'foo.bar'),
827 (3, 'rel/dir', 'foo.bar'),
789 ])
828 ])
790 def test_add_file_into_repo(self, cnt, location, filename, backend,
829 def test_add_file_into_repo(self, cnt, location, filename, backend,
791 csrf_token):
830 csrf_token):
792 repo = backend.create_repo()
831 repo = backend.create_repo()
793 response = self.app.post(
832 response = self.app.post(
794 url(
833 url(
795 'files_add',
834 'files_add',
796 repo_name=repo.repo_name,
835 repo_name=repo.repo_name,
797 revision='tip', f_path='/'),
836 revision='tip', f_path='/'),
798 params={
837 params={
799 'content': "foo",
838 'content': "foo",
800 'filename': filename,
839 'filename': filename,
801 'location': location,
840 'location': location,
802 'csrf_token': csrf_token,
841 'csrf_token': csrf_token,
803 },
842 },
804 status=302)
843 status=302)
805 assert_session_flash(
844 assert_session_flash(
806 response, 'Successfully committed to %s'
845 response, 'Successfully committed to %s'
807 % os.path.join(location, filename))
846 % os.path.join(location, filename))
808
847
809 def test_edit_file_view(self, backend):
848 def test_edit_file_view(self, backend):
810 response = self.app.get(
849 response = self.app.get(
811 url(
850 url(
812 'files_edit_home',
851 'files_edit_home',
813 repo_name=backend.repo_name,
852 repo_name=backend.repo_name,
814 revision=backend.default_head_id,
853 revision=backend.default_head_id,
815 f_path='vcs/nodes.py'),
854 f_path='vcs/nodes.py'),
816 status=200)
855 status=200)
817 response.mustcontain("Module holding everything related to vcs nodes.")
856 response.mustcontain("Module holding everything related to vcs nodes.")
818
857
819 def test_edit_file_view_not_on_branch(self, backend):
858 def test_edit_file_view_not_on_branch(self, backend):
820 repo = backend.create_repo()
859 repo = backend.create_repo()
821 backend.ensure_file("vcs/nodes.py")
860 backend.ensure_file("vcs/nodes.py")
822
861
823 response = self.app.get(
862 response = self.app.get(
824 url(
863 url(
825 'files_edit_home',
864 'files_edit_home',
826 repo_name=repo.repo_name,
865 repo_name=repo.repo_name,
827 revision='tip', f_path='vcs/nodes.py'),
866 revision='tip', f_path='vcs/nodes.py'),
828 status=302)
867 status=302)
829 assert_session_flash(
868 assert_session_flash(
830 response,
869 response,
831 'You can only edit files with revision being a valid branch')
870 'You can only edit files with revision being a valid branch')
832
871
833 def test_edit_file_view_commit_changes(self, backend, csrf_token):
872 def test_edit_file_view_commit_changes(self, backend, csrf_token):
834 repo = backend.create_repo()
873 repo = backend.create_repo()
835 backend.ensure_file("vcs/nodes.py", content="print 'hello'")
874 backend.ensure_file("vcs/nodes.py", content="print 'hello'")
836
875
837 response = self.app.post(
876 response = self.app.post(
838 url(
877 url(
839 'files_edit',
878 'files_edit',
840 repo_name=repo.repo_name,
879 repo_name=repo.repo_name,
841 revision=backend.default_head_id,
880 revision=backend.default_head_id,
842 f_path='vcs/nodes.py'),
881 f_path='vcs/nodes.py'),
843 params={
882 params={
844 'content': "print 'hello world'",
883 'content': "print 'hello world'",
845 'message': 'I committed',
884 'message': 'I committed',
846 'filename': "vcs/nodes.py",
885 'filename': "vcs/nodes.py",
847 'csrf_token': csrf_token,
886 'csrf_token': csrf_token,
848 },
887 },
849 status=302)
888 status=302)
850 assert_session_flash(
889 assert_session_flash(
851 response, 'Successfully committed to vcs/nodes.py')
890 response, 'Successfully committed to vcs/nodes.py')
852 tip = repo.get_commit(commit_idx=-1)
891 tip = repo.get_commit(commit_idx=-1)
853 assert tip.message == 'I committed'
892 assert tip.message == 'I committed'
854
893
855 def test_edit_file_view_commit_changes_default_message(self, backend,
894 def test_edit_file_view_commit_changes_default_message(self, backend,
856 csrf_token):
895 csrf_token):
857 repo = backend.create_repo()
896 repo = backend.create_repo()
858 backend.ensure_file("vcs/nodes.py", content="print 'hello'")
897 backend.ensure_file("vcs/nodes.py", content="print 'hello'")
859
898
860 commit_id = (
899 commit_id = (
861 backend.default_branch_name or
900 backend.default_branch_name or
862 backend.repo.scm_instance().commit_ids[-1])
901 backend.repo.scm_instance().commit_ids[-1])
863
902
864 response = self.app.post(
903 response = self.app.post(
865 url(
904 url(
866 'files_edit',
905 'files_edit',
867 repo_name=repo.repo_name,
906 repo_name=repo.repo_name,
868 revision=commit_id,
907 revision=commit_id,
869 f_path='vcs/nodes.py'),
908 f_path='vcs/nodes.py'),
870 params={
909 params={
871 'content': "print 'hello world'",
910 'content': "print 'hello world'",
872 'message': '',
911 'message': '',
873 'filename': "vcs/nodes.py",
912 'filename': "vcs/nodes.py",
874 'csrf_token': csrf_token,
913 'csrf_token': csrf_token,
875 },
914 },
876 status=302)
915 status=302)
877 assert_session_flash(
916 assert_session_flash(
878 response, 'Successfully committed to vcs/nodes.py')
917 response, 'Successfully committed to vcs/nodes.py')
879 tip = repo.get_commit(commit_idx=-1)
918 tip = repo.get_commit(commit_idx=-1)
880 assert tip.message == 'Edited file vcs/nodes.py via RhodeCode Enterprise'
919 assert tip.message == 'Edited file vcs/nodes.py via RhodeCode Enterprise'
881
920
882 def test_delete_file_view(self, backend):
921 def test_delete_file_view(self, backend):
883 self.app.get(url(
922 self.app.get(url(
884 'files_delete_home',
923 'files_delete_home',
885 repo_name=backend.repo_name,
924 repo_name=backend.repo_name,
886 revision='tip', f_path='vcs/nodes.py'))
925 revision='tip', f_path='vcs/nodes.py'))
887
926
888 def test_delete_file_view_not_on_branch(self, backend):
927 def test_delete_file_view_not_on_branch(self, backend):
889 repo = backend.create_repo()
928 repo = backend.create_repo()
890 backend.ensure_file('vcs/nodes.py')
929 backend.ensure_file('vcs/nodes.py')
891
930
892 response = self.app.get(
931 response = self.app.get(
893 url(
932 url(
894 'files_delete_home',
933 'files_delete_home',
895 repo_name=repo.repo_name,
934 repo_name=repo.repo_name,
896 revision='tip', f_path='vcs/nodes.py'),
935 revision='tip', f_path='vcs/nodes.py'),
897 status=302)
936 status=302)
898 assert_session_flash(
937 assert_session_flash(
899 response,
938 response,
900 'You can only delete files with revision being a valid branch')
939 'You can only delete files with revision being a valid branch')
901
940
902 def test_delete_file_view_commit_changes(self, backend, csrf_token):
941 def test_delete_file_view_commit_changes(self, backend, csrf_token):
903 repo = backend.create_repo()
942 repo = backend.create_repo()
904 backend.ensure_file("vcs/nodes.py")
943 backend.ensure_file("vcs/nodes.py")
905
944
906 response = self.app.post(
945 response = self.app.post(
907 url(
946 url(
908 'files_delete_home',
947 'files_delete_home',
909 repo_name=repo.repo_name,
948 repo_name=repo.repo_name,
910 revision=backend.default_head_id,
949 revision=backend.default_head_id,
911 f_path='vcs/nodes.py'),
950 f_path='vcs/nodes.py'),
912 params={
951 params={
913 'message': 'i commited',
952 'message': 'i commited',
914 'csrf_token': csrf_token,
953 'csrf_token': csrf_token,
915 },
954 },
916 status=302)
955 status=302)
917 assert_session_flash(
956 assert_session_flash(
918 response, 'Successfully deleted file vcs/nodes.py')
957 response, 'Successfully deleted file vcs/nodes.py')
919
958
920
959
921 def assert_files_in_response(response, files, params):
960 def assert_files_in_response(response, files, params):
922 template = (
961 template = (
923 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"')
962 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"')
924 _assert_items_in_response(response, files, template, params)
963 _assert_items_in_response(response, files, template, params)
925
964
926
965
927 def assert_dirs_in_response(response, dirs, params):
966 def assert_dirs_in_response(response, dirs, params):
928 template = (
967 template = (
929 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"')
968 'href="/%(repo_name)s/files/%(commit_id)s/%(name)s"')
930 _assert_items_in_response(response, dirs, template, params)
969 _assert_items_in_response(response, dirs, template, params)
931
970
932
971
933 def _assert_items_in_response(response, items, template, params):
972 def _assert_items_in_response(response, items, template, params):
934 for item in items:
973 for item in items:
935 item_params = {'name': item}
974 item_params = {'name': item}
936 item_params.update(params)
975 item_params.update(params)
937 response.mustcontain(template % item_params)
976 response.mustcontain(template % item_params)
938
977
939
978
940 def assert_timeago_in_response(response, items, params):
979 def assert_timeago_in_response(response, items, params):
941 for item in items:
980 for item in items:
942 response.mustcontain(h.age_component(params['date']))
981 response.mustcontain(h.age_component(params['date']))
982
983
984
985 @pytest.mark.usefixtures("autologin_user", "app")
986 class TestSideBySideDiff:
987
988 def test_diff2way(self, app, backend, backend_stub):
989 f_path = 'content'
990 commit1_content = 'content-25d7e49c18b159446c'
991 commit2_content = 'content-603d6c72c46d953420'
992 repo = backend.create_repo()
993
994 commit1 = _commit_change(
995 repo.repo_name, filename=f_path, content=commit1_content,
996 message='A', vcs_type=backend.alias, parent=None, newfile=True)
997
998 commit2 = _commit_change(
999 repo.repo_name, filename=f_path, content=commit2_content,
1000 message='B, child of A', vcs_type=backend.alias, parent=commit1)
1001
1002 response = self.app.get(url(
1003 controller='files', action='diff_2way',
1004 repo_name=repo.repo_name,
1005 diff1=commit1.raw_id,
1006 diff2=commit2.raw_id,
1007 f_path=f_path))
1008
1009 assert_response = AssertResponse(response)
1010 response.mustcontain(
1011 ('Side-by-side Diff r0:%s ... r1:%s') % ( commit1.short_id, commit2.short_id ))
1012 response.mustcontain('id="compare"')
1013 response.mustcontain((
1014 "var orig1_url = '/%s/raw/%s/%s';\n"
1015 "var orig2_url = '/%s/raw/%s/%s';") %
1016 ( repo.repo_name, commit1.raw_id, f_path,
1017 repo.repo_name, commit2.raw_id, f_path))
1018
1019
1020 def test_diff2way_with_empty_file(self, app, backend, backend_stub):
1021 commits = [
1022 {'message': 'First commit'},
1023 {'message': 'Commit with binary',
1024 'added': [nodes.FileNode('file.empty', content='')]},
1025 ]
1026 f_path='file.empty'
1027 repo = backend.create_repo(commits=commits)
1028 commit_id1 = repo.get_commit(commit_idx=0).raw_id
1029 commit_id2 = repo.get_commit(commit_idx=1).raw_id
1030
1031 response = self.app.get(url(
1032 controller='files', action='diff_2way',
1033 repo_name=repo.repo_name,
1034 diff1=commit_id1,
1035 diff2=commit_id2,
1036 f_path=f_path))
1037
1038 assert_response = AssertResponse(response)
1039 if backend.alias == 'svn':
1040 assert_session_flash( response,
1041 ('%(file_path)s has not changed') % { 'file_path': 'file.empty' })
1042 else:
1043 response.mustcontain(
1044 ('Side-by-side Diff r0:%s ... r1:%s') % ( repo.get_commit(commit_idx=0).short_id, repo.get_commit(commit_idx=1).short_id ))
1045 response.mustcontain('id="compare"')
1046 response.mustcontain((
1047 "var orig1_url = '/%s/raw/%s/%s';\n"
1048 "var orig2_url = '/%s/raw/%s/%s';") %
1049 ( repo.repo_name, commit_id1, f_path,
1050 repo.repo_name, commit_id2, f_path))
1051
1052
1053 def test_empty_diff_2way_redirect_to_summary_with_alert(self, app, backend):
1054 commit_id_range = {
1055 'hg': (
1056 '25d7e49c18b159446cadfa506a5cf8ad1cb04067',
1057 '603d6c72c46d953420c89d36372f08d9f305f5dd'),
1058 'git': (
1059 '6fc9270775aaf5544c1deb014f4ddd60c952fcbb',
1060 '03fa803d7e9fb14daa9a3089e0d1494eda75d986'),
1061 'svn': (
1062 '335',
1063 '337'),
1064 }
1065 f_path = 'setup.py'
1066
1067 commit_ids = commit_id_range[backend.alias]
1068
1069 response = self.app.get(url(
1070 controller='files', action='diff_2way',
1071 repo_name=backend.repo_name,
1072 diff2=commit_ids[0],
1073 diff1=commit_ids[1],
1074 f_path=f_path))
1075
1076 assert_response = AssertResponse(response)
1077 assert_session_flash( response,
1078 ('%(file_path)s has not changed') % { 'file_path': f_path })
General Comments 0
You need to be logged in to leave comments. Login now