open-design/apps/web/tests/components/SettingsDialog.test.ts
marco 5dd70b5016
Some checks failed
ci / Validate workspace (push) Successful in 12m32s
landing-page-ci / Validate landing page (push) Successful in 9m41s
landing-page-deploy / Deploy landing page (push) Failing after 5m23s
github-metrics / Generate repository metrics SVG (push) Failing after 2m3s
refresh-contributors-wall / Refresh contributors wall cache bust (push) Failing after 11s
Initial import: open-design source for helix-mind.ai distribution
This repository contains the open-design daemon CLI source code, built
and packaged at https://helix-mind.ai/cli/open-design/latest.tgz for use
by the HelixMind /design slash command.

Licenses: Apache-2.0 (root) + MIT (skills/*)
2026-05-06 20:50:24 +02:00

226 lines
6.9 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
agentRefreshOptionsForConfig,
isValidApiBaseUrl,
switchApiProtocolConfig,
updateAgentCliEnvValue,
updateCurrentApiProtocolConfig,
} from '../../src/components/SettingsDialog';
import type { AppConfig } from '../../src/types';
const baseConfig: AppConfig = {
mode: 'api',
apiKey: 'sk-test',
apiProtocol: 'anthropic',
baseUrl: 'https://api.anthropic.com',
model: 'claude-sonnet-4-5',
apiProviderBaseUrl: 'https://api.anthropic.com',
agentId: null,
skillId: null,
designSystemId: null,
};
describe('SettingsDialog API protocol switching', () => {
it('stores the current custom protocol config while preserving custom endpoint details', () => {
const config: AppConfig = {
...baseConfig,
apiKey: 'anthropic-key',
apiProviderBaseUrl: null,
baseUrl: 'https://my-proxy.example.com',
model: 'my-model',
};
const next = switchApiProtocolConfig(config, 'openai');
expect(next).toMatchObject({
mode: 'api',
apiProtocol: 'openai',
apiKey: '',
baseUrl: 'https://my-proxy.example.com',
model: 'my-model',
apiProviderBaseUrl: null,
});
expect(next.apiProtocolConfigs?.anthropic).toMatchObject({
apiKey: 'anthropic-key',
baseUrl: 'https://my-proxy.example.com',
model: 'my-model',
apiProviderBaseUrl: null,
});
});
it('restores each protocol draft instead of leaking shared field values', () => {
const openai = switchApiProtocolConfig(baseConfig, 'openai');
const openaiEdited = updateCurrentApiProtocolConfig(openai, {
apiKey: 'openai-key',
baseUrl: 'https://openai-proxy.example.com',
model: 'openai-model',
apiProviderBaseUrl: null,
});
const google = switchApiProtocolConfig(openaiEdited, 'google');
const googleEdited = updateCurrentApiProtocolConfig(google, {
apiKey: 'google-key',
baseUrl: 'https://google-proxy.example.com',
model: 'google-model',
apiProviderBaseUrl: null,
});
const restoredOpenai = switchApiProtocolConfig(googleEdited, 'openai');
expect(restoredOpenai).toMatchObject({
mode: 'api',
apiProtocol: 'openai',
apiKey: 'openai-key',
baseUrl: 'https://openai-proxy.example.com',
model: 'openai-model',
apiProviderBaseUrl: null,
});
expect(restoredOpenai.apiProtocolConfigs?.google).toMatchObject({
apiKey: 'google-key',
baseUrl: 'https://google-proxy.example.com',
model: 'google-model',
apiProviderBaseUrl: null,
});
});
it('loads the new protocol default on first visit', () => {
expect(switchApiProtocolConfig(baseConfig, 'openai')).toMatchObject({
mode: 'api',
apiProtocol: 'openai',
apiKey: '',
baseUrl: 'https://api.openai.com/v1',
model: 'gpt-4o',
apiProviderBaseUrl: 'https://api.openai.com/v1',
});
});
it('auto-fills Google defaults when switching from a selected known provider', () => {
expect(switchApiProtocolConfig(baseConfig, 'google')).toMatchObject({
mode: 'api',
apiProtocol: 'google',
apiKey: '',
baseUrl: 'https://generativelanguage.googleapis.com',
model: 'gemini-2.0-flash',
apiProviderBaseUrl: 'https://generativelanguage.googleapis.com',
});
});
it('keeps Azure API version in the Azure draft only', () => {
const config: AppConfig = {
...baseConfig,
apiProtocol: 'azure',
apiKey: 'azure-key',
model: 'deployment-one',
apiVersion: '2024-10-21',
};
const next = switchApiProtocolConfig(config, 'openai');
expect(next).toMatchObject({
apiProtocol: 'openai',
apiKey: '',
apiVersion: '',
});
expect(next.apiProtocolConfigs?.azure).toMatchObject({
apiKey: 'azure-key',
model: 'deployment-one',
apiVersion: '2024-10-21',
});
});
});
describe('SettingsDialog API Base URL validation', () => {
it('accepts public http/https URLs and loopback local providers', () => {
expect(isValidApiBaseUrl('https://api.openai.com/v1')).toBe(true);
expect(isValidApiBaseUrl('http://localhost:11434/v1')).toBe(true);
expect(isValidApiBaseUrl('http://127.0.0.1:11434/v1')).toBe(true);
expect(isValidApiBaseUrl('http://[::1]:11434/v1')).toBe(true);
expect(isValidApiBaseUrl(' https://resource.openai.azure.com ')).toBe(true);
expect(isValidApiBaseUrl('ddddd')).toBe(false);
expect(isValidApiBaseUrl('api.openai.com/v1')).toBe(false);
expect(isValidApiBaseUrl('ftp://api.example.com')).toBe(false);
expect(isValidApiBaseUrl('http:api.example.com')).toBe(false);
expect(isValidApiBaseUrl('https://')).toBe(false);
expect(isValidApiBaseUrl('http://10.0.0.5:11434/v1')).toBe(false);
expect(isValidApiBaseUrl('http://169.254.1.5:11434/v1')).toBe(false);
expect(isValidApiBaseUrl('http://172.16.0.5:11434/v1')).toBe(false);
expect(isValidApiBaseUrl('http://192.168.1.5:11434/v1')).toBe(false);
});
});
describe('SettingsDialog agent CLI env settings', () => {
it('updates supported per-agent CLI env values without dropping sibling agents', () => {
const config: AppConfig = {
...baseConfig,
mode: 'daemon',
agentCliEnv: {
codex: { CODEX_HOME: '~/.codex-alt' },
},
};
const next = updateAgentCliEnvValue(
config,
'claude',
'CLAUDE_CONFIG_DIR',
' ~/.claude-2 ',
);
expect(next.agentCliEnv).toEqual({
claude: { CLAUDE_CONFIG_DIR: '~/.claude-2' },
codex: { CODEX_HOME: '~/.codex-alt' },
});
});
it('removes empty per-agent CLI env entries', () => {
const config: AppConfig = {
...baseConfig,
mode: 'daemon',
agentCliEnv: {
claude: { CLAUDE_CONFIG_DIR: '~/.claude-2' },
codex: { CODEX_HOME: '~/.codex-alt' },
},
};
const next = updateAgentCliEnvValue(
config,
'claude',
'CLAUDE_CONFIG_DIR',
'',
);
expect(next.agentCliEnv).toEqual({
codex: { CODEX_HOME: '~/.codex-alt' },
});
});
it('passes pending CLI env prefs through agent rescan options', () => {
const config: AppConfig = {
...baseConfig,
mode: 'daemon',
agentCliEnv: {
claude: { CLAUDE_CONFIG_DIR: '~/.claude-pending' },
},
};
expect(agentRefreshOptionsForConfig(config)).toEqual({
throwOnError: true,
agentCliEnv: {
claude: { CLAUDE_CONFIG_DIR: '~/.claude-pending' },
},
});
});
it('passes an empty CLI env object through agent rescan after fields are cleared', () => {
const config: AppConfig = {
...baseConfig,
mode: 'daemon',
agentCliEnv: {},
};
expect(agentRefreshOptionsForConfig(config)).toEqual({
throwOnError: true,
agentCliEnv: {},
});
});
});