예제 #1
0
async def test_save_apikey_scoped(redis_cache):
    api_access_key = "a" * 32
    api_secret_key = "s" * 32
    account_id = github_types.GitHubAccountIdType(12345)
    account_login = github_types.GitHubLogin("login")
    app = application.Application(
        redis_cache,
        0,
        "app name",
        api_access_key,
        api_secret_key,
        {"id": account_id, "login": account_login},
    )

    await app.save_to_cache()
    rapp = await application.Application._retrieve_from_cache(
        redis_cache, api_access_key, api_secret_key, account_login
    )
    assert app == rapp

    # wrong scope
    app = await application.Application._retrieve_from_cache(
        redis_cache,
        api_access_key,
        api_secret_key,
        github_types.GitHubLogin("another login"),
    )
    assert app is None
    app = await application.Application._retrieve_from_cache(
        redis_cache,
        api_access_key,
        api_secret_key,
        None,
    )
    assert app is None
예제 #2
0
def main() -> None:
    parser = argparse.ArgumentParser(description="Mergify admin tools")
    subparsers = parser.add_subparsers(dest="command")
    suspend_parser = subparsers.add_parser("suspend",
                                           help="Suspend an installation")
    suspend_parser.add_argument("organization", help="Organization login")
    unsuspend_parser = subparsers.add_parser("unsuspend",
                                             help="Unsuspend an installation")
    unsuspend_parser.add_argument("organization", help="Organization login")

    args = parser.parse_args()

    try:
        if args.command == "suspend":
            asyncio.run(
                suspended("PUT", github_types.GitHubLogin(args.organization)))
        elif args.command == "unsuspend":
            asyncio.run(
                suspended("DELETE",
                          github_types.GitHubLogin(args.organization)))
        else:
            parser.print_help()
    except KeyboardInterrupt:
        print("Interruped...")
    except BrokenPipeError:
        pass
예제 #3
0
async def test_application_tokens_via_env(monkeypatch, redis_cache):
    api_access_key1 = "1" * 32
    api_secret_key1 = "1" * 32
    account_id1 = github_types.GitHubAccountIdType(12345)
    account_login1 = github_types.GitHubLogin("login1")

    api_access_key2 = "2" * 32
    api_secret_key2 = "2" * 32
    account_id2 = github_types.GitHubAccountIdType(67891)
    account_login2 = github_types.GitHubLogin("login2")

    with pytest.raises(application.ApplicationUserNotFound):
        await application.ApplicationOnPremise.get(
            redis_cache, api_access_key1, api_secret_key1, None
        )

    with pytest.raises(application.ApplicationUserNotFound):
        await application.ApplicationOnPremise.get(
            redis_cache, api_access_key2, api_secret_key2, None
        )

    monkeypatch.setattr(
        config,
        "APPLICATION_APIKEYS",
        config.ApplicationAPIKeys(
            f"{api_access_key1}{api_secret_key1}:{account_id1}:{account_login1},{api_access_key2}{api_secret_key2}:{account_id2}:{account_login2}"
        ),
    )

    app = await application.ApplicationOnPremise.get(
        redis_cache, api_access_key1, api_secret_key1, account_login1
    )
    assert app.account_scope["id"] == account_id1

    app = await application.ApplicationOnPremise.get(
        redis_cache, api_access_key2, api_secret_key2, account_login2
    )
    assert app.account_scope["id"] == account_id2

    app = await application.ApplicationOnPremise.get(
        redis_cache, api_access_key1, api_secret_key1, None
    )
    assert app.account_scope["id"] == account_id1

    app = await application.ApplicationOnPremise.get(
        redis_cache, api_access_key2, api_secret_key2, None
    )
    assert app.account_scope["id"] == account_id2

    # wrong scope
    with pytest.raises(application.ApplicationUserNotFound):
        await application.ApplicationOnPremise.get(
            redis_cache, api_access_key1, api_secret_key1, account_id2
        )
    with pytest.raises(application.ApplicationUserNotFound):
        await application.ApplicationOnPremise.get(
            redis_cache, api_access_key2, api_secret_key2, account_id1
        )
예제 #4
0
async def test_client_installation_HTTP_500(httpserver: httpserver.HTTPServer) -> None:
    httpserver.expect_request("/users/owner/installation").respond_with_data(
        "This is an 5XX error", status=500
    )
    with mock.patch(
        "mergify_engine.config.GITHUB_API_URL",
        httpserver.url_for("/")[:-1],
    ):
        async with github.AsyncGithubInstallationClient(
            github.get_auth(github_types.GitHubLogin("owner"))
        ) as client:
            with pytest.raises(http.HTTPServerSideError) as exc_info:
                await client.get(httpserver.url_for("/"))

    # 5 retries
    assert len(httpserver.log) == 5

    assert exc_info.value.message == "This is an 5XX error"
    assert exc_info.value.status_code == 500
    assert exc_info.value.response.status_code == 500
    assert str(exc_info.value.request.url) == httpserver.url_for(
        "/users/owner/installation"
    )

    httpserver.check_assertions()
예제 #5
0
def _url_parser(
    url: str,
) -> typing.Tuple[github_types.GitHubLogin,
                  typing.Optional[github_types.GitHubRepositoryName],
                  typing.Optional[github_types.GitHubPullRequestNumber], ]:

    path = [
        el for el in urllib.parse.urlparse(url).path.split("/") if el != ""
    ]

    pull_number: typing.Optional[str]
    repo: typing.Optional[str]

    try:
        owner, repo, _, pull_number = path
    except ValueError:
        pull_number = None
        try:
            owner, repo = path
        except ValueError:
            if len(path) == 1:
                owner = path[0]
                repo = None
            else:
                raise ValueError

    return (
        github_types.GitHubLogin(owner),
        None if repo is None else github_types.GitHubRepositoryName(repo),
        None if pull_number is None else github_types.GitHubPullRequestNumber(
            int(pull_number)),
    )
예제 #6
0
async def test_get_mergify_config_invalid(
        invalid: str, redis_cache: utils.RedisCache) -> None:
    with pytest.raises(InvalidRules):

        async def item(*args, **kwargs):
            return github_types.GitHubContentFile({
                "content":
                encodebytes(invalid.encode()).decode(),
                "path":
                ".mergify.yml",
                "type":
                "file",
                "sha":
                "azertyu",
            })

        client = mock.Mock()
        client.item.return_value = item()
        installation = context.Installation(
            github_types.GitHubAccountIdType(0),
            github_types.GitHubLogin("foobar"),
            subscription.Subscription(redis_cache, 0, False, "", frozenset()),
            client,
            redis_cache,
        )
        repository = context.Repository(
            installation,
            github_types.GitHubRepositoryName("xyz"),
            github_types.GitHubRepositoryIdType(0),
        )

        config_file = await repository.get_mergify_config_file()
        assert config_file is not None
        get_mergify_config(config_file)
예제 #7
0
 async def get(
     cls: typing.Type[ApplicationClassT],
     redis: redis_utils.RedisCache,
     api_access_key: str,
     api_secret_key: str,
     account_scope: typing.Optional[github_types.GitHubLogin],
 ) -> ApplicationClassT:
     data = config.APPLICATION_APIKEYS.get(api_access_key)
     if data is None or data["api_secret_key"] != api_secret_key:
         raise ApplicationUserNotFound()
     if account_scope is not None and account_scope != data["account_login"]:
         raise ApplicationUserNotFound()
     return cls(
         redis,
         0,
         "on-premise-app",
         api_access_key,
         api_secret_key,
         ApplicationAccountScope({
             "id":
             github_types.GitHubAccountIdType(data["account_id"]),
             "login":
             github_types.GitHubLogin(data["account_login"]),
         }),
     )
예제 #8
0
async def test_get_mergify_config_location_from_cache(
    redis_cache: utils.RedisCache, ) -> None:
    client = mock.AsyncMock()
    client.auth.owner = "foo"
    client.item.side_effect = [
        http.HTTPNotFound("Not Found",
                          request=mock.Mock(),
                          response=mock.Mock()),
        http.HTTPNotFound("Not Found",
                          request=mock.Mock(),
                          response=mock.Mock()),
        github_types.GitHubContentFile({
            "content":
            encodebytes("whatever".encode()).decode(),
            "type":
            "file",
            "path":
            ".github/mergify.yml",
            "sha":
            github_types.SHAType("zeazeaze"),
        }),
    ]

    installation = context.Installation(
        github_types.GitHubAccountIdType(0),
        github_types.GitHubLogin("foo"),
        subscription.Subscription(redis_cache, 0, False, "", frozenset()),
        client,
        redis_cache,
    )
    repository = context.Repository(installation,
                                    github_types.GitHubRepositoryName("bar"))

    await repository.get_mergify_config_file()
    assert client.item.call_count == 3
    client.item.assert_has_calls([
        mock.call("/repos/foo/bar/contents/.mergify.yml"),
        mock.call("/repos/foo/bar/contents/.mergify/config.yml"),
        mock.call("/repos/foo/bar/contents/.github/mergify.yml"),
    ])

    client.item.reset_mock()
    client.item.side_effect = [
        github_types.GitHubContentFile({
            "content":
            encodebytes("whatever".encode()).decode(),
            "type":
            "file",
            "path":
            ".github/mergify.yml",
            "sha":
            github_types.SHAType("zeazeaze"),
        }),
    ]
    repository._cache = context.RepositoryCache()
    await repository.get_mergify_config_file()
    assert client.item.call_count == 1
    client.item.assert_has_calls([
        mock.call("/repos/foo/bar/contents/.github/mergify.yml"),
    ])
예제 #9
0
async def test_get_already_merged_summary(
    merged_by: str,
    raw_config: str,
    result: str,
    context_getter: conftest.ContextGetterFixture,
) -> None:
    ctxt = await context_getter(
        github_types.GitHubPullRequestNumber(1),
        merged=True,
        merged_by=github_types.GitHubAccount({
            "id":
            github_types.GitHubAccountIdType(1),
            "login":
            github_types.GitHubLogin(merged_by),
            "type":
            "User",
            "avatar_url":
            "",
        }),
    )
    ctxt.repository._caches.branch_protections[github_types.GitHubRefType(
        "main")] = None

    file = context.MergifyConfigFile(
        type="file",
        content="whatever",
        sha=github_types.SHAType("azertyuiop"),
        path="whatever",
        decoded_content=raw_config,
    )

    config = rules.get_mergify_config(file)
    match = await config["pull_request_rules"].get_pull_request_rule(ctxt)
    assert result == await actions_runner.get_already_merged_summary(
        ctxt, match)
예제 #10
0
async def send_refresh(
    redis_cache: utils.RedisCache,
    redis_stream: utils.RedisStream,
    repository: github_types.GitHubRepository,
    pull_request_number: typing.Optional[
        github_types.GitHubPullRequestNumber] = None,
    ref: typing.Optional[github_types.GitHubRefType] = None,
    action: github_types.GitHubEventRefreshActionType = "user",
) -> None:
    data = github_types.GitHubEventRefresh({
        "action": action,
        "ref": ref,
        "repository": repository,
        "pull_request_number": pull_request_number,
        "sender": {
            "login": github_types.GitHubLogin("<internal>"),
            "id": github_types.GitHubAccountIdType(0),
            "type": "User",
            "avatar_url": "",
        },
        "organization": repository["owner"],
        "installation": {
            "id": github_types.GitHubInstallationIdType(0),
            "account": repository["owner"],
        },
    })
    await filter_and_dispatch(redis_cache, redis_stream, "refresh",
                              str(uuid.uuid4()), data)
예제 #11
0
async def test_client_installation_token_with_owner_name(
    httpserver: httpserver.HTTPServer, ) -> None:
    await _do_test_client_installation_token(
        httpserver,
        "/users/owner/installation",
        owner_name=github_types.GitHubLogin("owner"),
    )
예제 #12
0
 def _extract_owner(
     self, stream_name: StreamNameType
 ) -> typing.Tuple[github_types.GitHubLogin, github_types.GitHubAccountIdType]:
     stream_splitted = stream_name.split("~")[1:]
     return (
         github_types.GitHubLogin(stream_splitted[0]),
         github_types.GitHubAccountIdType(int(stream_splitted[1])),
     )
예제 #13
0
def test_seats_renamed_account_repo() -> None:
    user1 = count_seats.SeatAccount(
        github_types.GitHubAccountIdType(123),
        github_types.GitHubLogin("user1"),
    )
    user1bis = count_seats.SeatAccount(
        github_types.GitHubAccountIdType(123),
        github_types.GitHubLogin("user1bis"),
    )
    user2 = count_seats.SeatAccount(
        github_types.GitHubAccountIdType(456),
        github_types.GitHubLogin("user2"),
    )
    user2bis = count_seats.SeatAccount(
        github_types.GitHubAccountIdType(456),
        github_types.GitHubLogin("user2bis"),
    )

    users = {user1, user2, user2bis, user1bis}
    assert len(users) == 2
    assert list(users)[0].login == "user2"
    assert list(users)[1].login == "user1"

    repo1 = count_seats.SeatRepository(
        github_types.GitHubRepositoryIdType(123),
        github_types.GitHubRepositoryName("repo1"),
    )
    repo1bis = count_seats.SeatRepository(
        github_types.GitHubRepositoryIdType(123),
        github_types.GitHubRepositoryName("repo1bis"),
    )
    repo2 = count_seats.SeatRepository(
        github_types.GitHubRepositoryIdType(456),
        github_types.GitHubRepositoryName("repo2"),
    )
    repo2bis = count_seats.SeatRepository(
        github_types.GitHubRepositoryIdType(456),
        github_types.GitHubRepositoryName("repo2bis"),
    )

    repos = {repo1, repo2, repo2bis, repo1bis}
    assert repos == {repo1, repo2}
예제 #14
0
 def _get_users_from_config() -> typing.List[UserTokensUser]:
     return [{
         "login":
         github_types.GitHubLogin(login),
         "oauth_access_token":
         github_types.GitHubOAuthToken(oauth_access_token),
         "email":
         None,
         "name":
         None,
     } for login, oauth_access_token in config.ACCOUNT_TOKENS.items()]
예제 #15
0
    async def _resolve_login(
            self, name: str) -> typing.List[github_types.GitHubLogin]:
        if not name:
            return []
        elif not isinstance(name, str):
            return [github_types.GitHubLogin(name)]
        elif name[0] != "@":
            return [github_types.GitHubLogin(name)]

        if "/" in name:
            organization, _, team_slug = name.partition("/")
            if not team_slug or "/" in team_slug:
                # Not a team slug
                return [github_types.GitHubLogin(name)]
            organization = github_types.GitHubLogin(organization[1:])
            if organization != self.pull["base"]["repo"]["owner"]["login"]:
                # TODO(sileht): We don't have the permissions, maybe we should report this
                return [github_types.GitHubLogin(name)]
            team_slug = github_types.GitHubTeamSlug(team_slug)
        else:
            team_slug = github_types.GitHubTeamSlug(name[1:])

        try:
            return await self.repository.installation.get_team_members(
                team_slug)
        except http.HTTPClientSideError as e:
            self.log.warning(
                "fail to get the organization, team or members",
                team=name,
                status_code=e.status_code,
                detail=e.message,
            )
        return [github_types.GitHubLogin(name)]
예제 #16
0
def repository(redis_cache, fake_client):
    gh_owner = github_types.GitHubAccount({
        "login":
        github_types.GitHubLogin("user"),
        "id":
        github_types.GitHubAccountIdType(0),
        "type":
        "User",
        "avatar_url":
        "",
    })

    gh_repo = github_types.GitHubRepository({
        "full_name":
        "user/name",
        "name":
        github_types.GitHubRepositoryName("name"),
        "private":
        False,
        "id":
        github_types.GitHubRepositoryIdType(0),
        "owner":
        gh_owner,
        "archived":
        False,
        "url":
        "",
        "html_url":
        "",
        "default_branch":
        github_types.GitHubRefType("ref"),
    })
    installation = context.Installation(
        github_types.GitHubAccountIdType(123),
        github_types.GitHubLogin("user"),
        subscription.Subscription(redis_cache, 0, False, "", frozenset()),
        fake_client,
        redis_cache,
    )
    return context.Repository(installation, gh_repo)
예제 #17
0
def repository(redis_cache, fake_client):
    installation = context.Installation(
        github_types.GitHubAccountIdType(123),
        github_types.GitHubLogin("user"),
        subscription.Subscription(redis_cache, 0, False, "", frozenset()),
        fake_client,
        redis_cache,
    )
    return context.Repository(
        installation,
        github_types.GitHubRepositoryName("name"),
        github_types.GitHubRepositoryIdType(123),
    )
예제 #18
0
async def test_client_401_raise_ratelimit(
        httpserver: httpserver.HTTPServer) -> None:
    owner = github_types.GitHubLogin("owner")
    repo = "repo"

    httpserver.expect_request("/users/owner/installation").respond_with_json({
        "id":
        12345,
        "target_type":
        "User",
        "permissions": {
            "checks": "write",
            "contents": "write",
            "pull_requests": "write",
        },
        "account": {
            "login": "******",
            "id": 12345
        },
    })
    httpserver.expect_request(
        "/app/installations/12345/access_tokens").respond_with_json(
            {
                "token": "<token>",
                "expires_at": "2100-12-31T23:59:59Z"
            },
            headers={
                "X-RateLimit-Remaining": 5000,
                "X-RateLimit-Reset": 1234567890
            },
        )

    httpserver.expect_oneshot_request(
        "/repos/owner/repo/pull/1").respond_with_json(
            {"message": "quota !"},
            status=403,
            headers={
                "X-RateLimit-Remaining": 0,
                "X-RateLimit-Reset": 1234567890
            },
        )

    with mock.patch(
            "mergify_engine.config.GITHUB_API_URL",
            httpserver.url_for("/")[:-1],
    ):
        async with github.aget_client(owner) as client:
            with pytest.raises(exceptions.RateLimited):
                await client.item(f"/repos/{owner}/{repo}/pull/1")

    httpserver.check_assertions()
예제 #19
0
async def test_update_apikey(redis_cache):
    api_access_key = "a" * 32
    api_secret_key = "s" * 32
    account_id = github_types.GitHubAccountIdType(12345)
    account_login = github_types.GitHubLogin("login")

    app = application.Application(
        redis_cache,
        0,
        "app name",
        api_access_key,
        api_secret_key,
        {"id": account_id, "login": account_login},
    )

    await app.save_to_cache()
    rapp = await application.Application._retrieve_from_cache(
        redis_cache, api_access_key, api_secret_key, account_login
    )
    assert app == rapp

    await application.Application.update(
        redis_cache,
        api_access_key,
        application.ApplicationDashboardJSON(
            {
                "id": 1,
                "name": "new name",
                "github_account": {
                    "id": 424242,
                    "login": "******",
                    "type": "User",
                    "avatar_url": "",
                },
            }
        ),
    )
    rapp = await application.Application._retrieve_from_cache(
        redis_cache, api_access_key, api_secret_key, account_login
    )
    expected_app = application.Application(
        redis_cache,
        1,
        "new name",
        api_access_key,
        api_secret_key,
        {"id": 424242, "login": "******"},
        ttl=application.Application.RETENTION_SECONDS,
    )

    assert expected_app == rapp
예제 #20
0
async def _resolve_login(
    ctxt: context.Context, name: str
) -> typing.List[github_types.GitHubLogin]:
    if not name:
        return []
    elif not isinstance(name, str):
        return [github_types.GitHubLogin(name)]
    elif name[0] != "@":
        return [github_types.GitHubLogin(name)]

    if "/" in name:
        organization, _, team_slug = name.partition("/")
        if not team_slug or "/" in team_slug:
            raise LiveResolutionFailure(f"Team `{name}` is invalid")
        organization = github_types.GitHubLogin(organization[1:])
        expected_organization = ctxt.pull["base"]["repo"]["owner"]["login"]
        if organization != expected_organization:
            raise LiveResolutionFailure(
                f"Team `{name}` is not part of the organization `{expected_organization}`"
            )
        team_slug = github_types.GitHubTeamSlug(team_slug)
    else:
        team_slug = github_types.GitHubTeamSlug(name[1:])

    try:
        return await ctxt.repository.installation.get_team_members(team_slug)
    except http.HTTPNotFound:
        raise LiveResolutionFailure(f"Team `{name}` does not exist")
    except http.HTTPClientSideError as e:
        ctxt.log.warning(
            "fail to get the organization, team or members",
            team=name,
            status_code=e.status_code,
            detail=e.message,
        )
        raise LiveResolutionFailure(
            f"Failed retrieve team `{name}`, details: {e.message}"
        )
예제 #21
0
def _check_GitHubLogin_format(
    value: typing.Optional[str],
    _type: typing.Literal["login", "organization"] = "login",
) -> github_types.GitHubLogin:
    # GitHub says login cannot:
    # - start with an hyphen
    # - ends with an hyphen
    # - contains something else than hyphen and alpha numericals characters
    if not value:
        raise voluptuous.Invalid(f"A GitHub {_type} cannot be an empty string")
    if (value[0] == "-" or value[-1] == "-" or not value.isascii()
            or not value.replace("-", "").isalnum()):
        raise voluptuous.Invalid(f"GitHub {_type} contains invalid characters")
    return github_types.GitHubLogin(value)
예제 #22
0
def fake_repository(
    redis_links: redis_utils.RedisLinks,
    fake_subscription: subscription.Subscription,
) -> context.Repository:
    gh_owner = github_types.GitHubAccount({
        "login":
        github_types.GitHubLogin("Mergifyio"),
        "id":
        github_types.GitHubAccountIdType(0),
        "type":
        "User",
        "avatar_url":
        "",
    })

    gh_repo = github_types.GitHubRepository({
        "full_name":
        "Mergifyio/mergify-engine",
        "name":
        github_types.GitHubRepositoryName("mergify-engine"),
        "private":
        False,
        "id":
        github_types.GitHubRepositoryIdType(0),
        "owner":
        gh_owner,
        "archived":
        False,
        "url":
        "",
        "html_url":
        "",
        "default_branch":
        github_types.GitHubRefType("main"),
    })
    installation_json = github_types.GitHubInstallation({
        "id":
        github_types.GitHubInstallationIdType(12345),
        "target_type":
        gh_owner["type"],
        "permissions": {},
        "account":
        gh_owner,
    })

    fake_client = mock.Mock()
    installation = context.Installation(installation_json, fake_subscription,
                                        fake_client, redis_links)
    return context.Repository(installation, gh_repo)
예제 #23
0
async def _refresh(
    owner: github_types.GitHubLogin,
    repo: str,
    action: github_types.GitHubEventRefreshActionType = "user",
    ref: typing.Optional[github_types.GitHubRefType] = None,
    pull_request: typing.Optional[github_types.GitHubPullRequest] = None,
) -> responses.Response:
    data = github_types.GitHubEventRefresh({
        "action": action,
        "organization": {
            "login": owner,
            "id": github_types.GitHubAccountIdType(0),
            "type": "Organization",
        },
        "installation": {
            "id": 0,
            "account": {
                "login": owner,
                "id": github_types.GitHubAccountIdType(0),
                "type": "Organization",
            },
        },
        "repository": {
            "default_branch": github_types.GitHubRefType(""),
            "id": github_types.GitHubRepositoryIdType(0),
            "private": False,
            "archived": False,
            "url": "",
            "name": repo,
            "owner": {
                "login": owner,
                "id": github_types.GitHubAccountIdType(0),
                "type": "Organization",
            },
            "full_name": f"{owner}/{repo}",
        },
        "sender": {
            "login": github_types.GitHubLogin("<internal>"),
            "id": github_types.GitHubAccountIdType(0),
            "type": "User",
        },
        "ref": ref,
        "pull_request": pull_request,
    })

    await github_events.filter_and_dispatch(_AREDIS_STREAM, "refresh",
                                            str(uuid.uuid4()), data)

    return responses.Response("Refresh queued", status_code=202)
예제 #24
0
async def test_client_abuse_403_no_header(
        httpserver: httpserver.HTTPServer) -> None:

    abuse_message = ("You have triggered an abuse detection mechanism. "
                     "Please wait a few minutes before you try again.")
    httpserver.expect_request("/users/owner/installation").respond_with_json({
        "id":
        12345,
        "target_type":
        "User",
        "permissions": {
            "checks": "write",
            "contents": "write",
            "pull_requests": "write",
        },
        "account": {
            "login": "******",
            "id": 12345
        },
    })
    httpserver.expect_request(
        "/app/installations/12345/access_tokens").respond_with_json({
            "token":
            "<token>",
            "expires_at":
            "2100-12-31T23:59:59Z"
        })

    httpserver.expect_oneshot_request("/").respond_with_json(
        {"message": abuse_message},
        status=403,
    )

    with mock.patch(
            "mergify_engine.config.GITHUB_API_URL",
            httpserver.url_for("/")[:-1],
    ):
        async with github.AsyncGithubInstallationClient(
                github.get_auth(github_types.GitHubLogin("owner"))) as client:
            with pytest.raises(http.HTTPClientSideError) as exc_info:
                await client.get(httpserver.url_for("/"))

    assert exc_info.value.message == abuse_message
    assert exc_info.value.status_code == 403
    assert exc_info.value.response.status_code == 403
    assert str(exc_info.value.request.url) == httpserver.url_for("/")
    assert len(httpserver.log) == 3

    httpserver.check_assertions()
예제 #25
0
async def send_refresh(
    pull: github_types.GitHubPullRequest,
    action: github_types.GitHubEventRefreshActionType = "user",
) -> None:
    data = github_types.GitHubEventRefresh({
        "action":
        action,
        "ref":
        None,
        "repository":
        pull["base"]["repo"],
        "pull_request":
        pull,
        "ref":
        None,
        "sender": {
            "login": github_types.GitHubLogin("<internal>"),
            "id": github_types.GitHubAccountIdType(0),
            "type": "User",
        },
        "organization":
        pull["base"]["repo"]["owner"],
        "installation": {
            "id": 0,
            "account": {
                "login": github_types.GitHubLogin(""),
                "id": github_types.GitHubAccountIdType(0),
                "type": "User",
            },
        },
    })
    redis = await utils.create_aredis_for_stream()
    try:
        await filter_and_dispatch(redis, "refresh", str(uuid.uuid4()), data)
    finally:
        redis.connection_pool.disconnect()
예제 #26
0
async def _send_refresh(
    redis_stream: "redis_utils.RedisStream",
    repository: github_types.GitHubRepository,
    action: github_types.GitHubEventRefreshActionType,
    source: str,
    pull_request_number: typing.Optional[
        github_types.GitHubPullRequestNumber] = None,
    ref: typing.Optional[github_types.GitHubRefType] = None,
    score: typing.Optional[str] = None,
) -> None:
    # TODO(sileht): move refresh stuff into it's own file
    # Break circular import
    from mergify_engine import github_events
    from mergify_engine import worker

    data = github_types.GitHubEventRefresh({
        "action": action,
        "source": source,
        "ref": ref,
        "pull_request_number": pull_request_number,
        "repository": repository,
        "sender": {
            "login": github_types.GitHubLogin("<internal>"),
            "id": github_types.GitHubAccountIdType(0),
            "type": "User",
            "avatar_url": "",
        },
        "organization": repository["owner"],
        "installation": {
            "id": github_types.GitHubInstallationIdType(0),
            "account": repository["owner"],
            "target_type": repository["owner"]["type"],
            "permissions": {},
        },
    })

    slim_event = github_events._extract_slim_event("refresh", data)
    await worker.push(
        redis_stream,
        repository["owner"]["id"],
        repository["owner"]["login"],
        repository["id"],
        repository["name"],
        pull_request_number,
        "refresh",
        slim_event,
        None,
    )
예제 #27
0
async def test_client_401_raise_ratelimit(
        respx_mock: respx.MockRouter) -> None:
    owner_id = github_types.GitHubAccountIdType(12345)
    owner_login = github_types.GitHubLogin("owner")
    repo = "repo"

    respx_mock.get("/user/12345/installation").respond(
        200,
        json={
            "id": 12345,
            "target_type": "User",
            "permissions": {
                "checks": "write",
                "contents": "write",
                "pull_requests": "write",
            },
            "account": {
                "login": "******",
                "id": 12345
            },
        },
    )

    respx_mock.post("/app/installations/12345/access_tokens").respond(
        200,
        json={
            "token": "<token>",
            "expires_at": "2100-12-31T23:59:59Z"
        },
        headers={
            "X-RateLimit-Remaining": "5000",
            "X-RateLimit-Reset": "1234567890",
        },
    )

    respx_mock.get("/repos/owner/repo/pull/1").respond(
        403,
        json={"message": "quota !"},
        headers={
            "X-RateLimit-Remaining": "0",
            "X-RateLimit-Reset": "1234567890"
        },
    )

    installation_json = await github.get_installation_from_account_id(owner_id)
    async with github.aget_client(installation_json) as client:
        with pytest.raises(exceptions.RateLimited):
            await client.item(f"/repos/{owner_login}/{repo}/pull/1")
예제 #28
0
async def test_run_command_with_user(
    user_id: int,
    permission: str,
    comment: str,
    result: typing.Optional[str],
    context_getter: conftest.ContextGetterFixture,
) -> None:

    user = github_types.GitHubAccount(
        {
            "id": github_types.GitHubAccountIdType(user_id),
            "login": github_types.GitHubLogin("wall-e"),
            "type": "Bot",
            "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
        }, )

    client = mock.Mock()
    client.item = mock.AsyncMock()
    client.item.return_value = {
        "permission": permission,
        "user": user,
    }
    client.post = mock.AsyncMock()

    ctxt = await context_getter(github_types.GitHubPullRequestNumber(1))
    ctxt.repository.installation.client = client

    await commands_runner.handle(
        ctxt=ctxt,
        mergify_config=EMPTY_CONFIG,
        comment="unrelated",
        user=None,
        rerun=True,
    )
    assert len(client.post.call_args_list) == 0

    await commands_runner.handle(
        ctxt=ctxt,
        mergify_config=EMPTY_CONFIG,
        comment=comment,
        user=user,
    )

    if result is None:
        assert len(client.post.call_args_list) == 0
    else:
        assert len(client.post.call_args_list) == 1
        assert result in client.post.call_args_list[0][1]["json"]["body"]
예제 #29
0
async def test_client_installation_HTTP_404(
        httpserver: httpserver.HTTPServer) -> None:
    httpserver.expect_request("/users/owner/installation").respond_with_json(
        {"message": "Repository not found"}, status=404)
    with mock.patch(
            "mergify_engine.config.GITHUB_API_URL",
            httpserver.url_for("/")[:-1],
    ):
        async with github.AsyncGithubInstallationClient(
                github.get_auth(github_types.GitHubLogin("owner"))) as client:
            with pytest.raises(exceptions.MergifyNotInstalled):
                await client.get(httpserver.url_for("/"))

    assert len(httpserver.log) == 1

    httpserver.check_assertions()
예제 #30
0
 async def get(cls: typing.Type[UserTokensT], redis: utils.RedisCache,
               owner_id: int) -> UserTokensT:
     return cls(
         redis,
         owner_id,
         [{
             "id":
             github_types.GitHubAccountIdType(_id),
             "login":
             github_types.GitHubLogin(login),
             "oauth_access_token":
             github_types.GitHubOAuthToken(oauth_access_token),
             "email":
             None,
             "name":
             None,
         } for _id, login, oauth_access_token in config.ACCOUNT_TOKENS],
     )