##// END OF EJS Templates
fix(tests): on jenkins ensure re-creation of the path
super-admin -
r5609:cccd61c3 default
parent child Browse files
Show More
@@ -1,1242 +1,1244 b''
1 1 # Copyright (C) 2010-2024 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import datetime
20 20 import mock
21 21 import os
22 22 import sys
23 23 import shutil
24 24
25 25 import pytest
26 26
27 27 from rhodecode.lib.utils import make_db_config
28 28 from rhodecode.lib.vcs.backends.base import Reference
29 29 from rhodecode.lib.vcs.backends.git import GitRepository, GitCommit, discover_git_version
30 30 from rhodecode.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
31 31 from rhodecode.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState, SubModuleNode
32 32 from rhodecode.tests import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir
33 33 from rhodecode.tests.vcs.conftest import BackendTestMixin
34 34
35 35
36 36 pytestmark = pytest.mark.backends("git")
37 37
38 38
39 39 DIFF_FROM_REMOTE = rb"""diff --git a/foobar b/foobar
40 40 new file mode 100644
41 41 index 0000000..f6ea049
42 42 --- /dev/null
43 43 +++ b/foobar
44 44 @@ -0,0 +1 @@
45 45 +foobar
46 46 \ No newline at end of file
47 47 diff --git a/foobar2 b/foobar2
48 48 new file mode 100644
49 49 index 0000000..e8c9d6b
50 50 --- /dev/null
51 51 +++ b/foobar2
52 52 @@ -0,0 +1 @@
53 53 +foobar2
54 54 \ No newline at end of file
55 55 """
56 56
57 57
58 58 def callable_get_diff(*args, **kwargs):
59 59 return DIFF_FROM_REMOTE
60 60
61 61
62 62 class TestGitRepository(object):
63 63 @pytest.fixture(autouse=True)
64 64 def prepare(self, request, baseapp):
65 65 self.repo = GitRepository(TEST_GIT_REPO, bare=True)
66 66 self.repo.count()
67 67
68 68 def get_clone_repo(self, tmpdir):
69 69 """
70 70 Return a non bare clone of the base repo.
71 71 """
72 72 clone_path = str(tmpdir.join("clone-repo"))
73 73 repo_clone = GitRepository(clone_path, create=True, src_url=self.repo.path, bare=False)
74 74
75 75 return repo_clone
76 76
77 77 def get_empty_repo(self, tmpdir, bare=False):
78 78 """
79 79 Return a non bare empty repo.
80 80 """
81 81 clone_path = str(tmpdir.join("empty-repo"))
82 82 return GitRepository(clone_path, create=True, bare=bare)
83 83
84 84 def test_wrong_repo_path(self):
85 85 wrong_repo_path = "/tmp/errorrepo_git"
86 86 with pytest.raises(RepositoryError):
87 87 GitRepository(wrong_repo_path)
88 88
89 89 def test_repo_clone(self, tmp_path_factory):
90 90 repo = GitRepository(TEST_GIT_REPO)
91 91 clone_path = f"{tmp_path_factory.mktemp('_')}_{TEST_GIT_REPO_CLONE}"
92 92 repo_clone = GitRepository(clone_path, src_url=TEST_GIT_REPO, create=True, do_workspace_checkout=True)
93 93
94 94 assert len(repo.commit_ids) == len(repo_clone.commit_ids)
95 95 # Checking hashes of commits should be enough
96 96 for commit in repo.get_commits():
97 97 raw_id = commit.raw_id
98 98 assert raw_id == repo_clone.get_commit(raw_id).raw_id
99 99
100 100 def test_repo_clone_without_create(self):
101 101 with pytest.raises(RepositoryError):
102 102 GitRepository(TEST_GIT_REPO_CLONE + "_wo_create", src_url=TEST_GIT_REPO)
103 103
104 104 def test_repo_clone_with_update(self, tmp_path_factory):
105 105 repo = GitRepository(TEST_GIT_REPO)
106 106 clone_path = "{}_{}_update".format(tmp_path_factory.mktemp("_"), TEST_GIT_REPO_CLONE)
107 107
108 108 repo_clone = GitRepository(clone_path, create=True, src_url=TEST_GIT_REPO, do_workspace_checkout=True)
109 109 assert len(repo.commit_ids) == len(repo_clone.commit_ids)
110 110
111 111 # check if current workdir was updated
112 112 fpath = os.path.join(clone_path, "MANIFEST.in")
113 113 assert os.path.isfile(fpath)
114 114
115 115 def test_repo_clone_without_update(self, tmp_path_factory):
116 116 repo = GitRepository(TEST_GIT_REPO)
117 117 clone_path = "{}_{}_without_update".format(tmp_path_factory.mktemp("_"), TEST_GIT_REPO_CLONE)
118 118 repo_clone = GitRepository(clone_path, create=True, src_url=TEST_GIT_REPO, do_workspace_checkout=False)
119 119 assert len(repo.commit_ids) == len(repo_clone.commit_ids)
120 120 # check if current workdir was *NOT* updated
121 121 fpath = os.path.join(clone_path, "MANIFEST.in")
122 122 # Make sure it's not bare repo
123 123 assert not repo_clone.bare
124 124 assert not os.path.isfile(fpath)
125 125
126 126 def test_repo_clone_into_bare_repo(self, tmp_path_factory):
127 127 repo = GitRepository(TEST_GIT_REPO)
128 128 clone_path = "{}_{}_bare.git".format(tmp_path_factory.mktemp("_"), TEST_GIT_REPO_CLONE)
129 129 repo_clone = GitRepository(clone_path, create=True, src_url=repo.path, bare=True)
130 130 assert repo_clone.bare
131 131
132 132 def test_create_repo_is_not_bare_by_default(self):
133 133 repo = GitRepository(get_new_dir("not-bare-by-default"), create=True)
134 134 assert not repo.bare
135 135
136 136 def test_create_bare_repo(self):
137 137 repo = GitRepository(get_new_dir("bare-repo"), create=True, bare=True)
138 138 assert repo.bare
139 139
140 140 def test_update_server_info(self):
141 141 self.repo._update_server_info()
142 142
143 143 def test_fetch(self, vcsbackend_git):
144 144 # Note: This is a git specific part of the API, it's only implemented
145 145 # by the git backend.
146 146 source_repo = vcsbackend_git.repo
147 147 target_repo = vcsbackend_git.create_repo(bare=True)
148 148 target_repo.fetch(source_repo.path)
149 149 # Note: Get a fresh instance, avoids caching trouble
150 150 target_repo = vcsbackend_git.backend(target_repo.path)
151 151 assert len(source_repo.commit_ids) == len(target_repo.commit_ids)
152 152
153 153 def test_commit_ids(self):
154 154 # there are 112 commits (by now)
155 155 # so we can assume they would be available from now on
156 156 subset = {
157 157 "c1214f7e79e02fc37156ff215cd71275450cffc3",
158 158 "38b5fe81f109cb111f549bfe9bb6b267e10bc557",
159 159 "fa6600f6848800641328adbf7811fd2372c02ab2",
160 160 "102607b09cdd60e2793929c4f90478be29f85a17",
161 161 "49d3fd156b6f7db46313fac355dca1a0b94a0017",
162 162 "2d1028c054665b962fa3d307adfc923ddd528038",
163 163 "d7e0d30fbcae12c90680eb095a4f5f02505ce501",
164 164 "ff7ca51e58c505fec0dd2491de52c622bb7a806b",
165 165 "dd80b0f6cf5052f17cc738c2951c4f2070200d7f",
166 166 "8430a588b43b5d6da365400117c89400326e7992",
167 167 "d955cd312c17b02143c04fa1099a352b04368118",
168 168 "f67b87e5c629c2ee0ba58f85197e423ff28d735b",
169 169 "add63e382e4aabc9e1afdc4bdc24506c269b7618",
170 170 "f298fe1189f1b69779a4423f40b48edf92a703fc",
171 171 "bd9b619eb41994cac43d67cf4ccc8399c1125808",
172 172 "6e125e7c890379446e98980d8ed60fba87d0f6d1",
173 173 "d4a54db9f745dfeba6933bf5b1e79e15d0af20bd",
174 174 "0b05e4ed56c802098dfc813cbe779b2f49e92500",
175 175 "191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e",
176 176 "45223f8f114c64bf4d6f853e3c35a369a6305520",
177 177 "ca1eb7957a54bce53b12d1a51b13452f95bc7c7e",
178 178 "f5ea29fc42ef67a2a5a7aecff10e1566699acd68",
179 179 "27d48942240f5b91dfda77accd2caac94708cc7d",
180 180 "622f0eb0bafd619d2560c26f80f09e3b0b0d78af",
181 181 "e686b958768ee96af8029fe19c6050b1a8dd3b2b",
182 182 }
183 183 assert subset.issubset(set(self.repo.commit_ids))
184 184
185 185 def test_slicing(self):
186 186 # 4 1 5 10 95
187 187 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5), (10, 20, 10), (5, 100, 95)]:
188 188 commit_ids = list(self.repo[sfrom:sto])
189 189 assert len(commit_ids) == size
190 190 assert commit_ids[0] == self.repo.get_commit(commit_idx=sfrom)
191 191 assert commit_ids[-1] == self.repo.get_commit(commit_idx=sto - 1)
192 192
193 193 def test_branches(self):
194 194 # TODO: Need more tests here
195 195 # Removed (those are 'remotes' branches for cloned repo)
196 196 # assert 'master' in self.repo.branches
197 197 # assert 'gittree' in self.repo.branches
198 198 # assert 'web-branch' in self.repo.branches
199 199 for __, commit_id in self.repo.branches.items():
200 200 assert isinstance(self.repo.get_commit(commit_id), GitCommit)
201 201
202 202 def test_tags(self):
203 203 # TODO: Need more tests here
204 204 assert "v0.1.1" in self.repo.tags
205 205 assert "v0.1.2" in self.repo.tags
206 206 for __, commit_id in self.repo.tags.items():
207 207 assert isinstance(self.repo.get_commit(commit_id), GitCommit)
208 208
209 209 def _test_single_commit_cache(self, commit_id):
210 210 commit = self.repo.get_commit(commit_id)
211 211 assert commit_id in self.repo.commits
212 212 assert commit is self.repo.commits[commit_id]
213 213
214 214 def test_initial_commit(self):
215 215 commit_id = self.repo.commit_ids[0]
216 216 init_commit = self.repo.get_commit(commit_id)
217 217 init_author = init_commit.author
218 218
219 219 assert init_commit.message == "initial import\n"
220 220 assert init_author == "Marcin Kuzminski <marcin@python-blog.com>"
221 221 assert init_author == init_commit.committer
222 222 for path in ("vcs/__init__.py", "vcs/backends/BaseRepository.py", "vcs/backends/__init__.py"):
223 223 assert isinstance(init_commit.get_node(path), FileNode)
224 224 for path in ("", "vcs", "vcs/backends"):
225 225 assert isinstance(init_commit.get_node(path), DirNode)
226 226
227 227 with pytest.raises(NodeDoesNotExistError):
228 228 init_commit.get_node(path="foobar")
229 229
230 230 node = init_commit.get_node("vcs/")
231 231 assert hasattr(node, "kind")
232 232 assert node.kind == NodeKind.DIR
233 233
234 234 node = init_commit.get_node("vcs")
235 235 assert hasattr(node, "kind")
236 236 assert node.kind == NodeKind.DIR
237 237
238 238 node = init_commit.get_node("vcs/__init__.py")
239 239 assert hasattr(node, "kind")
240 240 assert node.kind == NodeKind.FILE
241 241
242 242 def test_not_existing_commit(self):
243 243 with pytest.raises(RepositoryError):
244 244 self.repo.get_commit("f" * 40)
245 245
246 246 def test_commit10(self):
247 247 commit10 = self.repo.get_commit(self.repo.commit_ids[9])
248 248 README = """===
249 249 VCS
250 250 ===
251 251
252 252 Various Version Control System management abstraction layer for Python.
253 253
254 254 Introduction
255 255 ------------
256 256
257 257 TODO: To be written...
258 258
259 259 """
260 260 node = commit10.get_node("README.rst")
261 261 assert node.kind == NodeKind.FILE
262 262 assert node.str_content == README
263 263
264 264 def test_head(self):
265 265 assert self.repo.head == self.repo.get_commit().raw_id
266 266
267 267 def test_checkout_with_create(self, tmpdir):
268 268 repo_clone = self.get_clone_repo(tmpdir)
269 269
270 270 new_branch = "new_branch"
271 271 assert repo_clone._current_branch() == "master"
272 272 assert set(repo_clone.branches) == {"master"}
273 273 repo_clone._checkout(new_branch, create=True)
274 274
275 275 # Branches is a lazy property so we need to recrete the Repo object.
276 276 repo_clone = GitRepository(repo_clone.path)
277 277 assert set(repo_clone.branches) == {"master", new_branch}
278 278 assert repo_clone._current_branch() == new_branch
279 279
280 280 def test_checkout(self, tmpdir):
281 281 repo_clone = self.get_clone_repo(tmpdir)
282 282
283 283 repo_clone._checkout("new_branch", create=True)
284 284 repo_clone._checkout("master")
285 285
286 286 assert repo_clone._current_branch() == "master"
287 287
288 288 def test_checkout_same_branch(self, tmpdir):
289 289 repo_clone = self.get_clone_repo(tmpdir)
290 290
291 291 repo_clone._checkout("master")
292 292 assert repo_clone._current_branch() == "master"
293 293
294 294 def test_checkout_branch_already_exists(self, tmpdir):
295 295 repo_clone = self.get_clone_repo(tmpdir)
296 296
297 297 with pytest.raises(RepositoryError):
298 298 repo_clone._checkout("master", create=True)
299 299
300 300 def test_checkout_bare_repo(self):
301 301 with pytest.raises(RepositoryError):
302 302 self.repo._checkout("master")
303 303
304 304 def test_current_branch_bare_repo(self):
305 305 with pytest.raises(RepositoryError):
306 306 self.repo._current_branch()
307 307
308 308 def test_current_branch_empty_repo(self, tmpdir):
309 309 repo = self.get_empty_repo(tmpdir)
310 310 assert repo._current_branch() is None
311 311
312 312 def test_local_clone(self, tmp_path_factory):
313 313 clone_path = str(tmp_path_factory.mktemp("test-local-clone"))
314 314 self.repo._local_clone(clone_path, "master")
315 315 repo_clone = GitRepository(clone_path)
316 316
317 317 assert self.repo.commit_ids == repo_clone.commit_ids
318 318
319 319 def test_local_clone_with_specific_branch(self, tmpdir):
320 320 source_repo = self.get_clone_repo(tmpdir)
321 321
322 322 # Create a new branch in source repo
323 323 new_branch_commit = source_repo.commit_ids[-3]
324 324 source_repo._checkout(new_branch_commit)
325 325 source_repo._checkout("new_branch", create=True)
326 326
327 327 clone_path = str(tmpdir.join("git-clone-path-1"))
328 328 source_repo._local_clone(clone_path, "new_branch")
329 329 repo_clone = GitRepository(clone_path)
330 330
331 331 assert source_repo.commit_ids[: -3 + 1] == repo_clone.commit_ids
332 332
333 333 clone_path = str(tmpdir.join("git-clone-path-2"))
334 334 source_repo._local_clone(clone_path, "master")
335 335 repo_clone = GitRepository(clone_path)
336 336
337 337 assert source_repo.commit_ids == repo_clone.commit_ids
338 338
339 339 def test_local_clone_fails_if_target_exists(self):
340 340 with pytest.raises(RepositoryError):
341 341 self.repo._local_clone(self.repo.path, "master")
342 342
343 343 def test_local_fetch(self, tmpdir):
344 344 target_repo = self.get_empty_repo(tmpdir)
345 345 source_repo = self.get_clone_repo(tmpdir)
346 346
347 347 # Create a new branch in source repo
348 348 master_commit = source_repo.commit_ids[-1]
349 349 new_branch_commit = source_repo.commit_ids[-3]
350 350 source_repo._checkout(new_branch_commit)
351 351 source_repo._checkout("new_branch", create=True)
352 352
353 353 target_repo._local_fetch(source_repo.path, "new_branch")
354 354 assert target_repo._last_fetch_heads() == [new_branch_commit]
355 355
356 356 target_repo._local_fetch(source_repo.path, "master")
357 357 assert target_repo._last_fetch_heads() == [master_commit]
358 358
359 359 def test_local_fetch_from_bare_repo(self, tmpdir):
360 360 target_repo = self.get_empty_repo(tmpdir)
361 361 target_repo._local_fetch(self.repo.path, "master")
362 362
363 363 master_commit = self.repo.commit_ids[-1]
364 364 assert target_repo._last_fetch_heads() == [master_commit]
365 365
366 366 def test_local_fetch_from_same_repo(self):
367 367 with pytest.raises(ValueError):
368 368 self.repo._local_fetch(self.repo.path, "master")
369 369
370 370 def test_local_fetch_branch_does_not_exist(self, tmpdir):
371 371 target_repo = self.get_empty_repo(tmpdir)
372 372
373 373 with pytest.raises(RepositoryError):
374 374 target_repo._local_fetch(self.repo.path, "new_branch")
375 375
376 376 def test_local_pull(self, tmpdir):
377 377 target_repo = self.get_empty_repo(tmpdir)
378 378 source_repo = self.get_clone_repo(tmpdir)
379 379
380 380 # Create a new branch in source repo
381 381 master_commit = source_repo.commit_ids[-1]
382 382 new_branch_commit = source_repo.commit_ids[-3]
383 383 source_repo._checkout(new_branch_commit)
384 384 source_repo._checkout("new_branch", create=True)
385 385
386 386 target_repo._local_pull(source_repo.path, "new_branch")
387 387 target_repo = GitRepository(target_repo.path)
388 388 assert target_repo.head == new_branch_commit
389 389
390 390 target_repo._local_pull(source_repo.path, "master")
391 391 target_repo = GitRepository(target_repo.path)
392 392 assert target_repo.head == master_commit
393 393
394 394 def test_local_pull_in_bare_repo(self):
395 395 with pytest.raises(RepositoryError):
396 396 self.repo._local_pull(self.repo.path, "master")
397 397
398 398 def test_local_merge(self, tmpdir):
399 399 target_repo = self.get_empty_repo(tmpdir)
400 400 source_repo = self.get_clone_repo(tmpdir)
401 401
402 402 # Create a new branch in source repo
403 403 master_commit = source_repo.commit_ids[-1]
404 404 new_branch_commit = source_repo.commit_ids[-3]
405 405 source_repo._checkout(new_branch_commit)
406 406 source_repo._checkout("new_branch", create=True)
407 407
408 408 # This is required as one cannot do a -ff-only merge in an empty repo.
409 409 target_repo._local_pull(source_repo.path, "new_branch")
410 410
411 411 target_repo._local_fetch(source_repo.path, "master")
412 412 merge_message = "Merge message\n\nDescription:..."
413 413 user_name = "Albert Einstein"
414 414 user_email = "albert@einstein.com"
415 415 target_repo._local_merge(merge_message, user_name, user_email, target_repo._last_fetch_heads())
416 416
417 417 target_repo = GitRepository(target_repo.path)
418 418 assert target_repo.commit_ids[-2] == master_commit
419 419 last_commit = target_repo.get_commit(target_repo.head)
420 420 assert last_commit.message.strip() == merge_message
421 421 assert last_commit.author == "%s <%s>" % (user_name, user_email)
422 422
423 423 assert not os.path.exists(os.path.join(target_repo.path, ".git", "MERGE_HEAD"))
424 424
425 425 def test_local_merge_raises_exception_on_conflict(self, vcsbackend_git):
426 426 target_repo = vcsbackend_git.create_repo(number_of_commits=1)
427 427 vcsbackend_git.ensure_file(b"README", b"I will conflict with you!!!")
428 428
429 429 target_repo._local_fetch(self.repo.path, "master")
430 430 with pytest.raises(RepositoryError):
431 431 target_repo._local_merge("merge_message", "user name", "user@name.com", target_repo._last_fetch_heads())
432 432
433 433 # Check we are not left in an intermediate merge state
434 434 assert not os.path.exists(os.path.join(target_repo.path, ".git", "MERGE_HEAD"))
435 435
436 436 def test_local_merge_into_empty_repo(self, tmpdir):
437 437 target_repo = self.get_empty_repo(tmpdir)
438 438
439 439 # This is required as one cannot do a -ff-only merge in an empty repo.
440 440 target_repo._local_fetch(self.repo.path, "master")
441 441 with pytest.raises(RepositoryError):
442 442 target_repo._local_merge("merge_message", "user name", "user@name.com", target_repo._last_fetch_heads())
443 443
444 444 def test_local_merge_in_bare_repo(self):
445 445 with pytest.raises(RepositoryError):
446 446 self.repo._local_merge("merge_message", "user name", "user@name.com", None)
447 447
448 448 def test_local_push_non_bare(self, tmpdir):
449 449 target_repo = self.get_empty_repo(tmpdir)
450 450
451 451 pushed_branch = "pushed_branch"
452 452 self.repo._local_push("master", target_repo.path, pushed_branch)
453 453 # Fix the HEAD of the target repo, or otherwise GitRepository won't
454 454 # report any branches.
455 455 with open(os.path.join(target_repo.path, ".git", "HEAD"), "w") as f:
456 456 f.write("ref: refs/heads/%s" % pushed_branch)
457 457
458 458 target_repo = GitRepository(target_repo.path)
459 459
460 460 assert target_repo.branches[pushed_branch] == self.repo.branches["master"]
461 461
462 462 def test_local_push_bare(self, tmpdir):
463 463 target_repo = self.get_empty_repo(tmpdir, bare=True)
464 464
465 465 pushed_branch = "pushed_branch"
466 466 self.repo._local_push("master", target_repo.path, pushed_branch)
467 467 # Fix the HEAD of the target repo, or otherwise GitRepository won't
468 468 # report any branches.
469 469 with open(os.path.join(target_repo.path, "HEAD"), "w") as f:
470 470 f.write("ref: refs/heads/%s" % pushed_branch)
471 471
472 472 target_repo = GitRepository(target_repo.path)
473 473
474 474 assert target_repo.branches[pushed_branch] == self.repo.branches["master"]
475 475
476 476 def test_local_push_non_bare_target_branch_is_checked_out(self, tmpdir):
477 477 target_repo = self.get_clone_repo(tmpdir)
478 478
479 479 pushed_branch = "pushed_branch"
480 480 # Create a new branch in source repo
481 481 new_branch_commit = target_repo.commit_ids[-3]
482 482 target_repo._checkout(new_branch_commit)
483 483 target_repo._checkout(pushed_branch, create=True)
484 484
485 485 self.repo._local_push("master", target_repo.path, pushed_branch)
486 486
487 487 target_repo = GitRepository(target_repo.path)
488 488
489 489 assert target_repo.branches[pushed_branch] == self.repo.branches["master"]
490 490
491 491 def test_local_push_raises_exception_on_conflict(self, vcsbackend_git):
492 492 target_repo = vcsbackend_git.create_repo(number_of_commits=1)
493 493 with pytest.raises(RepositoryError):
494 494 self.repo._local_push("master", target_repo.path, "master")
495 495
496 496 def test_hooks_can_be_enabled_via_env_variable_for_local_push(self, tmpdir):
497 497 target_repo = self.get_empty_repo(tmpdir, bare=True)
498 498
499 499 with mock.patch.object(self.repo, "run_git_command") as run_mock:
500 500 self.repo._local_push("master", target_repo.path, "master", enable_hooks=True)
501 501 env = run_mock.call_args[1]["extra_env"]
502 502 assert "RC_SKIP_HOOKS" not in env
503 503
504 504 def _add_failing_hook(self, repo_path, hook_name, bare=False):
505 505 path_components = ["hooks", hook_name] if bare else [".git", "hooks", hook_name]
506 506 hook_path = os.path.join(repo_path, *path_components)
507 507 with open(hook_path, "w") as f:
508 508 script_lines = [
509 509 "#!%s" % sys.executable,
510 510 "import os",
511 511 "import sys",
512 512 'if os.environ.get("RC_SKIP_HOOKS"):',
513 513 " sys.exit(0)",
514 514 "sys.exit(1)",
515 515 ]
516 516 f.write("\n".join(script_lines))
517 517 os.chmod(hook_path, 0o755)
518 518
519 519 def test_local_push_does_not_execute_hook(self, tmpdir):
520 520 target_repo = self.get_empty_repo(tmpdir)
521 521
522 522 pushed_branch = "pushed_branch"
523 523 self._add_failing_hook(target_repo.path, "pre-receive")
524 524 self.repo._local_push("master", target_repo.path, pushed_branch)
525 525 # Fix the HEAD of the target repo, or otherwise GitRepository won't
526 526 # report any branches.
527 527 with open(os.path.join(target_repo.path, ".git", "HEAD"), "w") as f:
528 528 f.write("ref: refs/heads/%s" % pushed_branch)
529 529
530 530 target_repo = GitRepository(target_repo.path)
531 531
532 532 assert target_repo.branches[pushed_branch] == self.repo.branches["master"]
533 533
534 534 def test_local_push_executes_hook(self, tmpdir):
535 535 target_repo = self.get_empty_repo(tmpdir, bare=True)
536 536 self._add_failing_hook(target_repo.path, "pre-receive", bare=True)
537 537 with pytest.raises(RepositoryError):
538 538 self.repo._local_push("master", target_repo.path, "master", enable_hooks=True)
539 539
540 540 def test_maybe_prepare_merge_workspace(self):
541 541 workspace = self.repo._maybe_prepare_merge_workspace(
542 542 2, "pr2", Reference("branch", "master", "unused"), Reference("branch", "master", "unused")
543 543 )
544 544
545 545 assert os.path.isdir(workspace)
546 546 workspace_repo = GitRepository(workspace)
547 547 assert workspace_repo.branches == self.repo.branches
548 548
549 549 # Calling it a second time should also succeed
550 550 workspace = self.repo._maybe_prepare_merge_workspace(
551 551 2, "pr2", Reference("branch", "master", "unused"), Reference("branch", "master", "unused")
552 552 )
553 553 assert os.path.isdir(workspace)
554 554
555 555 def test_maybe_prepare_merge_workspace_different_refs(self):
556 556 workspace = self.repo._maybe_prepare_merge_workspace(
557 557 2, "pr2", Reference("branch", "master", "unused"), Reference("branch", "develop", "unused")
558 558 )
559 559
560 560 assert os.path.isdir(workspace)
561 561 workspace_repo = GitRepository(workspace)
562 562 assert workspace_repo.branches == self.repo.branches
563 563
564 564 # Calling it a second time should also succeed
565 565 workspace = self.repo._maybe_prepare_merge_workspace(
566 566 2, "pr2", Reference("branch", "master", "unused"), Reference("branch", "develop", "unused")
567 567 )
568 568 assert os.path.isdir(workspace)
569 569
570 570 def test_cleanup_merge_workspace(self):
571 571 workspace = self.repo._maybe_prepare_merge_workspace(
572 572 2, "pr3", Reference("branch", "master", "unused"), Reference("branch", "master", "unused")
573 573 )
574 574 self.repo.cleanup_merge_workspace(2, "pr3")
575 575
576 576 assert not os.path.exists(workspace)
577 577
578 578 def test_cleanup_merge_workspace_invalid_workspace_id(self):
579 579 # No assert: because in case of an inexistent workspace this function
580 580 # should still succeed.
581 581 self.repo.cleanup_merge_workspace(1, "pr4")
582 582
583 583 def test_set_refs(self):
584 584 test_ref = "refs/test-refs/abcde"
585 585 test_commit_id = "ecb86e1f424f2608262b130db174a7dfd25a6623"
586 586
587 587 self.repo.set_refs(test_ref, test_commit_id)
588 588 stdout, _ = self.repo.run_git_command(["show-ref"])
589 589 assert test_ref in stdout
590 590 assert test_commit_id in stdout
591 591
592 592 def test_remove_ref(self):
593 593 test_ref = "refs/test-refs/abcde"
594 594 test_commit_id = "ecb86e1f424f2608262b130db174a7dfd25a6623"
595 595 self.repo.set_refs(test_ref, test_commit_id)
596 596 stdout, _ = self.repo.run_git_command(["show-ref"])
597 597 assert test_ref in stdout
598 598 assert test_commit_id in stdout
599 599
600 600 self.repo.remove_ref(test_ref)
601 601 stdout, _ = self.repo.run_git_command(["show-ref"])
602 602 assert test_ref not in stdout
603 603 assert test_commit_id not in stdout
604 604
605 605
606 606 class TestGitCommit(object):
607 607 @pytest.fixture(autouse=True)
608 608 def prepare(self):
609 609 self.repo = GitRepository(TEST_GIT_REPO)
610 610
611 611 def test_default_commit(self):
612 612 tip = self.repo.get_commit()
613 613 assert tip == self.repo.get_commit(None)
614 614 assert tip == self.repo.get_commit("tip")
615 615
616 616 def test_root_node(self):
617 617 tip = self.repo.get_commit()
618 618 assert tip.root is tip.get_node("")
619 619
620 620 def test_lazy_fetch(self):
621 621 """
622 622 Test if commit's nodes expands and are cached as we walk through
623 623 the commit. This test is somewhat hard to write as order of tests
624 624 is a key here. Written by running command after command in a shell.
625 625 """
626 626 commit_id = "2a13f185e4525f9d4b59882791a2d397b90d5ddc"
627 627 assert commit_id in self.repo.commit_ids
628 628 commit = self.repo.get_commit(commit_id)
629 629 assert len(commit.nodes) == 0
630 630 root = commit.root
631 631 assert len(commit.nodes) == 1
632 632 assert len(root.nodes) == 8
633 633 # accessing root.nodes updates commit.nodes
634 634 assert len(commit.nodes) == 9
635 635
636 636 docs = root.get_node("docs")
637 637 # we haven't yet accessed anything new as docs dir was already cached
638 638 assert len(commit.nodes) == 9
639 639 assert len(docs.nodes) == 8
640 640 # accessing docs.nodes updates commit.nodes
641 641 assert len(commit.nodes) == 17
642 642
643 643 assert docs is commit.get_node("docs")
644 644 assert docs is root.nodes[0]
645 645 assert docs is root.dirs[0]
646 646 assert docs is commit.get_node("docs")
647 647
648 648 def test_nodes_with_commit(self):
649 649 commit_id = "2a13f185e4525f9d4b59882791a2d397b90d5ddc"
650 650 commit = self.repo.get_commit(commit_id)
651 651 root = commit.root
652 652 docs = root.get_node("docs")
653 653 assert docs is commit.get_node("docs")
654 654 api = docs.get_node("api")
655 655 assert api is commit.get_node("docs/api")
656 656 index = api.get_node("index.rst")
657 657 assert index is commit.get_node("docs/api/index.rst")
658 658 assert index is commit.get_node("docs").get_node("api").get_node("index.rst")
659 659
660 660 def test_branch_and_tags(self):
661 661 """
662 662 rev0 = self.repo.commit_ids[0]
663 663 commit0 = self.repo.get_commit(rev0)
664 664 assert commit0.branch == 'master'
665 665 assert commit0.tags == []
666 666
667 667 rev10 = self.repo.commit_ids[10]
668 668 commit10 = self.repo.get_commit(rev10)
669 669 assert commit10.branch == 'master'
670 670 assert commit10.tags == []
671 671
672 672 rev44 = self.repo.commit_ids[44]
673 673 commit44 = self.repo.get_commit(rev44)
674 674 assert commit44.branch == 'web-branch'
675 675
676 676 tip = self.repo.get_commit('tip')
677 677 assert 'tip' in tip.tags
678 678 """
679 679 # Those tests would fail - branches are now going
680 680 # to be changed at main API in order to support git backend
681 681 pass
682 682
683 683 def test_file_size(self):
684 684 to_check = (
685 685 ("c1214f7e79e02fc37156ff215cd71275450cffc3", "vcs/backends/BaseRepository.py", 502),
686 686 ("d7e0d30fbcae12c90680eb095a4f5f02505ce501", "vcs/backends/hg.py", 854),
687 687 ("6e125e7c890379446e98980d8ed60fba87d0f6d1", "setup.py", 1068),
688 688 ("d955cd312c17b02143c04fa1099a352b04368118", "vcs/backends/base.py", 2921),
689 689 ("ca1eb7957a54bce53b12d1a51b13452f95bc7c7e", "vcs/backends/base.py", 3936),
690 690 ("f50f42baeed5af6518ef4b0cb2f1423f3851a941", "vcs/backends/base.py", 6189),
691 691 )
692 692 for commit_id, path, size in to_check:
693 693 node = self.repo.get_commit(commit_id).get_node(path)
694 694 assert node.is_file()
695 695 assert node.size == size
696 696
697 697 def test_file_history_from_commits(self):
698 698 node = self.repo[10].get_node("setup.py")
699 699 commit_ids = [commit.raw_id for commit in node.history]
700 700 assert ["ff7ca51e58c505fec0dd2491de52c622bb7a806b"] == commit_ids
701 701
702 702 node = self.repo[20].get_node("setup.py")
703 703 node_ids = [commit.raw_id for commit in node.history]
704 704 assert ["191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e", "ff7ca51e58c505fec0dd2491de52c622bb7a806b"] == node_ids
705 705
706 706 # special case we check history from commit that has this particular
707 707 # file changed this means we check if it's included as well
708 708 node = self.repo.get_commit("191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e").get_node("setup.py")
709 709 node_ids = [commit.raw_id for commit in node.history]
710 710 assert ["191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e", "ff7ca51e58c505fec0dd2491de52c622bb7a806b"] == node_ids
711 711
712 712 def test_file_history(self):
713 713 # we can only check if those commits are present in the history
714 714 # as we cannot update this test every time file is changed
715 715 files = {
716 716 "setup.py": [
717 717 "54386793436c938cff89326944d4c2702340037d",
718 718 "51d254f0ecf5df2ce50c0b115741f4cf13985dab",
719 719 "998ed409c795fec2012b1c0ca054d99888b22090",
720 720 "5e0eb4c47f56564395f76333f319d26c79e2fb09",
721 721 "0115510b70c7229dbc5dc49036b32e7d91d23acd",
722 722 "7cb3fd1b6d8c20ba89e2264f1c8baebc8a52d36e",
723 723 "2a13f185e4525f9d4b59882791a2d397b90d5ddc",
724 724 "191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e",
725 725 "ff7ca51e58c505fec0dd2491de52c622bb7a806b",
726 726 ],
727 727 "vcs/nodes.py": [
728 728 "33fa3223355104431402a888fa77a4e9956feb3e",
729 729 "fa014c12c26d10ba682fadb78f2a11c24c8118e1",
730 730 "e686b958768ee96af8029fe19c6050b1a8dd3b2b",
731 731 "ab5721ca0a081f26bf43d9051e615af2cc99952f",
732 732 "c877b68d18e792a66b7f4c529ea02c8f80801542",
733 733 "4313566d2e417cb382948f8d9d7c765330356054",
734 734 "6c2303a793671e807d1cfc70134c9ca0767d98c2",
735 735 "54386793436c938cff89326944d4c2702340037d",
736 736 "54000345d2e78b03a99d561399e8e548de3f3203",
737 737 "1c6b3677b37ea064cb4b51714d8f7498f93f4b2b",
738 738 "2d03ca750a44440fb5ea8b751176d1f36f8e8f46",
739 739 "2a08b128c206db48c2f0b8f70df060e6db0ae4f8",
740 740 "30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b",
741 741 "ac71e9503c2ca95542839af0ce7b64011b72ea7c",
742 742 "12669288fd13adba2a9b7dd5b870cc23ffab92d2",
743 743 "5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382",
744 744 "12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5",
745 745 "5eab1222a7cd4bfcbabc218ca6d04276d4e27378",
746 746 "f50f42baeed5af6518ef4b0cb2f1423f3851a941",
747 747 "d7e390a45f6aa96f04f5e7f583ad4f867431aa25",
748 748 "f15c21f97864b4f071cddfbf2750ec2e23859414",
749 749 "e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade",
750 750 "ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b",
751 751 "84dec09632a4458f79f50ddbbd155506c460b4f9",
752 752 "0115510b70c7229dbc5dc49036b32e7d91d23acd",
753 753 "2a13f185e4525f9d4b59882791a2d397b90d5ddc",
754 754 "3bf1c5868e570e39569d094f922d33ced2fa3b2b",
755 755 "b8d04012574729d2c29886e53b1a43ef16dd00a1",
756 756 "6970b057cffe4aab0a792aa634c89f4bebf01441",
757 757 "dd80b0f6cf5052f17cc738c2951c4f2070200d7f",
758 758 "ff7ca51e58c505fec0dd2491de52c622bb7a806b",
759 759 ],
760 760 "vcs/backends/git.py": [
761 761 "4cf116ad5a457530381135e2f4c453e68a1b0105",
762 762 "9a751d84d8e9408e736329767387f41b36935153",
763 763 "cb681fb539c3faaedbcdf5ca71ca413425c18f01",
764 764 "428f81bb652bcba8d631bce926e8834ff49bdcc6",
765 765 "180ab15aebf26f98f714d8c68715e0f05fa6e1c7",
766 766 "2b8e07312a2e89e92b90426ab97f349f4bce2a3a",
767 767 "50e08c506174d8645a4bb517dd122ac946a0f3bf",
768 768 "54000345d2e78b03a99d561399e8e548de3f3203",
769 769 ],
770 770 }
771 771 for path, commit_ids in files.items():
772 772 node = self.repo.get_commit(commit_ids[0]).get_node(path)
773 773 node_ids = [commit.raw_id for commit in node.history]
774 774 assert set(commit_ids).issubset(set(node_ids)), (
775 775 "We assumed that %s is subset of commit_ids for which file %s "
776 776 "has been changed, and history of that node returned: %s" % (commit_ids, path, node_ids)
777 777 )
778 778
779 779 def test_file_annotate(self):
780 780 files = {
781 781 "vcs/backends/__init__.py": {
782 782 "c1214f7e79e02fc37156ff215cd71275450cffc3": {
783 783 "lines_no": 1,
784 784 "commits": [
785 785 "c1214f7e79e02fc37156ff215cd71275450cffc3",
786 786 ],
787 787 },
788 788 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647": {
789 789 "lines_no": 21,
790 790 "commits": [
791 791 "49d3fd156b6f7db46313fac355dca1a0b94a0017",
792 792 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
793 793 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
794 794 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
795 795 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
796 796 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
797 797 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
798 798 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
799 799 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
800 800 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
801 801 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
802 802 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
803 803 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
804 804 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
805 805 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
806 806 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
807 807 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
808 808 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
809 809 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
810 810 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
811 811 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
812 812 ],
813 813 },
814 814 "e29b67bd158580fc90fc5e9111240b90e6e86064": {
815 815 "lines_no": 32,
816 816 "commits": [
817 817 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
818 818 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
819 819 "5eab1222a7cd4bfcbabc218ca6d04276d4e27378",
820 820 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
821 821 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
822 822 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
823 823 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
824 824 "54000345d2e78b03a99d561399e8e548de3f3203",
825 825 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
826 826 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
827 827 "78c3f0c23b7ee935ec276acb8b8212444c33c396",
828 828 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
829 829 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
830 830 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
831 831 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
832 832 "2a13f185e4525f9d4b59882791a2d397b90d5ddc",
833 833 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
834 834 "78c3f0c23b7ee935ec276acb8b8212444c33c396",
835 835 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
836 836 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
837 837 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
838 838 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
839 839 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
840 840 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
841 841 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
842 842 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
843 843 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
844 844 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
845 845 "992f38217b979d0b0987d0bae3cc26dac85d9b19",
846 846 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
847 847 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
848 848 "16fba1ae9334d79b66d7afed2c2dfbfa2ae53647",
849 849 ],
850 850 },
851 851 },
852 852 }
853 853
854 854 for fname, commit_dict in files.items():
855 855 for commit_id, __ in commit_dict.items():
856 856 commit = self.repo.get_commit(commit_id)
857 857
858 858 l1_1 = [x[1] for x in commit.get_file_annotate(fname)]
859 859 l1_2 = [x[2]().raw_id for x in commit.get_file_annotate(fname)]
860 860 assert l1_1 == l1_2
861 861 l1 = l1_1
862 862 l2 = files[fname][commit_id]["commits"]
863 863 assert l1 == l2, (
864 864 "The lists of commit_ids for %s@commit_id %s"
865 865 "from annotation list should match each other, "
866 866 "got \n%s \nvs \n%s " % (fname, commit_id, l1, l2)
867 867 )
868 868
869 869 def test_files_state(self):
870 870 """
871 871 Tests state of FileNodes.
872 872 """
873 873 node = self.repo.get_commit("e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0").get_node("vcs/utils/diffs.py")
874 874 assert node.state, NodeState.ADDED
875 875 assert node.added
876 876 assert not node.changed
877 877 assert not node.not_changed
878 878 assert not node.removed
879 879
880 880 node = self.repo.get_commit("33fa3223355104431402a888fa77a4e9956feb3e").get_node(".hgignore")
881 881 assert node.state, NodeState.CHANGED
882 882 assert not node.added
883 883 assert node.changed
884 884 assert not node.not_changed
885 885 assert not node.removed
886 886
887 887 node = self.repo.get_commit("e29b67bd158580fc90fc5e9111240b90e6e86064").get_node("setup.py")
888 888 assert node.state, NodeState.NOT_CHANGED
889 889 assert not node.added
890 890 assert not node.changed
891 891 assert node.not_changed
892 892 assert not node.removed
893 893
894 894 # If node has REMOVED state then trying to fetch it would raise
895 895 # CommitError exception
896 896 commit = self.repo.get_commit("fa6600f6848800641328adbf7811fd2372c02ab2")
897 897 path = "vcs/backends/BaseRepository.py"
898 898 with pytest.raises(NodeDoesNotExistError):
899 899 commit.get_node(path)
900 900 # but it would be one of ``removed`` (commit's attribute)
901 901 assert path in [rf.path for rf in commit.removed]
902 902
903 903 commit = self.repo.get_commit("54386793436c938cff89326944d4c2702340037d")
904 904 changed = ["setup.py", "tests/test_nodes.py", "vcs/backends/hg.py", "vcs/nodes.py"]
905 905 assert set(changed) == set([f.path for f in commit.changed])
906 906
907 907 def test_unicode_branch_refs(self):
908 908 unicode_branches = {
909 909 "refs/heads/unicode": "6c0ce52b229aa978889e91b38777f800e85f330b",
910 910 "refs/heads/uniΓ§ΓΆβˆ‚e": "ΓΌrl",
911 911 }
912 912 with mock.patch(("rhodecode.lib.vcs.backends.git.repository" ".GitRepository._refs"), unicode_branches):
913 913 branches = self.repo.branches
914 914
915 915 assert "unicode" in branches
916 916 assert "uniΓ§ΓΆβˆ‚e" in branches
917 917
918 918 def test_unicode_tag_refs(self):
919 919 unicode_tags = {
920 920 "refs/tags/unicode": "6c0ce52b229aa978889e91b38777f800e85f330b",
921 921 "refs/tags/uniΓ§ΓΆβˆ‚e": "6c0ce52b229aa978889e91b38777f800e85f330b",
922 922 }
923 923 with mock.patch(("rhodecode.lib.vcs.backends.git.repository" ".GitRepository._refs"), unicode_tags):
924 924 tags = self.repo.tags
925 925
926 926 assert "unicode" in tags
927 927 assert "uniΓ§ΓΆβˆ‚e" in tags
928 928
929 929 def test_commit_message_is_unicode(self):
930 930 for commit in self.repo:
931 931 assert type(commit.message) == str
932 932
933 933 def test_commit_author_is_unicode(self):
934 934 for commit in self.repo:
935 935 assert type(commit.author) == str
936 936
937 937 def test_repo_files_content_types(self):
938 938 commit = self.repo.get_commit()
939 939 for node in commit.get_node("/"):
940 940 if node.is_file():
941 941 assert type(node.content) == bytes
942 942 assert type(node.str_content) == str
943 943
944 944 def test_wrong_path(self):
945 945 # There is 'setup.py' in the root dir but not there:
946 946 path = "foo/bar/setup.py"
947 947 tip = self.repo.get_commit()
948 948 with pytest.raises(VCSError):
949 949 tip.get_node(path)
950 950
951 951 @pytest.mark.parametrize(
952 952 "author_email, commit_id",
953 953 [
954 954 ("marcin@python-blog.com", "c1214f7e79e02fc37156ff215cd71275450cffc3"),
955 955 ("lukasz.balcerzak@python-center.pl", "ff7ca51e58c505fec0dd2491de52c622bb7a806b"),
956 956 ("none@none", "8430a588b43b5d6da365400117c89400326e7992"),
957 957 ],
958 958 )
959 959 def test_author_email(self, author_email, commit_id):
960 960 commit = self.repo.get_commit(commit_id)
961 961 assert author_email == commit.author_email
962 962
963 963 @pytest.mark.parametrize(
964 964 "author, commit_id",
965 965 [
966 966 ("Marcin Kuzminski", "c1214f7e79e02fc37156ff215cd71275450cffc3"),
967 967 ("Lukasz Balcerzak", "ff7ca51e58c505fec0dd2491de52c622bb7a806b"),
968 968 ("marcink", "8430a588b43b5d6da365400117c89400326e7992"),
969 969 ],
970 970 )
971 971 def test_author_username(self, author, commit_id):
972 972 commit = self.repo.get_commit(commit_id)
973 973 assert author == commit.author_name
974 974
975 975
976 976 class TestLargeFileRepo(object):
977 977 def test_large_file(self, backend_git):
978 978 conf = make_db_config()
979 979 git_largefiles_store = conf.get("vcs_git_lfs", "store_location")
980 980
981 981 repo = backend_git.create_test_repo("largefiles", conf)
982 982
983 983 tip = repo.scm_instance().get_commit()
984 984 node = tip.get_node("1MB.zip")
985 985
986 986
987 987 # extract stored LF node into the origin cache
988 988 repo_lfs_store: str = os.path.join(repo.repo_path, repo.repo_name, "lfs_store")
989 989
990 990 oid: str = "7b331c02e313c7599d5a90212e17e6d3cb729bd2e1c9b873c302a63c95a2f9bf"
991 991 # where the OID actually is INSIDE the repo...
992 992 oid_path = os.path.join(repo_lfs_store, oid)
993 993
994 994 # Note: oid path depends on LFSOidStore.store_suffix. Once it will be changed update below line accordingly
995 995 oid_destination = os.path.join(git_largefiles_store, f"objects/{oid[:2]}/{oid[2:4]}/{oid}")
996 996
997 spec_path = os.path.dirname(oid_destination)
998 os.makedirs(spec_path, exist_ok=True)
997 999 shutil.copy(oid_path, oid_destination)
998 1000
999 1001 lf_node = node.get_largefile_node()
1000 1002
1001 1003 assert lf_node.is_largefile() is True
1002 1004 assert lf_node.size == 1024000
1003 1005 assert lf_node.name == "1MB.zip"
1004 1006
1005 1007
1006 1008 @pytest.mark.usefixtures("vcs_repository_support")
1007 1009 class TestGitSpecificWithRepo(BackendTestMixin):
1008 1010 @classmethod
1009 1011 def _get_commits(cls):
1010 1012 return [
1011 1013 {
1012 1014 "message": "Initial",
1013 1015 "author": "Joe Doe <joe.doe@example.com>",
1014 1016 "date": datetime.datetime(2010, 1, 1, 20),
1015 1017 "added": [
1016 1018 FileNode(b"foobar/static/js/admin/base.js", content=b"base"),
1017 1019 FileNode(b"foobar/static/admin", content=b"admin", mode=0o120000), # this is a link
1018 1020 FileNode(b"foo", content=b"foo"),
1019 1021 ],
1020 1022 },
1021 1023 {
1022 1024 "message": "Second",
1023 1025 "author": "Joe Doe <joe.doe@example.com>",
1024 1026 "date": datetime.datetime(2010, 1, 1, 22),
1025 1027 "added": [
1026 1028 FileNode(b"foo2", content=b"foo2"),
1027 1029 ],
1028 1030 },
1029 1031 ]
1030 1032
1031 1033 def test_paths_slow_traversing(self):
1032 1034 commit = self.repo.get_commit()
1033 1035 assert (
1034 1036 commit.get_node("foobar").get_node("static").get_node("js").get_node("admin").get_node("base.js").content
1035 1037 == b"base"
1036 1038 )
1037 1039
1038 1040 def test_paths_fast_traversing(self):
1039 1041 commit = self.repo.get_commit()
1040 1042 assert commit.get_node("foobar/static/js/admin/base.js").content == b"base"
1041 1043
1042 1044 def test_get_diff_runs_git_command_with_hashes(self):
1043 1045 comm1 = self.repo[0]
1044 1046 comm2 = self.repo[1]
1045 1047
1046 1048 with mock.patch.object(self.repo, "_remote", return_value=mock.Mock()) as remote_mock:
1047 1049 remote_mock.diff = mock.MagicMock(side_effect=callable_get_diff)
1048 1050 self.repo.get_diff(comm1, comm2)
1049 1051
1050 1052 remote_mock.diff.assert_called_once_with(
1051 1053 comm1.raw_id, comm2.raw_id, file_filter=None, opt_ignorews=False, context=3
1052 1054 )
1053 1055
1054 1056 def test_get_diff_runs_git_command_with_str_hashes(self):
1055 1057 comm2 = self.repo[1]
1056 1058
1057 1059 with mock.patch.object(self.repo, "_remote", return_value=mock.Mock()) as remote_mock:
1058 1060 remote_mock.diff = mock.MagicMock(side_effect=callable_get_diff)
1059 1061 self.repo.get_diff(self.repo.EMPTY_COMMIT, comm2)
1060 1062
1061 1063 remote_mock.diff.assert_called_once_with(
1062 1064 self.repo.EMPTY_COMMIT.raw_id, comm2.raw_id, file_filter=None, opt_ignorews=False, context=3
1063 1065 )
1064 1066
1065 1067 def test_get_diff_runs_git_command_with_path_if_its_given(self):
1066 1068 comm1 = self.repo[0]
1067 1069 comm2 = self.repo[1]
1068 1070
1069 1071 with mock.patch.object(self.repo, "_remote", return_value=mock.Mock()) as remote_mock:
1070 1072 remote_mock.diff = mock.MagicMock(side_effect=callable_get_diff)
1071 1073 self.repo.get_diff(comm1, comm2, "foo")
1072 1074
1073 1075 remote_mock.diff.assert_called_once_with(
1074 1076 self.repo._lookup_commit(0), comm2.raw_id, file_filter="foo", opt_ignorews=False, context=3
1075 1077 )
1076 1078
1077 1079
1078 1080 @pytest.mark.usefixtures("vcs_repository_support")
1079 1081 class TestGitRegression(BackendTestMixin):
1080 1082 @classmethod
1081 1083 def _get_commits(cls):
1082 1084 return [
1083 1085 {
1084 1086 "message": "Initial",
1085 1087 "author": "Joe Doe <joe.doe@example.com>",
1086 1088 "date": datetime.datetime(2010, 1, 1, 20),
1087 1089 "added": [
1088 1090 FileNode(b"bot/__init__.py", content=b"base"),
1089 1091 FileNode(b"bot/templates/404.html", content=b"base"),
1090 1092 FileNode(b"bot/templates/500.html", content=b"base"),
1091 1093 ],
1092 1094 },
1093 1095 {
1094 1096 "message": "Second",
1095 1097 "author": "Joe Doe <joe.doe@example.com>",
1096 1098 "date": datetime.datetime(2010, 1, 1, 22),
1097 1099 "added": [
1098 1100 FileNode(b"bot/build/migrations/1.py", content=b"foo2"),
1099 1101 FileNode(b"bot/build/migrations/2.py", content=b"foo2"),
1100 1102 FileNode(b"bot/build/static/templates/f.html", content=b"foo2"),
1101 1103 FileNode(b"bot/build/static/templates/f1.html", content=b"foo2"),
1102 1104 FileNode(b"bot/build/templates/err.html", content=b"foo2"),
1103 1105 FileNode(b"bot/build/templates/err2.html", content=b"foo2"),
1104 1106 ],
1105 1107 },
1106 1108 ]
1107 1109
1108 1110 @pytest.mark.parametrize(
1109 1111 "path, expected_paths",
1110 1112 [
1111 1113 ("bot", ["bot/build", "bot/templates", "bot/__init__.py"]),
1112 1114 ("bot/build", ["bot/build/migrations", "bot/build/static", "bot/build/templates"]),
1113 1115 ("bot/build/static", ["bot/build/static/templates"]),
1114 1116 ("bot/build/static/templates", ["bot/build/static/templates/f.html", "bot/build/static/templates/f1.html"]),
1115 1117 ("bot/build/templates", ["bot/build/templates/err.html", "bot/build/templates/err2.html"]),
1116 1118 ("bot/templates/", ["bot/templates/404.html", "bot/templates/500.html"]),
1117 1119 ],
1118 1120 )
1119 1121 def test_similar_paths(self, path, expected_paths):
1120 1122 commit = self.repo.get_commit()
1121 1123 paths = [n.path for n in commit.get_nodes(path)]
1122 1124 assert paths == expected_paths
1123 1125
1124 1126
1125 1127 class TestDiscoverGitVersion(object):
1126 1128 def test_returns_git_version(self, baseapp):
1127 1129 version = discover_git_version()
1128 1130 assert version
1129 1131
1130 1132 def test_returns_empty_string_without_vcsserver(self):
1131 1133 mock_connection = mock.Mock()
1132 1134 mock_connection.discover_git_version = mock.Mock(side_effect=Exception)
1133 1135 with mock.patch("rhodecode.lib.vcs.connection.Git", mock_connection):
1134 1136 version = discover_git_version()
1135 1137 assert version == ""
1136 1138
1137 1139
1138 1140 class TestGetSubmoduleUrl(object):
1139 1141 def test_submodules_file_found(self):
1140 1142 commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1)
1141 1143 node = mock.Mock()
1142 1144
1143 1145 with mock.patch.object(commit, "get_node", return_value=node) as get_node_mock:
1144 1146 node.str_content = (
1145 1147 '[submodule "subrepo1"]\n' "\tpath = subrepo1\n" "\turl = https://code.rhodecode.com/dulwich\n"
1146 1148 )
1147 1149 result = commit._get_submodule_url("subrepo1")
1148 1150 get_node_mock.assert_called_once_with(".gitmodules")
1149 1151 assert result == "https://code.rhodecode.com/dulwich"
1150 1152
1151 1153 def test_complex_submodule_path(self):
1152 1154 commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1)
1153 1155 node = mock.Mock()
1154 1156
1155 1157 with mock.patch.object(commit, "get_node", return_value=node) as get_node_mock:
1156 1158 node.str_content = (
1157 1159 '[submodule "complex/subrepo/path"]\n'
1158 1160 "\tpath = complex/subrepo/path\n"
1159 1161 "\turl = https://code.rhodecode.com/dulwich\n"
1160 1162 )
1161 1163 result = commit._get_submodule_url("complex/subrepo/path")
1162 1164 get_node_mock.assert_called_once_with(".gitmodules")
1163 1165 assert result == "https://code.rhodecode.com/dulwich"
1164 1166
1165 1167 def test_submodules_file_not_found(self):
1166 1168 commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1)
1167 1169 with mock.patch.object(commit, "get_node", side_effect=NodeDoesNotExistError):
1168 1170 result = commit._get_submodule_url("complex/subrepo/path")
1169 1171 assert result is None
1170 1172
1171 1173 def test_path_not_found(self):
1172 1174 commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1)
1173 1175 node = mock.Mock()
1174 1176
1175 1177 with mock.patch.object(commit, "get_node", return_value=node) as get_node_mock:
1176 1178 node.str_content = (
1177 1179 '[submodule "subrepo1"]\n' "\tpath = subrepo1\n" "\turl = https://code.rhodecode.com/dulwich\n"
1178 1180 )
1179 1181 result = commit._get_submodule_url("subrepo2")
1180 1182 get_node_mock.assert_called_once_with(".gitmodules")
1181 1183 assert result is None
1182 1184
1183 1185 def test_returns_cached_values(self):
1184 1186 commit = GitCommit(repository=mock.Mock(), raw_id="abcdef12", idx=1)
1185 1187 node = mock.Mock()
1186 1188
1187 1189 with mock.patch.object(commit, "get_node", return_value=node) as get_node_mock:
1188 1190 node.str_content = (
1189 1191 '[submodule "subrepo1"]\n' "\tpath = subrepo1\n" "\turl = https://code.rhodecode.com/dulwich\n"
1190 1192 )
1191 1193 for _ in range(3):
1192 1194 commit._get_submodule_url("subrepo1")
1193 1195 get_node_mock.assert_called_once_with(".gitmodules")
1194 1196
1195 1197 def test_get_node_returns_a_link(self):
1196 1198 repository = mock.Mock()
1197 1199 repository.alias = "git"
1198 1200 commit = GitCommit(repository=repository, raw_id="abcdef12", idx=1)
1199 1201 submodule_url = "https://code.rhodecode.com/dulwich"
1200 1202 get_id_patch = mock.patch.object(commit, "_get_tree_id_for_path", return_value=(1, "link"))
1201 1203 get_submodule_patch = mock.patch.object(commit, "_get_submodule_url", return_value=submodule_url)
1202 1204
1203 1205 with get_id_patch, get_submodule_patch as submodule_mock:
1204 1206 node = commit.get_node("/abcde")
1205 1207
1206 1208 submodule_mock.assert_called_once_with("/abcde")
1207 1209 assert type(node) == SubModuleNode
1208 1210 assert node.url == submodule_url
1209 1211
1210 1212 def test_get_nodes_returns_links(self):
1211 1213 repository = mock.MagicMock()
1212 1214 repository.alias = "git"
1213 1215 repository._remote.tree_items.return_value = [("subrepo", "stat", 1, "link")]
1214 1216 commit = GitCommit(repository=repository, raw_id="abcdef12", idx=1)
1215 1217 submodule_url = "https://code.rhodecode.com/dulwich"
1216 1218 get_id_patch = mock.patch.object(commit, "_get_tree_id_for_path", return_value=(1, "tree"))
1217 1219 get_submodule_patch = mock.patch.object(commit, "_get_submodule_url", return_value=submodule_url)
1218 1220
1219 1221 with get_id_patch, get_submodule_patch as submodule_mock:
1220 1222 nodes = commit.get_nodes("/abcde")
1221 1223
1222 1224 submodule_mock.assert_called_once_with("/abcde/subrepo")
1223 1225 assert len(nodes) == 1
1224 1226 assert type(nodes[0]) == SubModuleNode
1225 1227 assert nodes[0].url == submodule_url
1226 1228
1227 1229
1228 1230 class TestGetShadowInstance(object):
1229 1231
1230 1232 @pytest.fixture()
1231 1233 def repo(self, vcsbackend_git):
1232 1234 _git_repo = vcsbackend_git.repo
1233 1235
1234 1236 mock.patch.object(_git_repo, "config", mock.Mock())
1235 1237 connection_mock = mock.Mock(unsafe=True, name="connection.Hg")
1236 1238
1237 1239 mock.patch("rhodecode.lib.vcs.connection.Git", connection_mock)
1238 1240 return _git_repo
1239 1241
1240 1242 def test_getting_shadow_instance_copies_config(self, repo):
1241 1243 shadow = repo.get_shadow_instance(repo.path)
1242 1244 assert shadow.config.serialize() == repo.config.serialize()
General Comments 0
You need to be logged in to leave comments. Login now