Files
OmniRoute/tests/unit/web-session-contract.test.ts
Diego Rodrigues de Sa e Souza a94fe23e89 fix(release): drain the twelve reds every PR against release/v3.8.51 was born with (#11940)
* fix(release): drain the twelve reds every PR against release/v3.8.51 was born with

Measured on the cycle tip: fifteen unit files were red on every PR. Two came
from the v3.8.50 sync-back (fixed in #11929); the other thirteen predate it and
are the branch's own drift. This sweep clears all of them but the ESLint debt
(#11924), each with the smallest change that keeps the guard honest:

- .env.example + ENVIRONMENT.md: NEXT_PUBLIC_SW_BUILD_ID / OMNIROUTE_SW_BUILD_ID /
  SOURCE_VERSION (#11779 service-worker cache busting) documented — the env/docs
  contract gate was failing on every PR.
- stryker.conf.json: the six tests the mutation gate found covering mutated modules
  (four retirement runtime-block suites, combo connection-aware expansion, tunnel
  error sanitization) registered in tap.testFiles.
- dependency-allowlist: eslint-plugin-react-hooks 7.0.1 approved; its findings are
  tracked in #11924.
- i18n: the six combo.sort.* strings (d5dfcfff58) translated for vi (strict parity)
  and pt-BR.
- docs/providers/CHATGPT_WEB.md: the retirement test is migration-168, not 163.
- g4f gateways: authHint now says member key, which the discontinued-providers
  guard asserts.
- tests realigned to the catalog the branch actually ships: qwen-web (#11713) and
  chatgpt-web (#11720) are retired, so web-session-contract and
  token-health-check-webcookie use perplexity-web, grok-web and chatgpt-web-codex.
- db-core-init: the two minimal legacy fixtures gained the columns migrations 164-168
  UPDATE (error_code, last_error*, test_status) — they exist on every real legacy DB
  (base CREATE TABLE); the fixtures simply never declared them.
- no-js-extension guard: a .js specifier whose target is a genuine JavaScript file
  (open-sse/lib/deepseek-pow-hash.js, shared with a worker) is not the #10674
  defect; the test now skips targets that exist as .js.

All twelve files pass locally; docs-sync, docs-counts, env-doc-sync, the tap
drift gate and the fabricated-docs gates are green on the tree.

* test(release): move the deferred-finish translator test into a collected path

tests/unit/translator/ is not one of the unit collectors (package.json test:unit,
merge-train.sh, build-test-impact-map, check-test-discovery), so the suite that
dd35750e5f added there never ran — check:test-discovery flagged it as a new orphan
on every PR. Relocated next to its sibling openai-to-claude-trailing-usage-11817
under tests/unit/, where the root glob collects it (5/5 pass).

* fix(dashboard): type the four sort-method sites #11812 left red on the dashboard typecheck ratchet

d5dfcfff58 added the combo model sort and raised combos/page.tsx from 23 to 27
scoped TypeScript errors (TS2339 +1, TS2345 +2, TS2322 +1), which fails
check:dashboard-typecheck on every PR against release/v3.8.51:

- initialSortMethod: sanitizeComboRuntimeConfig() is untyped, so config.modelSort is
  unknown; narrow it before reading .method (normalizeSortMethod takes unknown anyway).
- handleAddModels: the batch path passes ComboBuilderDraftModelStep[] to the ComboStep[]
  sort helpers without the cast handleSortChange already uses; mirror it.
- ComboSortSelect expects a translate-with-fallback (k, f) => string, but received
  next-intl's Translator whose second argument is a values object. Pass the page's
  getI18nOrFallback adapter instead of the raw translator — that is also what makes
  the `has()` check and the fallback text actually work at runtime.

Baseline untouched (no widening). Scoped tsc: 0 new/regressed errors.
2026-08-28 18:18:33 -03:00

100 lines
3.6 KiB
TypeScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { listExtractionConfigs } from "../../open-sse/services/tokenExtractionConfig.ts";
import {
buildWebSessionContract,
WEB_SESSION_CONTRACT_VERSION,
} from "../../src/lib/providers/webSessionContract.ts";
import { getWebSessionCredentialRequirement } from "../../src/shared/providers/webSessionCredentials.ts";
test("web-session contract mirrors canonical extraction and credential metadata", () => {
const contract = buildWebSessionContract();
assert.equal(contract.version, WEB_SESSION_CONTRACT_VERSION);
const expected = listExtractionConfigs().flatMap((config) => {
const requirement = getWebSessionCredentialRequirement(config.providerId);
return requirement && requirement.kind !== "none" ? [{ config, requirement }] : [];
});
assert.equal(contract.providers.length, expected.length);
assert.equal(
new Set(contract.providers.map((provider) => provider.providerId)).size,
expected.length
);
for (const { config, requirement } of expected) {
const published = contract.providers.find(
(provider) => provider.providerId === config.providerId
);
assert.ok(published, `${config.providerId} must be published`);
assert.equal(published.displayName, config.displayName);
assert.equal(published.loginUrl, config.loginUrl);
assert.equal(published.homeUrl, config.homeUrl);
assert.deepEqual(published.tokenSources, config.tokenSources);
assert.equal(published.credential.kind, requirement.kind);
assert.deepEqual(published.credential.storageKeys, [...requirement.storageKeys]);
assert.equal(published.credential.acceptsFullCookieHeader, requirement.acceptsFullCookieHeader);
}
});
test("web-session contract preserves representative token and cookie semantics", () => {
const providers = new Map(
buildWebSessionContract().providers.map((provider) => [provider.providerId, provider])
);
assert.equal(providers.get("deepseek-web")?.credential.kind, "token");
assert.equal(providers.get("zai-web")?.credential.kind, "token");
assert.equal(providers.get("gemini-web")?.credential.kind, "cookie");
assert.equal(providers.get("perplexity-web")?.credential.kind, "cookie");
assert.ok(
providers
.get("deepseek-web")
?.tokenSources.some((source) => source.type === "localStorage" && source.key === "userToken")
);
assert.ok(
providers
.get("gemini-web")
?.tokenSources.some(
(source) =>
source.type === "cookie" &&
source.name === "__Secure-1PSID" &&
source.domain === ".google.com"
)
);
});
test("web-session contract excludes credential values and operator-only guidance", () => {
const serialized = JSON.stringify(buildWebSessionContract());
for (const forbidden of [
"placeholder",
"instructions",
"pollingConfig",
"credentialName",
"guideSteps",
"guideNote",
]) {
assert.equal(
serialized.includes(`\"${forbidden}\"`),
false,
`${forbidden} must not be published`
);
}
});
test("web-session contract route remains management-authenticated", () => {
const source = readFileSync(
new URL("../../src/app/api/providers/web-session-contract/route.ts", import.meta.url),
"utf8"
);
const authCall = source.indexOf("requireManagementAuth(request)");
const responseCall = source.indexOf("NextResponse.json(buildWebSessionContract())");
assert.ok(authCall >= 0, "route must require management authentication");
assert.ok(responseCall > authCall, "authentication must run before contract publication");
});