From 21b4810aa827bccfd54df2beec6cca4a75baa95a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 2 May 2026 20:00:40 +0000 Subject: [PATCH] Handle GitHub token revocation failures Co-authored-by: Armen Zambrano G. --- src/github_app.py | 15 ++++++++++---- tests/test_github_app.py | 44 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 tests/test_github_app.py diff --git a/src/github_app.py b/src/github_app.py index c18b6a9..5ff4a65 100644 --- a/src/github_app.py +++ b/src/github_app.py @@ -4,12 +4,15 @@ from __future__ import annotations import contextlib +import logging import time from typing import Generator import jwt import requests +logger = logging.getLogger(__name__) + class GithubAppToken: def __init__(self, private_key, app_id) -> None: @@ -29,10 +32,14 @@ def get_token(self, installation_id: int) -> Generator[str, None, None]: # This token expires in an hour yield resp["token"] finally: - requests.delete( - "https://api.github.com/installation/token", - headers={"Authorization": f"token {resp['token']}"}, - ) + try: + revoke_req = requests.delete( + "https://api.github.com/installation/token", + headers={"Authorization": f"token {resp['token']}"}, + ) + revoke_req.raise_for_status() + except requests.RequestException: + logger.warning("Failed to revoke GitHub App installation token.", exc_info=True) def get_jwt_token(self, private_key, app_id): payload = { diff --git a/tests/test_github_app.py b/tests/test_github_app.py new file mode 100644 index 0000000..e167142 --- /dev/null +++ b/tests/test_github_app.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +import pytest +import requests + +from src.github_app import GithubAppToken + + +def test_get_token_ignores_revocation_request_failure(monkeypatch): + token = "github-token" + app_token = GithubAppToken.__new__(GithubAppToken) + app_token.headers = {"Authorization": "Bearer jwt-token"} + + class TokenResponse: + def raise_for_status(self): + pass + + def json(self): + return {"token": token} + + monkeypatch.setattr(requests, "post", lambda **kwargs: TokenResponse()) + + def fail_revocation(*args, **kwargs): + raise requests.ConnectionError("remote disconnected") + + monkeypatch.setattr(requests, "delete", fail_revocation) + + with app_token.get_token(123) as returned_token: + assert returned_token == token + + +def test_get_token_raises_when_token_request_fails(monkeypatch): + app_token = GithubAppToken.__new__(GithubAppToken) + app_token.headers = {"Authorization": "Bearer jwt-token"} + + class TokenResponse: + def raise_for_status(self): + raise requests.HTTPError("server error") + + monkeypatch.setattr(requests, "post", lambda **kwargs: TokenResponse()) + + with pytest.raises(requests.HTTPError): + with app_token.get_token(123): + pass