mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
test: cover copilot auth branches
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
7cfc891317
commit
dcb3e8bdcb
1 changed files with 154 additions and 0 deletions
|
|
@ -4,6 +4,8 @@ import asyncio
|
|||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from urllib import error as urllib_error
|
||||
|
||||
import pytest
|
||||
|
||||
|
|
@ -15,6 +17,42 @@ def test_read_cached_oauth_token_prefers_env(monkeypatch: pytest.MonkeyPatch) ->
|
|||
assert copilot_auth.read_cached_oauth_token() == "gho-env"
|
||||
|
||||
|
||||
def test_should_exchange_oauth_token_supports_truthy_values(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
for raw in ("1", "true", "YES", "On"):
|
||||
monkeypatch.setenv("GITHUB_COPILOT_USE_TOKEN_EXCHANGE", raw)
|
||||
assert copilot_auth._should_exchange_oauth_token() is True
|
||||
|
||||
monkeypatch.setenv("GITHUB_COPILOT_USE_TOKEN_EXCHANGE", "off")
|
||||
assert copilot_auth._should_exchange_oauth_token() is False
|
||||
|
||||
|
||||
def test_resolve_token_file_paths_prefers_override(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITHUB_COPILOT_TOKEN_FILE", "~/custom-token.json")
|
||||
|
||||
paths = copilot_auth._resolve_token_file_paths()
|
||||
|
||||
assert paths == [Path("~/custom-token.json").expanduser()]
|
||||
|
||||
|
||||
def test_resolve_token_file_paths_includes_localappdata_and_config(
|
||||
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
||||
) -> None:
|
||||
monkeypatch.delenv("GITHUB_COPILOT_TOKEN_FILE", raising=False)
|
||||
monkeypatch.setenv("LOCALAPPDATA", str(tmp_path / "local"))
|
||||
monkeypatch.setattr(copilot_auth.Path, "home", staticmethod(lambda: tmp_path / "home"))
|
||||
|
||||
paths = copilot_auth._resolve_token_file_paths()
|
||||
|
||||
assert paths == [
|
||||
tmp_path / "local" / "github-copilot" / "apps.json",
|
||||
tmp_path / "local" / "github-copilot" / "hosts.json",
|
||||
tmp_path / "home" / ".config" / "github-copilot" / "apps.json",
|
||||
tmp_path / "home" / ".config" / "github-copilot" / "hosts.json",
|
||||
]
|
||||
|
||||
|
||||
def test_read_cached_oauth_token_falls_back_to_gh_cli(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("GITHUB_COPILOT_GITHUB_TOKEN", raising=False)
|
||||
monkeypatch.delenv("GITHUB_COPILOT_TOKEN", raising=False)
|
||||
|
|
@ -100,6 +138,41 @@ def test_read_gh_cli_oauth_token_uses_hostname(monkeypatch: pytest.MonkeyPatch)
|
|||
assert calls == [["gh", "auth", "token", "--hostname", "example.ghe.com"]]
|
||||
|
||||
|
||||
def test_read_gh_cli_oauth_token_returns_none_when_invocation_fails(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
def fake_run(*args: object, **kwargs: object) -> None: # noqa: ANN002, ANN003
|
||||
raise OSError("gh missing")
|
||||
|
||||
monkeypatch.setattr(copilot_auth.subprocess, "run", fake_run)
|
||||
|
||||
assert copilot_auth._read_gh_cli_oauth_token() is None
|
||||
|
||||
|
||||
def test_read_gh_cli_oauth_token_returns_none_for_nonzero_exit(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
copilot_auth.subprocess,
|
||||
"run",
|
||||
lambda *args, **kwargs: SimpleNamespace(returncode=1, stdout="ignored"),
|
||||
)
|
||||
|
||||
assert copilot_auth._read_gh_cli_oauth_token() is None
|
||||
|
||||
|
||||
def test_read_gh_cli_oauth_token_returns_none_for_blank_stdout(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
copilot_auth.subprocess,
|
||||
"run",
|
||||
lambda *args, **kwargs: SimpleNamespace(returncode=0, stdout=" \n"),
|
||||
)
|
||||
|
||||
assert copilot_auth._read_gh_cli_oauth_token() is None
|
||||
|
||||
|
||||
def test_resolve_client_bearer_token_prefers_api_token(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITHUB_COPILOT_API_TOKEN", "copilot-api")
|
||||
monkeypatch.setenv("GITHUB_COPILOT_TOKEN", "gho-oauth")
|
||||
|
|
@ -107,6 +180,12 @@ def test_resolve_client_bearer_token_prefers_api_token(monkeypatch: pytest.Monke
|
|||
assert copilot_auth.resolve_client_bearer_token() == "copilot-api"
|
||||
|
||||
|
||||
def test_has_oauth_auth_false_when_no_tokens(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setattr(copilot_auth, "resolve_client_bearer_token", lambda: None)
|
||||
|
||||
assert copilot_auth.has_oauth_auth() is False
|
||||
|
||||
|
||||
def test_is_copilot_api_url_matches_expected_hosts() -> None:
|
||||
assert copilot_auth.is_copilot_api_url("https://api.githubcopilot.com/v1/chat/completions")
|
||||
assert copilot_auth.is_copilot_api_url("wss://api.githubcopilot.com/v1/responses")
|
||||
|
|
@ -208,3 +287,78 @@ def test_token_provider_can_exchange_when_enabled(monkeypatch: pytest.MonkeyPatc
|
|||
assert first.token == "copilot-api"
|
||||
assert second.token == "copilot-api"
|
||||
assert calls["count"] == 1
|
||||
|
||||
|
||||
def test_token_provider_prefers_explicit_api_token(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("GITHUB_COPILOT_API_TOKEN", "copilot-api")
|
||||
monkeypatch.setenv("GITHUB_COPILOT_API_URL", "https://api.githubcopilot.com")
|
||||
|
||||
token = asyncio.run(copilot_auth.CopilotTokenProvider().get_api_token())
|
||||
|
||||
assert token.token == "copilot-api"
|
||||
assert token.api_url == "https://api.githubcopilot.com"
|
||||
|
||||
|
||||
def test_token_provider_raises_without_oauth_token(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("GITHUB_COPILOT_API_TOKEN", raising=False)
|
||||
monkeypatch.setattr(copilot_auth, "read_cached_oauth_token", lambda: None)
|
||||
|
||||
with pytest.raises(RuntimeError, match="No GitHub Copilot OAuth token"):
|
||||
asyncio.run(copilot_auth.CopilotTokenProvider().get_api_token())
|
||||
|
||||
|
||||
def test_exchange_token_raises_when_exchange_returns_empty_token(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
provider = copilot_auth.CopilotTokenProvider()
|
||||
monkeypatch.setattr(
|
||||
provider,
|
||||
"_exchange_token_sync",
|
||||
staticmethod(lambda headers: {"token": "", "expires_at": int(time.time()) + 1}),
|
||||
)
|
||||
|
||||
with pytest.raises(RuntimeError, match="empty token"):
|
||||
asyncio.run(provider._exchange_token("gho-oauth"))
|
||||
|
||||
|
||||
def test_exchange_token_sync_raises_for_http_error(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
class DummyResponse:
|
||||
def read(self) -> bytes:
|
||||
return b'{"message":"Not Found"}'
|
||||
|
||||
def close(self) -> None:
|
||||
return None
|
||||
|
||||
def fake_urlopen(request, timeout: float): # noqa: ANN001, ANN202
|
||||
raise urllib_error.HTTPError(
|
||||
url=request.full_url,
|
||||
code=404,
|
||||
msg="Not Found",
|
||||
hdrs=None,
|
||||
fp=DummyResponse(),
|
||||
)
|
||||
|
||||
monkeypatch.setattr(copilot_auth.urllib_request, "urlopen", fake_urlopen)
|
||||
|
||||
with pytest.raises(RuntimeError, match="HTTP 404"):
|
||||
copilot_auth.CopilotTokenProvider._exchange_token_sync({"Authorization": "token test"})
|
||||
|
||||
|
||||
def test_apply_copilot_api_auth_returns_original_headers_for_non_copilot_url() -> None:
|
||||
headers = asyncio.run(
|
||||
copilot_auth.apply_copilot_api_auth(
|
||||
{"authorization": "Bearer downstream-token"},
|
||||
url="https://api.openai.com/v1/chat/completions",
|
||||
)
|
||||
)
|
||||
|
||||
assert headers == {"authorization": "Bearer downstream-token"}
|
||||
|
||||
|
||||
def test_read_windows_copilot_cli_oauth_token_returns_none_without_windll(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(copilot_auth.os, "name", "nt")
|
||||
monkeypatch.delattr(copilot_auth.ctypes, "WinDLL", raising=False)
|
||||
|
||||
assert copilot_auth._read_windows_copilot_cli_oauth_token() is None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue