import test from 'node:test'; import assert from 'node:assert/strict'; import { createModelCatalogue } from '../src/model-catalogue.mjs'; test('catalogue caches per business, refreshes on expiry and invalidation, and forces a fresh launch read', async () => { let clock = 1000, calls = 0; const client = { getCredentials: async () => ({ files: [] }), getCredentialModels: async () => ({ models: [] }), getUpstreamGroups: async () => { calls++; return [{ 'base-url': 'https://api.z.ai/api/anthropic', keys: [{}], models: [{ name: 'fixture-id' }] }]; }, }; const service = createModelCatalogue({ client: () => client, settings: () => ({ 'model-provider': ['cliproxy'] }), now: () => clock }); const [first, second] = await Promise.all([service.read('one'), service.read('one')]); assert.equal(calls, 1); assert.deepEqual(first, second); assert.equal(first.models[0].key, 'zai-fixture-id'); await service.read('one'); assert.equal(calls, 1); await service.read('two'); assert.equal(calls, 2); await service.read('one', { fresh: true }); assert.equal(calls, 3); clock += 30001; await service.read('one'); assert.equal(calls, 4); service.invalidate(); await service.read('one'); assert.equal(calls, 5); }); test('disabled, refused, and failed providers never retain authorization', async () => { let enabled = true, failed = false; const client = { getCredentials: async () => ({ files: [] }), getCredentialModels: async () => ({ models: [] }), getUpstreamGroups: async () => { if (failed) throw Error('fixture secret'); return [{ 'base-url': 'https://api.z.ai/api/anthropic', keys: [{}], models: [{ name: 'fixture-id' }] }]; } }; const service = createModelCatalogue({ client: () => client, settings: () => ({ 'model-provider': enabled ? ['cliproxy'] : [] }) }); assert.equal((await service.read('one')).models[0].available, true); enabled = false; assert.equal((await service.read('one')).status, 'disabled'); enabled = true; failed = true; const out = await service.read('one', { fresh: true }); assert.equal(out.status, 'unavailable'); assert.ok(out.models.every(m => !m.available)); assert.equal(JSON.stringify(out).includes('fixture secret'), false); }); test('launch admission refreshes selection and refuses a removed model and incompatible harness', async t => { const { startBroker } = await import('../src/runtime.mjs'); const { businessDoc, rolesCopy, systemFor, writeJson } = await import('../../organization/tests/helpers.mjs'); const { mkdtempSync, mkdirSync, rmSync } = await import('node:fs'); const { tmpdir } = await import('node:os'); const { join } = await import('node:path'); const root=mkdtempSync(join(tmpdir(),'model-admission-')); t.after(()=>rmSync(root,{recursive:true,force:true})); const rolesDir=rolesCopy(root), doc=businessDoc(root,'demo'); doc.plugins={...doc.plugins,harness:['pi'],'model-provider':['cliproxy']}; doc.launch.models['zai-fixture-id']={provider:'zai',model:'fixture-id',module:'cliproxy',upstream:'zai'}; doc.roles.coder.vars={...doc.roles.coder.vars,harness:'pi',model:'zai-fixture-id',thinking:'low'}; writeJson(join(root,'businesses/demo.json'),doc); mkdirSync(join(root,'data'),{mode:0o700}); let present=true, reads=0; const service={invalidate(){},read:async(_,{fresh})=>{assert.equal(fresh,true);reads++;return {status:'ready',models:present?[{module:'cliproxy',upstream:'zai',model:'fixture-id',available:true,harnesses:['pi']}]:[]};}}; const bus=await startBroker({dataRoot:join(root,'data'),businesses:{demo:{id:'demo',human:'jason',arbiters:{technical:'chief',delivery:'chief'},roles:{chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder:{authority:{withinRole:[],crossRole:[]}}}}},organization:{dir:root,rolesDir,system:systemFor(root)},modelCatalogue:service});t.after(()=>bus.close()); const human=bus.broker.bindHuman({business:'demo',human:'jason',via:'cli',outsideAgent:true}); const revision=bus.broker.request(human,{verb:'limits'}).revision; const {checkedTransport}=await import('../../harness/src/bundle.mjs'); const entry=doc.launch.models['zai-fixture-id']; const request={business:'demo',instance:'coder',modelKey:'zai-fixture-id',harness:'pi',revision,entry,provider:entry.provider,model:entry.model,account:null,route:'primary',transport:checkedTransport(entry)}; assert.deepEqual(await bus.modelAdmission(request),{allow:true}); present=false; assert.deepEqual(await bus.modelAdmission(request),{allow:false,code:'model-provider-unavailable'}); assert.equal(reads,2); }); test('an account mutation fences reads begun before or during the write', async () => { let release;const gate=new Promise(r=>{release=r;}); const client={getCredentials:async()=>({files:[]}),getCredentialModels:async()=>({models:[]}),getUpstreamGroups:async()=>{await gate;return [{'base-url':'https://api.z.ai/api/anthropic',keys:[{}],models:[{name:'fixture-id'}]}];}}; const service=createModelCatalogue({client:()=>client,settings:()=>({'model-provider':['cliproxy']})}); const read=service.read('one',{fresh:true});service.begin(); assert.equal((await service.read('one',{fresh:true})).status,'unavailable'); release();assert.equal((await read).status,'unavailable');service.end(); assert.equal((await service.read('one',{fresh:true})).status,'ready'); }); test('a different enabled fixture provider supplies legacy mapping and admission without a core case', async t => { const {startBroker}=await import('../src/runtime.mjs');const {businessDoc,rolesCopy,systemFor,writeJson}=await import('../../organization/tests/helpers.mjs'); const {mkdtempSync,mkdirSync,rmSync}=await import('node:fs');const {tmpdir}=await import('node:os');const {join}=await import('node:path'); const root=mkdtempSync(join(tmpdir(),'second-provider-'));t.after(()=>rmSync(root,{recursive:true,force:true}));const rolesDir=rolesCopy(root),doc=businessDoc(root,'demo'); doc.plugins={...doc.plugins,harness:['pi'],'model-provider':['second-fixture']};doc.launch.models['fixture-legacy']={provider:'fixture',model:'fixture-model'};doc.roles.coder.vars={...doc.roles.coder.vars,harness:'pi',model:'fixture-legacy',thinking:'low'}; writeJson(join(root,'businesses/demo.json'),doc);mkdirSync(join(root,'data'),{mode:0o700}); const transport={routeId:'fixture',endpoint:'v1',api:'openai-completions',piProvider:'fixture',compat:{thinkingFormat:'openai',zaiToolStream:false}}; const module={route:({selection})=>selection.provider==='fixture'?{provider:selection.provider,model:selection.model,upstream:'fixture',...transport}:{code:'unsupported'},preflight:({selection,catalogue})=>({allow:catalogue.models.some(m=>m.upstream===selection.upstream&&m.model===selection.model)})}; const makeProviderLoader=()=>({load:async()=>({refused:[]}),snapshot:()=>[{name:'second-fixture',module}]}); const offered={key:'fixture-model',module:'second-fixture',upstream:'fixture',provider:'fixture',model:'fixture-model',label:'Fixture',available:true,reason:null,harnesses:['pi']}; const modelCatalogue={read:async()=>({status:'ready',models:[offered],notices:[]}),invalidate(){}}; const bus=await startBroker({dataRoot:join(root,'data'),businesses:{demo:{id:'demo',human:'jason',arbiters:{technical:'chief',delivery:'chief'},roles:{chief: { definition: 'chief-of-staff', authority: { withinRole: ['decision.resolve.technical', 'decision.resolve.delivery'], crossRole: [] } }, coder:{authority:{withinRole:[],crossRole:[]}}}}},organization:{dir:root,rolesDir,system:systemFor(root)},modelCatalogue,makeProviderLoader});t.after(()=>bus.close()); const human=bus.broker.bindHuman({business:'demo',human:'jason',via:'cli',outsideAgent:true});const view=await bus.broker.request(human,{verb:'routes'}); assert.equal(view.catalogue.legacy['fixture-legacy'].module,'second-fixture'); const answer=await bus.modelAdmission({business:'demo',instance:'coder',modelKey:'fixture-legacy',harness:'pi',revision:view.revision,entry:doc.launch.models['fixture-legacy'],provider:'fixture',model:'fixture-model',account:null,route:'primary',transport}); assert.deepEqual(answer,{allow:true}); }); test('shared generation invalidates cached catalogues and an uncertain fence makes them unavailable', async () => { let version = 0, uncertain = false, calls = 0; const lock = { generation: () => version, assertCertain: () => { if (uncertain) throw Error('uncertain'); } }; const client = { getCredentials: async () => ({ files: [] }), getCredentialModels: async () => ({ models: [] }), getUpstreamGroups: async () => { calls++; return [{ 'base-url': 'https://api.z.ai/api/anthropic', keys: [{}], models: [{ name: 'fixture-id' }] }]; } }; const service = createModelCatalogue({ client: () => client, settings: () => ({ 'model-provider': ['cliproxy'] }), proxyLock: lock }); assert.equal((await service.read('one')).status, 'ready'); version++; assert.equal((await service.read('one')).status, 'ready'); assert.equal(calls, 2); uncertain = true; assert.equal((await service.read('one')).status, 'unavailable'); });