|
19 | 19 |
|
20 | 20 | import pytest |
21 | 21 |
|
| 22 | +from mergify_cli.stack import changes |
22 | 23 | from mergify_cli.stack import push |
23 | 24 | from mergify_cli.tests import utils as test_utils |
24 | 25 |
|
@@ -491,6 +492,142 @@ async def test_stack_update_keep_title_and_body( |
491 | 492 | } |
492 | 493 |
|
493 | 494 |
|
| 495 | +@pytest.mark.respx(base_url="https://api.github.com/") |
| 496 | +async def test_stack_dry_run_does_not_rebase( |
| 497 | + git_mock: test_utils.GitMock, |
| 498 | + respx_mock: respx.MockRouter, |
| 499 | +) -> None: |
| 500 | + git_mock.commit( |
| 501 | + test_utils.Commit( |
| 502 | + sha="commit1_sha", |
| 503 | + title="Title commit 1", |
| 504 | + message="Message commit 1", |
| 505 | + change_id="I29617d37762fd69809c255d7e7073cb11f8fbf50", |
| 506 | + ), |
| 507 | + ) |
| 508 | + git_mock.finalize() |
| 509 | + git_mock.mock("rev-list", "--count", "HEAD..origin/main", output="0") |
| 510 | + |
| 511 | + respx_mock.get("/user").respond(200, json={"login": "author"}) |
| 512 | + respx_mock.get("/search/issues").respond(200, json={"items": []}) |
| 513 | + |
| 514 | + with pytest.raises(SystemExit, match="0"): |
| 515 | + await push.stack_push( |
| 516 | + github_server="https://api.github.com/", |
| 517 | + token="", |
| 518 | + skip_rebase=False, |
| 519 | + next_only=False, |
| 520 | + branch_prefix="", |
| 521 | + dry_run=True, |
| 522 | + trunk=("origin", "main"), |
| 523 | + ) |
| 524 | + |
| 525 | + # Dry-run never rebases. |
| 526 | + assert not git_mock.has_been_called_with("pull", "--rebase", "origin", "main") |
| 527 | + |
| 528 | + # No branches are pushed. |
| 529 | + assert not git_mock.has_been_called_with( |
| 530 | + "push", |
| 531 | + "-f", |
| 532 | + "origin", |
| 533 | + "commit1_sha:refs/heads/current-branch/I29617d37762fd69809c255d7e7073cb11f8fbf50", |
| 534 | + ) |
| 535 | + |
| 536 | + |
| 537 | +@pytest.mark.respx(base_url="https://api.github.com/") |
| 538 | +async def test_stack_dry_run_behind_flips_up_to_date_to_update( |
| 539 | + git_mock: test_utils.GitMock, |
| 540 | + respx_mock: respx.MockRouter, |
| 541 | +) -> None: |
| 542 | + # PR exists with matching SHA — normally "skip-up-to-date". |
| 543 | + # But branch is behind base, so rebase would change SHAs → "update". |
| 544 | + git_mock.commit( |
| 545 | + test_utils.Commit( |
| 546 | + sha="commit1_sha", |
| 547 | + title="Title commit 1", |
| 548 | + message="Message commit 1", |
| 549 | + change_id="I29617d37762fd69809c255d7e7073cb11f8fbf50", |
| 550 | + ), |
| 551 | + ) |
| 552 | + git_mock.finalize() |
| 553 | + git_mock.mock("rev-list", "--count", "HEAD..origin/main", output="3") |
| 554 | + |
| 555 | + respx_mock.get("/user").respond(200, json={"login": "author"}) |
| 556 | + respx_mock.get("/search/issues").respond( |
| 557 | + 200, |
| 558 | + json={ |
| 559 | + "items": [ |
| 560 | + { |
| 561 | + "pull_request": { |
| 562 | + "url": "https://api.github.com/repos/user/repo/pulls/42", |
| 563 | + }, |
| 564 | + }, |
| 565 | + ], |
| 566 | + }, |
| 567 | + ) |
| 568 | + respx_mock.get("/repos/user/repo/pulls/42").respond( |
| 569 | + 200, |
| 570 | + json={ |
| 571 | + "html_url": "", |
| 572 | + "head": { |
| 573 | + "sha": "commit1_sha", |
| 574 | + "ref": "current-branch/I29617d37762fd69809c255d7e7073cb11f8fbf50", |
| 575 | + }, |
| 576 | + "state": "open", |
| 577 | + "merged_at": None, |
| 578 | + "draft": False, |
| 579 | + }, |
| 580 | + ) |
| 581 | + |
| 582 | + with pytest.raises(SystemExit, match="0"): |
| 583 | + await push.stack_push( |
| 584 | + github_server="https://api.github.com/", |
| 585 | + token="", |
| 586 | + skip_rebase=False, |
| 587 | + next_only=False, |
| 588 | + branch_prefix="", |
| 589 | + dry_run=True, |
| 590 | + trunk=("origin", "main"), |
| 591 | + ) |
| 592 | + |
| 593 | + # Dry-run never rebases. |
| 594 | + assert not git_mock.has_been_called_with("pull", "--rebase", "origin", "main") |
| 595 | + |
| 596 | + |
| 597 | +@pytest.mark.respx(base_url="https://api.github.com/") |
| 598 | +async def test_stack_dry_run_skip_rebase( |
| 599 | + git_mock: test_utils.GitMock, |
| 600 | + respx_mock: respx.MockRouter, |
| 601 | +) -> None: |
| 602 | + git_mock.commit( |
| 603 | + test_utils.Commit( |
| 604 | + sha="commit1_sha", |
| 605 | + title="Title commit 1", |
| 606 | + message="Message commit 1", |
| 607 | + change_id="I29617d37762fd69809c255d7e7073cb11f8fbf50", |
| 608 | + ), |
| 609 | + ) |
| 610 | + git_mock.finalize() |
| 611 | + |
| 612 | + respx_mock.get("/user").respond(200, json={"login": "author"}) |
| 613 | + respx_mock.get("/search/issues").respond(200, json={"items": []}) |
| 614 | + |
| 615 | + with pytest.raises(SystemExit, match="0"): |
| 616 | + await push.stack_push( |
| 617 | + github_server="https://api.github.com/", |
| 618 | + token="", |
| 619 | + skip_rebase=True, |
| 620 | + next_only=False, |
| 621 | + branch_prefix="", |
| 622 | + dry_run=True, |
| 623 | + trunk=("origin", "main"), |
| 624 | + ) |
| 625 | + |
| 626 | + # Rebase check is skipped when --skip-rebase is passed. |
| 627 | + assert not git_mock.has_been_called_with("rev-list", "--count", "HEAD..origin/main") |
| 628 | + assert not git_mock.has_been_called_with("pull", "--rebase", "origin", "main") |
| 629 | + |
| 630 | + |
494 | 631 | @pytest.mark.respx(base_url="https://api.github.com/") |
495 | 632 | async def test_stack_on_destination_branch_raises_an_error( |
496 | 633 | git_mock: test_utils.GitMock, |
@@ -535,3 +672,26 @@ async def test_stack_without_common_commit_raises_an_error( |
535 | 672 | dry_run=False, |
536 | 673 | trunk=("origin", "main"), |
537 | 674 | ) |
| 675 | + |
| 676 | + |
| 677 | +def test_replace_local_action_flips_up_to_date() -> None: |
| 678 | + def _make_local_change(action: changes.ActionT) -> changes.LocalChange: |
| 679 | + return changes.LocalChange( |
| 680 | + id=changes.ChangeId(""), |
| 681 | + pull=None, |
| 682 | + commit_sha="", |
| 683 | + title="", |
| 684 | + message="", |
| 685 | + base_branch="", |
| 686 | + dest_branch="", |
| 687 | + action=action, |
| 688 | + ) |
| 689 | + |
| 690 | + planned = changes.Changes(stack_prefix="") |
| 691 | + planned.locals.append(_make_local_change("skip-up-to-date")) |
| 692 | + planned.locals.append(_make_local_change("create")) |
| 693 | + |
| 694 | + planned.replace_local_action(old="skip-up-to-date", new="update") |
| 695 | + |
| 696 | + assert planned.locals[0].action == "update" |
| 697 | + assert planned.locals[1].action == "create" |
0 commit comments