test: cover copilot auth branches

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
JerrettDavis 2026-04-22 00:23:28 -05:00
parent 7cfc891317
commit dcb3e8bdcb

View file

@ -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