async def test_new_upstream_commits_to_pull_down(mock_input_a):
    """
    Test case for "origin/review-branch has new commits that I must pull down"
    (no new local commits that origin doesn't have yet)
    """
    with temp_repo() as upstream:
        upstream_default_branch = upstream.active_branch
        upstream_feature_branch = upstream.create_head("feature-123")
        # upstream_feature_branch.checkout()
        with temp_repo_clone(upstream, ['feature-123']) as downstream:
            assert ", ".join(sorted_repo_branch_names(
                downstream)) == f"feature-123, {upstream_default_branch.name}"
            upstream_feature_branch.checkout()
            create_git_history(upstream, [
                (('file_0.txt', 'content for file 0'), 'second commit'),
                (('file_1.txt', 'content for file 1'), 'third commit'),
                (('file_2.txt', 'content for file 2'), 'fourth commit'),
            ])
            upstream_default_branch.checkout()
            assert upstream.active_branch.name in ['main', 'master']
            downstream.heads['feature-123'].checkout()
            opts = ValidateOptions(
                ValidateCLIOptions(verbose=False,
                                   cwd=downstream.working_dir,
                                   auto_fetch=True))
            assert opts.is_verbose() == False
            with fake_cliux(log_level=INFO) as (cli, get_lines):
                assert cli.log_level == INFO
                await do_validate(cli=cli, opts=opts)
                assert strip_ansi(
                    "".join(get_lines())
                ) == """determined that local branch feature-123 tracks upstream branch feature-123 on remote origin
def test_create_temp_clone():
    with temp_repo() as upstream:
        commit_all_modified_tracked_files(upstream, "initial commit")
        assert upstream.is_dirty(
        ) == False, 'before create_git_history, repo is "clean"'
        create_git_history(upstream, [
            (('hello1.txt', 'hello world!'), 'second commit'),
            (('hello2.txt', 'hello world!'), 'third commit'),
            (('hello3.txt', 'hello world!'), 'fourth commit'),
            (('hello4.txt', 'hello world!'), 'fifth commit'),
        ])
        assert upstream.is_dirty(
        ) == False, 'after create_git_history, repo is left "clean"'
        upstream_feature_branch = upstream.create_head('feature-456')
        upstream_feature_branch.checkout()
        assert upstream.active_branch.name == 'feature-456'
        assert upstream.is_dirty(
        ) == False, f"{upstream.index.diff(other=upstream_feature_branch.commit)}"
        create_git_history(
            upstream, [(('hello5.txt', 'hello world!'), 'feature commit')])
        assert upstream.is_dirty() == False
        upstream.heads['master'].checkout()
        with temp_repo_clone(upstream, ['feature-456']) as downstream:
            assert downstream.bare == False
            assert list(sorted(map(lambda h: h.name,
                                   downstream.heads))) == sorted(
                                       ['master', 'feature-456'])
async def test_review_branch_not_yet_pushed(mock_input):
    """
    Test case for a new review branch that does not yet track any remote branch
    (presumably, it hasn't yet been pushed)
    """
    with temp_repo() as upstream:
        merge_base = upstream.active_branch.commit
        assert merge_base is not None
        with temp_repo_clone(upstream) as downstream:
            new_branch = downstream.create_head("feature-123")
            new_branch.checkout()
            assert downstream.active_branch.name == "feature-123"
            create_git_history(
                downstream,
                [(('my-file.txt', 'sample content'), 'second commit')])
            assert downstream.heads['feature-123'] is not None
            downstream_default_branch = git_default_branch(downstream)
            assert downstream_default_branch == upstream.active_branch.name
            with fake_cliux(log_level=INFO) as (cli, get_lines):
                opts = ValidateOptions(
                    ValidateCLIOptions(verbose=True,
                                       cwd=downstream.working_dir))
                await do_validate(cli=cli, opts=opts)
                assert strip_ansi(
                    "".join(get_lines())
                ) == """git_guardrails has completed without taking any action.
async def test_push_from_default_branch(mock_input):
    """
    Test case for "push while on the default branch instead of a review branch"
    """
    with temp_repo() as upstream:
        with temp_repo_clone(upstream) as downstream:
            with fake_cliux(log_level=INFO) as (cli, get_lines):
                opts = ValidateOptions(
                    ValidateCLIOptions(verbose=False,
                                       cwd=downstream.working_dir))
                await do_validate(cli=cli, opts=opts)
                assert strip_ansi(
                    "".join(get_lines())
                ) == """git_guardrails has completed without taking any action.
async def test_no_connect_to_remote(mock_input):
    """
    Test case for "can't connect to git remote"
    """
    with pytest.raises(GitRemoteConnectivityException):
        with temp_repo() as upstream:
            with temp_repo_clone(upstream) as downstream:
                downstream_origin: Remote = downstream.remotes['origin']
                assert downstream_origin is not None
                downstream_origin.set_url(new_url='https://example.com')
                with fake_cliux(log_level=INFO) as (cli, get_lines):
                    opts = ValidateOptions(
                        ValidateCLIOptions(verbose=False,
                                           cwd=downstream.working_dir))
                    await do_validate(cli=cli, opts=opts)
                    assert strip_ansi("".join(get_lines())) == ""
async def test_review_branch_commit_count_hard_fail():
    try:
        with temp_repo() as upstream:
            upstream_default_branch = upstream.active_branch
            upstream.create_head('mnorth-review-111')
            create_git_history(upstream, [
                (("demo_0.txt", "content for demo_0"), "demo 0 commit"),
                (("demo_1.txt", "content for demo_1"), "demo 1 commit"),
                (("demo_2.txt", "content for demo_2"), "demo 2 commit"),
                (("demo_3.txt", "content for demo_3"), "demo 3 commit"),
            ])
            upstream_default_branch.checkout()
            with temp_repo_clone(upstream,
                                 ['mnorth-review-111']) as downstream:
                # downstream_default_branch = downstream.active_branch
                downstream_review_branch = downstream.heads[
                    'mnorth-review-111']
                assert downstream_review_branch is not None
                downstream_review_branch.checkout()
                create_git_history(downstream, [
                    (("demo_4.txt", "content for demo_4"), "demo 4 commit"),
                    (("demo_5.txt", "content for demo_5"), "demo 5 commit"),
                    (("demo_6.txt", "content for demo_6"), "demo 6 commit"),
                    (("demo_7.txt", "content for demo_7"), "demo 7 commit"),
                    (("demo_8.txt", "content for demo_8"), "demo 8 commit"),
                    (("demo_9.txt", "content for demo_9"), "demo 9 commit"),
                    (("demo_10.txt", "content for demo_10"), "demo 10 commit"),
                    (("demo_11.txt", "content for demo_11"), "demo 11 commit"),
                    (("demo_12.txt", "content for demo_12"), "demo 12 commit"),
                    (("demo_13.txt", "content for demo_13"), "demo 13 commit"),
                    (("demo_14.txt", "content for demo_14"), "demo 14 commit"),
                    (("demo_15.txt", "content for demo_15"), "demo 15 commit"),
                ])
                with fake_cliux(log_level=INFO) as (cli, get_lines):
                    opts = ValidateOptions(
                        ValidateCLIOptions(verbose=True,
                                           commit_count_soft_fail_threshold=5,
                                           commit_count_hard_fail_threshold=10,
                                           cwd=downstream.working_dir))
                    await do_validate(cli=cli, opts=opts)
                    assert False, 'Error should have already been thrown by this point'
    except LikelyUserErrorException as ex:
        assert ex is not None
        assert strip_ansi(
            str(ex)) == """DANGER: VERY LARGE NUMBER OF REVIEW BRANCH COMMITS
def setup_need_to_fetch_scenario() -> Iterator[Tuple[Repo, Repo]]:
    with temp_repo() as upstream:
        # Create some commit activity on origin/master
        commit_all_modified_tracked_files(upstream, "initial commit")
        create_git_history(upstream, [
            (('old_file1.txt', 'hello from an existing commit!'),
             'second commit'),
            (('old_file2.txt', 'hello from an existing commit!'),
             'third commit'),
            (('old_file3.txt', 'hello from an existing commit!'),
             'fourth commit'),
            (('old_file4.txt', 'hello from an existing commit!'),
             'fifth commit'),
        ])
        # Create a review branch and check it out
        upstream_default_branch = upstream.active_branch
        upstream_review999 = upstream.create_head('review-999')
        upstream_review999.checkout()
        # Create some commit history on origin/review-999
        create_git_history(upstream, [
            (('hello1.txt', 'hello world!'), 'second commit'),
            (('hello2.txt', 'hello world!'), 'third commit'),
            (('hello3.txt', 'hello world!'), 'fourth commit'),
            (('hello4.txt', 'hello world!'), 'fifth commit'),
        ])
        # Switch back to origin/master
        upstream_default_branch.checkout()
        # Create a "downstream" clone of the repo. This would be the user's working copy
        with temp_repo_clone(upstream, ['review-999']) as downstream:
            # Make sure the downstream clone is initially on the default branch
            assert downstream.active_branch.name in ['main', 'master']
            # Make sure the downstream clone has a local copy of the review branch
            downstream_review999 = downstream.heads['review-999']
            assert downstream_review999 is not None
            # Create some new commits on origin/review-999
            upstream_review999.checkout()
            create_git_history(upstream, [
                (('hello5.txt', 'hello world! 21'), 'purple commit'),
                (('hello6.txt', 'hello world! 22'), 'brown commit'),
                (('hello7.txt', 'hello world! 23'), 'yellow commit'),
                (('hello8.txt', 'hello world! 24'), 'orange commit'),
            ])
            upstream_default_branch.checkout()
            yield (upstream, downstream)
async def test_review_branch_commit_count_soft_fail():
    with temp_repo() as upstream:
        upstream_default_branch = upstream.active_branch
        upstream.create_head('mnorth-review-111')
        create_git_history(upstream, [
            (("demo_0.txt", "content for demo_0"), "demo 0 commit"),
            (("demo_1.txt", "content for demo_1"), "demo 1 commit"),
            (("demo_2.txt", "content for demo_2"), "demo 2 commit"),
            (("demo_3.txt", "content for demo_3"), "demo 3 commit"),
        ])
        upstream_default_branch.checkout()
        with temp_repo_clone(upstream, ['mnorth-review-111']) as downstream:
            # downstream_default_branch = downstream.active_branch
            downstream_review_branch = downstream.heads['mnorth-review-111']
            assert downstream_review_branch is not None
            downstream_review_branch.checkout()
            create_git_history(downstream, [
                (("demo_4.txt", "content for demo_4"), "demo 4 commit"),
                (("demo_5.txt", "content for demo_5"), "demo 5 commit"),
                (("demo_6.txt", "content for demo_6"), "demo 6 commit"),
                (("demo_7.txt", "content for demo_7"), "demo 7 commit"),
                (("demo_8.txt", "content for demo_8"), "demo 8 commit"),
                (("demo_9.txt", "content for demo_9"), "demo 9 commit"),
                (("demo_10.txt", "content for demo_10"), "demo 10 commit"),
                (("demo_11.txt", "content for demo_11"), "demo 11 commit"),
                (("demo_12.txt", "content for demo_12"), "demo 12 commit"),
                (("demo_13.txt", "content for demo_13"), "demo 13 commit"),
                (("demo_14.txt", "content for demo_14"), "demo 14 commit"),
                (("demo_15.txt", "content for demo_15"), "demo 15 commit"),
            ])
            with fake_cliux(log_level=INFO) as (cli, get_lines):
                opts = ValidateOptions(
                    ValidateCLIOptions(verbose=True,
                                       commit_count_soft_fail_threshold=10,
                                       commit_count_auto_bypass_soft_fail=True,
                                       cwd=downstream.working_dir))
                await do_validate(cli=cli, opts=opts)
                assert strip_ansi(
                    "".join(get_lines())
                ) == """determined that local branch mnorth-review-111 tracks upstream branch mnorth-review-111 on remote origin