openpencil/tests/app/ai/models/store.test.ts
Danila Poyarkov 2f79278df1
refactor: read stored settings and external responses with Valibot (#937)
* refactor(settings): read stored preferences with a Valibot schema

Each field falls back to its default on its own, so one bad value keeps the rest, as before. Tests pin the behavior; they pass against the previous implementation too.

* refactor(mcp): read stored MCP connections with a Valibot schema

A connection schema checks the ID, trimmed name, transport, and URL rule; a bearer token is kept only for the connection's own credential. A new test covers the credential check and normalization, and passes against the previous implementation.

* refactor(clipboard): read Figma's image batch response with a Valibot schema

* refactor(mcp): read tool descriptors with a Valibot schema

The descriptor type now follows the schema, and tests cover what a descriptor must contain.

* refactor(ai): read stored model settings with Valibot schemas

Connections and model profiles are schemas with their defaults and bounds; the thinking-level migration and the checks between connections, models, and role assignments stay as code. Characterization tests written against the previous implementation pin the behavior.
2026-10-07 09:28:42 +00:00

117 lines
3.2 KiB
TypeScript

import { describe, expect, test } from 'bun:test'
import { parseAIModelSettings } from '@/app/ai/models/store'
import type { AIModelConnection } from '@/app/ai/models/types'
const connection = {
id: 'connection-a',
providerID: 'openrouter',
customBaseURL: '',
customAPIType: 'completions',
credentialProfileId: 'default'
} satisfies AIModelConnection
const profile = {
id: 'model-a',
name: 'Design',
connectionId: 'connection-a',
modelID: 'custom/model',
customModelID: '',
maxOutputTokens: 16_384,
thinkingLevel: 'default',
capabilities: ['tools']
}
function settings(overrides: Record<string, unknown> = {}) {
return {
version: 1,
connections: [connection],
models: [profile],
assignments: { design: 'model-a', review: 'design', fast: null, vision: null },
...overrides
}
}
describe('parseAIModelSettings', () => {
test('rejects settings it cannot use', () => {
for (const stored of [null, 'settings', { ...settings(), version: 2 }]) {
expect(parseAIModelSettings(stored)).toBeNull()
}
expect(parseAIModelSettings(settings({ models: [] }))).toBeNull()
})
test('drops connections and models that are malformed or point nowhere', () => {
const parsed = parseAIModelSettings(
settings({
connections: [
connection,
{ ...connection, id: 'connection-b', providerID: 'unknown-provider' },
{ ...connection, id: '' },
'connection'
],
models: [
profile,
{ ...profile, id: 'model-b', connectionId: 'connection-b' },
{ ...profile, id: 'profile-c' },
null
]
})
)
expect(parsed?.connections.map((candidate) => candidate.id)).toEqual(['connection-a'])
expect(parsed?.models.map((candidate) => candidate.id)).toEqual(['model-a'])
})
test('fills and bounds the fields of a stored model', () => {
const parsed = parseAIModelSettings(
settings({
connections: [{ ...connection, customAPIType: 'soap', customBaseURL: 7 }],
models: [
{
id: 'model-a',
connectionId: 'connection-a',
maxOutputTokens: 1_000_000,
harnessPermissionMode: 'everything',
capabilities: ['vision', 'tools', 'vision', 'audio']
}
]
})
)
expect(parsed?.connections[0]).toEqual({
...connection,
customBaseURL: '',
customAPIType: 'completions'
})
expect(parsed?.models[0]).toEqual({
id: 'model-a',
name: 'Model',
connectionId: 'connection-a',
modelID: '',
customModelID: '',
maxOutputTokens: 128_000,
thinkingLevel: 'default',
harnessPermissionMode: undefined,
capabilities: ['vision', 'tools']
})
})
test('assigns roles only to models that can take them', () => {
const parsed = parseAIModelSettings(
settings({
models: [profile, { ...profile, id: 'model-b', capabilities: ['tools', 'vision'] }],
assignments: {
design: 'model-missing',
review: 'model-b',
fast: 'model-x',
vision: 'design'
}
})
)
expect(parsed?.assignments).toEqual({
design: 'model-a',
review: 'model-b',
fast: null,
vision: null
})
})
})