diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json index d2a5990b436..2fbf0cfbf1f 100644 --- a/config/quality/file-size-baseline.json +++ b/config/quality/file-size-baseline.json @@ -1,5 +1,6 @@ { "_comment": "Catraca de tamanho (check-file-size.mjs). frozen so pode encolher; arquivos novos <= cap. --update ratcheta.", + "_rebaseline_2026_06_20_4023_web_cookie_noauth_validation": "PR #4023 (oyi77) own growth: src/lib/providers/validation.ts 4450->4518 (+68 = a new validateWebCookieProvider that probes the provider's /models endpoint — 401/403 => AUTH_007 SESSION_EXPIRED, any other status => valid session, empty cookie => invalid, provider-not-in-registry => unsupported — plus a local STANDARD_USER_AGENT const for the probe). Cohesive validator at the validateProviderApiKey dispatch; not extractable. Covered by tests/unit/provider-validation-web-cookie-auth007.test.ts. Heavily curated on merge — the PR's branch was badly stale-based (squash-base-stale), so its tree was DESTRUCTIVE: providers/index.ts deleted live providers openadapter/dit/tokenrouter (added by #4313) and the executor/base.ts edits reverted release fixes (#4037 duckduckgo host, theoldllm gpt5 models, base.ts fetch-start-timeout). Only the purely-additive validation feature was kept (validation.ts validateWebCookieProvider + errorCodes AUTH_007 + the test). Dropped: 5 malformed new registry entries (used non-RegistryEntry fields defaultModel/auth + referenced non-existent executors -> tsc TS2353), the destructive providers/index.ts + executor reverts, the unrelated pr-*.sh automation scripts, and evals/types.ts (belongs to the deferred evals modularization #4422). Also removed the PR's fragile 'Phase 2' executor probe (ran a live upstream chat during validation + classified any 'auth'-containing error as SESSION_EXPIRED) and rewrote the test to install its fetch mock before module load (the original mocked too late and silently hit live chatgpt.com).", "_rebaseline_2026_06_20_3368_pool_tools": "PR #3368 own growth: open-sse/mcp-server/server.ts 1468->1509 (+41 = register the web-session pool observability tools — poolStatus/poolSessions/poolWarm — at the existing createMcpServer registration chokepoint, with their read:health/write:resilience scope wiring). Reconciled late: the server.ts bump was a separate commit that did not ride along when the feature commit was cherry-picked onto release/v3.8.32 (squash-base-stale rebuild). Thin cohesive registration at the single tool-registration boundary; not extractable. Covered by tests/unit/mcp-pool-tools-3368.test.ts.", "_rebaseline_2026_06_20_4401_webfetch_validators": "PR #4401 own growth: src/lib/providers/validation.ts 4428->4450 (+22 = two new API-key validators — Firecrawl + Jina Reader — each a cohesive provider validator branch mirroring the existing ones; 401/403->invalid else->valid). Not extractable (it IS the per-provider validator list). Covered by tests/unit/provider-validation-webfetch-4401.test.ts.", "_rebaseline_2026_06_20_4380_parse_once": "PR #4380 own growth: src/sse/handlers/chat.ts 1486->1491 (+5 = thread the once-parsed request body from the route guard into handleChat, replacing the duplicate re-parse). The reusable body accessor lives in the new src/sse/handlers/requestBody.ts ( = { httpStatus: 400, category: "AUTH", }, + AUTH_007: { + code: "AUTH_007", + message: "Session expired (re-login required)", + httpStatus: 401, + category: "AUTH", + }, // ── Proxy ── PROXY_001: { diff --git a/tests/unit/provider-validation-web-cookie-auth007.test.ts b/tests/unit/provider-validation-web-cookie-auth007.test.ts new file mode 100644 index 00000000000..c53705af040 --- /dev/null +++ b/tests/unit/provider-validation-web-cookie-auth007.test.ts @@ -0,0 +1,102 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +// The validator probes the provider's /models endpoint via safeOutboundFetch → +// fetchWithTimeout, which binds globalThis.fetch at MODULE LOAD time. The mock MUST be +// installed BEFORE importing validation.ts — a late reassignment (inside a test) is +// ignored and the validator hits the real network instead. (That made the 401/403 +// assertions pass only by coincidence — live chatgpt.com returns 401/403 — while the +// 200 case failed.) A mutable `nextResponse` lets each test vary the probe result, and +// `fetchCalls` proves the mocked probe ran rather than the live network. +let nextResponse: { status: number; body: string } = { status: 200, body: "{}" }; +let fetchCalls = 0; +globalThis.fetch = (async () => { + fetchCalls++; + return new Response(nextResponse.body, { + status: nextResponse.status, + headers: { "content-type": "application/json" }, + }); +}) as typeof fetch; + +const { validateWebCookieProvider } = await import("../../src/lib/providers/validation.ts"); + +function mockFetch(status: number, body: string) { + nextResponse = { status, body }; + fetchCalls = 0; +} + +test("should_return_AUTH_007_when_models_endpoint_returns_401", async () => { + mockFetch(401, JSON.stringify({ error: "unauthorized" })); + + const result = await validateWebCookieProvider({ + provider: "chatgpt-web", + apiKey: "expired_cookie=session=abc123", + providerSpecificData: {}, + }); + + assert.equal(fetchCalls, 1, "the /models probe must be the mocked fetch, not the live network"); + assert.equal(result.valid, false); + assert.equal(result.errorCode, "AUTH_007"); + assert.equal(result.error, "SESSION_EXPIRED"); +}); + +test("should_return_AUTH_007_when_models_endpoint_returns_403", async () => { + mockFetch(403, JSON.stringify({ error: "forbidden" })); + + const result = await validateWebCookieProvider({ + provider: "chatgpt-web", + apiKey: "expired_cookie=session=abc123", + providerSpecificData: {}, + }); + + assert.equal(fetchCalls, 1, "the /models probe must be the mocked fetch, not the live network"); + assert.equal(result.valid, false); + assert.equal(result.errorCode, "AUTH_007"); + assert.equal(result.error, "SESSION_EXPIRED"); +}); + +test("should_return_valid_when_models_endpoint_returns_200", async () => { + // A non-401/403 status from the /models probe means the cookie was accepted, + // so the session is treated as valid. + mockFetch(200, JSON.stringify({ ok: true, data: [] })); + + const result = await validateWebCookieProvider({ + provider: "chatgpt-web", + apiKey: "valid_cookie=session=abc123", + providerSpecificData: {}, + }); + + assert.equal(fetchCalls, 1, "the /models probe must be the mocked fetch, not the live network"); + assert.equal(result.valid, true); +}); + +test("should_return_unsupported_when_provider_not_in_registry", async () => { + mockFetch(200, "{}"); + + const result = await validateWebCookieProvider({ + provider: "nonexistent-provider", + apiKey: "some_cookie", + providerSpecificData: {}, + }); + + // Unknown provider short-circuits before any network probe. + assert.equal(fetchCalls, 0); + assert.equal(result.valid, false); + assert.equal(result.unsupported, true); +}); + +test("should_return_error_when_cookie_is_empty", async () => { + mockFetch(200, "{}"); + + const result = await validateWebCookieProvider({ + provider: "chatgpt-web", + apiKey: "", + providerSpecificData: {}, + }); + + // Empty cookie short-circuits before any network probe. + assert.equal(fetchCalls, 0); + assert.equal(result.valid, false); + assert.equal(result.unsupported, false); + assert.match(result.error, /cookie/i); +});