async def test_client_user_token(respx_mock: respx.MockRouter) -> None: 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.get("/", headers__contains={ "Authorization": "token <user-token>" }).respond(200, json={"work": True}) installation_json = await github.get_installation_from_account_id( github_types.GitHubAccountIdType(12345)) async with github.AsyncGithubInstallationClient( github.GithubAppInstallationAuth(installation_json)) as client: ret = await client.get( "/", oauth_token=github_types.GitHubOAuthToken("<user-token>"), ) assert ret.json()["work"]
async def test_client_installation_HTTP_404( respx_mock: respx.MockRouter) -> None: respx_mock.get("/user/12345/installation").respond( 404, json={"message": "Repository not found"}) with pytest.raises(exceptions.MergifyNotInstalled): await github.get_installation_from_account_id( github_types.GitHubAccountIdType(12345))
async def test_client_temporary_HTTP_500(respx_mock: respx.MockRouter) -> None: respx_mock.get("/").mock(side_effect=[ httpx.Response(500, text="This is a 5XX error"), httpx.Response(500, text="This is a 5XX error"), httpx.Response(500, text="This is a 5XX error"), httpx.Response(200, text="It works now !"), ]) async with http.AsyncClient() as client: await client.get("https://foobar/")
async def test_client_HTTP_500(respx_mock: respx.MockRouter) -> None: respx_mock.get("https://foobar/").respond(500, text="This is a 5XX error") async with http.AsyncClient() as client: with pytest.raises(http.HTTPServerSideError) as exc_info: await client.get("https://foobar/") assert exc_info.value.message == "This is a 5XX error" assert exc_info.value.status_code == 500 assert exc_info.value.response.status_code == 500 assert str(exc_info.value.request.url) == "https://foobar/"
async def test_client_installation_HTTP_301( respx_mock: respx.MockRouter) -> None: url_prefix = parse.urlparse(config.GITHUB_REST_API_URL).path respx_mock.get("/user/12345/installation").respond( 301, headers={"Location": f"{url_prefix}/repositories/12345/installation"}, ) respx_mock.get("/repositories/12345/installation").respond( 404, json={"message": "Repository not found"}) with pytest.raises(exceptions.MergifyNotInstalled): await github.get_installation_from_account_id( github_types.GitHubAccountIdType(12345))
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_client_installation_HTTP_500( respx_mock: respx.MockRouter) -> None: respx_mock.get("/user/12345/installation").mock(side_effect=[ httpx.Response(500, text="This is a 5XX error"), httpx.Response(500, text="This is a 5XX error"), httpx.Response(500, text="This is a 5XX error"), httpx.Response(500, text="This is a 5XX error"), httpx.Response(500, text="This is a 5XX error"), ]) with pytest.raises(http.HTTPServerSideError) as exc_info: await github.get_installation_from_account_id( github_types.GitHubAccountIdType(12345)) assert exc_info.value.message == "This is a 5XX error" assert exc_info.value.status_code == 500 assert exc_info.value.response.status_code == 500 assert (str(exc_info.value.request.url) == f"{config.GITHUB_REST_API_URL}/user/12345/installation")
async def test_client_abuse_403_no_header( respx_mock: respx.MockRouter) -> None: abuse_message = ("You have triggered an abuse detection mechanism. " "Please wait a few minutes before you try again.") 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" }) respx_mock.get("/").respond( 403, json={"message": abuse_message}, ) installation_json = await github.get_installation_from_account_id( github_types.GitHubAccountIdType(12345)) async with github.AsyncGithubInstallationClient( github.GithubAppInstallationAuth(installation_json)) as client: with pytest.raises(http.HTTPClientSideError) as exc_info: await client.get("/") 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) == f"{config.GITHUB_REST_API_URL}/"
async def test_client_installation_token_with_owner_id( respx_mock: respx.MockRouter, ) -> None: 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": "<installation-token>", "expires_at": "2100-12-31T23:59:59Z" }, ) respx_mock.get("/", headers__contains={ "Authorization": "token <installation-token>" }).respond(200, json={"work": True}) installation_json = await github.get_installation_from_account_id( github_types.GitHubAccountIdType(12345)) async with github.AsyncGithubInstallationClient( github.GithubAppInstallationAuth(installation_json)) as client: ret = await client.get("/") assert ret.json()["work"] assert isinstance(client.auth, github.GithubAppInstallationAuth) assert client.auth._installation is not None assert client.auth._installation["account"]["login"] == "testing" assert client.auth._installation["account"]["id"] == 12345
def mock_director_services(director_mockup: respx.MockRouter, app: FastAPI) -> MockRouter: mock_route = director_mockup.get( f"{app.state.settings.CATALOG_DIRECTOR.base_url}/services", name="list_services", ).respond(200, json={"data": ["blahblah"]}) response = httpx.get( f"{app.state.settings.CATALOG_DIRECTOR.base_url}/services") assert mock_route.called assert response.json() == {"data": ["blahblah"]} return director_mockup
async def test_client_access_token_HTTP_500( respx_mock: respx.MockRouter) -> None: 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 }, }, ) retries = [0] def error_500_tracker(_): retries[0] += 1 return httpx.Response(500, text="This is a 5XX error") respx_mock.post("/app/installations/12345/access_tokens").mock( side_effect=error_500_tracker) installation_json = await github.get_installation_from_account_id( github_types.GitHubAccountIdType(12345)) async with github.AsyncGithubInstallationClient( github.GithubAppInstallationAuth(installation_json)) as client: with pytest.raises(http.HTTPServerSideError) as exc_info: await client.get("/") assert exc_info.value.message == "This is a 5XX error" assert exc_info.value.status_code == 500 assert exc_info.value.response.status_code == 500 assert (str( exc_info.value.request.url ) == f"{config.GITHUB_REST_API_URL}/app/installations/12345/access_tokens")
async def _do_test_client_retry_429(respx_mock: respx.MockRouter, retry_after: str) -> datetime.datetime: records: typing.List[datetime.datetime] = [] def record_date(_): if records: records.append(date.utcnow()) return httpx.Response(200, text="It works now !") else: records.append(date.utcnow()) return httpx.Response( 429, text="This is a 429 error", headers={"Retry-After": retry_after}, ) respx_mock.get("/").mock(side_effect=record_date) async with http.AsyncClient() as client: await client.get("https://foobar/") return records[1]
async def test_configuration_check_not_needed_with_configuration_deleted( github_server: respx.MockRouter, redis_cache: utils.RedisCache ) -> None: github_server.get("/user/12345/installation").respond( 200, json={ "id": 12345, "permissions": { "checks": "write", "contents": "write", "pull_requests": "write", }, "target_type": GH_OWNER["type"], "account": GH_OWNER, }, ) github_server.get(f"{BASE_URL}/pulls/1",).respond( 200, json=typing.cast(typing.Dict[typing.Any, typing.Any], GH_PULL), ) github_server.get(f"{BASE_URL}/contents/.mergify.yml").respond( 200, json=typing.cast( typing.Dict[typing.Any, typing.Any], github_types.GitHubContentFile( { "type": "file", "content": FAKE_MERGIFY_CONTENT, "path": ".mergify.yml", "sha": github_types.SHAType( "739e5ec79e358bae7a150941a148b4131233ce2c" ), } ), ), ) # Summary is present, no need to redo the check github_server.get( f"{BASE_URL}/commits/{GH_PULL['head']['sha']}/check-runs" ).respond( 200, json={"check_runs": [SUMMARY_CHECK, CONFIGURATION_DELETED_CHECK]}, ) installation_json = await github.get_installation_from_account_id(GH_OWNER["id"]) async with github.AsyncGithubInstallationClient( github.GithubAppInstallationAuth(installation_json) ) as client: installation = context.Installation( installation_json, subscription.Subscription( redis_cache, 0, "", frozenset([subscription.Features.PUBLIC_REPOSITORY]), 0, ), client, redis_cache, mock.Mock(), ) repository = context.Repository(installation, GH_REPO) ctxt = await repository.get_pull_request_context( github_types.GitHubPullRequestNumber(1) ) main_config_file = await repository.get_mergify_config_file() changed = await engine._check_configuration_changes(ctxt, main_config_file) assert changed
async def test_configuration_initial( github_server: respx.MockRouter, redis_cache: utils.RedisCache ) -> None: github_server.get("/user/12345/installation").respond( 200, json={ "id": 12345, "permissions": { "checks": "write", "contents": "write", "pull_requests": "write", }, "target_type": GH_OWNER["type"], "account": GH_OWNER, }, ) github_server.get(f"{BASE_URL}/pulls/1",).respond( 200, json=typing.cast(typing.Dict[typing.Any, typing.Any], GH_PULL), ) github_server.route( respx.patterns.M(method="GET", path=f"{BASE_URL}/contents/.mergify.yml") & ~respx.patterns.M(params__contains={"ref": GH_PULL["merge_commit_sha"]}) ).respond(404) github_server.route( respx.patterns.M(method="GET", path=f"{BASE_URL}/contents/.mergify/config.yml") & ~respx.patterns.M(params__contains={"ref": GH_PULL["merge_commit_sha"]}) ).respond(404) github_server.route( respx.patterns.M(method="GET", path=f"{BASE_URL}/contents/.github/mergify.yml") & ~respx.patterns.M(params__contains={"ref": GH_PULL["merge_commit_sha"]}) ).respond(404) github_server.route( respx.patterns.M(method="GET", path=f"{BASE_URL}/contents/.mergify.yml") & respx.patterns.M(params__contains={"ref": GH_PULL["merge_commit_sha"]}) ).respond( 200, json=typing.cast( typing.Dict[typing.Any, typing.Any], github_types.GitHubContentFile( { "type": "file", "content": FAKE_MERGIFY_CONTENT, "path": ".mergify.yml", "sha": github_types.SHAType( "739e5ec79e358bae7a150941a148b4131233ce2c" ), } ), ), ) github_server.route( respx.patterns.M(method="GET", path=f"{BASE_URL}/contents/.github/mergify.yml") & respx.patterns.M(params__contains={"ref": GH_PULL["merge_commit_sha"]}) ).respond(404) github_server.route( respx.patterns.M(method="GET", path=f"{BASE_URL}/contents/.mergify/config.yml") & respx.patterns.M(params__contains={"ref": GH_PULL["merge_commit_sha"]}) ).respond(404) github_server.get( f"{BASE_URL}/commits/{GH_PULL['head']['sha']}/check-runs" ).respond(200, json={"check_runs": []}) github_server.post(f"{BASE_URL}/check-runs").respond( 200, json=typing.cast(typing.Dict[typing.Any, typing.Any], CHECK_RUN) ) installation_json = await github.get_installation_from_account_id(GH_OWNER["id"]) async with github.AsyncGithubInstallationClient( github.GithubAppInstallationAuth(installation_json) ) as client: installation = context.Installation( installation_json, subscription.Subscription( redis_cache, 0, "", frozenset([subscription.Features.PUBLIC_REPOSITORY]), 0, ), client, redis_cache, mock.Mock(), ) repository = context.Repository(installation, GH_REPO) ctxt = await repository.get_pull_request_context( github_types.GitHubPullRequestNumber(1) ) main_config_file = await repository.get_mergify_config_file() assert main_config_file is None changed = await engine._check_configuration_changes(ctxt, main_config_file) assert changed
async def test_message_format_client_HTTP_400( respx_mock: respx.MockRouter) -> None: respx_mock.get("https://foobar/").respond(400, json={ "message": "This is a 4XX error", "documentation_url": "fake_url" }) async with http.AsyncClient() as client: with pytest.raises(http.HTTPClientSideError) as exc_info: await client.get("https://foobar/") assert exc_info.value.message == "This is a 4XX error" respx_mock.get("https://foobar/").respond( 400, json={ "message": "error message", "errors": ["This is a 4XX error"], "documentation_url": "fake_url", }, ) async with http.AsyncClient() as client: with pytest.raises(http.HTTPClientSideError) as exc_info: await client.get("https://foobar/") assert exc_info.value.message == "This is a 4XX error" respx_mock.get("https://foobar/").respond( 400, json={ "message": "This is a 4XX error", "errors": [{ "resource": "test", "field": "test", "code": "test" }], "documentation_url": "fake_url", }, ) async with http.AsyncClient() as client: with pytest.raises(http.HTTPClientSideError) as exc_info: await client.get("https://foobar/") assert exc_info.value.message == "This is a 4XX error" respx_mock.get("https://foobar/").respond( 400, json={ "message": "error message", "errors": [{ "resource": "test", "code": "test", "field": "test", "message": "This is a 4XX error", }], "documentation_url": "fake_url", }, ) async with http.AsyncClient() as client: with pytest.raises(http.HTTPClientSideError) as exc_info: await client.get("https://foobar/") assert exc_info.value.message == "This is a 4XX error" respx_mock.get("https://foobar/").respond( 400, json={ "not_message_key": "false_key", "documentation_url": "fake_url", }, ) async with http.AsyncClient() as client: with pytest.raises(http.HTTPClientSideError) as exc_info: await client.get("https://foobar/") assert exc_info.value.message == "No error message provided by GitHub"