Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions apps/desktop/src/main/__tests__/assistant-stream.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ import { describe, it } from 'node:test';
import {
ASSISTANT_MAX_DELTA_CHARS,
ASSISTANT_MAX_TOTAL_CHARS,
applyAssistantComplete,
applyAssistantDelta,
applyAssistantComplete as applyAssistantCompleteWithLocale,
applyAssistantDelta as applyAssistantDeltaWithLocale,
} from '@maka/ui/assistant-stream';
// Tests exercise stream mechanics, not copy; pin zh so markers stay verbatim.
const applyAssistantDelta = (prev: string, delta: string, options?: Partial<Parameters<typeof applyAssistantDeltaWithLocale>[2]>) =>
applyAssistantDeltaWithLocale(prev, delta, { locale: 'zh-CN', ...options });
const applyAssistantComplete = (text: string, options?: Partial<Parameters<typeof applyAssistantCompleteWithLocale>[1]>) =>
applyAssistantCompleteWithLocale(text, { locale: 'zh-CN', ...options });

function visibleDeltaResult(result: ReturnType<typeof applyAssistantDelta>) {
return {
Expand Down
98 changes: 94 additions & 4 deletions apps/desktop/src/main/__tests__/health-center-copy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import assert from 'node:assert/strict';
import { test } from 'node:test';
import { connectionLastTestMessageDisplay } from '../../renderer/features/connection-settings/index.js';
import { getHealthCenterCopy } from '../../renderer/locales/settings-health-copy.js';

test('labels blocker counts as global across filtered health views', () => {
Expand All @@ -35,19 +36,108 @@ test('labels blocker counts as global across filtered health views', () => {
test('Traditional Chinese health copy localizes structured signal text', () => {
const copy = getHealthCenterCopy('zh-TW');
const signal = {
id: 'connection:test',
label: '測試 运行态',
id: 'connection:test:runtime',
label: '測試',
scope: 'llm_connection' as const,
layer: 'validation' as const,
status: 'ok' as const,
source: 'connection_test' as const,
checkedAt: 1,
message: '凭据与端点验证已通过。',
detail: '这是连接验证结果,不代表发送、流式输出或中断通路已经运行通过。',
message: 'validation_passed' as const,
detail: { kind: 'validation_scope_note' as const },
blocksSend: false,
};
assert.equal(copy.layers.configuration.label, '設定');
assert.equal(copy.signalLabel(signal), '測試 執行狀態');
assert.equal(copy.signalMessage(signal), '憑證與端點驗證已通過。');
assert.match(copy.signalDetail(signal) ?? '', /串流輸出/);
});

const signal = (overrides: Partial<import('@maka/core/health').HealthSignal>): import('@maka/core/health').HealthSignal => ({
id: 'connection:demo',
label: 'Demo',
scope: 'llm_connection',
layer: 'configuration',
status: 'info',
source: 'settings',
checkedAt: 0,
message: 'not_default_source',
...overrides,
});

test('renders configuration message codes distinctly in both locales', () => {
const zh = getHealthCenterCopy('zh-CN');
const en = getHealthCenterCopy('en');
assert.equal(zh.signalMessage(signal({ message: 'not_default_source' })), '不是工作区的默认模型来源。');
assert.equal(en.signalMessage(signal({ message: 'not_default_source' })), 'Not the workspace default model source.');
assert.equal(zh.signalMessage(signal({ message: 'no_models_enabled' })), '没有启用任何模型。');
assert.equal(en.signalMessage(signal({ message: 'no_models_enabled' })), 'No models are enabled on this connection.');
});

test('renders runtime probe details from structured params, not string parsing', () => {
const detail = { kind: 'runtime_probe_result', modelId: 'claude-sonnet-5', latencyMs: 812, errorClass: 'timeout' } as const;
assert.equal(
getHealthCenterCopy('zh-CN').signalDetail(signal({ detail })),
'模型=claude-sonnet-5 · 延迟=812ms · 错误类型=请求超时',
);
assert.equal(
getHealthCenterCopy('en').signalDetail(signal({ detail })),
'Model=claude-sonnet-5 · Latency=812ms · Error type=Request timed out',
);

const rateLimited = { ...detail, errorClass: 'rate_limit' };
assert.equal(
getHealthCenterCopy('zh-CN').signalDetail(signal({ detail: rateLimited })),
'模型=claude-sonnet-5 · 延迟=812ms · 错误类型=rate_limit',
);
assert.equal(
getHealthCenterCopy('en').signalDetail(signal({ detail: rateLimited })),
'Model=claude-sonnet-5 · Latency=812ms · Error type=rate_limit',
);
});

test('keeps zh capability reasons and hides wrong-locale ones', () => {
const zhReason = { kind: 'capability_reason', reason: '未配置平台凭据' } as const;
assert.equal(getHealthCenterCopy('zh-CN').signalDetail(signal({ detail: zhReason })), '未配置平台凭据');
assert.equal(
getHealthCenterCopy('en').signalDetail(signal({ detail: zhReason })),
'See the corresponding settings page for details.',
);

const enReason = { kind: 'capability_reason', reason: 'Slack requires a Bot Token.' } as const;
assert.equal(
getHealthCenterCopy('zh-CN').signalDetail(signal({ detail: enReason })),
'状态详情请见对应设置页。',
);
assert.equal(
getHealthCenterCopy('en').signalDetail(signal({ detail: enReason })),
'See the corresponding settings page for details.',
);
});

test('maps connection test error classes without exposing machine tokens', () => {
const auth = { kind: 'last_test_error_class', errorClass: 'auth' } as const;
assert.equal(getHealthCenterCopy('zh-CN').signalDetail(signal({ detail: auth })), '鉴权失败');
assert.equal(getHealthCenterCopy('en').signalDetail(signal({ detail: auth })), 'Authentication failed');

const unknown = { kind: 'last_test_message' } as const;
assert.equal(
getHealthCenterCopy('zh-CN').signalDetail(signal({ detail: unknown })),
'连接测试状态暂时无法显示,请重新测试。',
);
assert.equal(
getHealthCenterCopy('en').signalDetail(signal({ detail: unknown })),
'The connection test status is temporarily unavailable. Test again.',
);
});

test('maps connection test error classes in connection details', () => {
assert.equal(connectionLastTestMessageDisplay('auth', 'zh-CN'), '鉴权失败');
assert.equal(connectionLastTestMessageDisplay('auth', 'en'), 'Authentication failed');
});

test('suffixes runtime signal labels per locale from the id, not the producer', () => {
const runtime = signal({ id: 'connection:demo:runtime', label: 'Demo' });
assert.equal(getHealthCenterCopy('zh-CN').signalLabel(runtime), 'Demo 运行态');
assert.equal(getHealthCenterCopy('en').signalLabel(runtime), 'Demo runtime');
});
7 changes: 6 additions & 1 deletion apps/desktop/src/main/__tests__/thinking-stream.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@

import { strict as assert } from 'node:assert';
import { describe, it } from 'node:test';
import { applyThinkingComplete, applyThinkingDelta } from '@maka/ui';
import { applyThinkingComplete as applyThinkingCompleteWithLocale, applyThinkingDelta as applyThinkingDeltaWithLocale } from '@maka/ui';
// Tests exercise stream mechanics, not copy; pin zh so markers stay verbatim.
const applyThinkingDelta = (prev: string, delta: string, options?: Partial<Parameters<typeof applyThinkingDeltaWithLocale>[2]>) =>
applyThinkingDeltaWithLocale(prev, delta, { locale: 'zh-CN', ...options });
const applyThinkingComplete = (text: string, options?: Partial<Parameters<typeof applyThinkingCompleteWithLocale>[1]>) =>
applyThinkingCompleteWithLocale(text, { locale: 'zh-CN', ...options });

describe('applyThinkingDelta — secondary redaction', () => {
it('masks raw `Authorization: Bearer ...` text before storing', () => {
Expand Down
7 changes: 4 additions & 3 deletions apps/desktop/src/main/__tests__/tool-output-stream.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ import {
TOOL_STREAM_MAX_CHUNKS,
TOOL_STREAM_MAX_CHUNK_CHARS,
TOOL_STREAM_MAX_TOTAL_CHARS,
applyToolOutputChunk,
applyToolOutputChunk as applyToolOutputChunkWithLocale,
type ToolOutputChunk,
} from '@maka/ui';
// Tests exercise stream mechanics, not copy; pin zh so markers stay verbatim.
const applyToolOutputChunk = (prev: Parameters<typeof applyToolOutputChunkWithLocale>[0], chunk: Parameters<typeof applyToolOutputChunkWithLocale>[1], options?: Partial<Parameters<typeof applyToolOutputChunkWithLocale>[2]>) =>
applyToolOutputChunkWithLocale(prev, chunk, { locale: 'zh-CN', ...options });

function chunk(seq: number, text: string, stream: 'stdout' | 'stderr' = 'stdout', redacted = false): ToolOutputChunk {
return { seq, text, stream, redacted, createdAt: 1_700_000_000_000 + seq };
Expand All @@ -77,7 +80,6 @@ describe('applyToolOutputChunk — secondary redaction (defense in depth)', () =
assert.equal(result.chunks[0]!.redacted, true);
});


});

describe('applyToolOutputChunk — per-chunk cap', () => {
Expand Down Expand Up @@ -106,7 +108,6 @@ describe('applyToolOutputChunk — per-chunk cap', () => {
);
});


});

describe('applyToolOutputChunk — per-tool caps (count + total chars)', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ test('Traditional Chinese connection copy uses 回傳, 回應, and 發送', () =
assert.match(conversation.turnError.provider, /模型服務回傳錯誤/);

const provider = getProviderSettingsCopy('zh-TW');
assert.equal(provider.shared.lastTest['模型服务返回错误'], '模型服務回傳錯誤');
assert.equal(provider.shared.lastTest['provider returned an error'], '模型服務回傳錯誤');
assert.equal(provider.shared.lastTest.provider_unavailable, '模型服務回傳錯誤');

assert.equal(
settingsTestResultMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import { generalizedErrorMessageForLocale, redactSecrets } from '@maka/core/redaction';
import { type ConnectionTestResult } from '@maka/core/llm-connections';
import { type UiLocale } from '@maka/core/ui-locale';
import { type UiLocale, lookupCopy } from '@maka/core/ui-locale';
import { getProviderSettingsCopy } from './settings-provider-copy.js';
import { cleanErrorMessage } from '../../application/contracts/connection-error-cleaner.js';

Expand All @@ -32,8 +32,6 @@ export function providerPanelActionErrorMessage(error: unknown, locale: UiLocale
// wrapper — channel names like 'connections:fetchModels' contain "fetch",
// which the keyword classifier reads as a network error.
const cleaned = redactSecrets(cleanErrorMessage(error)).trim();
const known = (shared.lastTest as Readonly<Record<string, string>>)[cleaned.toLowerCase()];
if (known) return known;
// Main-process handlers throw display-ready Chinese copy; keep it instead
// of flattening it into a coarser classification or the generic fallback.
if (locale === 'zh-CN' && /[\u3400-\u9fff]/.test(cleaned)) return cleaned;
Expand Down Expand Up @@ -88,10 +86,9 @@ export function connectionLastTestMessageDisplay(message: string | undefined, lo
if (!message) return undefined;
const trimmed = message.trim();
if (!trimmed) return undefined;
const normalized = trimmed.toLowerCase();
const copy = getProviderSettingsCopy(locale).shared;
const known = (copy.lastTest as Readonly<Record<string, string>>)[normalized];
if (known) return known;
const classified = generalizedErrorMessageForLocale(new Error(trimmed), '', locale);
return classified || copy.statusUnavailable;
return (
lookupCopy(copy.lastTest, trimmed) ??
(generalizedErrorMessageForLocale(new Error(trimmed), '', locale) || copy.statusUnavailable)
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,7 @@ const zhCopy = {
filterMatches: (count: number) => (count === 0 ? '没有匹配的结果' : `${count} 个匹配结果`),
connectionStatuses: { retired: '已停用 · 请删除', reauth: '需要重新登录', disabledFailed: '暂不可用 · 上次连接失败', disabled: '暂不可用', failed: '上次连接失败' },
lastTest: {
'连接已验证': '连接已验证', '鉴权失败': '鉴权失败', '请求超时': '请求超时', '网络错误': '网络错误', '模型服务返回错误': '模型服务返回错误', '连接测试失败': '连接测试失败',
'connection verified': '连接已验证', 'authentication failed': '鉴权失败', 'request timed out': '请求超时', 'network error': '网络错误', 'provider returned an error': '模型服务返回错误', 'connection test failed': '连接测试失败',
'claude oauth 未登录。': 'Claude OAuth 未登录。', 'claude oauth 本地凭据读取失败。': 'Claude OAuth 本地凭据读取失败。', 'claude oauth 需要重新登录。': 'Claude OAuth 需要重新登录。', 'claude oauth 已登录。': 'Claude OAuth 已登录。', 'claude oauth 已退出登录。': 'Claude OAuth 已退出登录。',
'codex oauth 未登录。': 'Codex OAuth 未登录。', 'codex oauth 本地凭据读取失败。': 'Codex OAuth 本地凭据读取失败。', 'codex oauth 需要重新登录。': 'Codex OAuth 需要重新登录。', 'codex oauth 已登录。': 'Codex OAuth 已登录。', 'codex oauth 已退出登录。': 'Codex OAuth 已退出登录。',
'当前账号无可用 codex 模型。': '当前账号无可用 Codex 模型。', 'codex 模型列表获取失败。': 'Codex 模型列表获取失败。',
'github copilot 需要重新导入 github cli 登录。': 'GitHub Copilot 需要重新导入 GitHub CLI 登录。', 'github copilot 无法读取当前账号可用模型,请重新验证登录。': 'GitHub Copilot 无法读取当前账号可用模型,请重新验证登录。', 'github copilot 登录已导入。': 'GitHub Copilot 登录已导入。', 'github copilot 连接未能保存,请重新导入登录。': 'GitHub Copilot 连接未能保存,请重新导入登录。', 'github copilot 已移除本地登录。': 'GitHub Copilot 已移除本地登录。',
auth: '鉴权失败', timeout: '请求超时', provider_unavailable: '模型服务返回错误', network: '网络错误', invalid_response: '模型服务返回错误', unknown: '连接测试失败',
},
},
panel: {
Expand Down Expand Up @@ -360,12 +355,7 @@ const zhTwCopy = {
filterMatches: (count: number) => (count === 0 ? '沒有符合的結果' : `${count} 個符合結果`),
connectionStatuses: { retired: '已停用 · 請刪除', reauth: '需要重新登入', disabledFailed: '暫不可用 · 上次連線失敗', disabled: '暫不可用', failed: '上次連線失敗' },
lastTest: {
'连接已验证': '連線已驗證', '鉴权失败': '鑑權失敗', '请求超时': '請求超時', '网络错误': '網路錯誤', '模型服务返回错误': '模型服務回傳錯誤', '连接测试失败': '連線測試失敗',
'connection verified': '連線已驗證', 'authentication failed': '鑑權失敗', 'request timed out': '請求超時', 'network error': '網路錯誤', 'provider returned an error': '模型服務回傳錯誤', 'connection test failed': '連線測試失敗',
'claude oauth 未登录。': 'Claude OAuth 未登入。', 'claude oauth 本地凭据读取失败。': 'Claude OAuth 本地憑據讀取失敗。', 'claude oauth 需要重新登录。': 'Claude OAuth 需要重新登入。', 'claude oauth 已登录。': 'Claude OAuth 已登入。', 'claude oauth 已退出登录。': 'Claude OAuth 已退出登入。',
'codex oauth 未登录。': 'Codex OAuth 未登入。', 'codex oauth 本地凭据读取失败。': 'Codex OAuth 本地憑據讀取失敗。', 'codex oauth 需要重新登录。': 'Codex OAuth 需要重新登入。', 'codex oauth 已登录。': 'Codex OAuth 已登入。', 'codex oauth 已退出登录。': 'Codex OAuth 已退出登入。',
'当前账号无可用 codex 模型。': '目前帳號無可用 Codex 模型。', 'codex 模型列表获取失败。': 'Codex 模型列表取得失敗。',
'github copilot 需要重新导入 github cli 登录。': 'GitHub Copilot 需要重新匯入 GitHub CLI 登入。', 'github copilot 无法读取当前账号可用模型,请重新验证登录。': 'GitHub Copilot 無法讀取目前帳號可用模型,請重新驗證登入。', 'github copilot 登录已导入。': 'GitHub Copilot 登入已匯入。', 'github copilot 连接未能保存,请重新导入登录。': 'GitHub Copilot 連線未能儲存,請重新匯入登入。', 'github copilot 已移除本地登录。': 'GitHub Copilot 已移除本地登入。',
auth: '鑑權失敗', timeout: '請求超時', provider_unavailable: '模型服務回傳錯誤', network: '網路錯誤', invalid_response: '模型服務回傳錯誤', unknown: '連線測試失敗',
},
},
panel: {
Expand Down Expand Up @@ -530,12 +520,7 @@ const enCopy: ProviderSettingsCopy = {
filterMatches: (count: number) => (count === 0 ? 'No matches' : count === 1 ? '1 match' : `${count} matches`),
connectionStatuses: { retired: 'Retired · delete it', reauth: 'Sign-in required', disabledFailed: 'Unavailable · last connection failed', disabled: 'Unavailable', failed: 'Last connection failed' },
lastTest: {
'连接已验证': 'Connection verified', '鉴权失败': 'Authentication failed', '请求超时': 'Request timed out', '网络错误': 'Network error', '模型服务返回错误': 'Model service returned an error', '连接测试失败': 'Connection test failed',
'connection verified': 'Connection verified', 'authentication failed': 'Authentication failed', 'request timed out': 'Request timed out', 'network error': 'Network error', 'provider returned an error': 'Model service returned an error', 'connection test failed': 'Connection test failed',
'claude oauth 未登录。': 'Claude OAuth is signed out.', 'claude oauth 本地凭据读取失败。': 'Could not read local Claude OAuth credentials.', 'claude oauth 需要重新登录。': 'Claude OAuth requires sign-in.', 'claude oauth 已登录。': 'Claude OAuth is signed in.', 'claude oauth 已退出登录。': 'Claude OAuth signed out.',
'codex oauth 未登录。': 'Codex OAuth is signed out.', 'codex oauth 本地凭据读取失败。': 'Could not read local Codex OAuth credentials.', 'codex oauth 需要重新登录。': 'Codex OAuth requires sign-in.', 'codex oauth 已登录。': 'Codex OAuth is signed in.', 'codex oauth 已退出登录。': 'Codex OAuth signed out.',
'当前账号无可用 codex 模型。': 'No Codex models are available for this account.', 'codex 模型列表获取失败。': 'Failed to fetch the Codex model list.',
'github copilot 需要重新导入 github cli 登录。': 'GitHub Copilot requires the GitHub CLI sign-in to be imported again.', 'github copilot 无法读取当前账号可用模型,请重新验证登录。': 'GitHub Copilot could not read models available to this account. Verify sign-in again.', 'github copilot 登录已导入。': 'GitHub Copilot sign-in imported.', 'github copilot 连接未能保存,请重新导入登录。': 'The GitHub Copilot connection could not be saved. Import sign-in again.', 'github copilot 已移除本地登录。': 'Local GitHub Copilot sign-in removed.',
auth: 'Authentication failed', timeout: 'Request timed out', provider_unavailable: 'Model service returned an error', network: 'Network error', invalid_response: 'Model service returned an error', unknown: 'Connection test failed',
},
},
panel: {
Expand Down
Loading