##// END OF EJS Templates
tests: fixed stream tests
marcink -
r3535:c4153b0f default
parent child Browse files
Show More
@@ -1,483 +1,483 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Test suite for making push/pull operations, on specially modified INI files
23 23
24 24 .. important::
25 25
26 26 You must have git >= 1.8.5 for tests to work fine. With 68b939b git started
27 27 to redirect things to stderr instead of stdout.
28 28 """
29 29
30 30
31 31 import time
32 32
33 33 import pytest
34 34
35 35 from rhodecode.lib import rc_cache
36 36 from rhodecode.model.auth_token import AuthTokenModel
37 37 from rhodecode.model.db import Repository, UserIpMap, CacheKey
38 38 from rhodecode.model.meta import Session
39 39 from rhodecode.model.repo import RepoModel
40 40 from rhodecode.model.user import UserModel
41 41 from rhodecode.tests import (GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN)
42 42
43 43 from rhodecode.tests.vcs_operations import (
44 44 Command, _check_proper_clone, _check_proper_git_push,
45 45 _add_files_and_push, HG_REPO_WITH_GROUP, GIT_REPO_WITH_GROUP)
46 46
47 47
48 48 @pytest.mark.usefixtures("disable_locking", "disable_anonymous_user")
49 49 class TestVCSOperations(object):
50 50
51 51 def test_clone_hg_repo_by_admin(self, rc_web_server, tmpdir):
52 52 clone_url = rc_web_server.repo_clone_url(HG_REPO)
53 53 stdout, stderr = Command('/tmp').execute(
54 54 'hg clone', clone_url, tmpdir.strpath)
55 55 _check_proper_clone(stdout, stderr, 'hg')
56 56
57 57 def test_clone_hg_repo_by_admin_pull_protocol(self, rc_web_server, tmpdir):
58 58 clone_url = rc_web_server.repo_clone_url(HG_REPO)
59 59 stdout, stderr = Command('/tmp').execute(
60 60 'hg clone --pull', clone_url, tmpdir.strpath)
61 61 _check_proper_clone(stdout, stderr, 'hg')
62 62
63 63 def test_clone_hg_repo_by_admin_pull_stream_protocol(self, rc_web_server, tmpdir):
64 64 clone_url = rc_web_server.repo_clone_url(HG_REPO)
65 65 stdout, stderr = Command('/tmp').execute(
66 66 'hg clone --pull --stream', clone_url, tmpdir.strpath)
67 assert '225 files to transfer, 1.04 MB of data' in stdout
68 assert 'transferred 1.04 MB' in stdout
67 assert 'files to transfer,' in stdout
68 assert 'transferred 1.' in stdout
69 69 assert '114 files updated,' in stdout
70 70
71 71 def test_clone_git_repo_by_admin(self, rc_web_server, tmpdir):
72 72 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
73 73 cmd = Command('/tmp')
74 74 stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath)
75 75 _check_proper_clone(stdout, stderr, 'git')
76 76 cmd.assert_returncode_success()
77 77
78 78 def test_clone_git_repo_by_admin_with_git_suffix(self, rc_web_server, tmpdir):
79 79 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
80 80 cmd = Command('/tmp')
81 81 stdout, stderr = cmd.execute('git clone', clone_url+".git", tmpdir.strpath)
82 82 _check_proper_clone(stdout, stderr, 'git')
83 83 cmd.assert_returncode_success()
84 84
85 85 def test_clone_hg_repo_by_id_by_admin(self, rc_web_server, tmpdir):
86 86 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
87 87 clone_url = rc_web_server.repo_clone_url('_%s' % repo_id)
88 88 stdout, stderr = Command('/tmp').execute(
89 89 'hg clone', clone_url, tmpdir.strpath)
90 90 _check_proper_clone(stdout, stderr, 'hg')
91 91
92 92 def test_clone_git_repo_by_id_by_admin(self, rc_web_server, tmpdir):
93 93 repo_id = Repository.get_by_repo_name(GIT_REPO).repo_id
94 94 clone_url = rc_web_server.repo_clone_url('_%s' % repo_id)
95 95 cmd = Command('/tmp')
96 96 stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath)
97 97 _check_proper_clone(stdout, stderr, 'git')
98 98 cmd.assert_returncode_success()
99 99
100 100 def test_clone_hg_repo_with_group_by_admin(self, rc_web_server, tmpdir):
101 101 clone_url = rc_web_server.repo_clone_url(HG_REPO_WITH_GROUP)
102 102 stdout, stderr = Command('/tmp').execute(
103 103 'hg clone', clone_url, tmpdir.strpath)
104 104 _check_proper_clone(stdout, stderr, 'hg')
105 105
106 106 def test_clone_git_repo_with_group_by_admin(self, rc_web_server, tmpdir):
107 107 clone_url = rc_web_server.repo_clone_url(GIT_REPO_WITH_GROUP)
108 108 cmd = Command('/tmp')
109 109 stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath)
110 110 _check_proper_clone(stdout, stderr, 'git')
111 111 cmd.assert_returncode_success()
112 112
113 113 def test_clone_git_repo_shallow_by_admin(self, rc_web_server, tmpdir):
114 114 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
115 115 cmd = Command('/tmp')
116 116 stdout, stderr = cmd.execute(
117 117 'git clone --depth=1', clone_url, tmpdir.strpath)
118 118
119 119 assert '' == stdout
120 120 assert 'Cloning into' in stderr
121 121 cmd.assert_returncode_success()
122 122
123 123 def test_clone_wrong_credentials_hg(self, rc_web_server, tmpdir):
124 124 clone_url = rc_web_server.repo_clone_url(HG_REPO, passwd='bad!')
125 125 stdout, stderr = Command('/tmp').execute(
126 126 'hg clone', clone_url, tmpdir.strpath)
127 127 assert 'abort: authorization failed' in stderr
128 128
129 129 def test_clone_wrong_credentials_git(self, rc_web_server, tmpdir):
130 130 clone_url = rc_web_server.repo_clone_url(GIT_REPO, passwd='bad!')
131 131 stdout, stderr = Command('/tmp').execute(
132 132 'git clone', clone_url, tmpdir.strpath)
133 133 assert 'fatal: Authentication failed' in stderr
134 134
135 135 def test_clone_git_dir_as_hg(self, rc_web_server, tmpdir):
136 136 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
137 137 stdout, stderr = Command('/tmp').execute(
138 138 'hg clone', clone_url, tmpdir.strpath)
139 139 assert 'HTTP Error 404: Not Found' in stderr
140 140
141 141 def test_clone_hg_repo_as_git(self, rc_web_server, tmpdir):
142 142 clone_url = rc_web_server.repo_clone_url(HG_REPO)
143 143 stdout, stderr = Command('/tmp').execute(
144 144 'git clone', clone_url, tmpdir.strpath)
145 145 assert 'not found' in stderr
146 146
147 147 def test_clone_non_existing_path_hg(self, rc_web_server, tmpdir):
148 148 clone_url = rc_web_server.repo_clone_url('trololo')
149 149 stdout, stderr = Command('/tmp').execute(
150 150 'hg clone', clone_url, tmpdir.strpath)
151 151 assert 'HTTP Error 404: Not Found' in stderr
152 152
153 153 def test_clone_non_existing_path_git(self, rc_web_server, tmpdir):
154 154 clone_url = rc_web_server.repo_clone_url('trololo')
155 155 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
156 156 assert 'not found' in stderr
157 157
158 158 def test_clone_hg_with_slashes(self, rc_web_server, tmpdir):
159 159 clone_url = rc_web_server.repo_clone_url('//' + HG_REPO)
160 160 stdout, stderr = Command('/tmp').execute('hg clone', clone_url, tmpdir.strpath)
161 161 assert 'HTTP Error 404: Not Found' in stderr
162 162
163 163 def test_clone_git_with_slashes(self, rc_web_server, tmpdir):
164 164 clone_url = rc_web_server.repo_clone_url('//' + GIT_REPO)
165 165 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
166 166 assert 'not found' in stderr
167 167
168 168 def test_clone_existing_path_hg_not_in_database(
169 169 self, rc_web_server, tmpdir, fs_repo_only):
170 170
171 171 db_name = fs_repo_only('not-in-db-hg', repo_type='hg')
172 172 clone_url = rc_web_server.repo_clone_url(db_name)
173 173 stdout, stderr = Command('/tmp').execute(
174 174 'hg clone', clone_url, tmpdir.strpath)
175 175 assert 'HTTP Error 404: Not Found' in stderr
176 176
177 177 def test_clone_existing_path_git_not_in_database(
178 178 self, rc_web_server, tmpdir, fs_repo_only):
179 179 db_name = fs_repo_only('not-in-db-git', repo_type='git')
180 180 clone_url = rc_web_server.repo_clone_url(db_name)
181 181 stdout, stderr = Command('/tmp').execute(
182 182 'git clone', clone_url, tmpdir.strpath)
183 183 assert 'not found' in stderr
184 184
185 185 def test_clone_existing_path_hg_not_in_database_different_scm(
186 186 self, rc_web_server, tmpdir, fs_repo_only):
187 187 db_name = fs_repo_only('not-in-db-git', repo_type='git')
188 188 clone_url = rc_web_server.repo_clone_url(db_name)
189 189 stdout, stderr = Command('/tmp').execute(
190 190 'hg clone', clone_url, tmpdir.strpath)
191 191 assert 'HTTP Error 404: Not Found' in stderr
192 192
193 193 def test_clone_existing_path_git_not_in_database_different_scm(
194 194 self, rc_web_server, tmpdir, fs_repo_only):
195 195 db_name = fs_repo_only('not-in-db-hg', repo_type='hg')
196 196 clone_url = rc_web_server.repo_clone_url(db_name)
197 197 stdout, stderr = Command('/tmp').execute(
198 198 'git clone', clone_url, tmpdir.strpath)
199 199 assert 'not found' in stderr
200 200
201 201 def test_clone_non_existing_store_path_hg(self, rc_web_server, tmpdir, user_util):
202 202 repo = user_util.create_repo()
203 203 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
204 204
205 205 # Damage repo by removing it's folder
206 206 RepoModel()._delete_filesystem_repo(repo)
207 207
208 208 stdout, stderr = Command('/tmp').execute(
209 209 'hg clone', clone_url, tmpdir.strpath)
210 210 assert 'HTTP Error 404: Not Found' in stderr
211 211
212 212 def test_clone_non_existing_store_path_git(self, rc_web_server, tmpdir, user_util):
213 213 repo = user_util.create_repo(repo_type='git')
214 214 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
215 215
216 216 # Damage repo by removing it's folder
217 217 RepoModel()._delete_filesystem_repo(repo)
218 218
219 219 stdout, stderr = Command('/tmp').execute(
220 220 'git clone', clone_url, tmpdir.strpath)
221 221 assert 'not found' in stderr
222 222
223 223 def test_push_new_file_hg(self, rc_web_server, tmpdir):
224 224 clone_url = rc_web_server.repo_clone_url(HG_REPO)
225 225 stdout, stderr = Command('/tmp').execute(
226 226 'hg clone', clone_url, tmpdir.strpath)
227 227
228 228 stdout, stderr = _add_files_and_push(
229 229 'hg', tmpdir.strpath, clone_url=clone_url)
230 230
231 231 assert 'pushing to' in stdout
232 232 assert 'size summary' in stdout
233 233
234 234 def test_push_new_file_git(self, rc_web_server, tmpdir):
235 235 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
236 236 stdout, stderr = Command('/tmp').execute(
237 237 'git clone', clone_url, tmpdir.strpath)
238 238
239 239 # commit some stuff into this repo
240 240 stdout, stderr = _add_files_and_push(
241 241 'git', tmpdir.strpath, clone_url=clone_url)
242 242
243 243 _check_proper_git_push(stdout, stderr)
244 244
245 245 def test_push_invalidates_cache(self, rc_web_server, tmpdir):
246 246 hg_repo = Repository.get_by_repo_name(HG_REPO)
247 247
248 248 # init cache objects
249 249 CacheKey.delete_all_cache()
250 250 cache_namespace_uid = 'cache_push_test.{}'.format(hg_repo.repo_id)
251 251 invalidation_namespace = CacheKey.REPO_INVALIDATION_NAMESPACE.format(
252 252 repo_id=hg_repo.repo_id)
253 253
254 254 inv_context_manager = rc_cache.InvalidationContext(
255 255 uid=cache_namespace_uid, invalidation_namespace=invalidation_namespace)
256 256
257 257 with inv_context_manager as invalidation_context:
258 258 # __enter__ will create and register cache objects
259 259 pass
260 260
261 261 # clone to init cache
262 262 clone_url = rc_web_server.repo_clone_url(hg_repo.repo_name)
263 263 stdout, stderr = Command('/tmp').execute(
264 264 'hg clone', clone_url, tmpdir.strpath)
265 265
266 266 cache_keys = hg_repo.cache_keys
267 267 assert cache_keys != []
268 268 for key in cache_keys:
269 269 assert key.cache_active is True
270 270
271 271 # PUSH that should trigger invalidation cache
272 272 stdout, stderr = _add_files_and_push(
273 273 'hg', tmpdir.strpath, clone_url=clone_url, files_no=1)
274 274
275 275 # flush...
276 276 Session().commit()
277 277 hg_repo = Repository.get_by_repo_name(HG_REPO)
278 278 cache_keys = hg_repo.cache_keys
279 279 assert cache_keys != []
280 280 for key in cache_keys:
281 281 # keys should be marked as not active
282 282 assert key.cache_active is False
283 283
284 284 def test_push_wrong_credentials_hg(self, rc_web_server, tmpdir):
285 285 clone_url = rc_web_server.repo_clone_url(HG_REPO)
286 286 stdout, stderr = Command('/tmp').execute(
287 287 'hg clone', clone_url, tmpdir.strpath)
288 288
289 289 push_url = rc_web_server.repo_clone_url(
290 290 HG_REPO, user='bad', passwd='name')
291 291 stdout, stderr = _add_files_and_push(
292 292 'hg', tmpdir.strpath, clone_url=push_url)
293 293
294 294 assert 'abort: authorization failed' in stderr
295 295
296 296 def test_push_wrong_credentials_git(self, rc_web_server, tmpdir):
297 297 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
298 298 stdout, stderr = Command('/tmp').execute(
299 299 'git clone', clone_url, tmpdir.strpath)
300 300
301 301 push_url = rc_web_server.repo_clone_url(
302 302 GIT_REPO, user='bad', passwd='name')
303 303 stdout, stderr = _add_files_and_push(
304 304 'git', tmpdir.strpath, clone_url=push_url)
305 305
306 306 assert 'fatal: Authentication failed' in stderr
307 307
308 308 def test_push_back_to_wrong_url_hg(self, rc_web_server, tmpdir):
309 309 clone_url = rc_web_server.repo_clone_url(HG_REPO)
310 310 stdout, stderr = Command('/tmp').execute(
311 311 'hg clone', clone_url, tmpdir.strpath)
312 312
313 313 stdout, stderr = _add_files_and_push(
314 314 'hg', tmpdir.strpath,
315 315 clone_url=rc_web_server.repo_clone_url('not-existing'))
316 316
317 317 assert 'HTTP Error 404: Not Found' in stderr
318 318
319 319 def test_push_back_to_wrong_url_git(self, rc_web_server, tmpdir):
320 320 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
321 321 stdout, stderr = Command('/tmp').execute(
322 322 'git clone', clone_url, tmpdir.strpath)
323 323
324 324 stdout, stderr = _add_files_and_push(
325 325 'git', tmpdir.strpath,
326 326 clone_url=rc_web_server.repo_clone_url('not-existing'))
327 327
328 328 assert 'not found' in stderr
329 329
330 330 def test_ip_restriction_hg(self, rc_web_server, tmpdir):
331 331 user_model = UserModel()
332 332 try:
333 333 user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
334 334 Session().commit()
335 335 time.sleep(2)
336 336 clone_url = rc_web_server.repo_clone_url(HG_REPO)
337 337 stdout, stderr = Command('/tmp').execute(
338 338 'hg clone', clone_url, tmpdir.strpath)
339 339 assert 'abort: HTTP Error 403: Forbidden' in stderr
340 340 finally:
341 341 # release IP restrictions
342 342 for ip in UserIpMap.getAll():
343 343 UserIpMap.delete(ip.ip_id)
344 344 Session().commit()
345 345
346 346 time.sleep(2)
347 347
348 348 stdout, stderr = Command('/tmp').execute(
349 349 'hg clone', clone_url, tmpdir.strpath)
350 350 _check_proper_clone(stdout, stderr, 'hg')
351 351
352 352 def test_ip_restriction_git(self, rc_web_server, tmpdir):
353 353 user_model = UserModel()
354 354 try:
355 355 user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
356 356 Session().commit()
357 357 time.sleep(2)
358 358 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
359 359 stdout, stderr = Command('/tmp').execute(
360 360 'git clone', clone_url, tmpdir.strpath)
361 361 msg = "The requested URL returned error: 403"
362 362 assert msg in stderr
363 363 finally:
364 364 # release IP restrictions
365 365 for ip in UserIpMap.getAll():
366 366 UserIpMap.delete(ip.ip_id)
367 367 Session().commit()
368 368
369 369 time.sleep(2)
370 370
371 371 cmd = Command('/tmp')
372 372 stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath)
373 373 cmd.assert_returncode_success()
374 374 _check_proper_clone(stdout, stderr, 'git')
375 375
376 376 def test_clone_by_auth_token(
377 377 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
378 378 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
379 379 'egg:rhodecode-enterprise-ce#rhodecode'])
380 380
381 381 user = user_util.create_user()
382 382 token = user.auth_tokens[1]
383 383
384 384 clone_url = rc_web_server.repo_clone_url(
385 385 HG_REPO, user=user.username, passwd=token)
386 386
387 387 stdout, stderr = Command('/tmp').execute(
388 388 'hg clone', clone_url, tmpdir.strpath)
389 389 _check_proper_clone(stdout, stderr, 'hg')
390 390
391 391 def test_clone_by_auth_token_expired(
392 392 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
393 393 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
394 394 'egg:rhodecode-enterprise-ce#rhodecode'])
395 395
396 396 user = user_util.create_user()
397 397 auth_token = AuthTokenModel().create(
398 398 user.user_id, 'test-token', -10, AuthTokenModel.cls.ROLE_VCS)
399 399 token = auth_token.api_key
400 400
401 401 clone_url = rc_web_server.repo_clone_url(
402 402 HG_REPO, user=user.username, passwd=token)
403 403
404 404 stdout, stderr = Command('/tmp').execute(
405 405 'hg clone', clone_url, tmpdir.strpath)
406 406 assert 'abort: authorization failed' in stderr
407 407
408 408 def test_clone_by_auth_token_bad_role(
409 409 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
410 410 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
411 411 'egg:rhodecode-enterprise-ce#rhodecode'])
412 412
413 413 user = user_util.create_user()
414 414 auth_token = AuthTokenModel().create(
415 415 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_API)
416 416 token = auth_token.api_key
417 417
418 418 clone_url = rc_web_server.repo_clone_url(
419 419 HG_REPO, user=user.username, passwd=token)
420 420
421 421 stdout, stderr = Command('/tmp').execute(
422 422 'hg clone', clone_url, tmpdir.strpath)
423 423 assert 'abort: authorization failed' in stderr
424 424
425 425 def test_clone_by_auth_token_user_disabled(
426 426 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
427 427 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
428 428 'egg:rhodecode-enterprise-ce#rhodecode'])
429 429 user = user_util.create_user()
430 430 user.active = False
431 431 Session().add(user)
432 432 Session().commit()
433 433 token = user.auth_tokens[1]
434 434
435 435 clone_url = rc_web_server.repo_clone_url(
436 436 HG_REPO, user=user.username, passwd=token)
437 437
438 438 stdout, stderr = Command('/tmp').execute(
439 439 'hg clone', clone_url, tmpdir.strpath)
440 440 assert 'abort: authorization failed' in stderr
441 441
442 442 def test_clone_by_auth_token_with_scope(
443 443 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
444 444 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
445 445 'egg:rhodecode-enterprise-ce#rhodecode'])
446 446 user = user_util.create_user()
447 447 auth_token = AuthTokenModel().create(
448 448 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_VCS)
449 449 token = auth_token.api_key
450 450
451 451 # manually set scope
452 452 auth_token.repo = Repository.get_by_repo_name(HG_REPO)
453 453 Session().add(auth_token)
454 454 Session().commit()
455 455
456 456 clone_url = rc_web_server.repo_clone_url(
457 457 HG_REPO, user=user.username, passwd=token)
458 458
459 459 stdout, stderr = Command('/tmp').execute(
460 460 'hg clone', clone_url, tmpdir.strpath)
461 461 _check_proper_clone(stdout, stderr, 'hg')
462 462
463 463 def test_clone_by_auth_token_with_wrong_scope(
464 464 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
465 465 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
466 466 'egg:rhodecode-enterprise-ce#rhodecode'])
467 467 user = user_util.create_user()
468 468 auth_token = AuthTokenModel().create(
469 469 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_VCS)
470 470 token = auth_token.api_key
471 471
472 472 # manually set scope
473 473 auth_token.repo = Repository.get_by_repo_name(GIT_REPO)
474 474 Session().add(auth_token)
475 475 Session().commit()
476 476
477 477 clone_url = rc_web_server.repo_clone_url(
478 478 HG_REPO, user=user.username, passwd=token)
479 479
480 480 stdout, stderr = Command('/tmp').execute(
481 481 'hg clone', clone_url, tmpdir.strpath)
482 482 assert 'abort: authorization failed' in stderr
483 483
General Comments 0
You need to be logged in to leave comments. Login now