Files
tkmind_go/ui/h5/llm-providers.test.mjs
john 4e21ca937a
Deploy Documentation / deploy (push) Has been cancelled
Canary / Prepare Version (push) Has been cancelled
Canary / build-cli (push) Has been cancelled
Canary / Upload Install Script (push) Has been cancelled
Canary / bundle-desktop (push) Has been cancelled
Canary / bundle-desktop-intel (push) Has been cancelled
Canary / bundle-desktop-linux (push) Has been cancelled
Canary / bundle-desktop-windows (push) Has been cancelled
Canary / bundle-desktop-windows-cuda (push) Has been cancelled
Canary / Release (push) Has been cancelled
Unused Dependencies / machete (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / Check Rust Code Format (push) Has been cancelled
CI / Build and Test Rust Project (push) Has been cancelled
CI / Build Rust Project on Windows (push) Has been cancelled
CI / Check MSRV (push) Has been cancelled
CI / Lint Rust Code (push) Has been cancelled
CI / Check Generated Schemas are Up-to-Date (push) Has been cancelled
CI / Test and Lint Electron Desktop App (push) Has been cancelled
CI / H5 Plaza Tests and Build (push) Has been cancelled
Live Provider Tests / check-fork (push) Has been cancelled
Live Provider Tests / changes (push) Has been cancelled
Live Provider Tests / Build Binary (push) Has been cancelled
Live Provider Tests / Smoke Tests (push) Has been cancelled
Live Provider Tests / Smoke Tests (Code Execution) (push) Has been cancelled
Live Provider Tests / Compaction Tests (push) Has been cancelled
Live Provider Tests / goose server HTTP integration tests (push) Has been cancelled
Publish Ask AI Bot Docker Image / docker (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Add TKMind platform extensions, H5/MindSpace stack, and deployment tooling.
Fork goose with custom MCP widgets, platform extensions (aider, git, web, search),
MindSpace H5 backend/frontend, Plaza/Ops UIs, and deploy scripts for tkmind.cn.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 21:30:20 +08:00

319 lines
10 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
createLlmProviderService,
CUSTOM_PROVIDER_ID,
decryptSecret,
encryptSecret,
LLM_PROVIDER_CATALOG,
maskApiKey,
normalizeApiUrl,
parseModelList,
resolveChatCompletionsUrl,
testRelayConnection,
syncProfileToGoosed,
} from './llm-providers.mjs';
test('encryptSecret round-trips with derived key', () => {
const encrypted = encryptSecret('sk-test-key-12345678', 'unit-test-secret');
const plain = decryptSecret(encrypted, 'unit-test-secret');
assert.equal(plain, 'sk-test-key-12345678');
});
test('parseModelList accepts comma and newline separated values', () => {
assert.deepEqual(parseModelList('qwen2.5:3b, llama3.2:1b\nmistral'), [
'qwen2.5:3b',
'llama3.2:1b',
'mistral',
]);
});
test('normalizeApiUrl adds http scheme', () => {
assert.equal(
normalizeApiUrl('127.0.0.1:18300/relay/buyer/v1/chat/completions'),
'http://127.0.0.1:18300/relay/buyer/v1/chat/completions',
);
});
test('maskApiKey hides middle segment', () => {
const masked = maskApiKey('sk-1234567890abcdef');
assert.ok(masked.startsWith('sk-1'));
assert.ok(masked.endsWith('cdef'));
assert.ok(masked.includes('*'));
});
test('createKey selects first profile and syncs to goosed', async () => {
const rows = [];
let selectedId = null;
const syncCalls = [];
const pool = {
async query(sql, params = []) {
if (sql.includes('COUNT(*) AS total FROM h5_llm_provider_keys')) {
return [[{ total: rows.length }]];
}
if (sql.includes('UPDATE h5_llm_provider_keys SET is_selected = 0')) {
for (const row of rows) row.is_selected = 0;
return [[]];
}
if (sql.includes('INSERT INTO h5_llm_provider_keys')) {
const encrypted = {
ciphertext: params[9],
iv: params[10],
tag: params[11],
};
const row = {
id: params[0],
provider_id: params[1],
provider_kind: params[2],
api_url: params[3],
base_path: params[4],
models_json: params[5],
goosed_provider_id: null,
engine: params[6],
relay_provider: params[7],
name: params[8],
api_key_ciphertext: encrypted.ciphertext,
api_key_iv: encrypted.iv,
api_key_tag: encrypted.tag,
default_model: params[12],
status: 'active',
is_selected: params[13],
created_at: params[14],
updated_at: params[15],
};
rows.push(row);
if (row.is_selected) selectedId = row.id;
return [[]];
}
if (sql.includes('SELECT * FROM h5_llm_provider_keys WHERE id = ?')) {
return [[rows.find((row) => row.id === params[0]) ?? null].filter(Boolean)];
}
if (sql.includes('ORDER BY is_selected DESC')) {
return [rows];
}
if (sql.includes('SELECT id FROM h5_llm_provider_keys WHERE name = ?')) {
return [[rows.find((row) => row.name === params[0])].filter(Boolean)];
}
if (sql.includes('SET goosed_provider_id = ?')) {
const target = rows.find((row) => row.id === params[3]);
if (target) {
target.goosed_provider_id = params[0];
target.provider_id = params[1];
}
return [[]];
}
throw new Error(`Unexpected SQL: ${sql}`);
},
};
const mockFetch = async (url, init) => {
syncCalls.push({ url: String(url), body: init?.body ? JSON.parse(init.body) : null });
if (String(url).includes('/config/custom-providers') && init?.method === 'POST') {
return { ok: true, json: async () => ({ provider_name: 'relay_ollama' }) };
}
return { ok: true, text: async () => '' };
};
const service = createLlmProviderService(pool, {
apiTarget: 'https://127.0.0.1:18006',
apiSecret: 'secret',
encryptionKey: 'unit-test-secret',
apiFetchImpl: mockFetch,
});
const result = await service.createKey({
providerId: 'custom_deepseek',
name: 'DeepSeek 主账号',
apiKey: 'sk-deepseek-test',
defaultModel: 'deepseek-chat',
});
assert.equal(result.ok, true);
assert.equal(result.key.isSelected, true);
assert.equal(rows.length, 1);
assert.equal(selectedId, rows[0].id);
assert.equal(syncCalls.filter((item) => item.body?.key === 'DEEPSEEK_API_KEY').length, 1);
});
test('create custom relay profile registers goosed custom provider', async () => {
const rows = [];
const mockFetch = async (url, init) => {
if (String(url).includes('/config/custom-providers') && init?.method === 'POST') {
const body = JSON.parse(init.body);
assert.equal(body.api_url, 'http://127.0.0.1:18300/relay/buyer/v1/chat/completions');
assert.deepEqual(body.models, ['qwen2.5:3b', 'llama3.2:1b']);
assert.equal(body.headers['X-Provider'], 'ollama');
return { ok: true, json: async () => ({ provider_name: 'relay_buyer' }) };
}
return { ok: true, text: async () => '' };
};
const pool = {
async query(sql, params = []) {
if (sql.includes('COUNT(*) AS total FROM h5_llm_provider_keys')) return [[{ total: 0 }]];
if (sql.includes('UPDATE h5_llm_provider_keys SET is_selected = 0')) return [[]];
if (sql.includes('INSERT INTO h5_llm_provider_keys')) {
rows.push({
id: params[0],
provider_id: params[1],
provider_kind: params[2],
api_url: params[3],
base_path: params[4],
models_json: params[5],
goosed_provider_id: null,
engine: params[6],
relay_provider: params[7],
name: params[8],
api_key_ciphertext: params[9],
api_key_iv: params[10],
api_key_tag: params[11],
default_model: params[12],
status: 'active',
is_selected: params[13],
created_at: params[14],
updated_at: params[15],
});
return [[]];
}
if (sql.includes('SELECT * FROM h5_llm_provider_keys WHERE id = ?')) {
return [[rows.find((row) => row.id === params[0]) ?? null].filter(Boolean)];
}
if (sql.includes('SELECT id FROM h5_llm_provider_keys WHERE name = ?')) return [[]];
if (sql.includes('SET goosed_provider_id = ?')) {
rows[0].goosed_provider_id = params[0];
rows[0].provider_id = params[1];
return [[]];
}
throw new Error(`Unexpected SQL: ${sql}`);
},
};
const service = createLlmProviderService(pool, {
apiTarget: 'https://127.0.0.1:18006',
apiSecret: 'secret',
encryptionKey: 'unit-test-secret',
apiFetchImpl: mockFetch,
});
const result = await service.createKey({
providerId: CUSTOM_PROVIDER_ID,
name: 'Relay Ollama',
apiKey: 'UqyHPKSSEZq0-oPnl8sru-7hZcJ2anPUL1yAVk866Vo',
apiUrl: 'http://127.0.0.1:18300/relay/buyer/v1/chat/completions',
models: ['qwen2.5:3b', 'llama3.2:1b'],
defaultModel: 'qwen2.5:3b',
relayProvider: 'ollama',
});
assert.equal(result.ok, true);
assert.equal(result.key.providerKind, 'custom');
assert.equal(rows[0].provider_kind, 'custom');
});
test('selectKey rejects disabled profile', async () => {
const row = {
id: 'key-1',
provider_id: 'custom_deepseek',
provider_kind: 'builtin',
name: 'disabled',
api_key_ciphertext: encryptSecret('sk-x', 'unit-test-secret').ciphertext,
api_key_iv: encryptSecret('sk-x', 'unit-test-secret').iv,
api_key_tag: encryptSecret('sk-x', 'unit-test-secret').tag,
default_model: 'deepseek-chat',
status: 'disabled',
is_selected: 0,
created_at: 1,
updated_at: 1,
};
const pool = {
async query(sql, params = []) {
if (sql.includes('SELECT * FROM h5_llm_provider_keys WHERE id = ?')) {
return [[row]];
}
throw new Error(`Unexpected SQL: ${sql}`);
},
};
const service = createLlmProviderService(pool, {
apiTarget: 'https://127.0.0.1:18006',
apiSecret: 'secret',
encryptionKey: 'unit-test-secret',
});
const result = await service.selectKey('key-1');
assert.equal(result.ok, false);
assert.match(result.message, /禁用/);
});
test('catalog includes custom provider template', () => {
const custom = LLM_PROVIDER_CATALOG.find((item) => item.id === CUSTOM_PROVIDER_ID);
assert.ok(custom);
assert.equal(custom.kind, 'custom');
});
test('resolveChatCompletionsUrl keeps full completions path', () => {
assert.equal(
resolveChatCompletionsUrl('http://127.0.0.1:18300/relay/buyer/v1/chat/completions'),
'http://127.0.0.1:18300/relay/buyer/v1/chat/completions',
);
});
test('testRelayConnection surfaces 401 as token error', async () => {
const mockFetch = async () => ({
ok: false,
status: 401,
text: async () => '{}',
});
const result = await testRelayConnection(
{
apiUrl: 'http://127.0.0.1:18300/relay/buyer/v1/chat/completions',
apiKey: 'bad-token',
model: 'qwen2.5:3b',
relayProvider: 'ollama',
},
mockFetch,
);
assert.equal(result.ok, false);
assert.match(result.message ?? '', /401/);
assert.match(result.message ?? '', /Token/);
});
test('testRelayConnection parses OpenAI-style response', async () => {
const mockFetch = async () => ({
ok: true,
text: async () =>
JSON.stringify({
choices: [{ message: { content: 'Hi there' } }],
}),
});
const result = await testRelayConnection(
{
apiUrl: 'http://127.0.0.1:18300/relay/buyer/v1/chat/completions',
apiKey: 'token',
model: 'qwen2.5:3b',
relayProvider: 'ollama',
},
mockFetch,
);
assert.equal(result.ok, true);
assert.equal(result.reply, 'Hi there');
});
test('syncProfileToGoosed writes provider, model and secret keys for builtin', async () => {
const writes = [];
const mockFetch = async (_url, init) => {
writes.push(JSON.parse(init.body));
return { ok: true, text: async () => '' };
};
await syncProfileToGoosed(
'https://127.0.0.1:18006',
'secret',
{
providerKind: 'builtin',
providerId: 'custom_deepseek',
defaultModel: 'deepseek-chat',
apiKey: 'sk-test',
},
mockFetch,
);
assert.deepEqual(
writes.map((item) => item.key),
['DEEPSEEK_API_KEY', 'GOOSE_PROVIDER', 'GOOSE_MODEL', 'TKMIND_PROVIDER', 'TKMIND_MODEL'],
);
});