headroom/tests/test_cli/test_copilot_auth.py
Noam Asor 5dbe3314a1
fix(auth): support GitHub Enterprise Copilot OAuth domain (#2192)
## Description

Adds GitHub Enterprise OAuth domain support for Copilot auth. When
`GITHUB_COPILOT_ENTERPRISE_URL` is set, the default OAuth domain
resolves to that enterprise host; explicit `--domain` values still take
precedence.

Closes #1152

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [x] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- `headroom/copilot_auth.py`: derive the default OAuth domain from
`GITHUB_COPILOT_ENTERPRISE_URL` when present, falling back to
`github.com` for unset or blank values.
- `headroom/cli/copilot_auth.py`: keep explicit CLI domain overrides
authoritative even when the enterprise env var is set.
- `README.md`: document the enterprise OAuth environment setting and
precedence.
- Added regression tests for enterprise URL handling, blank/unset
fallback, and explicit CLI override precedence.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [ ] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [x] Manual testing performed

### Test Output

```text
uv run pytest tests/test_copilot_auth.py tests/test_cli/test_copilot_auth.py -q
69 passed in 1.05s

uvx ruff@0.15.17 check headroom/cli/copilot_auth.py headroom/copilot_auth.py tests/test_cli/test_copilot_auth.py tests/test_copilot_auth.py
All checks passed!

uvx ruff@0.15.17 format --check headroom/cli/copilot_auth.py headroom/copilot_auth.py tests/test_cli/test_copilot_auth.py tests/test_copilot_auth.py
4 files already formatted
```

## Real Behavior Proof

- Environment: Windows 11 development checkout, Python 3.13.3, with
focused Copilot auth tests using monkeypatched enterprise env vars.
- Exact command / steps: ran the Copilot auth unit/CLI tests plus ruff
check and format-check against the touched auth files and tests.
- Observed result:
`GITHUB_COPILOT_ENTERPRISE_URL=https://ghe.example.com` resolves
`default_oauth_domain()` to `ghe.example.com`; unset/blank enterprise
env vars fall back to `github.com`; and `headroom copilot-auth login
--domain github.com` still honors the explicit override when enterprise
env vars are set.
- Not tested: live OAuth against a real GitHub Enterprise Server
instance.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] I have updated the CHANGELOG.md if applicable

## Screenshots (if applicable)

N/A - CLI/auth behavior and README update.

## Additional Notes

`GITHUB_COPILOT_ENTERPRISE_URL` takes precedence over
`GITHUB_COPILOT_ENTERPRISE_DOMAIN`; explicit `--domain` remains
authoritative for the login command.

---------

Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
Co-authored-by: Tejas Chopra <chopratejas@gmail.com>
2026-07-14 16:07:17 -04:00

85 lines
2.7 KiB
Python

from __future__ import annotations
from pathlib import Path
import pytest
from click.testing import CliRunner
from headroom.cli import main
def test_copilot_auth_login_saves_token(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
auth_file = tmp_path / "copilot_auth.json"
monkeypatch.setenv("HEADROOM_COPILOT_AUTH_FILE", str(auth_file))
monkeypatch.setattr(
"headroom.cli.copilot_auth.start_copilot_device_authorization",
lambda domain: {
"verification_uri": "https://github.com/login/device",
"user_code": "ABCD-1234",
"device_code": "device-code",
"interval": 1,
"expires_in": 900,
},
)
monkeypatch.setattr(
"headroom.cli.copilot_auth.poll_copilot_device_authorization",
lambda device_code, *, domain, interval, expires_in: "gho-headroom",
)
result = CliRunner().invoke(main, ["copilot-auth", "login"])
assert result.exit_code == 0, result.output
assert "https://github.com/login/device" in result.output
assert "ABCD-1234" in result.output
assert "gho-headroom" not in result.output
assert auth_file.exists()
def test_copilot_auth_status_reports_missing_login(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
monkeypatch.setenv("HEADROOM_COPILOT_AUTH_FILE", str(tmp_path / "missing.json"))
result = CliRunner().invoke(main, ["copilot-auth", "status"])
assert result.exit_code == 0, result.output
assert "Status: not logged in" in result.output
def test_copilot_auth_login_domain_override_wins_over_enterprise_env(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
auth_file = tmp_path / "copilot_auth.json"
monkeypatch.setenv("HEADROOM_COPILOT_AUTH_FILE", str(auth_file))
monkeypatch.setenv("GITHUB_COPILOT_ENTERPRISE_URL", "https://ghe.example.com")
captured: dict[str, object] = {}
def fake_start(domain: str) -> dict[str, object]:
captured["domain"] = domain
return {
"verification_uri": "https://github.com/login/device",
"user_code": "ABCD-1234",
"device_code": "device-code",
"interval": 1,
"expires_in": 900,
}
monkeypatch.setattr(
"headroom.cli.copilot_auth.start_copilot_device_authorization",
fake_start,
)
monkeypatch.setattr(
"headroom.cli.copilot_auth.poll_copilot_device_authorization",
lambda device_code, *, domain, interval, expires_in: "gho-headroom",
)
result = CliRunner().invoke(main, ["copilot-auth", "login", "--domain", "github.com"])
assert result.exit_code == 0, result.output
assert captured["domain"] == "github.com"