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
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
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 )
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()
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)), )
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)
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"]), }), )
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"), ])
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)
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)
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"), )
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])), )
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}
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()]
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)]
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)
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), )
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()
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
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}" )
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)
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)
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)
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()
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()
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, )
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")
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"]
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()
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], )