Exemplo n.º 1
0
def test_regression_error_before_update(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    check_run: CheckRun,
) -> None:
    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["ci/backend", "wip-app"]
    branch_protection.requiresStrictStatusChecks = True
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    contexts = [StatusContext(context="ci/backend", state=StatusState.SUCCESS)]
    check_run.name = "wip-app"
    check_run.conclusion = CheckConclusionState.SUCCESS
    with pytest.raises(NeedsBranchUpdate):
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=1,
            reviews=[review],
            check_runs=[check_run],
            contexts=contexts,
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 2
0
def test_require_automerge_label_false(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    """
    If the automerge_label is missing, but we have require_automerge_label set
    to false, enqueue the PR for merge
    """
    pull_request.labels = []
    config.merge.automerge_label = "automerge"
    config.merge.require_automerge_label = False
    mergeable(
        config=config,
        pull_request=pull_request,
        branch_protection=branch_protection,
        review_requests_count=0,
        reviews=[review],
        contexts=[context],
        check_runs=[],
        valid_signature=False,
        valid_merge_methods=[MergeMethod.merge, MergeMethod.squash],
    )
Exemplo n.º 3
0
def test_dont_update_before_block(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    """
    Regression test for when Kodiak would update a PR that is not mergeable.
    We were raising the NeedsBranchUpdate exception too early.
    """
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    branch_protection.requiresStrictStatusChecks = True
    with pytest.raises(NeedsBranchUpdate):
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 4
0
def test_failing_checks(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
    check_run: CheckRun,
) -> None:
    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    branch_protection.requiredStatusCheckContexts = ["ci/backend", "wip-app"]
    context.context = "ci/backend"
    context.state = StatusState.SUCCESS
    check_run.name = "wip-app"
    check_run.conclusion = CheckConclusionState.FAILURE
    with pytest.raises(NotQueueable, match="failing required status checks"):
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[check_run],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 5
0
def test_get_merge_body_empty(pull_request: queries.PullRequest) -> None:
    pull_request.body = "hello world"
    actual = get_merge_body(
        V1(
            version=1,
            merge=Merge(
                method=MergeMethod.squash,
                message=MergeMessage(body=MergeBodyStyle.empty),
            ),
        ),
        pull_request,
    )
    expected = dict(merge_method="squash", commit_message="")
    assert actual == expected
Exemplo n.º 6
0
def test_regression_mishandling_multiple_reviews_okay_reviews(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    check_run: CheckRun,
    context: StatusContext,
) -> None:
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND
    branch_protection.requiresApprovingReviews = True
    branch_protection.requiredApprovingReviewCount = 1
    first_review_date = datetime(2010, 5, 15)
    latest_review_date = first_review_date + timedelta(minutes=20)
    reviews = [
        PRReview(
            state=PRReviewState.CHANGES_REQUESTED,
            createdAt=first_review_date,
            author=PRReviewAuthor(login="******"),
            authorAssociation=CommentAuthorAssociation.CONTRIBUTOR,
        ),
        PRReview(
            state=PRReviewState.COMMENTED,
            createdAt=latest_review_date,
            author=PRReviewAuthor(login="******"),
            authorAssociation=CommentAuthorAssociation.CONTRIBUTOR,
        ),
        PRReview(
            state=PRReviewState.APPROVED,
            createdAt=latest_review_date,
            author=PRReviewAuthor(login="******"),
            authorAssociation=CommentAuthorAssociation.CONTRIBUTOR,
        ),
        PRReview(
            state=PRReviewState.APPROVED,
            createdAt=latest_review_date,
            author=PRReviewAuthor(login="******"),
            authorAssociation=CommentAuthorAssociation.CONTRIBUTOR,
        ),
    ]
    with pytest.raises(NeedsBranchUpdate):
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=1,
            reviews=reviews,
            check_runs=[check_run],
            contexts=[context],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 7
0
def pull_request() -> PullRequest:
    return PullRequest(
        id="FDExOlB1bGxSZXX1ZXN0MjgxODQ0Nzg7",
        number=142,
        mergeStateStatus=MergeStateStatus.CLEAN,
        state=PullRequestState.OPEN,
        mergeable=MergeableState.MERGEABLE,
        labels=["bugfix", "automerge"],
        latest_sha="f89be6c",
        baseRefName="master",
        headRefName="feature/hello-world",
        title="new feature",
        body="# some description",
        bodyText="some description",
        bodyHTML="<h1>some description</h1>",
    )
Exemplo n.º 8
0
def test_config_merge_optimistic_updates(
    pull_request: PullRequest, config: V1, branch_protection: BranchProtectionRule
) -> None:
    """
    If optimisitc_updates are enabled, branch updates should be prioritized over
    waiting for running status checks to complete.

    Otherwise, status checks should be checked before updating.
    """
    branch_protection.requiredApprovingReviewCount = 0

    branch_protection.requiresStrictStatusChecks = True
    pull_request.mergeStateStatus = MergeStateStatus.BEHIND

    branch_protection.requiresStatusChecks = True
    branch_protection.requiredStatusCheckContexts = ["ci/lint", "ci/test"]
    contexts: List[StatusContext] = []

    config.merge.optimistic_updates = True
    with pytest.raises(NeedsBranchUpdate):
        mergeable(
            app_id="1234",
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[],
            contexts=contexts,
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
    config.merge.optimistic_updates = False
    with pytest.raises(WaitingForChecks):
        mergeable(
            app_id="1234",
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[],
            contexts=contexts,
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 9
0
def test_get_merge_body_strip_html_comments(pull_request: queries.PullRequest,
                                            original: str,
                                            stripped: str) -> None:
    pull_request.body = "hello <!-- testing -->world"
    actual = get_merge_body(
        V1(
            version=1,
            merge=Merge(
                method=MergeMethod.squash,
                message=MergeMessage(body=MergeBodyStyle.pull_request_body,
                                     strip_html_comments=True),
            ),
        ),
        pull_request,
    )
    expected = dict(merge_method="squash", commit_message="hello world")
    assert actual == expected
Exemplo n.º 10
0
def test_unknown_blockage(
    pull_request: PullRequest, config: V1, branch_protection: BranchProtectionRule
) -> None:
    branch_protection.requiredApprovingReviewCount = 0
    branch_protection.requiresStatusChecks = False
    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    with pytest.raises(NotQueueable, match="determine why PR is blocked"):
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[],
            contexts=[],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 11
0
def test_missing_mergeability_state(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    with pytest.raises(MissingGithubMergeabilityState):
        pull_request.mergeable = MergeableState.UNKNOWN
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 12
0
def test_need_update(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    with pytest.raises(NeedsBranchUpdate):
        pull_request.mergeStateStatus = MergeStateStatus.BEHIND
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 13
0
def test_closed(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    with pytest.raises(NotQueueable, match="closed"):
        pull_request.state = PullRequestState.CLOSED
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 14
0
def test_requires_signature(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    branch_protection.requiresCommitSignatures = True
    with pytest.raises(NotQueueable, match="missing required signature"):
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 15
0
def test_missing_automerge_label(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    pull_request.labels = ["bug"]
    config.merge.automerge_label = "automerge"
    with pytest.raises(NotQueueable, match="missing automerge_label"):
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.merge, MergeMethod.squash],
        )
Exemplo n.º 16
0
def test_blocking_review(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    review.state = PRReviewState.CHANGES_REQUESTED
    with pytest.raises(NotQueueable, match="blocking review"):
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 17
0
def test_missing_required_context(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    pull_request.mergeStateStatus = MergeStateStatus.BLOCKED
    branch_protection.requiredStatusCheckContexts = ["ci/backend", "ci/frontend"]
    context.context = "ci/backend"
    with pytest.raises(WaitingForChecks, match="missing required status checks"):
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 18
0
def test_blacklisted(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    # a PR with a blacklisted label should not be mergeable
    with pytest.raises(NotQueueable, match="blacklist"):
        pull_request.labels = ["automerge", "dont-merge"]
        config.merge.automerge_label = "automerge"
        config.merge.blacklist_labels = ["dont-merge"]
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.merge, MergeMethod.squash],
        )
Exemplo n.º 19
0
def test_blacklist_title_match(
    pull_request: PullRequest,
    config: V1,
    branch_protection: BranchProtectionRule,
    review: PRReview,
    context: StatusContext,
) -> None:
    # a PR with a blacklisted title should not be mergeable
    with pytest.raises(NotQueueable, match="blacklist_title") as e_info:
        config.merge.blacklist_title_regex = "^WIP:.*"
        pull_request.title = "WIP: add fleeb to plumbus"
        mergeable(
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[review],
            contexts=[context],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.merge, MergeMethod.squash],
        )
    assert config.merge.blacklist_title_regex in str(e_info.value)
Exemplo n.º 20
0
def test_merge_state_status_draft(
    pull_request: PullRequest, config: V1, branch_protection: BranchProtectionRule
) -> None:
    """
    If optimisitc_updates are enabled, branch updates should be prioritized over
    waiting for running status checks to complete.

    Otherwise, status checks should be checked before updating.
    """
    pull_request.mergeStateStatus = MergeStateStatus.DRAFT

    with pytest.raises(NotQueueable, match="draft state"):
        mergeable(
            app_id="1234",
            config=config,
            pull_request=pull_request,
            branch_protection=branch_protection,
            review_requests_count=0,
            reviews=[],
            contexts=[],
            check_runs=[],
            valid_signature=False,
            valid_merge_methods=[MergeMethod.squash],
        )
Exemplo n.º 21
0
def create_event() -> EventInfoResponse:
    config = V1(version=1,
                merge=Merge(automerge_label="automerge",
                            method=MergeMethod.squash))
    pr = PullRequest(
        id="e14ff7599399478fb9dbc2dacb87da72",
        number=100,
        author=PullRequestAuthor(login="******", databaseId=49118, type="Bot"),
        mergeStateStatus=MergeStateStatus.BEHIND,
        state=PullRequestState.OPEN,
        isDraft=False,
        mergeable=MergeableState.MERGEABLE,
        isCrossRepository=False,
        labels=["automerge"],
        latest_sha="8d728d017cac4f5ba37533debe65730abe65730a",
        baseRefName="master",
        headRefName="df825f90-9825-424c-a97e-733522027e4c",
        title="Update README.md",
        body="",
        bodyText="",
        bodyHTML="",
        url="https://github.com/delos-corp/hive-mind/pull/324",
    )
    rep_info = RepoInfo(
        merge_commit_allowed=False,
        rebase_merge_allowed=False,
        squash_merge_allowed=True,
        is_private=True,
        delete_branch_on_merge=False,
    )
    branch_protection = BranchProtectionRule(
        requiresApprovingReviews=True,
        requiredApprovingReviewCount=2,
        requiresStatusChecks=True,
        requiredStatusCheckContexts=[
            "ci/circleci: frontend_lint",
            "ci/circleci: frontend_test",
        ],
        requiresStrictStatusChecks=True,
        requiresCodeOwnerReviews=False,
        requiresCommitSignatures=False,
        restrictsPushes=False,
        pushAllowances=NodeListPushAllowance(nodes=[]),
    )

    return EventInfoResponse(
        config=config,
        config_str="""\
version = 1
[merge]
method = "squash"
""",
        config_file_expression="master:.kodiak.toml",
        head_exists=True,
        pull_request=pr,
        repository=rep_info,
        branch_protection=branch_protection,
        review_requests=[],
        reviews=[],
        status_contexts=[],
        check_runs=[],
        valid_signature=True,
        valid_merge_methods=[MergeMethod.squash],
        subscription=None,
    )
Exemplo n.º 22
0
def block_event() -> EventInfoResponse:
    config = V1(version=1,
                merge=Merge(automerge_label="automerge",
                            method=MergeMethod.squash))
    pr = PullRequest(
        id="e14ff7599399478fb9dbc2dacb87da72",
        number=100,
        author=PullRequestAuthor(login="******", databaseId=49118, type="Bot"),
        mergeStateStatus=MergeStateStatus.BEHIND,
        state=PullRequestState.OPEN,
        mergeable=MergeableState.MERGEABLE,
        isCrossRepository=False,
        labels=["automerge"],
        latest_sha="8d728d017cac4f5ba37533debe65730abe65730a",
        baseRefName="master",
        headRefName="df825f90-9825-424c-a97e-733522027e4c",
        title="Update README.md",
        body="",
        bodyText="",
        bodyHTML="",
        url="https://github.com/delos-corp/hive-mind/pull/324",
    )
    rep_info = RepoInfo(
        merge_commit_allowed=False,
        rebase_merge_allowed=False,
        squash_merge_allowed=True,
        delete_branch_on_merge=True,
        is_private=True,
    )
    branch_protection = BranchProtectionRule(
        requiresApprovingReviews=True,
        requiredApprovingReviewCount=2,
        requiresStatusChecks=True,
        requiredStatusCheckContexts=[
            "ci/circleci: backend_lint",
            "ci/circleci: backend_test",
            "ci/circleci: frontend_lint",
            "ci/circleci: frontend_test",
            "WIP (beta)",
        ],
        requiresStrictStatusChecks=True,
        requiresCommitSignatures=False,
        restrictsPushes=True,
        pushAllowances=NodeListPushAllowance(nodes=[
            PushAllowance(actor=PushAllowanceActor(databaseId=None)),
            PushAllowance(actor=PushAllowanceActor(databaseId=53453)),
        ]),
    )

    return EventInfoResponse(
        config=config,
        config_str="""\
version = 1
[merge]
method = "squash"
""",
        config_file_expression="master:.kodiak.toml",
        head_exists=True,
        pull_request=pr,
        repository=rep_info,
        subscription=Subscription(
            account_id="D1606A79-A1A1-4550-BA7B-C9ED0D792B1E",
            subscription_blocker=None),
        branch_protection=branch_protection,
        review_requests=[
            PRReviewRequest(name="ghost"),
            PRReviewRequest(name="ghost-team"),
            PRReviewRequest(name="ghost-mannequin"),
        ],
        reviews=[
            PRReview(
                createdAt=arrow.get("2019-05-22T15:29:34Z").datetime,
                state=PRReviewState.COMMENTED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.WRITE),
            ),
            PRReview(
                createdAt=arrow.get("2019-05-22T15:29:52Z").datetime,
                state=PRReviewState.CHANGES_REQUESTED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.WRITE),
            ),
            PRReview(
                createdAt=arrow.get("2019-05-22T15:30:52Z").datetime,
                state=PRReviewState.COMMENTED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.ADMIN),
            ),
            PRReview(
                createdAt=arrow.get("2019-05-22T15:43:17Z").datetime,
                state=PRReviewState.APPROVED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.WRITE),
            ),
            PRReview(
                createdAt=arrow.get("2019-05-23T15:13:29Z").datetime,
                state=PRReviewState.APPROVED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.WRITE),
            ),
            PRReview(
                createdAt=arrow.get("2019-05-24T10:21:32Z").datetime,
                state=PRReviewState.APPROVED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.WRITE),
            ),
        ],
        status_contexts=[
            StatusContext(context="ci/circleci: backend_lint",
                          state=StatusState.SUCCESS),
            StatusContext(context="ci/circleci: backend_test",
                          state=StatusState.SUCCESS),
            StatusContext(context="ci/circleci: frontend_lint",
                          state=StatusState.SUCCESS),
            StatusContext(context="ci/circleci: frontend_test",
                          state=StatusState.SUCCESS),
        ],
        check_runs=[
            CheckRun(name="WIP (beta)",
                     conclusion=CheckConclusionState.SUCCESS)
        ],
        valid_signature=True,
        valid_merge_methods=[MergeMethod.squash],
    )
Exemplo n.º 23
0
def block_event(config_file_expression: str,
                config_str: str) -> EventInfoResponse:
    config = V1(version=1,
                merge=Merge(automerge_label="automerge",
                            method=MergeMethod.squash))
    pr = PullRequest(
        id="e14ff7599399478fb9dbc2dacb87da72",
        number=100,
        mergeStateStatus=MergeStateStatus.BEHIND,
        state=PullRequestState.OPEN,
        mergeable=MergeableState.MERGEABLE,
        isCrossRepository=False,
        labels=["automerge"],
        latest_sha="8d728d017cac4f5ba37533debe65730abe65730a",
        baseRefName="master",
        headRefName="df825f90-9825-424c-a97e-733522027e4c",
        title="Update README.md",
        body="",
        bodyText="",
        bodyHTML="",
    )
    rep_info = RepoInfo(
        merge_commit_allowed=False,
        rebase_merge_allowed=False,
        squash_merge_allowed=True,
    )
    branch_protection = BranchProtectionRule(
        requiresApprovingReviews=True,
        requiredApprovingReviewCount=2,
        requiresStatusChecks=True,
        requiredStatusCheckContexts=[
            "ci/circleci: backend_lint",
            "ci/circleci: backend_test",
            "ci/circleci: frontend_lint",
            "ci/circleci: frontend_test",
            "WIP (beta)",
        ],
        requiresStrictStatusChecks=True,
        requiresCommitSignatures=False,
    )

    return EventInfoResponse(
        config=config,
        config_str=config_str,
        config_file_expression=config_file_expression,
        head_exists=True,
        pull_request=pr,
        repo=rep_info,
        branch_protection=branch_protection,
        review_requests=[
            PRReviewRequest(name="ghost"),
            PRReviewRequest(name="ghost-team"),
            PRReviewRequest(name="ghost-mannequin"),
        ],
        reviews=[
            PRReview(
                createdAt=arrow.get("2019-05-22T15:29:34Z").datetime,
                state=PRReviewState.COMMENTED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.WRITE),
            ),
            PRReview(
                createdAt=arrow.get("2019-05-22T15:29:52Z").datetime,
                state=PRReviewState.CHANGES_REQUESTED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.WRITE),
            ),
            PRReview(
                createdAt=arrow.get("2019-05-22T15:30:52Z").datetime,
                state=PRReviewState.COMMENTED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.ADMIN),
            ),
            PRReview(
                createdAt=arrow.get("2019-05-22T15:43:17Z").datetime,
                state=PRReviewState.APPROVED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.WRITE),
            ),
            PRReview(
                createdAt=arrow.get("2019-05-23T15:13:29Z").datetime,
                state=PRReviewState.APPROVED,
                author=PRReviewAuthor(login="******",
                                      permission=Permission.WRITE),
            ),
        ],
        status_contexts=[
            StatusContext(context="ci/circleci: backend_lint",
                          state=StatusState.SUCCESS),
            StatusContext(context="ci/circleci: backend_test",
                          state=StatusState.SUCCESS),
            StatusContext(context="ci/circleci: frontend_lint",
                          state=StatusState.SUCCESS),
            StatusContext(context="ci/circleci: frontend_test",
                          state=StatusState.SUCCESS),
        ],
        check_runs=[
            CheckRun(name="WIP (beta)",
                     conclusion=CheckConclusionState.SUCCESS)
        ],
        valid_signature=True,
        valid_merge_methods=[MergeMethod.squash],
    )