Exemple #1
0
def test_client_user_token(httpserver: httpserver.HTTPServer) -> None:
    with mock.patch(
            "mergify_engine.config.GITHUB_API_URL",
            httpserver.url_for("/")[:-1],
    ):
        httpserver.expect_request("/users/owner").respond_with_json({
            "login":
            "******",
            "id":
            12345,
        })
        httpserver.expect_request("/",
                                  headers={
                                      "Authorization": "token <user-token>"
                                  }).respond_with_json({"work": True},
                                                       status=200)

        with github.GithubInstallationClient(
                github.get_auth("owner")) as client:
            ret = client.get(
                httpserver.url_for("/"),
                oauth_token="<user-token>")  # type: ignore[call-arg]
            assert ret.json()["work"]

    assert len(httpserver.log) == 2
Exemple #2
0
async def test_subscription_on_premise_valid(
    redis_cache: utils.RedisCache,
    httpserver: httpserver.HTTPServer,
) -> None:

    httpserver.expect_request(
        "/on-premise/subscription/1234").respond_with_json({
            "subscription_active":
            True,
            "subscription_reason":
            "azertyuio",
            "features": [
                "private_repository",
                "large_repository",
                "priority_queues",
                "custom_checks",
                "random_request_reviews",
                "merge_bot_account",
                "queue_action",
            ],
        })

    with mock.patch(
            "mergify_engine.config.SUBSCRIPTION_BASE_URL",
            httpserver.url_for("/")[:-1],
    ):
        await subscription.SubscriptionDashboardOnPremise.get_subscription(
            redis_cache, 1234)

    assert len(httpserver.log) == 1
    httpserver.check_assertions()
Exemple #3
0
def test_market_event_forward(_: mock.PropertyMock,
                              httpserver: httpserver.HTTPServer) -> None:

    with open(
            os.path.join(os.path.dirname(__file__), "events",
                         "marketplace.json")) as f:
        data = f.read()

    headers = {
        "X-GitHub-Delivery": str(uuid.uuid4()),
        "X-GitHub-Event": "purchased",
        "X-Hub-Signature":
        f"sha1={utils.compute_hmac(data.encode(), config.WEBHOOK_SECRET)}",
        "User-Agent": "GitHub-Hookshot/044aadd",
        "Content-Type": "application/json",
    }
    httpserver.expect_request("/", method="POST", data=data,
                              headers=headers).respond_with_data("")

    with mock.patch(
            "mergify_engine.config.WEBHOOK_MARKETPLACE_FORWARD_URL",
            httpserver.url_for("/"),
    ):
        with testclient.TestClient(root.app) as client:
            client.post("/marketplace", data=data, headers=headers)

    httpserver.check_assertions()  # type: ignore[no-untyped-call]
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()
Exemple #5
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()
Exemple #6
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()
Exemple #7
0
async def test_send_seats(httpserver: httpserver.HTTPServer) -> None:
    httpserver.expect_request(
        "/on-premise/report",
        method="POST",
        json={"seats": 5, "write_users": 5, "active_users": 2, "engine_version": "dev"},
    ).respond_with_data("Accepted", status=201)
    with mock.patch(
        "mergify_engine.config.SUBSCRIPTION_BASE_URL",
        httpserver.url_for("/")[:-1],
    ):
        await count_seats.send_seats(count_seats.SeatsCountResultT(5, 2))

    assert len(httpserver.log) == 1

    httpserver.check_assertions()  # type: ignore [no-untyped-call]
async def test_client_HTTP_500(httpserver: httpserver.HTTPServer) -> None:
    httpserver.expect_request("/").respond_with_data("This is an 5XX error", status=500)

    async with http.AsyncClient() 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("/")

    httpserver.check_assertions()
Exemple #9
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()
Exemple #10
0
def test_client_temporary_HTTP_500(httpserver: httpserver.HTTPServer) -> None:
    httpserver.expect_oneshot_request("/").respond_with_data(
        "This is an 5XX error", status=500)
    httpserver.expect_oneshot_request("/").respond_with_data(
        "This is an 5XX error", status=500)
    httpserver.expect_oneshot_request("/").respond_with_data(
        "This is an 5XX error", status=500)
    httpserver.expect_request("/").respond_with_data("It works now !",
                                                     status=200)

    with http.Client() as client:
        client.get(httpserver.url_for("/"))

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

    httpserver.check_assertions()
Exemple #11
0
def test_proxy_connect(httpserver: httpserver.HTTPServer):
    """
    Check that ghosttunnel can correctly proxy cleartext data (http)
    towards a TLS server.
    """
    # Set up HTTPS server
    # TODO: mTLS ? not sure if handled…
    assert httpserver.ssl_context is not None
    httpserver.ssl_context.load_cert_chain("tests/assets/server.pem", "tests/assets/server.key")
    httpserver.expect_request("/aaa").respond_with_data('bbb')
    target = ProxyTarget(host=httpserver.host, port=httpserver.port)

    with GhostunnelWrapper(target, "tests/assets/ca.pem") as g:
        g.wait_for_readiness()
        r = rq.get("http://localhost:8083/aaa")
    assert r.status_code == 200
    assert r.content == b"bbb"
Exemple #12
0
async def test_subscription_on_premise_invalid_sub(
    redis_cache: utils.RedisCache,
    httpserver: httpserver.HTTPServer,
) -> None:

    httpserver.expect_request(
        "/on-premise/subscription/1234").respond_with_json(
            {"message": "error"}, status=403)

    with mock.patch(
            "mergify_engine.config.SUBSCRIPTION_BASE_URL",
            httpserver.url_for("/")[:-1],
    ):
        with pytest.raises(exceptions.MergifyNotInstalled):
            await subscription.SubscriptionDashboardOnPremise.get_subscription(
                redis_cache, 1234)

    assert len(httpserver.log) == 1
    httpserver.check_assertions()
Exemple #13
0
async def test_client_access_token_HTTP_500(
        httpserver: httpserver.HTTPServer) -> None:
    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_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("/"))

    # installation request + 5 retries
    assert len(httpserver.log) == 6

    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(
        "/app/installations/12345/access_tokens")

    httpserver.check_assertions()
async def _do_test_client_retry_429(
    httpserver: httpserver.HTTPServer, retry_after: str, expected_seconds: int
) -> None:
    records = []

    def record_date(_):
        records.append(datetime.datetime.utcnow())
        return Response("It works now !", 200)

    httpserver.expect_oneshot_request("/").respond_with_data(
        "This is an 429 error", status=429, headers={"Retry-After": retry_after}
    )
    httpserver.expect_request("/").respond_with_handler(record_date)

    async with http.AsyncClient() as client:
        now = datetime.datetime.utcnow()
        await client.get(httpserver.url_for("/"))

    assert len(httpserver.log) == 2
    elapsed_seconds = (records[0] - now).total_seconds()
    assert expected_seconds - 1 < elapsed_seconds <= expected_seconds + 1
    httpserver.check_assertions()
Exemple #15
0
def test_client_installation_HTTP_301(
        httpserver: httpserver.HTTPServer) -> None:
    httpserver.expect_request("/users/owner/installation").respond_with_data(
        status=301,
        headers={
            "Location": httpserver.url_for("/repositories/12345/installation")
        },
    )
    httpserver.expect_request(
        "/repositories/12345/installation").respond_with_json(
            {"message": "Repository not found"}, status=404)
    with mock.patch(
            "mergify_engine.config.GITHUB_API_URL",
            httpserver.url_for("/")[:-1],
    ):
        with github.GithubInstallationClient(
                github.get_auth("owner")) as client:
            with pytest.raises(exceptions.MergifyNotInstalled):
                client.get(httpserver.url_for("/"))

    assert len(httpserver.log) == 2

    httpserver.check_assertions()
Exemple #16
0
async def github_server(
    httpserver: httpserver.HTTPServer, monkeypatch: pytest.MonkeyPatch
) -> typing.AsyncGenerator[httpserver.HTTPServer, None]:
    monkeypatch.setattr(config, "GITHUB_API_URL", httpserver.url_for("/")[:-1])
    monkeypatch.setattr(github.CachedToken, "STORAGE", {})

    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": "<app_token>", "expires_at": "2100-12-31T23:59:59Z"})

    yield httpserver
Exemple #17
0
async def _do_test_client_installation_token(
    httpserver: httpserver.HTTPServer,
    endpoint: str,
    owner_name: typing.Optional[github_types.GitHubLogin] = None,
    owner_id: typing.Optional[github_types.GitHubAccountIdType] = None,
) -> None:

    with mock.patch(
            "mergify_engine.config.GITHUB_API_URL",
            httpserver.url_for("/")[:-1],
    ):

        httpserver.expect_request(endpoint).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":
                "<app_token>",
                "expires_at":
                "2100-12-31T23:59:59Z"
            })

        httpserver.expect_request("/",
                                  headers={
                                      "Authorization":
                                      "token <installation-token>"
                                  }).respond_with_json({"work": True},
                                                       status=200)

        async with github.AsyncGithubInstallationClient(
                github.get_auth(owner_name=owner_name,
                                owner_id=owner_id)) as client:

            ret = await client.get(httpserver.url_for("/"))
            assert ret.json()["work"]
            assert client.auth.owner == "testing"
            assert client.auth.owner_id == 12345

    assert len(httpserver.log) == 3

    httpserver.check_assertions()
Exemple #18
0
def test_client_installation_token(httpserver: httpserver.HTTPServer) -> None:
    with mock.patch(
            "mergify_engine.config.GITHUB_API_URL",
            httpserver.url_for("/")[:-1],
    ):
        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":
                "<app_token>",
                "expires_at":
                "2100-12-31T23:59:59Z"
            })

        httpserver.expect_request("/",
                                  headers={
                                      "Authorization":
                                      "token <installation-token>"
                                  }).respond_with_json({"work": True},
                                                       status=200)

        with github.GithubInstallationClient(
                github.get_auth("owner")) as client:
            ret = client.get(httpserver.url_for("/"))
            assert ret.json()["work"]

    assert len(httpserver.log) == 3

    httpserver.check_assertions()