llm-checker/tests/ollama-selector-localhost-fallback.test.js
signerless 50e4cab932 fix(ollama): reuse resolved loopback client in selectors
Route deterministic probes and AI evaluator chat requests through the shared Ollama client so they inherit localhost-to-loopback fallback behavior.\n\nAdd selector-specific regression coverage for Windows-style localhost failures and align Ollama env URL handling through the same client path.
2026-03-26 10:16:12 +01:00

130 lines
4.7 KiB
JavaScript

const assert = require('assert');
const DeterministicModelSelector = require('../src/models/deterministic-selector');
const AICheckSelector = require('../src/models/ai-check-selector');
function createJsonResponse(payload, status = 200) {
return {
ok: status >= 200 && status < 300,
status,
statusText: status === 200 ? 'OK' : 'ERROR',
async json() {
return payload;
},
async text() {
return JSON.stringify(payload);
}
};
}
async function testProbeAndEvaluatorUseResolvedFallbackURL() {
const originalFetch = global.fetch;
const calls = [];
global.fetch = async (url, options = {}) => {
calls.push({ url, options });
if (url === 'http://localhost:11434/api/version') {
const error = new Error('request to http://localhost:11434/api/version failed, reason: connect ECONNREFUSED ::1:11434');
error.code = 'ECONNREFUSED';
throw error;
}
if (url === 'http://127.0.0.1:11434/api/version') {
return createJsonResponse({ version: '0.18.2' });
}
if (url === 'http://127.0.0.1:11434/api/generate') {
const body = JSON.parse(options.body);
assert.strictEqual(body.model, 'llama3.2:3b');
assert.strictEqual(body.options.num_predict, 128);
return createJsonResponse({
response: 'unit tests keep regressions contained',
eval_count: 96,
eval_duration: 2_000_000_000
});
}
if (url === 'http://127.0.0.1:11434/api/chat') {
const body = JSON.parse(options.body);
assert.strictEqual(body.model, 'llama3.2:3b');
assert.strictEqual(body.options.temperature, 0.1);
assert.strictEqual(body.options.num_ctx, 4096);
return createJsonResponse({
message: {
content: JSON.stringify({
winner: 'llama3.2:3b',
ranking: [
{
name: 'llama3.2:3b',
aiScore: 88,
shortWhy: 'best local fit'
}
]
})
}
});
}
throw new Error(`Unexpected URL in selector fallback test: ${url}`);
};
try {
const deterministicSelector = new DeterministicModelSelector();
const probeTPS = await deterministicSelector.runSingleProbe('llama3.2:3b', 'general');
assert(Number.isFinite(probeTPS) && probeTPS > 0, `Expected positive probe TPS, got ${probeTPS}`);
assert.strictEqual(
deterministicSelector.ollamaClient.baseURL,
'http://127.0.0.1:11434',
'probe path should persist the resolved fallback base URL'
);
const aiCheckSelector = new AICheckSelector();
const aiResult = await aiCheckSelector.callOllamaEvaluator('llama3.2:3b', {
hardware: { category: 'general' },
candidates: [
{
name: 'llama3.2:3b',
paramsB: 3,
quant: 'Q4_K_M',
requiredGB: 2,
installed: true
}
]
});
assert.strictEqual(aiResult.winner, 'llama3.2:3b');
assert.strictEqual(aiResult.ranking.length, 1);
assert.strictEqual(aiResult.ranking[0].name, 'llama3.2:3b');
assert.strictEqual(
aiCheckSelector.ollamaClient.baseURL,
'http://127.0.0.1:11434',
'evaluator path should persist the resolved fallback base URL'
);
const urls = calls.map((entry) => entry.url);
assert.ok(urls.includes('http://127.0.0.1:11434/api/generate'), 'probe path should use IPv4 fallback URL');
assert.ok(urls.includes('http://127.0.0.1:11434/api/chat'), 'evaluator path should use IPv4 fallback URL');
assert.ok(!urls.includes('http://localhost:11434/api/generate'), 'probe path should not keep using localhost after fallback');
assert.ok(!urls.includes('http://localhost:11434/api/chat'), 'evaluator path should not keep using localhost after fallback');
} finally {
global.fetch = originalFetch;
}
}
async function run() {
await testProbeAndEvaluatorUseResolvedFallbackURL();
console.log('ollama-selector-localhost-fallback.test.js: OK');
}
if (require.main === module) {
run().catch((error) => {
console.error('ollama-selector-localhost-fallback.test.js: FAILED');
console.error(error);
process.exit(1);
});
}
module.exports = { run };