/** Page-store join: directory × namespaces × credentials, with last-good rows on failure. */ import { describe, expect, it } from 'vitest' import type { RpcResponse } from '@deepseek-ai/dsh-client-connection/client' import { messageOf, ModelsSettingsStore } from '../src/client/store.ts' let nextRpc = 0 function ok(value: T): RpcResponse { return { rpcId: `r-${nextRpc++}` as never, result: { ok: true, value } } } function fail(message: string): RpcResponse { return { rpcId: `r-${nextRpc++}` as never, result: { ok: false, error: { code: 'internal', message, details: {} } } } } const DIRECTORY = [ { provider: 'deepseek-official', displayName: 'DeepSeek', settingsNs: 'llm-deepseek', settingsPath: [], active: true }, { provider: 'openai', displayName: 'openai', settingsNs: 'llm-pi-ai', settingsPath: ['providers', 'openai'], active: true }, { provider: 'anthropic', displayName: 'anthropic', settingsNs: 'llm-pi-ai', settingsPath: ['providers', 'anthropic'], active: false }, { provider: 'ghost', displayName: 'Ghost', settingsNs: '', settingsPath: [], active: true }, ] const NAMESPACES = [ { ns: 'llm-deepseek', schema: {}, value: { apiKeyEnv: 'DEEPSEEK_API_KEY', baseURL: 'https://base' }, base: { baseURL: 'https://base' }, applies: 'live' as const, secrets: [{ path: ['apiKey'], set: false }], revision: 0, }, { ns: 'llm-pi-ai', schema: {}, value: { providers: { openai: { apiKeyEnv: 'OPENAI_API_KEY' } } }, user: { providers: { openai: { apiKeyEnv: 'OPENAI_API_KEY' } } }, applies: 'live' as const, secrets: [], revision: 0, }, ] function api(overrides: { providers?: () => Promise> describeSettings?: () => Promise> describeCredentials?: (refs: string[]) => Promise }>> } = {}) { const seenRefs: string[][] = [] const face = { llm: { providers: overrides.providers ?? (() => Promise.resolve(ok({ providers: DIRECTORY }))), models: () => Promise.resolve(ok({ groups: [], failures: [] })), }, settings: { describe: overrides.describeSettings ?? (() => Promise.resolve(ok({ writable: true, hasDocument: false, namespaces: NAMESPACES }))), update: () => Promise.resolve(fail('unused')), replace: () => Promise.resolve(fail('unused')), }, credentials: { describe: (payload: { refs: string[] }) => { seenRefs.push(payload.refs) return (overrides.describeCredentials ?? (refs => Promise.resolve(ok({ credentials: Object.fromEntries(refs.map(ref => [ref, { configured: ref === 'OPENAI_API_KEY', writable: true }])), }))))(payload.refs) }, set: () => Promise.resolve(ok({})), unset: () => Promise.resolve(ok({})), }, } return { face: face as never, seenRefs } } describe('ModelsSettingsStore', () => { it('joins rows with configured, removable, and credential state', async () => { const { face, seenRefs } = api() const store = new ModelsSettingsStore(face) await store.load() const state = store.store.getSnapshot() expect(state.status).toBe('ready') expect(state.writable).toBe(true) expect(state.credentialError).toBeNull() expect(seenRefs).toEqual([['DEEPSEEK_API_KEY', 'OPENAI_API_KEY']]) const byProvider = new Map(state.rows.map(row => [row.entry.provider, row])) expect(byProvider.get('deepseek-official')).toMatchObject({ configured: true, removable: false, apiKeyEnv: 'DEEPSEEK_API_KEY', credential: { configured: false, writable: true }, literalApiKeyConfigured: false, }) expect(byProvider.get('openai')).toMatchObject({ configured: true, removable: true, apiKeyEnv: 'OPENAI_API_KEY', credential: { configured: true }, }) expect(byProvider.get('anthropic')).toMatchObject({ configured: false, removable: false }) expect(byProvider.get('anthropic')?.apiKeyEnv).toBeUndefined() expect(byProvider.get('ghost')).toMatchObject({ configured: false, removable: false }) expect(state.namespaces.get('llm-pi-ai')?.ns).toBe('llm-pi-ai') }) it('degrades the credential badge, not the page, when the credential domain fails', async () => { const { face } = api({ describeCredentials: () => Promise.resolve(fail('no provider')) }) const store = new ModelsSettingsStore(face) await store.load() const state = store.store.getSnapshot() expect(state.status).toBe('ready') expect(state.credentialError).toBe('no provider') expect(state.rows.every(row => row.credential === undefined)).toBe(true) }) it('settles a credential transport rejection without leaving the store loading', async () => { const { face } = api({ describeCredentials: () => Promise.reject(new Error('credential transport down')), }) const store = new ModelsSettingsStore(face) await expect(store.load()).resolves.toBeUndefined() expect(store.store.getSnapshot()).toMatchObject({ status: 'ready', credentialError: 'credential transport down', }) }) it('stringifies a non-Error credential transport rejection', async () => { const { face } = api({ // oxlint-disable-next-line typescript/prefer-promise-reject-errors -- the non-Error rejection is the scenario describeCredentials: () => Promise.reject('credential transport refusal'), }) const store = new ModelsSettingsStore(face) await expect(store.load()).resolves.toBeUndefined() expect(store.store.getSnapshot().credentialError).toBe('credential transport refusal') }) it('joins a configured literal key from the redacted secret sidecar', async () => { const { face } = api({ describeSettings: () => Promise.resolve(ok({ writable: true, hasDocument: false, namespaces: [{ ...NAMESPACES[0], secrets: [ { path: ['apiKey', 'nested'], set: true }, { path: ['different'], set: true }, { path: ['apiKey'], set: true }, ], }] as never, })), providers: () => Promise.resolve(ok({ providers: [DIRECTORY[0]] as never })), }) const store = new ModelsSettingsStore(face) await store.load() expect(store.store.getSnapshot().rows[0]).toMatchObject({ literalApiKeyConfigured: true, apiKeyEnv: 'DEEPSEEK_API_KEY', }) }) it('surfaces a directory failure and keeps the last good rows', async () => { const { face } = api() const store = new ModelsSettingsStore(face) await store.load() expect(store.store.getSnapshot().rows).toHaveLength(4) const broken = api({ providers: () => Promise.resolve(fail('directory down')) }) const failing = new ModelsSettingsStore(broken.face) await failing.load() expect(failing.store.getSnapshot()).toMatchObject({ status: 'error', error: 'directory down' }) // The first store's snapshot is untouched by the second's failure. expect(store.store.getSnapshot().status).toBe('ready') }) it('lets the newest load win over a stale slow response', async () => { let release: (() => void) | undefined const gate = new Promise((resolve) => { release = resolve }) let call = 0 const { face } = api({ providers: async () => { call += 1 if (call === 1) { await gate return fail('stale slow failure') } return ok({ providers: DIRECTORY }) }, }) const store = new ModelsSettingsStore(face) const first = store.load() const second = store.load() release?.() await Promise.all([first, second]) expect(store.store.getSnapshot().status).toBe('ready') }) }) describe('edge joins', () => { it('treats a non-object profile as having no credential reference', async () => { const { face } = api({ describeSettings: () => Promise.resolve(ok({ writable: true, hasDocument: false, namespaces: [{ ns: 'llm-pi-ai', schema: {}, value: { providers: { weird: 'oops' } }, applies: 'live' as const, secrets: [], revision: 0, }] as never, })), providers: () => Promise.resolve(ok({ providers: [ { provider: 'weird', displayName: 'weird', settingsNs: 'llm-pi-ai', settingsPath: ['providers', 'weird'], active: false }, ] as never, })), }) const store = new ModelsSettingsStore(face) await store.load() const state = store.store.getSnapshot() expect(state.rows[0]).toMatchObject({ configured: true, removable: false }) expect(state.rows[0]?.apiKeyEnv).toBeUndefined() }) it('skips the credential describe entirely when no row names a reference', async () => { const { face, seenRefs } = api({ describeSettings: () => Promise.resolve(ok({ writable: true, hasDocument: false, namespaces: [{ ns: 'llm-pi-ai', schema: {}, value: { providers: {} }, applies: 'live' as const, secrets: [], revision: 0 }] as never, })), providers: () => Promise.resolve(ok({ providers: [ { provider: 'anthropic', displayName: 'anthropic', settingsNs: 'llm-pi-ai', settingsPath: ['providers', 'anthropic'], active: false }, ] as never, })), }) const store = new ModelsSettingsStore(face) await store.load() expect(seenRefs).toEqual([]) expect(store.store.getSnapshot().status).toBe('ready') }) it('surfaces a settings describe failure', async () => { const { face } = api({ describeSettings: () => Promise.resolve(fail('settings down')) }) const store = new ModelsSettingsStore(face) await store.load() expect(store.store.getSnapshot()).toMatchObject({ status: 'error', error: 'settings down' }) }) it('stringifies a non-Error load failure', async () => { // The wire can surface non-Error throwables; the store must stringify them. // oxlint-disable-next-line typescript/prefer-promise-reject-errors -- the non-Error rejection is the scenario const { face } = api({ providers: () => Promise.reject('plain refusal') }) const store = new ModelsSettingsStore(face) await store.load() expect(store.store.getSnapshot()).toMatchObject({ status: 'error', error: 'plain refusal' }) }) it('drops a stale successful response after a newer load finished', async () => { let release: (() => void) | undefined const gate = new Promise((resolve) => { release = resolve }) let call = 0 const { face } = api({ providers: async () => { call += 1 if (call === 1) { await gate return ok({ providers: [] as never }) } return ok({ providers: DIRECTORY }) }, }) const store = new ModelsSettingsStore(face) const first = store.load() const second = store.load() await second release?.() await first // The stale empty directory never overwrote the newer join. expect(store.store.getSnapshot().rows).toHaveLength(4) }) }) describe('messageOf', () => { it('reads an Error message, and stringifies anything else a rejection may carry', () => { // The wire layer rejects with an Error, but a host or a runtime can reject // with any value, and the page still has to render something. expect(messageOf(new Error('connection lost'))).toBe('connection lost') expect(messageOf('the host refused')).toBe('the host refused') expect(messageOf(undefined)).toBe('undefined') }) })