diff --git a/integration-tests/globalSetup.ts b/integration-tests/globalSetup.ts index 02cea6859..1b00829fa 100644 --- a/integration-tests/globalSetup.ts +++ b/integration-tests/globalSetup.ts @@ -110,6 +110,58 @@ export async function setup() { console.log(`\nIntegration test output directory: ${runDir}`); console.log(`SDK E2E test output directory: ${sdkE2eRunDir}`); console.log(`CLI path: ${process.env['TEST_CLI_PATH']}`); + + await authPreflight(); +} + +/** + * Fail fast on a bad gateway credential. + * + * The model-driving E2E tests each spawn the CLI, which aborts on a failed + * auth preflight (HTTP 401) with exit code 1. When the gateway key is expired + * or revoked, that surfaces as ~200 opaque "CLI process exited with code 1" + * assertion failures across the suite — a 25-minute red run with no obvious + * cause (see protoCLI#226). Probing `/models` once here turns that into a + * single clear message and aborts the whole suite immediately. + * + * Only runs when OpenAI-compatible creds are present; a missing key is left + * to the individual tests (some auth paths don't use these env vars). A + * network error is treated as transient and does not block the run — only an + * explicit 401/403 (credential rejection) fails the suite. + */ +async function authPreflight(): Promise { + const baseUrl = process.env['OPENAI_BASE_URL']; + const apiKey = process.env['OPENAI_API_KEY']; + if (!baseUrl || !apiKey) return; + + const modelsUrl = `${baseUrl.replace(/\/$/, '')}/models`; + let status: number | undefined; + try { + const res = await fetch(modelsUrl, { + method: 'GET', + headers: { Authorization: `Bearer ${apiKey}` }, + signal: AbortSignal.timeout(10_000), + }); + status = res.status; + } catch (err) { + // Network error / timeout — don't block the run on transient infra. + console.warn( + `Auth preflight skipped (network error reaching ${modelsUrl}): ${ + err instanceof Error ? err.message : String(err) + }`, + ); + return; + } + + if (status === 401 || status === 403) { + throw new Error( + `E2E auth preflight failed: HTTP ${status} from ${modelsUrl}.\n` + + `The gateway credential (OPENAI_API_KEY / OPENAI_BASE_URL) is invalid, ` + + `expired, or revoked. Rotate the repo secret before re-running — every ` + + `model-driving test will otherwise fail with "CLI process exited with code 1". ` + + `See protoCLI#226.`, + ); + } } export async function teardown() {