Files
OmniRoute/tests/unit/oauth-device-code-error-transparency.test.ts
danscMax 7ff2e5c0b5 fix(oauth): surface sanitized device-code error instead of a generic 500 (#7511)
The dynamic OAuth GET handler swallowed every thrown error into a generic
`{ error: "Internal server error" }` 500, so a device-code upstream failure
(qwen → qwen.ai, codebuddy-cn → copilot.tencent.com — geo-block / outage / bad
client) surfaced in the extension as an indistinguishable 'ошибка сервера
OmniRoute', hiding WHY it failed.

Route the caught error through sanitizeErrorMessage() (already imported, hard
rule #12) so the real reason ("Device code request failed: …", "CodeBuddy
state request failed (403)") reaches the client, falling back to the generic
only when the sanitizer yields nothing.

Regression guard: tests/unit/oauth-device-code-error-transparency.test.ts
(source-level — the route needs the full Next request/auth/upstream graph, so
behavioural validation belongs on a real build/VPS).
2026-07-18 11:35:05 -03:00

33 lines
1.4 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path";
// Regression guard for "qwen / codebuddy-cn device-code show 'ошибка сервера OmniRoute'".
// The dynamic OAuth GET handler used to swallow EVERY thrown error into a generic
// `{ error: "Internal server error" }` 500, so a device-code upstream failure (qwen.ai /
// copilot.tencent.com — geo-block, outage, bad client) was indistinguishable from a real
// server bug. The fix surfaces the SANITIZED upstream message via sanitizeErrorMessage.
//
// Source-level guard: the route runs inside the Next server (auth + module graph), so a
// behavioural test would need a full request/auth/upstream mock; this pins the exact change.
const here = dirname(fileURLToPath(import.meta.url));
const route = readFileSync(
resolve(here, "../../src/app/api/oauth/[provider]/[action]/route.ts"),
"utf8"
);
test("OAuth GET catch surfaces the sanitized upstream reason, not a bare generic 500", () => {
assert.match(
route,
/const detail = sanitizeErrorMessage\(/,
"the GET catch must sanitize and surface the real error"
);
assert.match(
route,
/error:\s*detail\s*\|\|\s*"Internal server error"/,
"the GET catch must return the sanitized detail (falling back to the generic only when empty)"
);
});