示例#1
0
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"]
示例#2
0
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))
示例#3
0
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/")
示例#4
0
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/"
示例#5
0
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))
示例#6
0
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")
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
示例#8
0
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")
示例#9
0
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]
示例#10
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")
示例#11
0
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}/"
示例#12
0
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
示例#13
0
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
示例#14
0
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
示例#15
0
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"