Files
OmniRoute/tests/unit/spark-openai-compatible-endpoint-7942.test.ts
FenjuFu 19cbe8ae14 fix(providers): route iflytek/sparkdesk to Spark's OpenAI-compatible host (#7942)
Both entries declare format: "openai" with authHeader: "bearer", but
pointed at spark-api.xf-yun.com — Spark's WebSocket host, which
authenticates with an HMAC-SHA256 signature over app_id/apiKey/apiSecret
and rejects bearer tokens. The OpenAI-compatible HTTP API lives on
spark-api-open.xf-yun.com/v1, so neither provider could complete a
request as configured.

sparkdesk also listed a "general" model; that is a WebSocket domain
value and is not accepted by the HTTP endpoint, so it becomes "lite"
(Spark Lite). The free-model catalog's sparkdesk row is updated to
match, and a regression test locks both baseUrls, the removed "general"
model id, and catalog/registry cross-reference.

Reconstructed against release/v3.8.49 (folds in the same-PR follow-up
"point sparkdesk free-catalog row at lite").

Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-07-21 13:16:52 -03:00

53 lines
2.4 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { iflytekProvider } from "../../open-sse/config/providers/registry/iflytek/index.ts";
import { sparkdeskProvider } from "../../open-sse/config/providers/registry/sparkdesk/index.ts";
import { FREE_MODEL_BUDGETS } from "../../open-sse/config/freeModelCatalog.data.ts";
// #7942: both entries declare format: "openai" + authHeader: "bearer", but pointed at
// spark-api.xf-yun.com — Spark's WebSocket host, which authenticates with an
// HMAC-SHA256 signature over app_id/apiKey/apiSecret and rejects bearer tokens. The
// OpenAI-compatible HTTP API lives on spark-api-open.xf-yun.com/v1.
test("#7942: iflytek registry baseUrl uses Spark's OpenAI-compatible HTTP host", () => {
assert.equal(
iflytekProvider.baseUrl,
"https://spark-api-open.xf-yun.com/v1/chat/completions",
"iflytek must route through the OpenAI-compatible HTTP endpoint, not the " +
"WebSocket-only spark-api.xf-yun.com host (which rejects bearer auth)"
);
});
test("#7942: sparkdesk registry baseUrl uses Spark's OpenAI-compatible HTTP host", () => {
assert.equal(
sparkdeskProvider.baseUrl,
"https://spark-api-open.xf-yun.com/v1/chat/completions",
"sparkdesk must route through the OpenAI-compatible HTTP endpoint, not the " +
"WebSocket-only spark-api.xf-yun.com/v3.1 host (which rejects bearer auth)"
);
});
test("#7942: sparkdesk no longer advertises the WebSocket-only 'general' domain", () => {
const modelIds = sparkdeskProvider.models.map((m) => m.id);
assert.ok(
!modelIds.includes("general"),
"'general' is a WebSocket-domain value rejected by the HTTP endpoint"
);
assert.ok(
modelIds.includes("lite"),
"sparkdesk should advertise 'lite' (Spark Lite) in place of the removed 'general' domain"
);
});
test("#7942: free-model catalog's sparkdesk row references a model the registry still advertises", () => {
const sparkdeskModelIds = new Set(sparkdeskProvider.models.map((m) => m.id));
const catalogRows = FREE_MODEL_BUDGETS.filter((row) => row.provider === "sparkdesk");
assert.ok(catalogRows.length > 0, "expected at least one sparkdesk row in the free catalog");
for (const row of catalogRows) {
assert.ok(
sparkdeskModelIds.has(row.modelId),
`free catalog references sparkdesk/${row.modelId}, which the registry no longer advertises`
);
}
});