Files
2026-07-13 13:20:22 +08:00

213 lines
6.8 KiB
TypeScript

/**
* @license
* Copyright 2025 AionUi (aionui.com)
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect, it } from 'vitest';
import { normalizeDbMessage } from '@/renderer/pages/conversation/Messages/hooks';
import type { IMessageText, IMessageTips } from '@/common/chat/chatLib';
describe('normalizeDbMessage', () => {
it('keeps persisted info tip localization metadata from db content', () => {
const normalized = normalizeDbMessage({
id: 'tip-info',
type: 'tips',
conversation_id: 'conversation-1',
position: 'center',
status: 'finish',
content: JSON.stringify({
content: '',
type: 'info',
code: 'ACP_EMPTY_TURN',
params: {
provider: 'OpenCode',
},
}),
} as unknown as IMessageTips) as IMessageTips;
expect(normalized.content).toEqual({
content: '',
type: 'info',
code: 'ACP_EMPTY_TURN',
params: {
provider: 'OpenCode',
},
});
});
it('keeps structured error metadata from persisted tips', () => {
const normalized = normalizeDbMessage({
id: 'tip-structured',
type: 'tips',
conversation_id: 'conversation-1',
position: 'center',
status: 'error',
content: JSON.stringify({
content: 'The upstream Agent failed while handling the request',
type: 'error',
source: 'send_failed',
code: 'BAD_GATEWAY',
error: {
message: 'The upstream Agent failed while handling the request',
code: 'UNKNOWN_UPSTREAM_ERROR',
ownership: 'unknown_upstream',
detail: 'ACP init failed: config file is invalid',
retryable: true,
feedback_recommended: true,
resolution: {
kind: 'start_new_session',
target: 'new_conversation',
},
},
}),
} as unknown as IMessageTips) as IMessageTips;
expect(normalized.content.error).toEqual({
message: 'The upstream Agent failed while handling the request',
code: 'UNKNOWN_UPSTREAM_ERROR',
ownership: 'unknown_upstream',
detail: 'ACP init failed: config file is invalid',
retryable: true,
feedback_recommended: true,
resolution: {
kind: 'start_new_session',
target: 'new_conversation',
},
});
});
it('restores persisted send failure tips as structured agent errors', () => {
const normalized = normalizeDbMessage({
id: 'tip-1',
type: 'tips',
conversation_id: 'conversation-1',
position: 'center',
status: 'error',
content: JSON.stringify({
content: 'Bad gateway: ACP init failed: config file is invalid',
type: 'error',
source: 'send_failed',
code: 'BAD_GATEWAY',
}),
} as unknown as IMessageTips) as IMessageTips;
expect(normalized.content).toEqual({
content: 'Bad gateway: ACP init failed: config file is invalid',
type: 'error',
error: {
message: 'Bad gateway: ACP init failed: config file is invalid',
code: 'UNKNOWN_UPSTREAM_ERROR',
ownership: 'unknown_upstream',
detail: 'Bad gateway: ACP init failed: config file is invalid',
retryable: true,
feedback_recommended: true,
},
});
});
it('prefers persisted workspace runtime errors over legacy unknown-upstream payloads', () => {
const normalized = normalizeDbMessage({
id: 'tip-runtime-workspace',
type: 'tips',
conversation_id: 'conversation-1',
position: 'center',
status: 'error',
content: JSON.stringify({
content: 'The current Agent failed to run in this workspace path',
type: 'error',
source: 'send_failed',
code: 'WORKSPACE_PATH_CONTAINS_WHITESPACE_RUNTIME_UNSUPPORTED',
details: {
workspace_path: '/Users/zhoukai/Documents/Archive ',
},
error: {
message: 'The current Agent failed to run in this workspace path',
code: 'UNKNOWN_UPSTREAM_ERROR',
ownership: 'unknown_upstream',
detail: '/Users/zhoukai/Documents/Archive . Make sure the workspace path exists and is accessible.',
retryable: true,
feedback_recommended: true,
},
}),
} as unknown as IMessageTips) as IMessageTips;
expect(normalized.content.error).toEqual({
message: 'The current Agent failed to run in this workspace path',
code: 'WORKSPACE_PATH_RUNTIME_UNAVAILABLE',
ownership: 'aionui',
detail: '/Users/zhoukai/Documents/Archive . Make sure the workspace path exists and is accessible.',
workspacePath: '/Users/zhoukai/Documents/Archive ',
retryable: false,
feedback_recommended: false,
});
});
it('normalizes team metadata when persisted text content is already an object', () => {
const normalized = normalizeDbMessage({
id: 'team-message-object',
type: 'text',
conversation_id: 'leader-conversation-1',
position: 'left',
status: 'finish',
content: {
content: '[Claude Assistant] idle',
teammate_message: true,
sender_name: 'Claude Assistant',
sender_backend: 'claude',
sender_conversation_id: 'teammate-conversation-1',
},
} as unknown as IMessageText) as IMessageText;
expect(normalized.content).toEqual({
content: '[Claude Assistant] idle',
teammateMessage: true,
senderName: 'Claude Assistant',
senderAgentType: 'claude',
senderConversationId: 'teammate-conversation-1',
});
});
it('normalizes team metadata when persisted text content is a JSON string', () => {
const normalized = normalizeDbMessage({
id: 'team-message-json',
type: 'text',
conversation_id: 'leader-conversation-1',
position: 'left',
status: 'finish',
content: JSON.stringify({
content: '[Codex Assistant] idle',
teammate_message: true,
sender_name: 'Codex Assistant',
sender_backend: 'codex',
sender_conversation_id: 'teammate-conversation-2',
}),
} as unknown as IMessageText) as IMessageText;
expect(normalized.content).toEqual({
content: '[Codex Assistant] idle',
teammateMessage: true,
senderName: 'Codex Assistant',
senderAgentType: 'codex',
senderConversationId: 'teammate-conversation-2',
});
});
it('keeps ordinary recovered text messages as plain text content', () => {
const normalized = normalizeDbMessage({
id: 'ordinary-text',
type: 'text',
conversation_id: 'conversation-1',
position: 'left',
status: 'finish',
content: {
content: 'ordinary assistant response',
},
} as unknown as IMessageText) as IMessageText;
expect(normalized.content).toEqual({
content: 'ordinary assistant response',
});
});
});