Files
OmniRoute/tests/unit/executor-kimi-web.test.ts
Ravi Tharuma 137e49e393 feat(search): first-class X Search via SuperGrok x_search (#10988)
5 — Provider x-search de primeira classe (SuperGrok/xAI x_search) em POST /v1/search e MCP omniroute_x_search. Fallback de credenciais xai-oauth→xao→xai; distinto de web search e do X Developer MCP.

Reconciliado com o release tip (que já incluía #10981 "skip catalog-default SearXNG" deste mesmo lote): merge trouxe 5 conflitos reais de contagem gerada (llm.txt/README.md/AGENTS.md/PROVIDER_REFERENCE.md/SVGs/46 mirrors i18n, todos verificados como bump puro 347→348, sem perda de conteúdo do HEAD) + 1 conflito real de mergeable=CONFLICTING.

Durante a validação, os 3 testes novos de SearXNG expuseram um bug real de interação com #10981: `isUnconfiguredLoopbackSearchProvider()` checava o baseUrl ESTÁTICO do catálogo em vez do baseUrl efetivo (após override de `provider_options.baseUrl` ou `providerSpecificData.baseUrl` da conexão), então QUALQUER request a searxng-search — mesmo com override customizado — era rejeitado como se fosse o default não-configurado. Corrigido em `open-sse/handlers/search.ts` (resolve o baseUrl efetivo via `resolveSearchBaseUrl()` antes do skip-check, tanto para o provider primário quanto o alternate). Um teste do próprio #10988 que assumia o comportamento pré-#10981 (default localhost:8888 sempre atendido) foi atualizado para refletir o comportamento já mesclado e intencional (503 quando não configurado).

Validação completa: typecheck limpo, 70/70 testes unit (search-route/search-registry/x-search-provider/searxng-loopback-default), 24/24 vitest MCP, 14/14 integration (search-providers-catalog), lint limpo nos arquivos tocados, docs-counts-sync OK (2 drifts soft pré-existentes, não relacionados), gates estáticos (file-size/complexity/cognitive/dead-code/changelog) todos OK.
2026-08-21 14:16:42 -03:00

248 lines
8.8 KiB
TypeScript

// Tests for the international Kimi web executor (www.kimi.com Connect-RPC API).
//
// Previously this provider targeted kimi.moonshot.cn; that domain now redirects
// every non-CN visitor to www.kimi.com, which uses a Connect-RPC streaming API.
// These tests pin the parser behavior of the Connect envelope framing and the
// JSON event-delta extractor.
import { describe, it } from "node:test";
import assert from "node:assert/strict";
const mod = await import("../../open-sse/executors/kimi-web.ts");
const { getModelsByProviderId } = await import("../../open-sse/config/providerModels.ts");
describe("KimiWebExecutor", () => {
it("can be instantiated", () => {
const executor = new mod.KimiWebExecutor();
assert.ok(executor);
});
it("execute returns a 400 error when no access token is provided", async () => {
const executor = new mod.KimiWebExecutor();
const result = await executor.execute({
model: "k2d6",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: { apiKey: "" },
signal: null,
} as never);
assert.equal(result.response.status, 400);
const body = (await result.response.json()) as { error: { code: string } };
assert.match(body.error.code, /HTTP_400|400/);
});
it("execute targets www.kimi.com (not kimi.moonshot.cn)", async () => {
const executor = new mod.KimiWebExecutor();
let capturedUrl = "";
const originalFetch = globalThis.fetch;
try {
globalThis.fetch = (async (url: Parameters<typeof fetch>[0]) => {
capturedUrl = String(url);
return new Response(new ReadableStream({ start: (c) => c.close() }), {
status: 200,
headers: { "content-type": "application/connect+json" },
});
}) as typeof fetch;
await executor.execute({
model: "k2d6",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: { apiKey: "opaque-kimi-access-token" },
signal: null,
} as never);
assert.ok(capturedUrl.startsWith("https://www.kimi.ai/"), `got ${capturedUrl}`);
assert.ok(!capturedUrl.includes("moonshot.cn"));
} finally {
globalThis.fetch = originalFetch;
}
});
it("builds the current snake_case ChatRequest without replaying cookies", async () => {
const executor = new mod.KimiWebExecutor();
const endStream = mod.frameConnectMessage("{}");
endStream[0] = 2;
let capturedInit: RequestInit | undefined;
const originalFetch = globalThis.fetch;
try {
globalThis.fetch = (async (_url: Parameters<typeof fetch>[0], init?: RequestInit) => {
capturedInit = init;
return new Response(endStream, {
status: 200,
headers: { "content-type": "application/connect+json" },
});
}) as typeof fetch;
const result = await executor.execute({
model: "k3",
body: {
model: "k3",
messages: [
{ role: "system", content: "Be terse." },
{ role: "user", content: "hi" },
],
tools: null,
functions: null,
},
stream: false,
credentials: { apiKey: "access_token=opaque-token" },
signal: null,
} as never);
assert.equal(result.response.status, 200);
const headers = capturedInit?.headers as Record<string, string>;
assert.equal(headers.Authorization, "Bearer opaque-token");
assert.equal(headers.Cookie, undefined);
const framed = capturedInit?.body as Uint8Array;
const decoded = mod.decodeConnectFrame(framed, 0);
const request = decoded.frame?.message as {
chat_id: string;
kimiplus_id: string;
scenario: string;
model?: unknown;
tools: unknown[];
message: { blocks: Array<{ text: { content: string } }> };
options: {
system_prompt: string;
thinking: boolean;
enable_plugin: boolean;
reasoning_effort: string;
context_length: string;
};
};
assert.equal(request.chat_id, "");
assert.equal(request.kimiplus_id, undefined);
assert.equal(request.scenario, "SCENARIO_K2D5");
assert.equal(request.model, undefined);
assert.deepEqual(request.tools, []);
assert.equal(request.message.blocks[0].text.content, "hi");
assert.equal(request.options.system_prompt, "Be terse.");
assert.equal(request.options.thinking, true);
assert.equal(request.options.enable_plugin, false);
assert.equal(request.options.reasoning_effort, "REASONING_EFFORT_NONE");
assert.equal(request.options.context_length, undefined);
} finally {
globalThis.fetch = originalFetch;
}
});
it("maps k2d6 effort exactly and rejects unsupported levels", async () => {
const executor = new mod.KimiWebExecutor();
const endStream = mod.frameConnectMessage("{}");
endStream[0] = 2;
const originalFetch = globalThis.fetch;
try {
globalThis.fetch = (async () => new Response(endStream, { status: 200 })) as typeof fetch;
const accepted = await executor.execute({
model: "k2d6",
body: {
model: "k2d6",
messages: [{ role: "user", content: "hi" }],
reasoning_effort: "low",
},
stream: false,
credentials: { apiKey: "opaque-token" },
signal: null,
} as never);
assert.equal(accepted.transformedBody.options.reasoning_effort, "REASONING_EFFORT_LOW");
const rejected = await executor.execute({
model: "k2d6",
body: {
model: "k2d6",
messages: [{ role: "user", content: "hi" }],
reasoning_effort: "high",
},
stream: false,
credentials: { apiKey: "opaque-token" },
signal: null,
} as never);
assert.equal(rejected.response.status, 400);
} finally {
globalThis.fetch = originalFetch;
}
});
});
describe("resolveModelConfig", () => {
const { resolveModelConfig } = mod;
it("maps k3 to the K2D5 route (same as K2.6, not premium OK Computer)", () => {
const cfg = resolveModelConfig("k3");
assert.ok(cfg);
assert.equal(cfg.scenario, "SCENARIO_K2D5");
assert.equal(cfg.kimiPlusId, undefined);
assert.deepEqual(cfg.supportedReasoningEfforts, [
"REASONING_EFFORT_NONE",
"REASONING_EFFORT_LOW",
]);
assert.equal(cfg.defaultReasoningEffort, "REASONING_EFFORT_NONE");
assert.deepEqual(cfg.supportedContextLengths, []);
assert.equal(cfg.defaultContextLength, undefined);
});
it("maps k2d6 to the K2D5 route and its exact effort enum", () => {
const cfg = resolveModelConfig("k2d6");
assert.ok(cfg);
assert.equal(cfg.scenario, "SCENARIO_K2D5");
assert.deepEqual(cfg.supportedReasoningEfforts, [
"REASONING_EFFORT_NONE",
"REASONING_EFFORT_LOW",
]);
assert.equal(cfg.defaultReasoningEffort, "REASONING_EFFORT_NONE");
});
it("does not silently route an unknown or unsupported agent model", () => {
assert.equal(resolveModelConfig("k2d6-thinking"), null);
assert.equal(resolveModelConfig("k3-agent-ultra"), null);
});
});
describe("kimi-web catalog", () => {
it("lists only currently supported non-agent web models", () => {
const models = getModelsByProviderId("kimi-web");
assert.deepEqual(
models.map((model) => ({ id: model.id, name: model.name })),
[
{ id: "k3", name: "K3" },
{ id: "k2d6", name: "K2.6" },
]
);
assert.ok(models.every((model) => model.supportsReasoning));
assert.ok(!models.some((model) => model.id.includes("agent")));
assert.ok(
!models.some((model) => ["kimi-default", "kimi-k2.6", "kimi-128k"].includes(model.id))
);
});
});
describe("extractKimiAccessToken", () => {
const { extractKimiAccessToken } = mod;
it("returns empty string for empty input", () => {
assert.equal(extractKimiAccessToken(""), "");
assert.equal(extractKimiAccessToken(" "), "");
});
it("accepts the current opaque localStorage access token", () => {
assert.equal(extractKimiAccessToken("opaque-token"), "opaque-token");
});
it("keeps legacy kimi-auth cookie input compatible", () => {
const jwt = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ4In0.signature";
const pasted = `_ga=GA1.1.x; theme=dark; kimi-auth=${jwt}; _gcl_au=1.1.x; lang=en-US`;
assert.equal(extractKimiAccessToken(pasted), jwt);
});
it("extracts access_token from storage-like input", () => {
assert.equal(extractKimiAccessToken("access_token=current-token"), "current-token");
});
it("strips a leading Authorization: Bearer label", () => {
assert.equal(extractKimiAccessToken("Authorization: Bearer current-token"), "current-token");
});
it("returns empty when no Kimi token is present", () => {
assert.equal(extractKimiAccessToken("foo=bar; baz=qux"), "");
});
});