@lobehub/chat 1.139.2 → 1.139.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.github/workflows/desktop-pr-build.yml +2 -2
  2. package/.github/workflows/docker-database.yml +1 -1
  3. package/.github/workflows/docker-pglite.yml +1 -1
  4. package/.github/workflows/docker.yml +1 -1
  5. package/.github/workflows/release-desktop-beta.yml +2 -2
  6. package/CHANGELOG.md +50 -0
  7. package/apps/desktop/package.json +1 -1
  8. package/changelog/v1.json +18 -0
  9. package/docs/development/basic/work-with-server-side-database.mdx +5 -5
  10. package/docs/development/basic/work-with-server-side-database.zh-CN.mdx +5 -5
  11. package/docs/development/tests/integration-testing.zh-CN.mdx +399 -0
  12. package/locales/ar/chat.json +3 -1
  13. package/locales/bg-BG/chat.json +3 -1
  14. package/locales/de-DE/chat.json +3 -1
  15. package/locales/en-US/chat.json +3 -1
  16. package/locales/es-ES/chat.json +3 -1
  17. package/locales/fa-IR/chat.json +3 -1
  18. package/locales/fr-FR/chat.json +3 -1
  19. package/locales/it-IT/chat.json +3 -1
  20. package/locales/ja-JP/chat.json +3 -1
  21. package/locales/ko-KR/chat.json +3 -1
  22. package/locales/nl-NL/chat.json +3 -1
  23. package/locales/pl-PL/chat.json +3 -1
  24. package/locales/pt-BR/chat.json +3 -1
  25. package/locales/ru-RU/chat.json +3 -1
  26. package/locales/tr-TR/chat.json +3 -1
  27. package/locales/vi-VN/chat.json +3 -1
  28. package/locales/zh-CN/chat.json +3 -1
  29. package/locales/zh-TW/chat.json +3 -1
  30. package/package.json +2 -2
  31. package/packages/database/package.json +2 -1
  32. package/packages/database/tests/test-utils.ts +1 -0
  33. package/packages/types/src/message/chat.ts +1 -0
  34. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatMinimap/index.tsx +28 -9
  35. package/src/features/DevPanel/index.tsx +7 -1
  36. package/src/features/ElectronTitlebar/UpdateNotification.tsx +19 -2
  37. package/src/locales/default/chat.ts +2 -0
  38. package/src/server/routers/lambda/{agent.test.ts → __tests__/agent.test.ts} +1 -1
  39. package/src/server/routers/lambda/__tests__/aiChat.test.ts +259 -0
  40. package/src/server/routers/lambda/{aiModel.test.ts → __tests__/aiModel.test.ts} +1 -1
  41. package/src/server/routers/lambda/{aiProvider.test.ts → __tests__/aiProvider.test.ts} +1 -1
  42. package/src/server/routers/lambda/{generation.test.ts → __tests__/generation.test.ts} +1 -1
  43. package/src/server/routers/lambda/{generationBatch.test.ts → __tests__/generationBatch.test.ts} +1 -1
  44. package/src/server/routers/lambda/{generationTopic.test.ts → __tests__/generationTopic.test.ts} +1 -1
  45. package/src/server/routers/lambda/__tests__/integration/README.md +110 -0
  46. package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +545 -0
  47. package/src/server/routers/lambda/__tests__/integration/setup.ts +36 -0
  48. package/src/server/routers/lambda/{user.test.ts → __tests__/user.test.ts} +1 -1
  49. package/src/server/routers/lambda/aiChat.ts +2 -0
  50. package/src/store/chat/slices/message/action.test.ts +92 -0
  51. package/src/store/chat/slices/message/action.ts +3 -1
  52. package/src/server/routers/lambda/aiChat.test.ts +0 -108
@@ -238,7 +238,7 @@ jobs:
238
238
 
239
239
  # 下载所有平台的构建产物
240
240
  - name: Download artifacts
241
- uses: actions/download-artifact@v4
241
+ uses: actions/download-artifact@v5
242
242
  with:
243
243
  path: release
244
244
  pattern: release-*
@@ -287,7 +287,7 @@ jobs:
287
287
 
288
288
  # 下载合并后的构建产物
289
289
  - name: Download merged artifacts
290
- uses: actions/download-artifact@v4
290
+ uses: actions/download-artifact@v5
291
291
  with:
292
292
  name: merged-release-pr
293
293
  path: release
@@ -118,7 +118,7 @@ jobs:
118
118
  fetch-depth: 0
119
119
 
120
120
  - name: Download digests
121
- uses: actions/download-artifact@v4
121
+ uses: actions/download-artifact@v5
122
122
  with:
123
123
  path: /tmp/digests
124
124
  pattern: digest-*
@@ -118,7 +118,7 @@ jobs:
118
118
  fetch-depth: 0
119
119
 
120
120
  - name: Download digests
121
- uses: actions/download-artifact@v4
121
+ uses: actions/download-artifact@v5
122
122
  with:
123
123
  path: /tmp/digests
124
124
  pattern: digest-*
@@ -118,7 +118,7 @@ jobs:
118
118
  fetch-depth: 0
119
119
 
120
120
  - name: Download digests
121
- uses: actions/download-artifact@v4
121
+ uses: actions/download-artifact@v5
122
122
  with:
123
123
  path: /tmp/digests
124
124
  pattern: digest-*
@@ -220,7 +220,7 @@ jobs:
220
220
 
221
221
  # 下载所有平台的构建产物
222
222
  - name: Download artifacts
223
- uses: actions/download-artifact@v4
223
+ uses: actions/download-artifact@v5
224
224
  with:
225
225
  path: release
226
226
  pattern: release-*
@@ -262,7 +262,7 @@ jobs:
262
262
  steps:
263
263
  # 下载合并后的构建产物
264
264
  - name: Download merged artifacts
265
- uses: actions/download-artifact@v4
265
+ uses: actions/download-artifact@v5
266
266
  with:
267
267
  name: merged-release
268
268
  path: release
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.139.4](https://github.com/lobehub/lobe-chat/compare/v1.139.3...v1.139.4)
6
+
7
+ <sup>Released on **2025-10-21**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Pass threadId to messages in sendMessageInServer.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Pass threadId to messages in sendMessageInServer, closes [#9808](https://github.com/lobehub/lobe-chat/issues/9808) ([d99a3a8](https://github.com/lobehub/lobe-chat/commit/d99a3a8))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 1.139.3](https://github.com/lobehub/lobe-chat/compare/v1.139.2...v1.139.3)
31
+
32
+ <sup>Released on **2025-10-21**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Show message author in minimap.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Show message author in minimap, closes [#9797](https://github.com/lobehub/lobe-chat/issues/9797) ([f6daefb](https://github.com/lobehub/lobe-chat/commit/f6daefb))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 1.139.2](https://github.com/lobehub/lobe-chat/compare/v1.139.1...v1.139.2)
6
56
 
7
57
  <sup>Released on **2025-10-20**</sup>
@@ -39,7 +39,7 @@
39
39
  "@electron-toolkit/eslint-config-prettier": "^3.0.0",
40
40
  "@electron-toolkit/eslint-config-ts": "^3.0.0",
41
41
  "@electron-toolkit/preload": "^3.0.1",
42
- "@electron-toolkit/tsconfig": "^1.0.1",
42
+ "@electron-toolkit/tsconfig": "^2.0.0",
43
43
  "@electron-toolkit/utils": "^4.0.0",
44
44
  "@lobechat/electron-client-ipc": "workspace:*",
45
45
  "@lobechat/electron-server-ipc": "workspace:*",
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Pass threadId to messages in sendMessageInServer."
6
+ ]
7
+ },
8
+ "date": "2025-10-21",
9
+ "version": "1.139.4"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Show message author in minimap."
15
+ ]
16
+ },
17
+ "date": "2025-10-21",
18
+ "version": "1.139.3"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "improvements": [
@@ -11,13 +11,13 @@ But here is the easier approach that can reduce your pain.
11
11
 
12
12
  ### Environment Configuration
13
13
 
14
- First, copy the example environment file to create your development configuration:
14
+ First, copy the example environment file to create your Docker Compose configuration:
15
15
 
16
16
  ```bash
17
- cp .env.example.development .env.development
17
+ cp docker-compose/local/.env.example docker-compose/local/.env
18
18
  ```
19
19
 
20
- This file contains all necessary environment variables for server-side database mode and configures:
20
+ Edit `docker-compose/local/.env` as needed for your development setup. This file contains all necessary environment variables for the Docker services and configures:
21
21
 
22
22
  - **Service Mode**: `NEXT_PUBLIC_SERVICE_MODE=server`
23
23
  - **Database**: PostgreSQL with connection string
@@ -72,7 +72,7 @@ When working with image generation features (text-to-image, image-to-image), the
72
72
 
73
73
  ### Image Generation Configuration
74
74
 
75
- The existing Docker Compose configuration already includes MinIO storage service and all necessary environment variables in `.env.example.development`. No additional setup is required.
75
+ The existing Docker Compose configuration already includes MinIO storage service and all necessary environment variables in `docker-compose/local/.env.example`. No additional setup is required.
76
76
 
77
77
  ### Image Generation Architecture
78
78
 
@@ -84,7 +84,7 @@ The image generation feature requires:
84
84
 
85
85
  ### Storage Configuration
86
86
 
87
- The `.env.example.development` file includes all necessary S3 environment variables:
87
+ The `docker-compose/local/.env.example` file includes all necessary S3 environment variables:
88
88
 
89
89
  ```bash
90
90
  # S3 Storage Configuration (MinIO for local development)
@@ -11,13 +11,13 @@ LobeChat 提供了内置的客户端数据库体验。
11
11
 
12
12
  ### 环境配置
13
13
 
14
- 首先,复制示例环境文件来创建你的开发配置:
14
+ 首先,复制示例环境文件来创建你的 Docker Compose 配置:
15
15
 
16
16
  ```bash
17
- cp .env.example.development .env.development
17
+ cp docker-compose/local/.env.example docker-compose/local/.env
18
18
  ```
19
19
 
20
- 此文件包含服务端数据库模式所需的所有环境变量,配置了:
20
+ 根据需要编辑 `docker-compose/local/.env` 文件以适应你的开发设置。此文件包含 Docker 服务所需的所有环境变量,配置了:
21
21
 
22
22
  - **服务模式**: `NEXT_PUBLIC_SERVICE_MODE=server`
23
23
  - **数据库**: 带连接字符串的 PostgreSQL
@@ -72,7 +72,7 @@ docker-compose -f docker-compose.development.yml ps
72
72
 
73
73
  ### 图像生成配置
74
74
 
75
- 现有的 Docker Compose 配置已经包含了 MinIO 存储服务以及 `.env.example.development` 中的所有必要环境变量。无需额外配置。
75
+ 现有的 Docker Compose 配置已经包含了 MinIO 存储服务以及 `docker-compose/local/.env.example` 中的所有必要环境变量。无需额外配置。
76
76
 
77
77
  ### 图像生成架构
78
78
 
@@ -84,7 +84,7 @@ docker-compose -f docker-compose.development.yml ps
84
84
 
85
85
  ### 存储配置
86
86
 
87
- `.env.example.development` 文件包含所有必要的 S3 环境变量:
87
+ `docker-compose/local/.env.example` 文件包含所有必要的 S3 环境变量:
88
88
 
89
89
  ```bash
90
90
  # S3 存储配置(本地开发使用 MinIO)
@@ -0,0 +1,399 @@
1
+ # 集成测试指南
2
+
3
+ ## 概述
4
+
5
+ 集成测试验证多个模块协同工作的正确性,确保完整的调用链路(Router → Service → Model → Database)正常运行。
6
+
7
+ ## 为什么需要集成测试?
8
+
9
+ 即使单元测试覆盖率很高(80%+),仍可能出现集成问题:
10
+
11
+ ### 常见问题示例
12
+
13
+ ```typescript
14
+ // ❌ 问题:参数在调用链中丢失
15
+ // Router 层
16
+ const messageId = await messageModel.create({
17
+ content: 'test',
18
+ sessionId: 'xxx',
19
+ topicId: 'yyy', // ← 传入了 topicId
20
+ });
21
+
22
+ // Model 层(假设实现有问题)
23
+ async create(data) {
24
+ return this.db.insert(messages).values({
25
+ content: data.content,
26
+ sessionId: data.sessionId,
27
+ // ❌ 忘记传递 topicId
28
+ });
29
+ }
30
+
31
+ // 结果:单元测试通过(因为 mock 了 Model),但实际运行时 topicId 丢失
32
+ ```
33
+
34
+ ### 集成测试能发现的问题
35
+
36
+ 1. **参数传递遗漏**: containerId、threadId、topicId 等在调用链中丢失
37
+ 2. **数据库约束**: 外键关系、级联删除等在 mock 中无法验证
38
+ 3. **事务完整性**: 跨表操作的原子性
39
+ 4. **权限验证**: 跨用户访问控制
40
+ 5. **真实场景**: 模拟用户的完整操作流程
41
+
42
+ ## 运行集成测试
43
+
44
+ ```bash
45
+ # 运行所有集成测试
46
+ pnpm test:integration
47
+
48
+ # 运行特定文件
49
+ pnpm vitest tests/integration/routers/message.integration.test.ts
50
+
51
+ # 监听模式
52
+ pnpm vitest tests/integration --watch
53
+
54
+ # 生成覆盖率报告
55
+ pnpm test:integration --coverage
56
+ ```
57
+
58
+ ## 目录结构
59
+
60
+ ```
61
+ tests/integration/
62
+ ├── README.md # 集成测试说明
63
+ ├── setup.ts # 通用设置和工具函数
64
+ └── routers/ # Router 层集成测试
65
+ ├── message.integration.test.ts # Message Router 测试
66
+ ├── session.integration.test.ts # Session Router 测试
67
+ ├── topic.integration.test.ts # Topic Router 测试
68
+ └── chat-flow.integration.test.ts # 完整聊天流程测试
69
+ ```
70
+
71
+ ## 编写集成测试
72
+
73
+ ### 基本模板
74
+
75
+ ```typescript
76
+ // @vitest-environment node
77
+ import { eq } from 'drizzle-orm';
78
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
79
+
80
+ import { getTestDB } from '@/database/models/__tests__/_util';
81
+ import { messages, sessions, users } from '@/database/schemas';
82
+ import { LobeChatDatabase } from '@/database/type';
83
+ import { messageRouter } from '@/server/routers/lambda/message';
84
+
85
+ import { cleanupTestUser, createTestContext, createTestUser } from '../setup';
86
+
87
+ describe('Your Feature Integration Tests', () => {
88
+ let serverDB: LobeChatDatabase;
89
+ let userId: string;
90
+
91
+ beforeEach(async () => {
92
+ // 1. 获取测试数据库
93
+ serverDB = await getTestDB();
94
+
95
+ // 2. 创建测试用户
96
+ userId = await createTestUser(serverDB);
97
+
98
+ // 3. 准备其他测试数据
99
+ // ...
100
+ });
101
+
102
+ afterEach(async () => {
103
+ // 清理测试数据
104
+ await cleanupTestUser(serverDB, userId);
105
+ });
106
+
107
+ it('should do something', async () => {
108
+ // 1. 创建 tRPC caller
109
+ const caller = messageRouter.createCaller(createTestContext(userId));
110
+
111
+ // 2. 执行操作
112
+ const result = await caller.someMethod({
113
+ /* params */
114
+ });
115
+
116
+ // 3. 验证结果
117
+ expect(result).toBeDefined();
118
+
119
+ // 4. 🔥 关键:从数据库验证
120
+ const [dbRecord] = await serverDB.select().from(messages).where(eq(messages.id, result));
121
+
122
+ expect(dbRecord).toMatchObject({
123
+ // 验证所有关键字段
124
+ });
125
+ });
126
+ });
127
+ ```
128
+
129
+ ### 最佳实践
130
+
131
+ #### 1. 测试完整的调用链路
132
+
133
+ ```typescript
134
+ it('should create message with correct associations', async () => {
135
+ const caller = messageRouter.createCaller(createTestContext(userId));
136
+
137
+ // 执行操作
138
+ const messageId = await caller.createMessage({
139
+ content: 'Test',
140
+ sessionId: testSessionId,
141
+ topicId: testTopicId,
142
+ });
143
+
144
+ // ✅ 从数据库验证,而不是只验证返回值
145
+ const [message] = await serverDB.select().from(messages).where(eq(messages.id, messageId));
146
+
147
+ expect(message.sessionId).toBe(testSessionId);
148
+ expect(message.topicId).toBe(testTopicId);
149
+ expect(message.userId).toBe(userId);
150
+ });
151
+ ```
152
+
153
+ #### 2. 测试级联操作
154
+
155
+ ```typescript
156
+ it('should cascade delete messages when session is deleted', async () => {
157
+ const sessionCaller = sessionRouter.createCaller(createTestContext(userId));
158
+ const messageCaller = messageRouter.createCaller(createTestContext(userId));
159
+
160
+ // 创建 session 和 messages
161
+ const sessionId = await sessionCaller.createSession({
162
+ /* ... */
163
+ });
164
+ await messageCaller.createMessage({ sessionId /* ... */ });
165
+
166
+ // 删除 session
167
+ await sessionCaller.removeSession({ id: sessionId });
168
+
169
+ // ✅ 验证相关消息也被删除
170
+ const remainingMessages = await serverDB
171
+ .select()
172
+ .from(messages)
173
+ .where(eq(messages.sessionId, sessionId));
174
+
175
+ expect(remainingMessages).toHaveLength(0);
176
+ });
177
+ ```
178
+
179
+ #### 3. 测试跨 Router 协作
180
+
181
+ ```typescript
182
+ it('should handle complete chat flow', async () => {
183
+ const sessionCaller = sessionRouter.createCaller(createTestContext(userId));
184
+ const topicCaller = topicRouter.createCaller(createTestContext(userId));
185
+ const messageCaller = messageRouter.createCaller(createTestContext(userId));
186
+
187
+ // 1. 创建 session
188
+ const sessionId = await sessionCaller.createSession({
189
+ /* ... */
190
+ });
191
+
192
+ // 2. 创建 topic
193
+ const topicId = await topicCaller.createTopic({ sessionId /* ... */ });
194
+
195
+ // 3. 创建 message
196
+ const messageId = await messageCaller.createMessage({
197
+ sessionId,
198
+ topicId,
199
+ /* ... */
200
+ });
201
+
202
+ // ✅ 验证完整的关联关系
203
+ const [message] = await serverDB.select().from(messages).where(eq(messages.id, messageId));
204
+
205
+ expect(message.sessionId).toBe(sessionId);
206
+ expect(message.topicId).toBe(topicId);
207
+ });
208
+ ```
209
+
210
+ #### 4. 测试错误场景
211
+
212
+ ```typescript
213
+ it('should prevent cross-user access', async () => {
214
+ // 用户 A 创建 session
215
+ const sessionId = await sessionRouter.createCaller(createTestContext(userA)).createSession({
216
+ /* ... */
217
+ });
218
+
219
+ // 用户 B 尝试访问
220
+ const callerB = messageRouter.createCaller(createTestContext(userB));
221
+
222
+ // ✅ 应该抛出错误
223
+ await expect(
224
+ callerB.createMessage({
225
+ sessionId,
226
+ content: 'Unauthorized',
227
+ }),
228
+ ).rejects.toThrow();
229
+ });
230
+ ```
231
+
232
+ #### 5. 测试并发场景
233
+
234
+ ```typescript
235
+ it('should handle concurrent operations', async () => {
236
+ const caller = messageRouter.createCaller(createTestContext(userId));
237
+
238
+ // 并发创建多个消息
239
+ const promises = Array.from({ length: 10 }, (_, i) =>
240
+ caller.createMessage({
241
+ content: `Message ${i}`,
242
+ sessionId: testSessionId,
243
+ }),
244
+ );
245
+
246
+ const messageIds = await Promise.all(promises);
247
+
248
+ // ✅ 验证所有消息都创建成功且唯一
249
+ expect(messageIds).toHaveLength(10);
250
+ expect(new Set(messageIds).size).toBe(10);
251
+ });
252
+ ```
253
+
254
+ ### 数据隔离
255
+
256
+ 每个测试用例应该独立,不依赖其他测试:
257
+
258
+ ```typescript
259
+ beforeEach(async () => {
260
+ // 为每个测试创建新的数据
261
+ userId = await createTestUser(serverDB);
262
+ testSessionId = await createTestSession(serverDB, userId);
263
+ });
264
+
265
+ afterEach(async () => {
266
+ // 清理测试数据
267
+ await cleanupTestUser(serverDB, userId);
268
+ });
269
+ ```
270
+
271
+ ### 测试命名
272
+
273
+ 使用清晰的命名描述测试意图:
274
+
275
+ ```typescript
276
+ // ✅ 好的命名
277
+ it('should create message with correct sessionId and topicId');
278
+ it('should cascade delete messages when session is deleted');
279
+ it('should prevent cross-user access to messages');
280
+
281
+ // ❌ 不好的命名
282
+ it('test message creation');
283
+ it('test delete');
284
+ ```
285
+
286
+ ## 与单元测试的区别
287
+
288
+ | 维度 | 单元测试 | 集成测试 |
289
+ | ------- | --------- | ------- |
290
+ | **范围** | 单个函数 / 类 | 多个模块协作 |
291
+ | **依赖** | Mock 外部依赖 | 使用真实依赖 |
292
+ | **数据库** | Mock | 真实测试数据库 |
293
+ | **速度** | 快(毫秒级) | 慢(秒级) |
294
+ | **数量** | 多(60%) | 少(30%) |
295
+ | **目的** | 验证逻辑正确性 | 验证集成正确性 |
296
+
297
+ ## 测试金字塔
298
+
299
+ ```
300
+ /\
301
+ /E2E\ ← 10% (关键业务流程)
302
+ /------\
303
+ / 集成 \ ← 30% (API 集成测试) ⭐ 本指南重点
304
+ /----------\
305
+ / 单元测试 \ ← 60% (已有 80%+)
306
+ /--------------\
307
+ ```
308
+
309
+ ## 覆盖目标
310
+
311
+ ### 优先级 P0(必须覆盖)
312
+
313
+ - ✅ 跨层级的 ID 传递(sessionId、topicId、containerId、threadId)
314
+ - ✅ 权限验证(用户只能访问自己的资源)
315
+ - ✅ 级联删除(删除 session 时相关数据也删除)
316
+ - ✅ 外键约束(不能创建不存在的关联)
317
+
318
+ ### 优先级 P1(应该覆盖)
319
+
320
+ - 并发场景(多个请求同时操作)
321
+ - 分页查询(正确的数据分页)
322
+ - 搜索功能(关键词搜索)
323
+ - 批量操作(批量创建 / 删除)
324
+
325
+ ### 优先级 P2(可以覆盖)
326
+
327
+ - 统计功能(计数、排名)
328
+ - 复杂查询(多条件筛选)
329
+ - 性能测试(大量数据场景)
330
+
331
+ ## 调试技巧
332
+
333
+ ### 1. 查看测试数据库状态
334
+
335
+ ```typescript
336
+ it('debug test', async () => {
337
+ // 执行操作
338
+ await caller.createMessage({
339
+ /* ... */
340
+ });
341
+
342
+ // 打印数据库状态
343
+ const allMessages = await serverDB.select().from(messages);
344
+ console.log('All messages:', allMessages);
345
+ });
346
+ ```
347
+
348
+ ### 2. 使用 Drizzle Studio
349
+
350
+ ```bash
351
+ # 启动 Drizzle Studio 查看测试数据库
352
+ pnpm db:studio
353
+ ```
354
+
355
+ ### 3. 保留测试数据
356
+
357
+ ```typescript
358
+ afterEach(async () => {
359
+ // 临时注释掉清理代码,保留数据用于调试
360
+ // await cleanupTestUser(serverDB, userId);
361
+ });
362
+ ```
363
+
364
+ ## 常见问题
365
+
366
+ ### Q: 集成测试很慢怎么办?
367
+
368
+ A:
369
+
370
+ 1. 只测试关键路径,不要过度测试
371
+ 2. 使用 `test.concurrent` 并行执行独立的测试
372
+ 3. 优化测试数据准备,避免重复创建
373
+
374
+ ### Q: 测试之间相互影响怎么办?
375
+
376
+ A:
377
+
378
+ 1. 确保每个测试使用独立的 userId
379
+ 2. 在 `afterEach` 中彻底清理数据
380
+ 3. 使用事务隔离(如果数据库支持)
381
+
382
+ ### Q: 如何测试需要认证的 API?
383
+
384
+ A: 使用 `createTestContext(userId)` 创建带认证信息的上下文:
385
+
386
+ ```typescript
387
+ const caller = messageRouter.createCaller(createTestContext(userId));
388
+ ```
389
+
390
+ ## 参考资料
391
+
392
+ - [Vitest 文档](https://vitest.dev/)
393
+ - [Drizzle ORM 文档](https://orm.drizzle.team/)
394
+ - [tRPC 测试指南](https://trpc.io/docs/server/testing)
395
+ - [测试金字塔](https://martinfowler.com/articles/practical-test-pyramid.html)
396
+
397
+ ## 贡献
398
+
399
+ 欢迎补充更多集成测试用例!请参考现有测试文件的风格。
@@ -228,7 +228,9 @@
228
228
  "minimap": {
229
229
  "jumpToMessage": "الانتقال إلى الرسالة رقم {{index}}",
230
230
  "nextMessage": "الرسالة التالية",
231
- "previousMessage": "الرسالة السابقة"
231
+ "previousMessage": "الرسالة السابقة",
232
+ "senderAssistant": "المساعد",
233
+ "senderUser": "أنت"
232
234
  },
233
235
  "newAgent": "مساعد جديد",
234
236
  "newGroupChat": "إنشاء دردشة جماعية جديدة",
@@ -228,7 +228,9 @@
228
228
  "minimap": {
229
229
  "jumpToMessage": "Отиди до съобщение № {{index}}",
230
230
  "nextMessage": "Следващо съобщение",
231
- "previousMessage": "Предишно съобщение"
231
+ "previousMessage": "Предишно съобщение",
232
+ "senderAssistant": "Асистент",
233
+ "senderUser": "Ти"
232
234
  },
233
235
  "newAgent": "Нов агент",
234
236
  "newGroupChat": "Създаване на нов групов чат",
@@ -228,7 +228,9 @@
228
228
  "minimap": {
229
229
  "jumpToMessage": "Zur Nachricht Nr. {{index}} springen",
230
230
  "nextMessage": "Nächste Nachricht",
231
- "previousMessage": "Vorherige Nachricht"
231
+ "previousMessage": "Vorherige Nachricht",
232
+ "senderAssistant": "Assistent",
233
+ "senderUser": "Du"
232
234
  },
233
235
  "newAgent": "Neuer Assistent",
234
236
  "newGroupChat": "Neue Gruppe erstellen",
@@ -228,7 +228,9 @@
228
228
  "minimap": {
229
229
  "jumpToMessage": "Jump to message {{index}}",
230
230
  "nextMessage": "Next message",
231
- "previousMessage": "Previous message"
231
+ "previousMessage": "Previous message",
232
+ "senderAssistant": "Assistant",
233
+ "senderUser": "You"
232
234
  },
233
235
  "newAgent": "New Assistant",
234
236
  "newGroupChat": "New Group Chat",
@@ -228,7 +228,9 @@
228
228
  "minimap": {
229
229
  "jumpToMessage": "Ir al mensaje número {{index}}",
230
230
  "nextMessage": "Mensaje siguiente",
231
- "previousMessage": "Mensaje anterior"
231
+ "previousMessage": "Mensaje anterior",
232
+ "senderAssistant": "Asistente",
233
+ "senderUser": "Tú"
232
234
  },
233
235
  "newAgent": "Nuevo asistente",
234
236
  "newGroupChat": "Crear nuevo chat de grupo",