Files
OmniRoute/tests/unit/free-pool-frontend-repro.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

112 lines
4.3 KiB
TypeScript

/**
* Regression test for #9046 — Free Pool proxy table stays empty despite synced stats.
*
* The API returns `{ success, data: { proxies, total, hasMore, stats, syncErrors } }`,
* but FreePoolTab.tsx was reading `data.items` and `data.total` from the top-level
* JSON — both undefined → empty table + "0 total proxies".
*
* This test verifies the payload normalization fix is present in the source code
* and that the correct contract keys are read by loadData().
*
* Run: node --import tsx/esm --test tests/unit/free-pool-frontend-repro.test.ts
*/
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
const FREEPOOL_TAB_PATH = resolve(
import.meta.dirname,
"../../src/app/(dashboard)/dashboard/settings/components/proxy/FreePoolTab.tsx"
);
test("FreePoolTab.loadData() reads from body.data.proxies (not data.items)", () => {
const src = readFileSync(FREEPOOL_TAB_PATH, "utf-8");
// The fix should use payload normalization: const payload = body?.data ?? body;
assert.ok(
src.includes("const payload = body?.data ?? body;") ||
src.includes("const payload = (body?.data ?? body);"),
"Expected payload normalization: const payload = body?.data ?? body;"
);
// Should read proxies from payload (not items from the top-level data)
assert.ok(
src.includes("payload.proxies ?? payload.items ?? []"),
"Expected setProxies to use payload.proxies with fallback to payload.items"
);
assert.ok(
src.includes("payload.total ?? 0"),
"Expected setTotal to use payload.total with fallback to 0"
);
});
test("FreePoolTab.loadData() no longer reads data.items directly from top-level JSON body", () => {
const src = readFileSync(FREEPOOL_TAB_PATH, "utf-8");
// Before the fix, line 88 was: setProxies(data.items || []);
// This pattern (reading "data.items" from the raw JSON body) should be gone.
const oldPattern = /setProxies\(\s*data\s*\.\s*items\s*(\|\|\s*\[\]\s*)?\)/;
assert.ok(
!oldPattern.test(src),
"Source must NOT contain setProxies(data.items || []) — should use payload.proxies"
);
});
test("FreePoolTab.loadData() no longer reads data.total directly from top-level JSON body", () => {
const src = readFileSync(FREEPOOL_TAB_PATH, "utf-8");
// Before the fix, line 89 was: setTotal(data.total ?? 0);
// This pattern should be gone.
const oldPattern = /setTotal\(\s*data\s*\.\s*total\s*(\?\?\s*0\s*)?\)/;
assert.ok(
!oldPattern.test(src),
"Source must NOT contain setTotal(data.total ?? 0) — should use payload.total"
);
});
// Simulate the actual API contract parsing to prove correctness
test("Payload normalization produces correct values with real API contract shape", () => {
// Simulate what fetch returns:
const apiResponse = {
success: true,
data: {
proxies: [
{ id: "p1", host: "16.163.88.228" },
{ id: "p2", host: "203.0.113.42" },
],
total: 254,
},
};
// THE BUG: reading from top-level body
const buggyProxies = (apiResponse as Record<string, unknown>).items ?? [];
const buggyTotal = (apiResponse as Record<string, unknown>).total ?? 0;
assert.equal(buggyProxies.length, 0, "BUG: data.items is undefined — should show empty table");
assert.equal(buggyTotal, 0, "BUG: data.total is undefined — should show 0 total");
// THE FIX: normalize through body?.data
const payload = (apiResponse as Record<string, unknown>)?.data ?? apiResponse;
const fixedProxies = (payload as Record<string, unknown>).proxies ?? (payload as Record<string, unknown>).items ?? [];
const fixedTotal = (payload as Record<string, unknown>).total ?? 0;
assert.equal(fixedProxies.length, 2, "FIX: payload.proxies contains 2 items");
assert.equal(fixedTotal, 254, "FIX: payload.total is 254");
});
// Also verify the backend contract is still correct
test("Backend route test asserts body.data.proxies contract", () => {
// Verify the route test asserts data.proxies, not data.items
const routeTestPath = resolve(
import.meta.dirname,
"./api/free-proxies-list-route.test.ts"
);
const routeTest = readFileSync(routeTestPath, "utf-8");
assert.ok(
routeTest.includes("body.data.proxies") || routeTest.includes("body.data.total"),
"Route test must assert body.data.proxies and body.data.total"
);
});