import test from 'node:test'; import assert from 'node:assert/strict'; import { semanticIndexReady, semanticIndexKey, semanticRetryVisible, semanticMediaReady } from '../src/semantic-setup-state.ts'; import { AutomaticIndexGate, semanticAvailable } from '../src/search-state.ts'; const text = () => ({ ready: true, generation: 1, pending: 2, downloading: true, warming: false, embedding: false, error: null, indexing_ready: true, text_model_ready: true, media_model_ready: false, requested_coverage: 'text_only' }); test('warm verified text can index during media download but not concurrent warmup or indexing', () => { assert.equal(semanticIndexReady(text()), true); for (const patch of [{ready:false}, {warming:true}, {embedding:true}, {indexing_ready:false}]) assert.equal(semanticIndexReady({...text(), ...patch}), false); }); test('media repair cannot authorize a text downgrade of an existing full index', () => { const status = {...text(), requested_coverage:'full_media', indexing_ready:false}; assert.equal(semanticAvailable(status), true); assert.equal(semanticIndexReady(status), false); }); test('an incompatible or unprepared generation never claims semantic availability', () => { assert.equal(semanticAvailable({...text(), coverage:null}), false); assert.equal(semanticAvailable({...text(), coverage:'text_only'}), true); assert.equal(semanticAvailable({...text(), coverage:'full_media'}), true); }); test('coverage promotion gets one new automatic attempt without a repeated polling loop', () => { const gate = new AutomaticIndexGate(); const early = semanticIndexKey(text(), 4); const promoted = semanticIndexKey({...text(), requested_coverage:'full_media'}, 4); assert.equal(gate.claim(early), true); assert.equal(gate.claim(early), false); assert.equal(gate.claim(promoted), true); assert.equal(gate.claim(promoted), false); }); test('failed media setup leaves text search available and retry visible', () => { const failed = {...text(), downloading:false, media_model_error:'Download failed'}; assert.equal(semanticAvailable(failed), true); assert.equal(semanticRetryVisible(failed), true); assert.equal(semanticMediaReady(failed), false); assert.equal(semanticRetryVisible({...text(), error:'Query failed'}), false); assert.equal(semanticRetryVisible({...text(), ready:false, error:'Setup failed'}), true); }); test('legacy full-pair status remains blocked during download and ready after setup', () => { const legacy = {ready:true,generation:1,pending:2,downloading:true,warming:false,embedding:false,error:null}; assert.equal(semanticIndexReady(legacy), false); assert.equal(semanticMediaReady(legacy), false); assert.equal(semanticIndexReady({...legacy,downloading:false}), true); assert.equal(semanticMediaReady({...legacy,downloading:false}), true); }); test('partial speech publication cannot create an automatic retry loop', () => { const gate = new AutomaticIndexGate(); const first = {...text(), speech:{ready:true,pending:true,requested_policy:'speech-v1-a',corpus_revision:7}}; assert.equal(gate.claim(semanticIndexKey(first,4)),true); assert.equal(gate.claim(semanticIndexKey({...first,generation:99,pending:0},4)),false); assert.equal(gate.claim(semanticIndexKey({...first,speech:{...first.speech,corpus_revision:8}},4)),true); assert.equal(gate.claim(semanticIndexKey({...first,speech:{...first.speech,requested_policy:'speech-v1-b'}},4)),true); });