open-swe/tests/test_reviewer.py
open-swe[bot] 85343fab63
feat: TTL and revocation handling for cached GitHub OAuth tokens [closes AB-2322] (#1280)
* feat: TTL and revocation handling for cached GitHub OAuth tokens [closes AB-2322]

Persist github_token_expires_at alongside github_token_encrypted, treat
expired cache entries as missing so we re-resolve before kicking off
runs, and invalidate the cached ciphertext on a downstream 401 so the
next invocation gets a fresh token instead of replaying a revoked one.

Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>

* webapp: forward installation-token expiry to reviewer cache writes

The three reviewer-thread persist sites in webapp.py were calling
get_github_app_installation_token() (no expiry) and persist_encrypted_github_token
without expires_at, so cached App tokens were treated as never-expiring even
though they actually expire in ~1 hour.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <51395795+johannes117@users.noreply.github.com>
Co-authored-by: Johannes du Plessis <johannes@langchain.dev>
2026-05-08 22:57:01 +00:00

56 lines
1.8 KiB
Python

from __future__ import annotations
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from langgraph.graph.state import RunnableConfig
from agent import reviewer
class _DummyAgent:
def with_config(self, config: dict[str, object]) -> _DummyAgent:
self.config = config
return self
@pytest.mark.asyncio
async def test_reviewer_uses_cached_thread_token_for_slack_review_request() -> None:
config: RunnableConfig = {
"configurable": {
"__is_for_execution__": True,
"thread_id": "reviewer-thread-id",
"source": "slack",
"review_requested": True,
},
"metadata": {},
}
dummy_agent = _DummyAgent()
with (
patch(
"agent.reviewer.get_github_token_from_thread",
new_callable=AsyncMock,
return_value=("app-token", "encrypted-token", None),
) as mock_get_thread_token,
patch("agent.reviewer.resolve_github_token", new_callable=AsyncMock) as mock_resolve_token,
patch(
"agent.reviewer.ensure_sandbox_for_thread",
new_callable=AsyncMock,
return_value=MagicMock(),
),
patch(
"agent.reviewer.aresolve_sandbox_work_dir",
new_callable=AsyncMock,
return_value="/workspace",
),
patch("agent.reviewer.make_model", return_value=MagicMock()),
patch("agent.reviewer.create_deep_agent", return_value=dummy_agent),
):
await reviewer.get_reviewer_agent(config)
metadata = config["metadata"]
assert isinstance(metadata, dict)
assert metadata["github_token_encrypted"] == "encrypted-token"
mock_get_thread_token.assert_awaited_once_with("reviewer-thread-id")
mock_resolve_token.assert_not_called()