| 1 | // Run: pnpm exec tsx src/__tests__/provider-failure-meta.test.ts |
| 2 | import assert from "node:assert/strict"; |
| 3 | |
| 4 | import { initialState, reducer } from "../lib/useController"; |
| 5 | import { historyMessagesToItems } from "../lib/historyItems"; |
| 6 | import { historyNoticeItems } from "../lib/controllerNotices"; |
| 7 | import { presentError } from "../lib/errorPresentation"; |
| 8 | import { en } from "../locales/en"; |
| 9 | import type { HistoryMessage } from "../lib/types"; |
| 10 | |
| 11 | const started = reducer(initialState, { type: "event", e: { kind: "turn_started" } }); |
| 12 | const failed = reducer(started, { |
| 13 | type: "event", |
| 14 | e: { |
| 15 | kind: "turn_done", |
| 16 | err: "Deepseek2 · Chat Completions: Request endpoint not found (HTTP 404).", |
| 17 | detail: "Connection ID: deepseek-anthropic\nRequest path: /anthropic/v1/chat/completions", |
| 18 | diagnostic: { kind: "request", status: 404, providerId: "deepseek-anthropic", providerDisplayName: "Deepseek2", protocol: "openai", requestPath: "/anthropic/v1/chat/completions" }, |
| 19 | }, |
| 20 | }); |
| 21 | const notice = failed.items.find((item) => item.kind === "notice" && item.level === "warn"); |
| 22 | |
| 23 | assert.equal( |
| 24 | notice?.kind === "notice" ? notice.text : "", |
| 25 | "Deepseek2 · Chat Completions: Request endpoint not found (HTTP 404).", |
| 26 | "provider display identity stays in the primary live error", |
| 27 | ); |
| 28 | assert.equal( |
| 29 | notice?.kind === "notice" ? notice.detail : "", |
| 30 | "Connection ID: deepseek-anthropic\nRequest path: /anthropic/v1/chat/completions", |
| 31 | "stable provider id and sanitized path stay in live diagnostic details", |
| 32 | ); |
| 33 | |
| 34 | const diagnostic = { kind: "transport_protocol", transportCode: "PROTOCOL_ERROR" }; |
| 35 | const history: HistoryMessage = { role: "notice", content: "opaque provider failure", level: "warn", diagnostic }; |
| 36 | const live = reducer(started, { type: "event", e: { kind: "turn_done", err: history.content, diagnostic } }); |
| 37 | const interrupted = reducer(started, { type: "event", e: { kind: "turn_done", status: "interrupted", err: history.content, diagnostic } }); |
| 38 | const liveNotice = reducer(started, { type: "event", e: { kind: "notice", level: "warn", text: history.content, diagnostic } }); |
| 39 | for (const items of [live.items, interrupted.items, liveNotice.items, historyMessagesToItems([history], "full").items, historyNoticeItems(history, "windowed")]) { |
| 40 | const item = items.find(item => item.kind === "notice" && item.level === "warn"); |
| 41 | assert.ok(item?.kind === "notice"); |
| 42 | assert.deepEqual(item.diagnostic, diagnostic, "live and both history projections retain structured evidence"); |
| 43 | const presentation = presentError(item.text, key => en[key], "en", item.diagnostic); |
| 44 | assert.equal(presentation.summary, en["error.transportProtocol"]); |
| 45 | assert.match(presentation.detail, /PROTOCOL_ERROR/); |
| 46 | } |
| 47 |