Files
OmniRoute/tests/unit/executor-pollinations.test.ts
Praveen K Palaniswamy 65e81158ab fix(ollama): route models by advertised capability (#11088)
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
2026-08-23 11:45:01 -03:00

133 lines
4.8 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { PollinationsExecutor } from "../../open-sse/executors/pollinations.ts";
test("#2987 PollinationsExecutor.buildUrl uses the gen.pollinations.ai gateway (not the legacy text host)", () => {
const executor = new PollinationsExecutor();
// Legacy text.pollinations.ai now 404s ("legacy API"); gen.pollinations.ai/v1
// is the current OpenAI-compatible endpoint and must be the primary.
assert.equal(
executor.buildUrl("openai", true),
"https://gen.pollinations.ai/v1/chat/completions"
);
// No legacy text.pollinations.ai endpoint should remain in the rotation.
assert.equal(
executor.buildUrl("openai", true, 1),
"https://gen.pollinations.ai/v1/chat/completions"
);
});
test("PollinationsExecutor.buildHeaders supports anonymous access and optional SSE accept", () => {
const executor = new PollinationsExecutor();
assert.deepEqual(executor.buildHeaders({}, true), {
"Content-Type": "application/json",
Accept: "text/event-stream",
});
});
test("PollinationsExecutor.buildHeaders sends API auth for the key-backed tier when configured", () => {
const executor = new PollinationsExecutor();
assert.deepEqual(executor.buildHeaders({ apiKey: "poll-key" }, true), {
"Content-Type": "application/json",
Authorization: "Bearer poll-key",
Accept: "text/event-stream",
});
});
test("PollinationsExecutor.transformRequest is a passthrough for alias models", () => {
const executor = new PollinationsExecutor();
const body = { model: "claude", messages: [{ role: "user", content: "hello" }] };
assert.equal(executor.transformRequest("claude", body, true, {}), body);
});
test("PollinationsExecutor enhances 401 errors for premium models with actionable guidance", async () => {
const executor = new PollinationsExecutor();
// Mock super.execute (BaseExecutor.prototype.execute) to throw a 401
const origBaseExec = Object.getPrototypeOf(Object.getPrototypeOf(executor)).execute;
Object.getPrototypeOf(Object.getPrototypeOf(executor)).execute = async function () {
const err = new Error(
"Authentication required. Please provide an API key via Authorization header (Bearer token) or ?key= query parameter."
);
(err as any).status = 401;
throw err;
};
try {
await executor.execute({
model: "claude",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: {},
});
assert.fail("Should have thrown");
} catch (err: any) {
assert.equal(err.status, 401);
assert.match(err.message, /Pollinations model "claude" requires an API key/);
assert.match(err.message, /enter\.pollinations\.ai/);
assert.match(err.message, /Free keyless models/);
} finally {
Object.getPrototypeOf(Object.getPrototypeOf(executor)).execute = origBaseExec;
}
});
test("anonymous premium model fails fast with guidance and never dispatches upstream (#9827)", async () => {
const executor = new PollinationsExecutor();
let dispatched = false;
const origBaseExec = Object.getPrototypeOf(Object.getPrototypeOf(executor)).execute;
Object.getPrototypeOf(Object.getPrototypeOf(executor)).execute = async function () {
dispatched = true;
return new Response("ok", { status: 200 });
};
try {
await executor.execute({
model: "gemini",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: {},
});
assert.fail("Should have thrown");
} catch (err) {
assert.equal(err.status, 401);
assert.match(err.message, /Pollinations model "gemini" requires an API key/);
assert.match(err.message, /Free keyless models/);
assert.equal(
dispatched,
false,
"upstream must not be called for premium models on the anonymous path"
);
} finally {
Object.getPrototypeOf(Object.getPrototypeOf(executor)).execute = origBaseExec;
}
});
test("PollinationsExecutor passes through 401 errors for non-premium models", async () => {
const executor = new PollinationsExecutor();
const origBaseExec = Object.getPrototypeOf(Object.getPrototypeOf(executor)).execute;
Object.getPrototypeOf(Object.getPrototypeOf(executor)).execute = async function () {
const err = new Error("Authentication required");
(err as any).status = 401;
throw err;
};
try {
await executor.execute({
model: "openai",
body: { messages: [{ role: "user", content: "hi" }] },
stream: false,
credentials: {},
});
assert.fail("Should have thrown");
} catch (err: any) {
assert.equal(err.status, 401);
// Free models should NOT get the enhanced message
assert.doesNotMatch(err.message, /requires an API key/);
} finally {
Object.getPrototypeOf(Object.getPrototypeOf(executor)).execute = origBaseExec;
}
});