@yivan-lab/pretty-please 1.3.1 → 1.5.0
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.
- package/README.md +250 -620
- package/bin/pls.tsx +178 -40
- package/dist/bin/pls.js +149 -27
- package/dist/package.json +10 -2
- package/dist/src/__integration__/command-generation.test.d.ts +5 -0
- package/dist/src/__integration__/command-generation.test.js +508 -0
- package/dist/src/__integration__/error-recovery.test.d.ts +5 -0
- package/dist/src/__integration__/error-recovery.test.js +511 -0
- package/dist/src/__integration__/shell-hook-workflow.test.d.ts +5 -0
- package/dist/src/__integration__/shell-hook-workflow.test.js +375 -0
- package/dist/src/__tests__/alias.test.d.ts +5 -0
- package/dist/src/__tests__/alias.test.js +421 -0
- package/dist/src/__tests__/chat-history.test.d.ts +5 -0
- package/dist/src/__tests__/chat-history.test.js +372 -0
- package/dist/src/__tests__/config.test.d.ts +5 -0
- package/dist/src/__tests__/config.test.js +822 -0
- package/dist/src/__tests__/history.test.d.ts +5 -0
- package/dist/src/__tests__/history.test.js +439 -0
- package/dist/src/__tests__/remote-history.test.d.ts +5 -0
- package/dist/src/__tests__/remote-history.test.js +641 -0
- package/dist/src/__tests__/remote.test.d.ts +5 -0
- package/dist/src/__tests__/remote.test.js +689 -0
- package/dist/src/__tests__/shell-hook-install.test.d.ts +5 -0
- package/dist/src/__tests__/shell-hook-install.test.js +413 -0
- package/dist/src/__tests__/shell-hook-remote.test.d.ts +5 -0
- package/dist/src/__tests__/shell-hook-remote.test.js +507 -0
- package/dist/src/__tests__/shell-hook.test.d.ts +5 -0
- package/dist/src/__tests__/shell-hook.test.js +440 -0
- package/dist/src/__tests__/sysinfo.test.d.ts +5 -0
- package/dist/src/__tests__/sysinfo.test.js +572 -0
- package/dist/src/__tests__/system-history.test.d.ts +5 -0
- package/dist/src/__tests__/system-history.test.js +457 -0
- package/dist/src/components/Chat.js +9 -28
- package/dist/src/config.d.ts +2 -0
- package/dist/src/config.js +30 -2
- package/dist/src/mastra-chat.js +10 -6
- package/dist/src/multi-step.js +10 -8
- package/dist/src/project-context.d.ts +22 -0
- package/dist/src/project-context.js +168 -0
- package/dist/src/prompts.d.ts +4 -4
- package/dist/src/prompts.js +23 -6
- package/dist/src/shell-hook.d.ts +32 -0
- package/dist/src/shell-hook.js +226 -33
- package/dist/src/sysinfo.d.ts +38 -9
- package/dist/src/sysinfo.js +245 -21
- package/dist/src/system-history.d.ts +18 -0
- package/dist/src/system-history.js +151 -0
- package/dist/src/ui/__tests__/theme.test.d.ts +5 -0
- package/dist/src/ui/__tests__/theme.test.js +688 -0
- package/dist/src/upgrade.js +3 -0
- package/dist/src/user-preferences.d.ts +44 -0
- package/dist/src/user-preferences.js +147 -0
- package/dist/src/utils/__tests__/platform-capabilities.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform-capabilities.test.js +214 -0
- package/dist/src/utils/__tests__/platform-exec.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform-exec.test.js +212 -0
- package/dist/src/utils/__tests__/platform-shell.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform-shell.test.js +300 -0
- package/dist/src/utils/__tests__/platform.test.d.ts +5 -0
- package/dist/src/utils/__tests__/platform.test.js +137 -0
- package/dist/src/utils/platform.d.ts +88 -0
- package/dist/src/utils/platform.js +331 -0
- package/package.json +10 -2
- package/src/__integration__/command-generation.test.ts +602 -0
- package/src/__integration__/error-recovery.test.ts +620 -0
- package/src/__integration__/shell-hook-workflow.test.ts +457 -0
- package/src/__tests__/alias.test.ts +545 -0
- package/src/__tests__/chat-history.test.ts +462 -0
- package/src/__tests__/config.test.ts +1043 -0
- package/src/__tests__/history.test.ts +538 -0
- package/src/__tests__/remote-history.test.ts +791 -0
- package/src/__tests__/remote.test.ts +866 -0
- package/src/__tests__/shell-hook-install.test.ts +510 -0
- package/src/__tests__/shell-hook-remote.test.ts +679 -0
- package/src/__tests__/shell-hook.test.ts +564 -0
- package/src/__tests__/sysinfo.test.ts +718 -0
- package/src/__tests__/system-history.test.ts +608 -0
- package/src/components/Chat.tsx +10 -37
- package/src/config.ts +29 -2
- package/src/mastra-chat.ts +12 -5
- package/src/multi-step.ts +11 -5
- package/src/project-context.ts +191 -0
- package/src/prompts.ts +26 -5
- package/src/shell-hook.ts +254 -32
- package/src/sysinfo.ts +326 -25
- package/src/system-history.ts +170 -0
- package/src/ui/__tests__/theme.test.ts +869 -0
- package/src/upgrade.ts +5 -0
- package/src/user-preferences.ts +178 -0
- package/src/utils/__tests__/platform-capabilities.test.ts +265 -0
- package/src/utils/__tests__/platform-exec.test.ts +278 -0
- package/src/utils/__tests__/platform-shell.test.ts +353 -0
- package/src/utils/__tests__/platform.test.ts +170 -0
- package/src/utils/platform.ts +431 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 聊天历史管理模块测试
|
|
3
|
+
* 测试聊天历史的读写、轮数限制等功能
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
7
|
+
|
|
8
|
+
// Mock fs 模块
|
|
9
|
+
vi.mock('fs', () => ({
|
|
10
|
+
default: {
|
|
11
|
+
existsSync: vi.fn(),
|
|
12
|
+
readFileSync: vi.fn(),
|
|
13
|
+
writeFileSync: vi.fn(),
|
|
14
|
+
mkdirSync: vi.fn(),
|
|
15
|
+
},
|
|
16
|
+
}))
|
|
17
|
+
|
|
18
|
+
// Mock os 模块
|
|
19
|
+
vi.mock('os', () => ({
|
|
20
|
+
default: {
|
|
21
|
+
homedir: vi.fn(() => '/home/testuser'),
|
|
22
|
+
},
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
// Mock config 模块
|
|
26
|
+
vi.mock('../config.js', () => ({
|
|
27
|
+
getConfig: vi.fn(() => ({
|
|
28
|
+
chatHistoryLimit: 10,
|
|
29
|
+
})),
|
|
30
|
+
}))
|
|
31
|
+
|
|
32
|
+
// Mock theme 模块
|
|
33
|
+
vi.mock('../ui/theme.js', () => ({
|
|
34
|
+
getCurrentTheme: vi.fn(() => ({
|
|
35
|
+
primary: '#007acc',
|
|
36
|
+
})),
|
|
37
|
+
}))
|
|
38
|
+
|
|
39
|
+
// Mock chalk
|
|
40
|
+
vi.mock('chalk', () => ({
|
|
41
|
+
default: {
|
|
42
|
+
bold: vi.fn((s: string) => s),
|
|
43
|
+
gray: vi.fn((s: string) => s),
|
|
44
|
+
hex: vi.fn(() => (s: string) => s),
|
|
45
|
+
},
|
|
46
|
+
}))
|
|
47
|
+
|
|
48
|
+
import fs from 'fs'
|
|
49
|
+
import os from 'os'
|
|
50
|
+
import { getConfig } from '../config.js'
|
|
51
|
+
|
|
52
|
+
// 获取 mock 函数引用
|
|
53
|
+
const mockFs = vi.mocked(fs)
|
|
54
|
+
const mockOs = vi.mocked(os)
|
|
55
|
+
const mockGetConfig = vi.mocked(getConfig)
|
|
56
|
+
|
|
57
|
+
// 模块状态重置辅助
|
|
58
|
+
async function resetChatHistoryModule() {
|
|
59
|
+
vi.resetModules()
|
|
60
|
+
return await import('../chat-history.js')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
vi.clearAllMocks()
|
|
65
|
+
mockOs.homedir.mockReturnValue('/home/testuser')
|
|
66
|
+
mockGetConfig.mockReturnValue({
|
|
67
|
+
chatHistoryLimit: 10,
|
|
68
|
+
} as any)
|
|
69
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
70
|
+
mockFs.writeFileSync.mockImplementation(() => {})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
vi.restoreAllMocks()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// getChatHistory 测试
|
|
79
|
+
// ============================================================================
|
|
80
|
+
|
|
81
|
+
describe('getChatHistory', () => {
|
|
82
|
+
it('应该返回聊天历史数组', async () => {
|
|
83
|
+
const mockHistory = [
|
|
84
|
+
{ role: 'user', content: '你好' },
|
|
85
|
+
{ role: 'assistant', content: '你好!有什么可以帮你的?' },
|
|
86
|
+
]
|
|
87
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
88
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(mockHistory))
|
|
89
|
+
|
|
90
|
+
const { getChatHistory } = await resetChatHistoryModule()
|
|
91
|
+
const history = getChatHistory()
|
|
92
|
+
|
|
93
|
+
expect(history).toHaveLength(2)
|
|
94
|
+
expect(history[0].role).toBe('user')
|
|
95
|
+
expect(history[1].role).toBe('assistant')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('文件不存在时应该返回空数组', async () => {
|
|
99
|
+
mockFs.existsSync.mockImplementation((path: any) => {
|
|
100
|
+
if (path.includes('chat_history.json')) return false
|
|
101
|
+
return true // 目录存在
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const { getChatHistory } = await resetChatHistoryModule()
|
|
105
|
+
const history = getChatHistory()
|
|
106
|
+
|
|
107
|
+
expect(history).toEqual([])
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('JSON 损坏时应该返回空数组', async () => {
|
|
111
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
112
|
+
mockFs.readFileSync.mockReturnValue('{invalid json')
|
|
113
|
+
|
|
114
|
+
const { getChatHistory } = await resetChatHistoryModule()
|
|
115
|
+
const history = getChatHistory()
|
|
116
|
+
|
|
117
|
+
expect(history).toEqual([])
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('应该创建配置目录(如果不存在)', async () => {
|
|
121
|
+
mockFs.existsSync.mockReturnValue(false)
|
|
122
|
+
|
|
123
|
+
const { getChatHistory } = await resetChatHistoryModule()
|
|
124
|
+
getChatHistory()
|
|
125
|
+
|
|
126
|
+
expect(mockFs.mkdirSync).toHaveBeenCalledWith(
|
|
127
|
+
expect.stringContaining('.please'),
|
|
128
|
+
{ recursive: true }
|
|
129
|
+
)
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// addChatMessage 测试
|
|
135
|
+
// ============================================================================
|
|
136
|
+
|
|
137
|
+
describe('addChatMessage', () => {
|
|
138
|
+
it('应该添加用户消息和助手消息', async () => {
|
|
139
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
140
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify([]))
|
|
141
|
+
|
|
142
|
+
let writtenContent: string = ''
|
|
143
|
+
mockFs.writeFileSync.mockImplementation((path: any, content: any) => {
|
|
144
|
+
writtenContent = content
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const { addChatMessage } = await resetChatHistoryModule()
|
|
148
|
+
addChatMessage('你好', '你好!有什么可以帮你的?')
|
|
149
|
+
|
|
150
|
+
const saved = JSON.parse(writtenContent)
|
|
151
|
+
expect(saved).toHaveLength(2)
|
|
152
|
+
expect(saved[0].role).toBe('user')
|
|
153
|
+
expect(saved[0].content).toBe('你好')
|
|
154
|
+
expect(saved[1].role).toBe('assistant')
|
|
155
|
+
expect(saved[1].content).toBe('你好!有什么可以帮你的?')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('应该追加到现有历史', async () => {
|
|
159
|
+
const existingHistory = [
|
|
160
|
+
{ role: 'user', content: '第一条' },
|
|
161
|
+
{ role: 'assistant', content: '回复第一条' },
|
|
162
|
+
]
|
|
163
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
164
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(existingHistory))
|
|
165
|
+
|
|
166
|
+
let writtenContent: string = ''
|
|
167
|
+
mockFs.writeFileSync.mockImplementation((path: any, content: any) => {
|
|
168
|
+
writtenContent = content
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
const { addChatMessage } = await resetChatHistoryModule()
|
|
172
|
+
addChatMessage('第二条', '回复第二条')
|
|
173
|
+
|
|
174
|
+
const saved = JSON.parse(writtenContent)
|
|
175
|
+
expect(saved).toHaveLength(4)
|
|
176
|
+
expect(saved[2].content).toBe('第二条')
|
|
177
|
+
expect(saved[3].content).toBe('回复第二条')
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('应该限制历史轮数(chatHistoryLimit)', async () => {
|
|
181
|
+
mockGetConfig.mockReturnValue({ chatHistoryLimit: 2 } as any)
|
|
182
|
+
|
|
183
|
+
// 已有 2 轮(4条消息)
|
|
184
|
+
const existingHistory = [
|
|
185
|
+
{ role: 'user', content: '1' },
|
|
186
|
+
{ role: 'assistant', content: 'r1' },
|
|
187
|
+
{ role: 'user', content: '2' },
|
|
188
|
+
{ role: 'assistant', content: 'r2' },
|
|
189
|
+
]
|
|
190
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
191
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(existingHistory))
|
|
192
|
+
|
|
193
|
+
let writtenContent: string = ''
|
|
194
|
+
mockFs.writeFileSync.mockImplementation((path: any, content: any) => {
|
|
195
|
+
writtenContent = content
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
const { addChatMessage } = await resetChatHistoryModule()
|
|
199
|
+
addChatMessage('3', 'r3')
|
|
200
|
+
|
|
201
|
+
const saved = JSON.parse(writtenContent)
|
|
202
|
+
// 应该保留最近 2 轮 = 4 条消息
|
|
203
|
+
expect(saved).toHaveLength(4)
|
|
204
|
+
expect(saved[0].content).toBe('2') // 最早的 1 轮被删除
|
|
205
|
+
expect(saved[2].content).toBe('3')
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('默认 chatHistoryLimit 为 10', async () => {
|
|
209
|
+
mockGetConfig.mockReturnValue({} as any)
|
|
210
|
+
|
|
211
|
+
const existingHistory: any[] = []
|
|
212
|
+
// 添加 11 轮
|
|
213
|
+
for (let i = 0; i < 11; i++) {
|
|
214
|
+
existingHistory.push({ role: 'user', content: `u${i}` })
|
|
215
|
+
existingHistory.push({ role: 'assistant', content: `a${i}` })
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
219
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(existingHistory))
|
|
220
|
+
|
|
221
|
+
let writtenContent: string = ''
|
|
222
|
+
mockFs.writeFileSync.mockImplementation((path: any, content: any) => {
|
|
223
|
+
writtenContent = content
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
const { addChatMessage } = await resetChatHistoryModule()
|
|
227
|
+
addChatMessage('新消息', '新回复')
|
|
228
|
+
|
|
229
|
+
const saved = JSON.parse(writtenContent)
|
|
230
|
+
// 应该保留最近 10 轮 = 20 条消息
|
|
231
|
+
expect(saved).toHaveLength(20)
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// ============================================================================
|
|
236
|
+
// clearChatHistory 测试
|
|
237
|
+
// ============================================================================
|
|
238
|
+
|
|
239
|
+
describe('clearChatHistory', () => {
|
|
240
|
+
it('应该清空聊天历史', async () => {
|
|
241
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
242
|
+
|
|
243
|
+
let writtenContent: string = ''
|
|
244
|
+
mockFs.writeFileSync.mockImplementation((path: any, content: any) => {
|
|
245
|
+
writtenContent = content
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const { clearChatHistory } = await resetChatHistoryModule()
|
|
249
|
+
clearChatHistory()
|
|
250
|
+
|
|
251
|
+
const saved = JSON.parse(writtenContent)
|
|
252
|
+
expect(saved).toEqual([])
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
// ============================================================================
|
|
257
|
+
// getChatHistoryFilePath 测试
|
|
258
|
+
// ============================================================================
|
|
259
|
+
|
|
260
|
+
describe('getChatHistoryFilePath', () => {
|
|
261
|
+
it('应该返回正确的历史文件路径', async () => {
|
|
262
|
+
mockOs.homedir.mockReturnValue('/home/testuser')
|
|
263
|
+
|
|
264
|
+
const { getChatHistoryFilePath } = await resetChatHistoryModule()
|
|
265
|
+
const filePath = getChatHistoryFilePath()
|
|
266
|
+
|
|
267
|
+
expect(filePath).toContain('.please')
|
|
268
|
+
expect(filePath).toContain('chat_history.json')
|
|
269
|
+
})
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
// ============================================================================
|
|
273
|
+
// getChatRoundCount 测试
|
|
274
|
+
// ============================================================================
|
|
275
|
+
|
|
276
|
+
describe('getChatRoundCount', () => {
|
|
277
|
+
it('空历史应该返回 0', async () => {
|
|
278
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
279
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify([]))
|
|
280
|
+
|
|
281
|
+
const { getChatRoundCount } = await resetChatHistoryModule()
|
|
282
|
+
const count = getChatRoundCount()
|
|
283
|
+
|
|
284
|
+
expect(count).toBe(0)
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it('应该计算正确的轮数', async () => {
|
|
288
|
+
const history = [
|
|
289
|
+
{ role: 'user', content: '1' },
|
|
290
|
+
{ role: 'assistant', content: 'r1' },
|
|
291
|
+
{ role: 'user', content: '2' },
|
|
292
|
+
{ role: 'assistant', content: 'r2' },
|
|
293
|
+
{ role: 'user', content: '3' },
|
|
294
|
+
{ role: 'assistant', content: 'r3' },
|
|
295
|
+
]
|
|
296
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
297
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(history))
|
|
298
|
+
|
|
299
|
+
const { getChatRoundCount } = await resetChatHistoryModule()
|
|
300
|
+
const count = getChatRoundCount()
|
|
301
|
+
|
|
302
|
+
expect(count).toBe(3)
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('奇数消息应该向下取整', async () => {
|
|
306
|
+
const history = [
|
|
307
|
+
{ role: 'user', content: '1' },
|
|
308
|
+
{ role: 'assistant', content: 'r1' },
|
|
309
|
+
{ role: 'user', content: '2' },
|
|
310
|
+
// 缺少 assistant 回复
|
|
311
|
+
]
|
|
312
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
313
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(history))
|
|
314
|
+
|
|
315
|
+
const { getChatRoundCount } = await resetChatHistoryModule()
|
|
316
|
+
const count = getChatRoundCount()
|
|
317
|
+
|
|
318
|
+
expect(count).toBe(1) // 3 条消息 / 2 = 1(向下取整)
|
|
319
|
+
})
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
// ============================================================================
|
|
323
|
+
// displayChatHistory 测试
|
|
324
|
+
// ============================================================================
|
|
325
|
+
|
|
326
|
+
describe('displayChatHistory', () => {
|
|
327
|
+
let consoleLogSpy: ReturnType<typeof vi.spyOn>
|
|
328
|
+
|
|
329
|
+
beforeEach(() => {
|
|
330
|
+
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
afterEach(() => {
|
|
334
|
+
consoleLogSpy.mockRestore()
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
it('应该显示对话历史', async () => {
|
|
338
|
+
const history = [
|
|
339
|
+
{ role: 'user', content: '检查磁盘' },
|
|
340
|
+
{ role: 'assistant', content: '好的,我帮你检查' },
|
|
341
|
+
{ role: 'user', content: '查看进程' },
|
|
342
|
+
{ role: 'assistant', content: '好的,我帮你查看' },
|
|
343
|
+
]
|
|
344
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
345
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(history))
|
|
346
|
+
|
|
347
|
+
const { displayChatHistory } = await resetChatHistoryModule()
|
|
348
|
+
displayChatHistory()
|
|
349
|
+
|
|
350
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
351
|
+
const allCalls = consoleLogSpy.mock.calls.map(call => call[0]).join('\n')
|
|
352
|
+
expect(allCalls).toContain('对话历史')
|
|
353
|
+
expect(allCalls).toContain('检查磁盘')
|
|
354
|
+
expect(allCalls).toContain('查看进程')
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
it('空历史时应该显示提示信息', async () => {
|
|
358
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
359
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify([]))
|
|
360
|
+
|
|
361
|
+
const { displayChatHistory } = await resetChatHistoryModule()
|
|
362
|
+
displayChatHistory()
|
|
363
|
+
|
|
364
|
+
const allCalls = consoleLogSpy.mock.calls.map(call => call[0]).join('\n')
|
|
365
|
+
expect(allCalls).toContain('暂无对话历史')
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
it('应该只显示用户消息(不显示助手消息)', async () => {
|
|
369
|
+
const history = [
|
|
370
|
+
{ role: 'user', content: '用户问题1' },
|
|
371
|
+
{ role: 'assistant', content: 'AI回答1' },
|
|
372
|
+
{ role: 'user', content: '用户问题2' },
|
|
373
|
+
{ role: 'assistant', content: 'AI回答2' },
|
|
374
|
+
]
|
|
375
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
376
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(history))
|
|
377
|
+
|
|
378
|
+
const { displayChatHistory } = await resetChatHistoryModule()
|
|
379
|
+
displayChatHistory()
|
|
380
|
+
|
|
381
|
+
const allCalls = consoleLogSpy.mock.calls.map(call => call[0]).join('\n')
|
|
382
|
+
expect(allCalls).toContain('用户问题1')
|
|
383
|
+
expect(allCalls).toContain('用户问题2')
|
|
384
|
+
// 助手消息不应该显示
|
|
385
|
+
expect(allCalls).not.toContain('AI回答1')
|
|
386
|
+
expect(allCalls).not.toContain('AI回答2')
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('应该显示配置信息', async () => {
|
|
390
|
+
mockGetConfig.mockReturnValue({ chatHistoryLimit: 15 } as any)
|
|
391
|
+
const history = [
|
|
392
|
+
{ role: 'user', content: '测试' },
|
|
393
|
+
{ role: 'assistant', content: '回复' },
|
|
394
|
+
]
|
|
395
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
396
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(history))
|
|
397
|
+
|
|
398
|
+
const { displayChatHistory } = await resetChatHistoryModule()
|
|
399
|
+
displayChatHistory()
|
|
400
|
+
|
|
401
|
+
const allCalls = consoleLogSpy.mock.calls.map(call => call[0]).join('\n')
|
|
402
|
+
expect(allCalls).toContain('配置')
|
|
403
|
+
expect(allCalls).toContain('15')
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
it('应该显示文件路径', async () => {
|
|
407
|
+
const history = [
|
|
408
|
+
{ role: 'user', content: '测试' },
|
|
409
|
+
{ role: 'assistant', content: '回复' },
|
|
410
|
+
]
|
|
411
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
412
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(history))
|
|
413
|
+
|
|
414
|
+
const { displayChatHistory } = await resetChatHistoryModule()
|
|
415
|
+
displayChatHistory()
|
|
416
|
+
|
|
417
|
+
const allCalls = consoleLogSpy.mock.calls.map(call => call[0]).join('\n')
|
|
418
|
+
expect(allCalls).toContain('文件')
|
|
419
|
+
expect(allCalls).toContain('chat_history.json')
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
it('应该显示正确的轮数', async () => {
|
|
423
|
+
const history = [
|
|
424
|
+
{ role: 'user', content: '问题1' },
|
|
425
|
+
{ role: 'assistant', content: '回答1' },
|
|
426
|
+
{ role: 'user', content: '问题2' },
|
|
427
|
+
{ role: 'assistant', content: '回答2' },
|
|
428
|
+
{ role: 'user', content: '问题3' },
|
|
429
|
+
{ role: 'assistant', content: '回答3' },
|
|
430
|
+
]
|
|
431
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
432
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(history))
|
|
433
|
+
|
|
434
|
+
const { displayChatHistory } = await resetChatHistoryModule()
|
|
435
|
+
displayChatHistory()
|
|
436
|
+
|
|
437
|
+
const allCalls = consoleLogSpy.mock.calls.map(call => call[0]).join('\n')
|
|
438
|
+
// 应该显示 "最近 3 轮"(3个用户消息)
|
|
439
|
+
expect(allCalls).toContain('3')
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
it('应该带编号显示消息', async () => {
|
|
443
|
+
const history = [
|
|
444
|
+
{ role: 'user', content: '第一条问题' },
|
|
445
|
+
{ role: 'assistant', content: '第一条回答' },
|
|
446
|
+
{ role: 'user', content: '第二条问题' },
|
|
447
|
+
{ role: 'assistant', content: '第二条回答' },
|
|
448
|
+
]
|
|
449
|
+
mockFs.existsSync.mockReturnValue(true)
|
|
450
|
+
mockFs.readFileSync.mockReturnValue(JSON.stringify(history))
|
|
451
|
+
|
|
452
|
+
const { displayChatHistory } = await resetChatHistoryModule()
|
|
453
|
+
displayChatHistory()
|
|
454
|
+
|
|
455
|
+
const allCalls = consoleLogSpy.mock.calls.map(call => call[0]).join('\n')
|
|
456
|
+
// 验证编号存在(1. 和 2.)
|
|
457
|
+
expect(allCalls).toContain('1')
|
|
458
|
+
expect(allCalls).toContain('2')
|
|
459
|
+
expect(allCalls).toContain('第一条问题')
|
|
460
|
+
expect(allCalls).toContain('第二条问题')
|
|
461
|
+
})
|
|
462
|
+
})
|