diff --git a/coverage_comment/github_client.py b/coverage_comment/github_client.py index 19165b64..3057e1ec 100644 --- a/coverage_comment/github_client.py +++ b/coverage_comment/github_client.py @@ -47,7 +47,6 @@ def __getattr__(self, attr): class GitHub: - """ GitHub client. """ @@ -58,9 +57,18 @@ def __init__(self, session: httpx.Client): def __getattr__(self, attr): return _Callable(self, "/%s" % attr) - def _http(self, method, path, *, bytes=False, **kw): + def _http( + self, + method: str, + path: str, + *, + bytes: bool = False, + headers: dict[str, str] | None = None, + **kw, + ): _method = method.lower() requests_kwargs = {} + header_kwargs = {"headers": headers} if headers else {} if _method == "get" and kw: requests_kwargs = {"params": kw} @@ -71,6 +79,7 @@ def _http(self, method, path, *, bytes=False, **kw): _method.upper(), path, timeout=TIMEOUT, + **header_kwargs, **requests_kwargs, ) if bytes: @@ -93,9 +102,13 @@ def _http(self, method, path, *, bytes=False, **kw): def response_contents( response: httpx.Response, -) -> JsonObject | bytes: +) -> JsonObject | str | bytes: if response.headers.get("content-type", "").startswith("application/json"): return response.json(object_hook=JsonObject) + if response.headers.get("content-type", "").startswith( + "application/vnd.github.raw+json" + ): + return response.text return response.content diff --git a/coverage_comment/storage.py b/coverage_comment/storage.py index 72357645..eb8e49a0 100644 --- a/coverage_comment/storage.py +++ b/coverage_comment/storage.py @@ -1,6 +1,5 @@ from __future__ import annotations -import base64 import contextlib import pathlib @@ -120,11 +119,16 @@ def get_datafile_contents( ) -> str | None: contents_path = github.repos(repository).contents(str(files.DATA_PATH)) try: - response = contents_path.get(ref=branch) + response = contents_path.get( + ref=branch, + # If we don't pass this header, the format of the answer will depend on + # the size of the file. With the header, we're sure to get the raw content. + headers={"Accept": "application/vnd.github.raw+json"}, + ) except github_client.NotFound: return None - return base64.b64decode(response.content).decode() + return response def get_raw_file_url( diff --git a/tests/integration/test_main.py b/tests/integration/test_main.py index a2419e90..10e563fa 100644 --- a/tests/integration/test_main.py +++ b/tests/integration/test_main.py @@ -1,6 +1,5 @@ from __future__ import annotations -import base64 import json import os import pathlib @@ -232,7 +231,7 @@ def test_action__pull_request__store_comment_not_targeting_default( session.register( "GET", "/repos/py-cov-action/foobar/contents/data.json", - )(json={"content": base64.b64encode(payload.encode()).decode()}) + )(text=payload, headers={"content-type": "application/vnd.github.raw+json"}) # Who am I session.register("GET", "/user")(json={"login": "foo"}) @@ -293,7 +292,7 @@ def test_action__pull_request__post_comment( session.register( "GET", "/repos/py-cov-action/foobar/contents/data.json", - )(json={"content": base64.b64encode(payload.encode()).decode()}) + )(text=payload, headers={"content-type": "application/vnd.github.raw+json"}) # Who am I session.register("GET", "/user")(json={"login": "foo"}) @@ -355,7 +354,7 @@ def test_action__push__non_default_branch( session.register( "GET", "/repos/py-cov-action/foobar/contents/data.json", - )(json={"content": base64.b64encode(payload.encode()).decode()}) + )(text=payload, headers={"content-type": "application/vnd.github.raw+json"}) session.register( "GET", @@ -444,7 +443,7 @@ def test_action__push__non_default_branch__no_pr( session.register( "GET", "/repos/py-cov-action/foobar/contents/data.json", - )(json={"content": base64.b64encode(payload.encode()).decode()}) + )(text=payload, headers={"content-type": "application/vnd.github.raw+json"}) session.register( "GET", @@ -498,7 +497,7 @@ def test_action__pull_request__force_store_comment( session.register( "GET", "/repos/py-cov-action/foobar/contents/data.json", - )(json={"content": base64.b64encode(payload.encode()).decode()}) + )(text=payload, headers={"content-type": "application/vnd.github.raw+json"}) git.register("git fetch origin main --depth=1000")() git.register("git diff --unified=0 FETCH_HEAD -- .")(stdout=DIFF_STDOUT) diff --git a/tests/unit/test_github_client.py b/tests/unit/test_github_client.py index 8aa147eb..3f07b271 100644 --- a/tests/unit/test_github_client.py +++ b/tests/unit/test_github_client.py @@ -13,6 +13,31 @@ def test_github_client__get(session, gh): assert gh.repos("a/b").issues().get(a=1) == {"foo": "bar"} +def test_github_client__get_text(session, gh): + session.register("GET", "/repos/a/b/issues", timeout=60, params={"a": 1})( + text="foobar", headers={"content-type": "application/vnd.github.raw+json"} + ) + + assert gh.repos("a/b").issues().get(a=1) == "foobar" + + +def test_github_client__get_bytes(session, gh): + session.register("GET", "/repos/a/b/issues", timeout=60, params={"a": 1})( + text="foobar", headers={"content-type": "application/vnd.github.raw+json"} + ) + + assert gh.repos("a/b").issues().get(a=1, bytes=True) == b"foobar" + + +def test_github_client__get_headers(session, gh): + session.register("GET", "/repos/a/b/issues", timeout=60, params={"a": 1})( + json={"foo": "bar"}, + headers={"X-foo": "yay"}, + ) + + assert gh.repos("a/b").issues().get(a=1, headers={"X-foo": "yay"}) == {"foo": "bar"} + + def test_github_client__post_non_json(session, gh): session.register("POST", "/repos/a/b/issues", timeout=60, json={"a": 1})() diff --git a/tests/unit/test_storage.py b/tests/unit/test_storage.py index 357dad54..2ba72a80 100644 --- a/tests/unit/test_storage.py +++ b/tests/unit/test_storage.py @@ -1,6 +1,5 @@ from __future__ import annotations -import base64 import pathlib import pytest @@ -142,9 +141,8 @@ def test_get_datafile_contents__not_found(gh, session): def test_get_datafile_contents(gh, session): - payload = base64.b64encode(b"yay").decode() session.register("GET", "/repos/foo/bar/contents/data.json", params={"ref": "baz"})( - json={"content": payload} + text="yay", headers={"content-type": "application/vnd.github.raw+json"} ) result = storage.get_datafile_contents(