Files
odysseus/tests/test_admin_device_flow_static.py
stocky789 1e0d9b92af feat: add ChatGPT Subscription provider (#2876)
* feat: Add ChatGPT Subscription support and related features

- Introduced a new provider option for ChatGPT Subscription in the endpoint selection UI.
- Implemented OAuth flow for ChatGPT Subscription sign-in, including polling for authorization status.
- Updated admin interface to handle ChatGPT Subscription, including disabling API key input and providing user guidance.
- Enhanced cost tracking logic to differentiate between subscription and non-subscription endpoints.
- Added new slash commands for managing skills, including listing, searching, and invoking skills.
- Implemented caching for skill catalog to optimize performance.
- Updated tests to cover new ChatGPT Subscription functionality and ensure proper endpoint probing.
- Refactored existing code to accommodate new features and improve maintainability.

* refactor: share provider device-flow setup

- reuse one device-flow backend for Copilot and ChatGPT Subscription
- add one frontend device-flow helper for Settings and /setup
- put GitHub Copilot back into Add Models, now as a dropdown option
- make provider selection just select; clicking Add starts sign-in
- stop ChatGPT Subscription setup from opening auth tabs automatically
- make /setup copilot and /setup chatgpt-subscription work from chat
- show ChatGPT Subscription in the /setup suggestions
- show the real error message when setup fails
- add focused tests for the shared flow and setup UI

* feat(chatgpt-subscription): harden credential lifecycle and streamline auth UX

Backend:
- Resolve runtime bearer for provider-auth endpoints at probe time via a
  shared _resolve_probe_key() that delegates to resolve_endpoint_runtime,
  applied across all probe/refresh call sites.
- Skip live completion probes and health pings for discovery-only providers
  (centralized behind _is_discovery_only_provider) — the Codex/Responses API
  has no such endpoints, so status is derived from cached models.
- Never persist the short lived ChatGPT bearer to the plaintext sessions
  table; proactively clear any stale bearer left by an earlier code path.
- Revoke orphaned ProviderAuthSession credentials when the last endpoint
  backing them is deleted (_delete_orphaned_provider_auth), surfaced via
  cleared_provider_auth in the delete response.

Frontend (admin.js):
- Auto-start the device-auth flow on provider selection so the authorization
  panel (code + Authorize) shows immediately instead of behind a "Sign in" click.
- Remove the redundant top button for device auth providers, move retry
  into the panel via an inline "Try again".
- Drop the self-evident hint text and add an execCommand clipboard fallback so
  Copy works in non-secure (HTTP/LAN) contexts.

* fix: harden chatgpt subscription provider

* chore: remove PR media from branch

* Fix chatgpt subscription recovery and token handling

---------

Co-authored-by: 5p00kyy <admin@5p00ky.dev>
2026-06-08 10:19:18 +02:00

66 lines
2.6 KiB
Python

"""Static regressions for Add Models provider device-flow UX."""
from pathlib import Path
_REPO = Path(__file__).resolve().parent.parent
_INDEX = (_REPO / "static" / "index.html").read_text(encoding="utf-8")
_ADMIN = (_REPO / "static" / "js" / "admin.js").read_text(encoding="utf-8")
def _between(src: str, start: str, end: str) -> str:
start_idx = src.index(start)
end_idx = src.index(end, start_idx)
return src[start_idx:end_idx]
def test_copilot_and_chatgpt_subscription_are_dropdown_device_auth_options():
assert 'value="copilot" data-logo="github" data-auth-flow="copilot">GitHub Copilot' in _INDEX
assert 'value="chatgpt-subscription" data-logo="openai" data-auth-flow="chatgpt-subscription">ChatGPT Subscription' in _INDEX
assert 'id="adm-deviceAuthStatus"' in _INDEX
def test_provider_selection_is_inert_and_add_button_starts_device_flow():
change_block = _between(_ADMIN, "provider.addEventListener('change'", "urlInput.addEventListener('input'")
add_block = _between(_ADMIN, "el('adm-epAddBtn').addEventListener('click'", "async function _startProviderDeviceAuth")
assert "_startProviderDeviceAuth" not in change_block
assert "_startProviderDeviceAuth(deviceAuthProvider" in add_block
def test_device_auth_selection_disables_and_dims_api_test_button():
form_block = _between(_ADMIN, "function _setApiFormForProvider()", "function _renderPickerMenu()")
assert "testBtn.disabled = true" in form_block
assert "testBtn.style.opacity = '0.45'" in form_block
assert "testBtn.style.cursor = 'not-allowed'" in form_block
assert "testBtn.disabled = false" in form_block
assert "testBtn.style.opacity = ''" in form_block
assert "testBtn.style.cursor = ''" in form_block
def test_device_auth_keeps_manual_auth_button_without_auto_opening_tab():
auth_block = _between(_ADMIN, "async function _startProviderDeviceAuth", "// Local \"Add\" button")
assert "Authorize with OpenAI" in auth_block
assert "Authorize on GitHub" in auth_block
assert "adm-copilot-panel" in auth_block
assert "adm-device-auth-copy" in auth_block
assert "openWindow: () => {}" in auth_block
assert "A new tab opened" not in auth_block
def test_loud_oauth_copy_and_removed_button_hooks_do_not_return():
forbidden = [
"Click Add to start",
"uses account sign-in",
"Uses ChatGPT/Codex OAuth, not an OpenAI API key.",
"adm-chatgptStatus",
"adm-chatgptConnectBtn",
"adm-copilotConnectBtn",
"adm-copilotStatus",
]
for needle in forbidden:
assert needle not in _INDEX
assert needle not in _ADMIN