Ejemplo n.º 1
0
async def test_mergeable_update_branch_immediately_mode_merging() -> None:
    """
    update branch immediately if configured. When we are merging we should raise the PollForever exception to keep the merge loop going instead of returning.
    """
    api = create_api()
    mergeable = create_mergeable()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()
    config = create_config()

    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    branch_protection.requiresStrictStatusChecks = True
    config.merge.update_branch_immediately = True

    with pytest.raises(PollForever):
        await mergeable(
            api=api,
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            merging=True,
        )
    assert api.set_status.call_count == 1
    assert api.dequeue.call_count == 0
    assert api.update_branch.call_count == 1
    assert "updating branch" in api.set_status.calls[0]["msg"]
    assert "branch updated because" in api.set_status.calls[0][
        "markdown_content"]

    # verify we haven't tried to merge the PR
    assert api.merge.called is False
    assert api.queue_for_merge.called is False
Ejemplo n.º 2
0
def test_get_merge_body_includes_pull_request_url_with_coauthor() -> None:
    """
    Coauthor should appear after the pull request url
    """
    pull_request = create_pull_request()
    actual = get_merge_body(
        config=V1(
            version=1,
            merge=Merge(message=MergeMessage(
                body=MergeBodyStyle.pull_request_body,
                include_pull_request_url=True,
                include_pull_request_author=True,
            )),
        ),
        pull_request=pull_request,
        merge_method=MergeMethod.squash,
        commits=[],
    )
    expected = MergeBody(
        merge_method="squash",
        commit_message="""\
# some description

PR-URL: https://github.com/example_org/example_repo/pull/65
Co-authored-by: Barry Berkman <*****@*****.**>""",
    )
    assert actual == expected
Ejemplo n.º 3
0
async def test_duplicate_check_suites() -> None:
    """
    Kodiak should only consider the most recent check run when evaluating a PR
    for merging.

    Regression test.
    """
    api = create_api()
    pull_request = create_pull_request()
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    branch_protection = create_branch_protection()
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["Pre-merge checks"]
    mergeable = create_mergeable()
    await mergeable(
        api=api,
        pull_request=pull_request,
        branch_protection=branch_protection,
        check_runs=[
            create_check_run(name="Pre-merge checks",
                             conclusion=CheckConclusionState.NEUTRAL),
            create_check_run(name="Pre-merge checks",
                             conclusion=CheckConclusionState.FAILURE),
            create_check_run(name="Pre-merge checks",
                             conclusion=CheckConclusionState.SUCCESS),
        ],
    )
    assert "enqueued for merge" in api.set_status.calls[0]["msg"]
    assert api.queue_for_merge.call_count == 1
Ejemplo n.º 4
0
async def test_mergeable_queue_in_progress() -> None:
    """
    If a PR has pending status checks or is behind, we still consider it eligible for merge and throw it in the merge queue.
    """
    api = create_api()
    mergeable = create_mergeable()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()
    config = create_config()
    context = create_context()

    config.merge.optimistic_updates = False
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    branch_protection.requiresStrictStatusChecks = True
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["ci/test-api"]
    context.state = StatusState.PENDING
    context.context = "ci/test-api"

    await mergeable(
        api=api,
        config=config,
        pull_request=pull_request,
        branch_protection=branch_protection,
        contexts=[context],
    )
    assert api.set_status.call_count == 1
    assert "enqueued for merge" in api.set_status.calls[0]["msg"]
    assert api.dequeue.call_count == 0
    assert api.queue_for_merge.call_count == 1

    # verify we haven't tried to merge the PR
    assert api.merge.called is False
    assert api.update_branch.called is False
Ejemplo n.º 5
0
async def test_mergeable_travis_ci_checks() -> None:
    """
    GitHub has some weird, _undocumented_ logic for continuous-integration/travis-ci where "continuous-integration/travis-ci/{pr,push}" become "continuous-integration/travis-ci" in requiredStatusChecks.
    """
    api = create_api()
    mergeable = create_mergeable()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()
    context = create_context()

    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = [
        "continuous-integration/travis-ci"
    ]
    context.state = StatusState.FAILURE
    context.context = "continuous-integration/travis-ci/pr"

    await mergeable(
        api=api,
        pull_request=pull_request,
        branch_protection=branch_protection,
        contexts=[context],
        check_runs=[],
    )
    assert api.set_status.call_count == 1
    assert api.dequeue.call_count == 1
    assert (
        "failing required status checks: {'continuous-integration/travis-ci/pr'}"
        in api.set_status.calls[0]["msg"])

    # verify we haven't tried to update/merge the PR
    assert api.update_branch.called is False
    assert api.merge.called is False
    assert api.queue_for_merge.called is False
Ejemplo n.º 6
0
async def test_mergeable_missing_requires_status_checks_failing_status_context(
) -> None:
    """
    If branch protection is enabled with requiresStatusChecks but a required check is failing we should not merge.
    """
    api = create_api()
    mergeable = create_mergeable()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()
    context = create_context()

    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["ci/test-api"]
    context.context = "ci/test-api"
    context.state = StatusState.FAILURE

    await mergeable(
        api=api,
        pull_request=pull_request,
        branch_protection=branch_protection,
        contexts=[context],
        check_runs=[],
    )
    assert api.set_status.call_count == 1
    assert api.dequeue.call_count == 1
    assert ("failing required status checks: {'ci/test-api'}"
            in api.set_status.calls[0]["msg"])

    # verify we haven't tried to update/merge the PR
    assert api.update_branch.called is False
    assert api.merge.called is False
    assert api.queue_for_merge.called is False
Ejemplo n.º 7
0
def test_get_merge_body_include_pull_request_author_mannequin() -> None:
    """
    Test case where actor is not a User and Bot to see how we handle weird cases.
    """
    pull_request = create_pull_request()
    pull_request.body = "hello world"
    pull_request.author.name = None
    pull_request.author.type = "Mannequin"

    actual = get_merge_body(
        config=V1(
            version=1,
            merge=Merge(message=MergeMessage(
                body=MergeBodyStyle.pull_request_body,
                include_pull_request_author=True,
            )),
        ),
        pull_request=pull_request,
        merge_method=MergeMethod.squash,
        commits=[],
    )
    expected = MergeBody(
        merge_method="squash",
        commit_message=
        "hello world\n\nCo-authored-by: barry <*****@*****.**>",
    )
    assert actual == expected
Ejemplo n.º 8
0
async def test_mergeable_wait_for_checks() -> None:
    """
    test merge.optimistic_updates when we don't have checks to wait for. Since merge.optimistic_updates is disabled we should update the branch.
    """
    mergeable = create_mergeable()
    api = create_api()
    config = create_config()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()
    config.merge.optimistic_updates = False
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    branch_protection.requiresStrictStatusChecks = True

    with pytest.raises(PollForever):
        await mergeable(
            api=api,
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            merging=True,
        )

    assert api.set_status.call_count == 1
    assert api.dequeue.call_count == 0
    assert api.update_branch.call_count == 1
    assert "updating branch" in api.set_status.calls[0]["msg"]
    # verify we haven't tried to merge the PR
    assert api.merge.called is False
    assert api.queue_for_merge.called is False
Ejemplo n.º 9
0
async def test_mergeable_update_branch_immediately() -> None:
    """
    update branch immediately if configured
    """
    api = create_api()
    mergeable = create_mergeable()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()
    config = create_config()

    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    branch_protection.requiresStrictStatusChecks = True
    config.merge.update_branch_immediately = True

    await mergeable(
        api=api,
        config=config,
        pull_request=pull_request,
        branch_protection=branch_protection,
    )
    assert api.set_status.call_count == 1
    assert api.dequeue.call_count == 0
    assert api.update_branch.call_count == 1
    assert "updating branch" in api.set_status.calls[0]["msg"]
    assert "branch updated because" in api.set_status.calls[0][
        "markdown_content"]

    # verify we haven't tried to merge the PR
    assert api.merge.called is False
    assert api.queue_for_merge.called is False
Ejemplo n.º 10
0
def test_get_merge_body_includes_pull_request_url() -> None:
    """
    Ensure that when the appropriate config option is set, we include the
    pull request url in the commit message.
    """
    pull_request = create_pull_request()
    actual = get_merge_body(
        config=V1(
            version=1,
            merge=Merge(
                message=MergeMessage(body=MergeBodyStyle.pull_request_body,
                                     include_pull_request_url=True)),
        ),
        pull_request=pull_request,
        merge_method=MergeMethod.squash,
        commits=[],
    )
    expected = MergeBody(
        merge_method="squash",
        commit_message="""\
# some description

PR-URL: https://github.com/example_org/example_repo/pull/65""",
    )
    assert actual == expected
Ejemplo n.º 11
0
async def test_mergeable_update_always_enabled_merging_behind_pull_request(
) -> None:
    """
    When we're merging with update.always enabled we don't want to update the
    branch using our update.always logic. We want to update using our merging
    logic so we trigger the PollForever exception necessary to continue our
    merge loop. If we used the update.always logic we'd eject a PR if it became
    out of sync during merge.
    """
    mergeable = create_mergeable()
    api = create_api()
    config = create_config()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()

    config.update.always = True
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    branch_protection.requiresStrictStatusChecks = True

    with pytest.raises(PollForever):
        await mergeable(
            api=api,
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            merging=True,
        )
    assert api.set_status.call_count == 1
    assert "updating branch" in api.set_status.calls[0]["msg"]
    assert api.update_branch.call_count == 1
    assert api.queue_for_merge.call_count == 0
    assert api.merge.call_count == 0
    assert api.dequeue.call_count == 0
Ejemplo n.º 12
0
async def test_mergeable_missing_required_review() -> None:
    """
    Don't merge when a review is required.
    """
    api = create_api()
    mergeable = create_mergeable()
    pull_request = create_pull_request()

    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    pull_request.reviewDecision = PullRequestReviewDecision.REVIEW_REQUIRED
    # previously with code owner blocked PRs Kodiak would update the pull
    # request, even if the pull request was blocked from merging.
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND

    await mergeable(
        api=api,
        pull_request=pull_request,
    )
    assert api.set_status.call_count == 1
    assert api.dequeue.call_count == 1
    assert "missing required review" in api.set_status.calls[0]["msg"]

    # verify we haven't tried to update/merge the PR
    assert api.update_branch.called is False
    assert api.merge.called is False
    assert api.queue_for_merge.called is False
Ejemplo n.º 13
0
def test_get_merge_body_include_coauthors_invalid_body_style() -> None:
    """
    We only include trailers for MergeBodyStyle.pull_request_body and MergeBodyStyle.empty. Verify we don't add coauthor trailers for MergeBodyStyle.github_default.
    """
    pull_request = create_pull_request()
    pull_request.body = "hello world"
    config = create_config()
    config.merge.message.include_coauthors = True
    config.merge.message.body = MergeBodyStyle.github_default
    actual = get_merge_body(
        config=config,
        pull_request=pull_request,
        merge_method=MergeMethod.merge,
        commits=[
            create_commit(database_id=9023904,
                          name="",
                          login="******",
                          type="User"),
            create_commit(database_id=590434,
                          name="Maeve Millay",
                          login="******",
                          type="Bot"),
        ],
    )
    expected = MergeBody(merge_method="merge", commit_message=None)
    assert actual == expected
Ejemplo n.º 14
0
async def test_mergeable_requires_conversation_resolution() -> None:
    """
    When requiresConversationResolution is enabled and we have unresolved review
    threads, we should block merge.
    """
    mergeable = create_mergeable()
    api = create_api()
    config = create_config()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()

    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    pull_request.reviewThreads.nodes = [
        ReviewThread(isCollapsed=True),
        ReviewThread(isCollapsed=False),
    ]
    branch_protection.requiresConversationResolution = True

    await mergeable(
        api=api,
        config=config,
        pull_request=pull_request,
        branch_protection=branch_protection,
    )
    assert api.set_status.call_count == 1
    assert "cannot merge (unresolved conversations)" in api.set_status.calls[
        0]["msg"]
Ejemplo n.º 15
0
async def test_mergeable_uncollapsed_reviews() -> None:
    """
    We should only block merge for unresolved review threads if
    requiresConversationResolution is enabled.
    """
    mergeable = create_mergeable()
    api = create_api()
    config = create_config()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()

    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    pull_request.reviewThreads.nodes = [
        ReviewThread(isCollapsed=True),
        ReviewThread(isCollapsed=False),
    ]

    await mergeable(
        api=api,
        config=config,
        pull_request=pull_request,
        branch_protection=branch_protection,
    )
    assert api.set_status.call_count == 1
    assert ("cannot merge (Merging blocked by GitHub requirements)"
            in api.set_status.calls[0]["msg"])
Ejemplo n.º 16
0
async def test_mergeable_do_not_merge_with_update_branch_immediately_waiting_for_checks(
) -> None:
    """
    merge.do_not_merge is only useful with merge.update_branch_immediately,
    Test when PR doesn't need update but is waiting for checks to finish.
    """
    mergeable = create_mergeable()
    api = create_api()
    pull_request = create_pull_request()
    config = create_config()
    branch_protection = create_branch_protection()
    context = create_context()
    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    config.merge.do_not_merge = True
    config.merge.update_branch_immediately = True
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["ci/test-api"]
    context.context = "ci/test-api"
    context.state = StatusState.PENDING

    await mergeable(
        api=api,
        config=config,
        pull_request=pull_request,
        branch_protection=branch_protection,
        contexts=[context],
    )
    assert api.set_status.called is True
    assert ("waiting for required status checks: {'ci/test-api'}"
            in api.set_status.calls[0]["msg"])

    assert api.update_branch.called is False
    assert api.queue_for_merge.called is False
    assert api.merge.called is False
    assert api.queue_for_merge.called is False
Ejemplo n.º 17
0
def test_pr_get_merge_body_empty() -> None:
    pull_request = create_pull_request()
    actual = get_merge_body(
        config=V1(version=1),
        pull_request=pull_request,
        merge_method=MergeMethod.squash,
        commits=[],
    )
    expected = MergeBody(merge_method="squash")
    assert actual == expected
Ejemplo n.º 18
0
async def test_mergeable_update_autoupdate_label() -> None:
    """
    Kodiak should update the PR when the autoupdate_label is set on the PR.
    """
    mergeable = create_mergeable()
    api = create_api()
    config = create_config()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()
    check_run = create_check_run()

    config.update.autoupdate_label = "update me please!"

    # create a pull requests that's behind and failing checks. We should still
    # update on failing checks.
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["ci/test-api"]
    check_run.name = "ci/test-api"
    check_run.conclusion = CheckConclusionState.FAILURE

    await mergeable(
        api=api,
        config=config,
        pull_request=pull_request,
        branch_protection=branch_protection,
        check_runs=[check_run],
    )
    assert (
        api.update_branch.call_count == 0
    ), "we shouldn't update when update.autoupdate_label isn't available on the PR"

    # PR should be updated when set on the PR
    pull_request.labels = [config.update.autoupdate_label]
    api = create_api()
    await mergeable(
        api=api,
        config=config,
        pull_request=pull_request,
        branch_protection=branch_protection,
        check_runs=[check_run],
    )

    assert api.update_branch.call_count == 1
    assert api.set_status.call_count == 1
    assert "updating branch" in api.set_status.calls[0]["msg"]
    assert "branch updated because" in api.set_status.calls[0][
        "markdown_content"]

    assert api.queue_for_merge.call_count == 0
    assert api.merge.call_count == 0
    assert api.dequeue.call_count == 0
Ejemplo n.º 19
0
def test_get_merge_body_empty() -> None:
    pull_request = create_pull_request()
    pull_request.body = "hello world"
    actual = get_merge_body(
        config=V1(
            version=1,
            merge=Merge(message=MergeMessage(body=MergeBodyStyle.empty))),
        pull_request=pull_request,
        merge_method=MergeMethod.squash,
        commits=[],
    )
    expected = MergeBody(merge_method="squash", commit_message="")
    assert actual == expected
Ejemplo n.º 20
0
def test_get_merge_body_strip_html_comments() -> None:
    pull_request = create_pull_request()
    pull_request.body = "hello <!-- testing -->world"
    actual = get_merge_body(
        config=V1(
            version=1,
            merge=Merge(
                message=MergeMessage(body=MergeBodyStyle.pull_request_body,
                                     strip_html_comments=True)),
        ),
        pull_request=pull_request,
        merge_method=MergeMethod.squash,
        commits=[],
    )
    expected = MergeBody(merge_method="squash", commit_message="hello world")
    assert actual == expected
Ejemplo n.º 21
0
async def test_mergeable_update_username_blacklist() -> None:
    """
    Kodiak should not update PR if user is blacklisted.
    """
    mergeable = create_mergeable()

    blacklist_config = create_config()
    blacklist_config.update.always = True
    blacklist_config.update.blacklist_usernames = ["mr-test"]
    blacklist_config.update.require_automerge_label = True

    ignored_config = create_config()
    ignored_config.update.always = True
    ignored_config.update.ignored_usernames = ["mr-test"]
    ignored_config.update.require_automerge_label = True

    pull_request = create_pull_request()
    pull_request.author.login = "******"
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND

    branch_protection = create_branch_protection()
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["ci/test-api"]

    check_run = create_check_run()
    check_run.name = "ci/test-api"
    check_run.conclusion = CheckConclusionState.FAILURE

    for is_merging in (True, False):
        for config in (blacklist_config, ignored_config):
            api = create_api()
            await mergeable(
                api=api,
                config=config,
                pull_request=pull_request,
                branch_protection=branch_protection,
                check_runs=[check_run],
                merging=is_merging,
            )
            assert api.update_branch.call_count == 0
            assert api.set_status.call_count == 1
            assert "updates blocked by update." in api.set_status.calls[0][
                "msg"]
            assert api.dequeue.call_count == 1

            assert api.queue_for_merge.call_count == 0
            assert api.merge.call_count == 0
Ejemplo n.º 22
0
def test_get_merge_body_includes_pull_request_url_github_default() -> None:
    """
    We should not set a commit message when merge.body = "github_default".
    """
    pull_request = create_pull_request()
    actual = get_merge_body(
        config=V1(
            version=1,
            merge=Merge(
                message=MergeMessage(body=MergeBodyStyle.github_default,
                                     include_pull_request_url=True)),
        ),
        pull_request=pull_request,
        merge_method=MergeMethod.squash,
        commits=[],
    )
    expected = MergeBody(merge_method="squash", commit_message=None)
    assert actual == expected
Ejemplo n.º 23
0
async def test_merge_mismatch_username() -> None:
    """
    We should only merge the pull request if the userrname is specified within
    "usernames" and the version type is in the "versions" field.

    """
    mergeable = create_mergeable()
    config = create_config()
    config.merge.automerge_dependencies.versions = ["minor", "patch"]
    config.merge.automerge_dependencies.usernames = ["dependabot"]
    pull_request = create_pull_request()
    pull_request.labels = []
    pull_request.author.login = "******"
    pull_request.title = "Bump lodash from 4.17.15 to 4.17.19"
    api = create_api()
    await mergeable(api=api, pull_request=pull_request, config=config)
    assert api.queue_for_merge.call_count == 0
    assert api.dequeue.call_count == 1
Ejemplo n.º 24
0
async def test_merge_disallowed_version() -> None:
    """
    We should only auto merge if the upgrade type is specified in "versions".

    So if a PR is a major upgrade, we should only auto merge if "major" is in
    the "versions" configuration.
    """
    mergeable = create_mergeable()
    config = create_config()
    config.merge.automerge_dependencies.versions = ["minor", "patch"]
    config.merge.automerge_dependencies.usernames = ["my-custom-dependabot"]
    pull_request = create_pull_request()
    pull_request.labels = []
    pull_request.author.login = "******"
    pull_request.title = "Bump lodash from 4.17.15 to 5.0.1"
    api = create_api()
    await mergeable(api=api, pull_request=pull_request, config=config)
    assert api.queue_for_merge.call_count == 0
    assert api.dequeue.call_count == 1
Ejemplo n.º 25
0
async def test_mergeable_missing_requires_status_checks_failing_check_run(
) -> None:
    """
    If branch protection is enabled with requiresStatusChecks but a required check is failing we should not merge.
    """
    api = create_api()
    mergeable = create_mergeable()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()
    check_run = create_check_run()

    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["ci/test-api"]
    check_run.name = "ci/test-api"
    check_run.conclusion = CheckConclusionState.FAILURE

    for index, check_run_conclusion in enumerate((
            CheckConclusionState.ACTION_REQUIRED,
            CheckConclusionState.FAILURE,
            CheckConclusionState.TIMED_OUT,
            CheckConclusionState.CANCELLED,
            CheckConclusionState.SKIPPED,
            CheckConclusionState.STALE,
    )):
        check_run.conclusion = check_run_conclusion
        await mergeable(
            api=api,
            pull_request=pull_request,
            branch_protection=branch_protection,
            check_runs=[check_run],
            contexts=[],
        )
        assert api.set_status.call_count == 1 + index
        assert api.dequeue.call_count == 1 + index
        assert ("failing required status checks: {'ci/test-api'}"
                in api.set_status.calls[index]["msg"])

    # verify we haven't tried to update/merge the PR
    assert api.update_branch.called is False
    assert api.merge.called is False
    assert api.queue_for_merge.called is False
Ejemplo n.º 26
0
async def test_mergeable_skippable_contexts_merging_pull_request() -> None:
    """
    If a skippable check hasn't finished but we're merging, we need to raise an exception to retry for a short period of time to allow the check to finish. We won't retry forever because skippable checks will likely never finish.
    """
    api = create_api()
    mergeable = create_mergeable()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()
    config = create_config()
    context = create_context()
    check_run = create_check_run()

    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["WIP", "ci/test-api"]
    config.merge.dont_wait_on_status_checks = ["WIP"]
    context.state = StatusState.PENDING
    context.context = "WIP"
    check_run.name = "ci/test-api"
    check_run.conclusion = CheckConclusionState.SUCCESS

    with pytest.raises(RetryForSkippableChecks):
        await mergeable(
            api=api,
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            check_runs=[check_run],
            contexts=[context],
            merging=True,
        )
    assert api.set_status.call_count == 1
    assert (
        "merging PR (waiting a bit for dont_wait_on_status_checks: ['WIP'])"
        in api.set_status.calls[0]["msg"])
    assert api.dequeue.call_count == 0

    # verify we haven't tried to update/merge the PR
    assert api.update_branch.called is False
    assert api.merge.called is False
    assert api.queue_for_merge.called is False
Ejemplo n.º 27
0
async def test_mergeable_queue_in_progress_with_ready_to_merge() -> None:
    """
    If a PR has pending status checks or is behind, we still consider it eligible for merge and throw it in the merge queue.

    regression test to verify that with config.merge.prioritize_ready_to_merge =
    true we don't attempt to merge a PR directly but called queue_for_merge
    instead. If the status checks haven't passed or the branch needs an update
    it's not good to be merged directly, but it can be queued for the merge
    queue.
    """
    api = create_api()
    mergeable = create_mergeable()
    pull_request = create_pull_request()
    branch_protection = create_branch_protection()
    config = create_config()
    context = create_context()

    config.merge.optimistic_updates = False
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    branch_protection.requiresStrictStatusChecks = True
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["ci/test-api"]
    context.state = StatusState.PENDING
    context.context = "ci/test-api"
    config.merge.prioritize_ready_to_merge = True

    await mergeable(
        api=api,
        config=config,
        pull_request=pull_request,
        branch_protection=branch_protection,
        contexts=[context],
    )
    assert api.set_status.call_count == 1
    assert "enqueued for merge" in api.set_status.calls[0]["msg"]
    assert api.dequeue.call_count == 0
    assert api.queue_for_merge.call_count == 1

    # verify we haven't tried to merge the PR
    assert api.merge.called is False
    assert api.update_branch.called is False
Ejemplo n.º 28
0
async def test_merge_no_version_found() -> None:
    """
    If we can't find a version from the PR title, we shouldn't merge.

    Packages don't necessarily use semver.
    """
    mergeable = create_mergeable()
    config = create_config()
    config.merge.automerge_dependencies.versions = ["minor", "patch"]
    config.merge.automerge_dependencies.usernames = ["my-custom-dependabot"]
    pull_request = create_pull_request()
    pull_request.labels = []
    pull_request.author.login = "******"

    for title in ("Bump lodash from 4.17.15 to",
                  "Bump lodash from griffin to phoenix"):
        pull_request.title = title
        api = create_api()
        await mergeable(api=api, pull_request=pull_request, config=config)
        assert api.queue_for_merge.call_count == 0
        assert api.dequeue.call_count == 1
Ejemplo n.º 29
0
async def test_merge_okay() -> None:
    """
    Happy case.

    The upgrade type (patch) is specified in "versions" and the PR author
    "my-custom-dependabot" is specified in "usernames".
    """
    mergeable = create_mergeable()
    config = create_config()
    config.merge.automerge_dependencies.versions = ["minor", "patch"]
    config.merge.automerge_dependencies.usernames = ["my-custom-dependabot"]
    pull_request = create_pull_request()
    pull_request.labels = []
    pull_request.author.login = "******"
    pull_request.title = "Bump lodash from 4.17.15 to 4.17.19"
    api = create_api()
    await mergeable(api=api, pull_request=pull_request, config=config)
    assert api.set_status.call_count == 1
    assert "enqueued" in api.set_status.calls[0]["msg"]
    assert api.queue_for_merge.call_count == 1
    assert api.dequeue.call_count == 0
Ejemplo n.º 30
0
def test_get_merge_body_include_pull_request_author_invalid_body_style(
) -> None:
    """
    We only include trailers MergeBodyStyle.pull_request_body and
    MergeBodyStyle.empty. Verify we don't include trailers for
    MergeBodyStyle.github_default.
    """
    pull_request = create_pull_request()
    pull_request.body = "hello world"
    config = create_config()
    config.merge.message.include_pull_request_author = True

    config.merge.message.body = MergeBodyStyle.github_default
    actual = get_merge_body(
        config=config,
        pull_request=pull_request,
        merge_method=MergeMethod.merge,
        commits=[],
    )
    expected = MergeBody(merge_method="merge", commit_message=None)
    assert actual == expected