import test from 'node:test'; import assert from 'node:assert/strict'; import { visionReadiness } from '../src/vision-readiness.js'; test('unsolved readiness means manual logging is always reachable in one action', () => { for (const status of [null, undefined, { enabled: false }, { enabled: true, providerReady: false }]) { const readiness = visionReadiness(status); assert.equal(readiness.ready, false, JSON.stringify(status)); assert.equal(readiness.manualOnly, true, JSON.stringify(status)); assert.equal(typeof readiness.manualLabel, 'string'); assert.match(readiness.manualLabel, /manual|without AI/i); } }); test('reports distinct phases so the UI never claims a worker is ready when it is not', () => { assert.equal(visionReadiness(null).phase, 'checking'); assert.equal(visionReadiness({ enabled: false }).phase, 'disabled'); assert.equal(visionReadiness({ enabled: true, providerReady: false }).phase, 'offline'); // The readiness contract widened in #15: answering the provider probe is no longer enough. assert.equal(visionReadiness({ enabled: true, providerReady: true }).phase, 'loading'); assert.equal(visionReadiness({ enabled: true, providerReady: true, modelSeen: false }).phase, 'loading'); assert.equal(visionReadiness({ enabled: true, providerReady: true, modelSeen: true }).phase, 'ready'); }); test('readiness waits for the pinned model to load before promising AI', () => { // Issue #15: a worker that answers /v1/models has not necessarily loaded the model yet. // Readiness must pass only after the pinned model is loaded, or the user is offered an // AI control that fails after the upload. const notLoaded = visionReadiness({ enabled: true, profile: 'selfhost', providerReady: true, modelSeen: false, model: 'SmolVLM2-2.2B-Instruct', }); assert.equal(notLoaded.phase, 'loading'); assert.equal(notLoaded.ready, false); assert.equal(notLoaded.manualOnly, true); assert.match(notLoaded.message, /model/i); assert.match(notLoaded.manualLabel, /manual|without AI/i); const unverified = visionReadiness({ enabled: true, profile: 'selfhost', providerReady: true }); assert.equal(unverified.ready, false, 'an unconfirmed model must never be treated as loaded'); }); test('a loaded pinned model keeps AI analysis available and does not force the manual path', () => { const readiness = visionReadiness({ enabled: true, providerReady: true, modelSeen: true, model: 'SmolVLM2-2.2B-Instruct' }); assert.equal(readiness.ready, true); assert.equal(readiness.manualOnly, false); assert.equal(readiness.manualLabel, ''); }); test('readiness never promises a symptom or diagnosis capability', () => { for (const status of [ null, { enabled: false }, { enabled: true, providerReady: false }, { enabled: true, providerReady: true, modelSeen: true }, { enabled: true, providerReady: true, modelSeen: false }, ]) { const readiness = visionReadiness(status); const copy = `${readiness.manualLabel} ${readiness.message}`; assert.doesNotMatch(copy, /diagnos|bleeding|cancer|symptom/i); } });