@zhin.js/core 1.1.0 → 1.1.2
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/lib/adapter.d.ts +1 -26
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +20 -117
- package/lib/adapter.js.map +1 -1
- package/lib/built/adapter-process.d.ts +0 -4
- package/lib/built/adapter-process.d.ts.map +1 -1
- package/lib/built/adapter-process.js +0 -95
- package/lib/built/adapter-process.js.map +1 -1
- package/lib/built/agent-preset.d.ts +2 -0
- package/lib/built/agent-preset.d.ts.map +1 -1
- package/lib/built/agent-preset.js +4 -0
- package/lib/built/agent-preset.js.map +1 -1
- package/lib/built/command.d.ts +4 -0
- package/lib/built/command.d.ts.map +1 -1
- package/lib/built/command.js +6 -0
- package/lib/built/command.js.map +1 -1
- package/lib/built/component.d.ts.map +1 -1
- package/lib/built/component.js +1 -0
- package/lib/built/component.js.map +1 -1
- package/lib/built/dispatcher.d.ts.map +1 -1
- package/lib/built/dispatcher.js +0 -13
- package/lib/built/dispatcher.js.map +1 -1
- package/lib/built/message-filter.d.ts +2 -0
- package/lib/built/message-filter.d.ts.map +1 -1
- package/lib/built/message-filter.js +5 -0
- package/lib/built/message-filter.js.map +1 -1
- package/lib/built/skill.d.ts +11 -0
- package/lib/built/skill.d.ts.map +1 -1
- package/lib/built/skill.js +14 -0
- package/lib/built/skill.js.map +1 -1
- package/lib/built/tool.d.ts +11 -44
- package/lib/built/tool.d.ts.map +1 -1
- package/lib/built/tool.js +14 -353
- package/lib/built/tool.js.map +1 -1
- package/lib/plugin.d.ts +1 -25
- package/lib/plugin.d.ts.map +1 -1
- package/lib/plugin.js +1 -77
- package/lib/plugin.js.map +1 -1
- package/lib/types.d.ts +0 -25
- package/lib/types.d.ts.map +1 -1
- package/package.json +10 -7
- package/CHANGELOG.md +0 -561
- package/REFACTORING_COMPLETE.md +0 -178
- package/REFACTORING_STATUS.md +0 -263
- package/src/adapter.ts +0 -275
- package/src/ai/index.ts +0 -55
- package/src/ai/providers/anthropic.ts +0 -379
- package/src/ai/providers/base.ts +0 -175
- package/src/ai/providers/index.ts +0 -13
- package/src/ai/providers/ollama.ts +0 -302
- package/src/ai/providers/openai.ts +0 -174
- package/src/ai/types.ts +0 -348
- package/src/bot.ts +0 -37
- package/src/built/adapter-process.ts +0 -177
- package/src/built/agent-preset.ts +0 -136
- package/src/built/ai-trigger.ts +0 -259
- package/src/built/command.ts +0 -108
- package/src/built/common-adapter-tools.ts +0 -242
- package/src/built/component.ts +0 -130
- package/src/built/config.ts +0 -335
- package/src/built/cron.ts +0 -156
- package/src/built/database.ts +0 -134
- package/src/built/dispatcher.ts +0 -496
- package/src/built/login-assist.ts +0 -131
- package/src/built/message-filter.ts +0 -390
- package/src/built/permission.ts +0 -151
- package/src/built/schema-feature.ts +0 -190
- package/src/built/skill.ts +0 -221
- package/src/built/tool.ts +0 -948
- package/src/command.ts +0 -87
- package/src/component.ts +0 -565
- package/src/cron.ts +0 -4
- package/src/errors.ts +0 -46
- package/src/feature.ts +0 -7
- package/src/index.ts +0 -53
- package/src/jsx-dev-runtime.ts +0 -2
- package/src/jsx-runtime.ts +0 -12
- package/src/jsx.ts +0 -135
- package/src/message.ts +0 -48
- package/src/models/system-log.ts +0 -20
- package/src/models/user.ts +0 -15
- package/src/notice.ts +0 -98
- package/src/plugin.ts +0 -896
- package/src/prompt.ts +0 -293
- package/src/request.ts +0 -95
- package/src/scheduler/index.ts +0 -19
- package/src/scheduler/scheduler.ts +0 -372
- package/src/scheduler/types.ts +0 -74
- package/src/tool-zod.ts +0 -115
- package/src/types-generator.ts +0 -78
- package/src/types.ts +0 -505
- package/src/utils.ts +0 -227
- package/tests/adapter.test.ts +0 -638
- package/tests/ai/ai-trigger.test.ts +0 -368
- package/tests/ai/providers.integration.test.ts +0 -227
- package/tests/ai/setup.ts +0 -308
- package/tests/ai/tool.test.ts +0 -800
- package/tests/bot.test.ts +0 -151
- package/tests/command.test.ts +0 -737
- package/tests/component-new.test.ts +0 -361
- package/tests/config.test.ts +0 -372
- package/tests/cron.test.ts +0 -82
- package/tests/dispatcher.test.ts +0 -293
- package/tests/errors.test.ts +0 -21
- package/tests/expression-evaluation.test.ts +0 -258
- package/tests/features-builtin.test.ts +0 -191
- package/tests/jsx-runtime.test.ts +0 -45
- package/tests/jsx.test.ts +0 -319
- package/tests/message-filter.test.ts +0 -566
- package/tests/message.test.ts +0 -402
- package/tests/notice.test.ts +0 -198
- package/tests/plugin.test.ts +0 -779
- package/tests/prompt.test.ts +0 -78
- package/tests/redos-protection.test.ts +0 -198
- package/tests/request.test.ts +0 -221
- package/tests/schema.test.ts +0 -248
- package/tests/skill-feature.test.ts +0 -179
- package/tests/test-utils.ts +0 -59
- package/tests/tool-feature.test.ts +0 -254
- package/tests/types.test.ts +0 -162
- package/tests/utils.test.ts +0 -135
- package/tsconfig.json +0 -24
package/tests/command.test.ts
DELETED
|
@@ -1,737 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
-
import { MessageCommand } from '../src/command'
|
|
3
|
-
import { Message } from '../src/message'
|
|
4
|
-
import { Plugin } from '../src/plugin'
|
|
5
|
-
import { App } from '../src/app'
|
|
6
|
-
|
|
7
|
-
// Mock segment-matcher
|
|
8
|
-
vi.mock('segment-matcher', () => {
|
|
9
|
-
class MatchResult {
|
|
10
|
-
matched: any[] = []
|
|
11
|
-
params: Record<string, any> = {}
|
|
12
|
-
remaining: any[] = []
|
|
13
|
-
|
|
14
|
-
addMatched(segment: any) {
|
|
15
|
-
this.matched.push(segment)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
addParam(name: string, value: any) {
|
|
19
|
-
this.params[name] = value
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
addRemaining(segment: any) {
|
|
23
|
-
this.remaining.push(segment)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
class SegmentMatcher {
|
|
28
|
-
constructor(public pattern: string) {}
|
|
29
|
-
|
|
30
|
-
match(content: any) {
|
|
31
|
-
// 简单的mock实现
|
|
32
|
-
if (Array.isArray(content) && content.length > 0) {
|
|
33
|
-
const text = content[0]?.data?.text || ''
|
|
34
|
-
if (text.includes(this.pattern)) {
|
|
35
|
-
const result = new MatchResult()
|
|
36
|
-
result.addMatched(content[0])
|
|
37
|
-
const value = text.replace(this.pattern, '').trim()
|
|
38
|
-
if (value) {
|
|
39
|
-
result.addParam('text', value)
|
|
40
|
-
}
|
|
41
|
-
return result
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return null
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return { SegmentMatcher, MatchResult }
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
// Mock App with permissions
|
|
52
|
-
const mockPermissionService = {
|
|
53
|
-
check: vi.fn(async (perm: string, message: any) => {
|
|
54
|
-
if (perm === 'adapter(discord)') {
|
|
55
|
-
return message.$adapter === 'discord'
|
|
56
|
-
}
|
|
57
|
-
if (perm === 'adapter(telegram)') {
|
|
58
|
-
return message.$adapter === 'telegram'
|
|
59
|
-
}
|
|
60
|
-
if (perm === 'adapter(email)') {
|
|
61
|
-
return message.$adapter === 'email'
|
|
62
|
-
}
|
|
63
|
-
if (perm === 'adapter(test)') {
|
|
64
|
-
return message.$adapter === 'test'
|
|
65
|
-
}
|
|
66
|
-
return true
|
|
67
|
-
})
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const mockApp = {
|
|
71
|
-
contextIsReady: vi.fn((name: string) => name === 'permission'),
|
|
72
|
-
inject: vi.fn((name: string) => {
|
|
73
|
-
if (name === 'permission') return mockPermissionService
|
|
74
|
-
return null
|
|
75
|
-
})
|
|
76
|
-
} as any
|
|
77
|
-
|
|
78
|
-
// 为多个权限测试创建特殊的 mock app
|
|
79
|
-
const multiPermitPermissionService = {
|
|
80
|
-
check: vi.fn(async (perm: string, message: any) => {
|
|
81
|
-
// 对于多个权限,只要有一个匹配就返回 true
|
|
82
|
-
if (perm === 'adapter(discord)' && message.$adapter === 'discord') {
|
|
83
|
-
return true
|
|
84
|
-
}
|
|
85
|
-
if (perm === 'adapter(telegram)' && message.$adapter === 'telegram') {
|
|
86
|
-
return true
|
|
87
|
-
}
|
|
88
|
-
if (perm === 'adapter(email)' && message.$adapter === 'email') {
|
|
89
|
-
return true
|
|
90
|
-
}
|
|
91
|
-
if (perm === 'adapter(test)' && message.$adapter === 'test') {
|
|
92
|
-
return true
|
|
93
|
-
}
|
|
94
|
-
return false
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const multiPermitMockApp = {
|
|
99
|
-
contextIsReady: vi.fn((name: string) => name === 'permission'),
|
|
100
|
-
inject: vi.fn((name: string) => {
|
|
101
|
-
if (name === 'permission') return multiPermitPermissionService
|
|
102
|
-
return null
|
|
103
|
-
})
|
|
104
|
-
} as any
|
|
105
|
-
|
|
106
|
-
describe('Command系统测试', () => {
|
|
107
|
-
describe('MessageCommand基础功能测试', () => {
|
|
108
|
-
it('应该正确创建MessageCommand实例', () => {
|
|
109
|
-
const command = new MessageCommand('hello')
|
|
110
|
-
expect(command).toBeInstanceOf(MessageCommand)
|
|
111
|
-
// 注意:pattern 属性可能不是公开的,这里只测试实例创建
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it('应该支持链式调用', () => {
|
|
115
|
-
const command = new MessageCommand('test')
|
|
116
|
-
.permit('adapter(discord)')
|
|
117
|
-
.action(() => 'response')
|
|
118
|
-
|
|
119
|
-
expect(command).toBeInstanceOf(MessageCommand)
|
|
120
|
-
expect(typeof command.permit).toBe('function')
|
|
121
|
-
expect(typeof command.action).toBe('function')
|
|
122
|
-
})
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
describe('权限(permit)测试', () => {
|
|
126
|
-
it('应该正确设置单个权限', async () => {
|
|
127
|
-
const command = new MessageCommand('hello')
|
|
128
|
-
.permit('adapter(discord)')
|
|
129
|
-
.action(() => 'Hello from Discord!')
|
|
130
|
-
|
|
131
|
-
// 匹配的适配器
|
|
132
|
-
const discordMessage: Message = {
|
|
133
|
-
$id: '1',
|
|
134
|
-
$adapter: 'discord',
|
|
135
|
-
$bot: 'discord-bot',
|
|
136
|
-
$content: [{ type: 'text', data: { text: 'hello world' } }],
|
|
137
|
-
$sender: { id: 'user1', name: 'User' },
|
|
138
|
-
$reply: vi.fn(),
|
|
139
|
-
$channel: { id: 'channel1', type: 'channel' },
|
|
140
|
-
$timestamp: Date.now(),
|
|
141
|
-
$raw: 'hello world'
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 不匹配的适配器
|
|
145
|
-
const telegramMessage: Message = {
|
|
146
|
-
$id: '2',
|
|
147
|
-
$adapter: 'telegram',
|
|
148
|
-
$bot: 'telegram-bot',
|
|
149
|
-
$content: [{ type: 'text', data: { text: 'hello world' } }],
|
|
150
|
-
$sender: { id: 'user2', name: 'User' },
|
|
151
|
-
$reply: vi.fn(),
|
|
152
|
-
$channel: { id: 'channel2', type: 'private' },
|
|
153
|
-
$timestamp: Date.now(),
|
|
154
|
-
$raw: 'hello world'
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const discordResult = await command.handle(discordMessage, mockApp)
|
|
158
|
-
const telegramResult = await command.handle(telegramMessage, mockApp)
|
|
159
|
-
|
|
160
|
-
expect(discordResult).toBe('Hello from Discord!')
|
|
161
|
-
expect(telegramResult).toBeUndefined()
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('应该正确设置多个权限', async () => {
|
|
165
|
-
const command = new MessageCommand('hello')
|
|
166
|
-
.permit('adapter(discord)')
|
|
167
|
-
.action(() => 'Hello!')
|
|
168
|
-
|
|
169
|
-
const discordMessage: Message = {
|
|
170
|
-
$id: '1',
|
|
171
|
-
$adapter: 'discord',
|
|
172
|
-
$bot: 'discord-bot',
|
|
173
|
-
$content: [{ type: 'text', data: { text: 'hello' } }],
|
|
174
|
-
$sender: { id: 'user1', name: 'User' },
|
|
175
|
-
$reply: vi.fn(),
|
|
176
|
-
$channel: { id: 'channel1', type: 'channel' },
|
|
177
|
-
$timestamp: Date.now(),
|
|
178
|
-
$raw: 'hello'
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const telegramMessage: Message = {
|
|
182
|
-
$id: '2',
|
|
183
|
-
$adapter: 'telegram',
|
|
184
|
-
$bot: 'telegram-bot',
|
|
185
|
-
$content: [{ type: 'text', data: { text: 'hello' } }],
|
|
186
|
-
$sender: { id: 'user2', name: 'User' },
|
|
187
|
-
$reply: vi.fn(),
|
|
188
|
-
$channel: { id: 'channel2', type: 'private' },
|
|
189
|
-
$timestamp: Date.now(),
|
|
190
|
-
$raw: 'hello'
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const emailMessage: Message = {
|
|
194
|
-
$id: '3',
|
|
195
|
-
$adapter: 'email',
|
|
196
|
-
$bot: 'email-bot',
|
|
197
|
-
$content: [{ type: 'text', data: { text: 'hello' } }],
|
|
198
|
-
$sender: { id: 'user3', name: 'User' },
|
|
199
|
-
$reply: vi.fn(),
|
|
200
|
-
$channel: { id: 'channel3', type: 'private' },
|
|
201
|
-
$timestamp: Date.now(),
|
|
202
|
-
$raw: 'hello'
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const discordResult = await command.handle(discordMessage, mockApp)
|
|
206
|
-
const telegramResult = await command.handle(telegramMessage, mockApp)
|
|
207
|
-
const emailResult = await command.handle(emailMessage, mockApp)
|
|
208
|
-
|
|
209
|
-
expect(discordResult).toBe('Hello!')
|
|
210
|
-
expect(telegramResult).toBeUndefined()
|
|
211
|
-
expect(emailResult).toBeUndefined()
|
|
212
|
-
})
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
describe('动作(action)测试', () => {
|
|
216
|
-
it('应该正确执行单个动作', async () => {
|
|
217
|
-
const actionSpy = vi.fn().mockReturnValue('Action executed!')
|
|
218
|
-
|
|
219
|
-
const command = new MessageCommand('test')
|
|
220
|
-
.action(actionSpy)
|
|
221
|
-
|
|
222
|
-
const message: Message = {
|
|
223
|
-
$id: '1',
|
|
224
|
-
$adapter: 'test',
|
|
225
|
-
$bot: 'test-bot',
|
|
226
|
-
$content: [{ type: 'text', data: { text: 'test message' } }],
|
|
227
|
-
$sender: { id: 'user1', name: 'User' },
|
|
228
|
-
$reply: vi.fn(),
|
|
229
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
230
|
-
$timestamp: Date.now(),
|
|
231
|
-
$raw: 'test message'
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const result = await command.handle(message, mockApp)
|
|
235
|
-
|
|
236
|
-
expect(actionSpy).toHaveBeenCalledWith(message, expect.any(Object))
|
|
237
|
-
expect(result).toBe('Action executed!')
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
it('应该正确执行多个动作', async () => {
|
|
241
|
-
const action1 = vi.fn()
|
|
242
|
-
const action2 = vi.fn().mockReturnValue('Second action result')
|
|
243
|
-
const action3 = vi.fn().mockReturnValue('Third action result')
|
|
244
|
-
|
|
245
|
-
const command = new MessageCommand('test')
|
|
246
|
-
.action(action1)
|
|
247
|
-
.action(action2)
|
|
248
|
-
.action(action3)
|
|
249
|
-
|
|
250
|
-
const message: Message = {
|
|
251
|
-
$id: '1',
|
|
252
|
-
$adapter: 'test',
|
|
253
|
-
$bot: 'test-bot',
|
|
254
|
-
$content: [{ type: 'text', data: { text: 'test' } }],
|
|
255
|
-
$sender: { id: 'user1', name: 'User' },
|
|
256
|
-
$reply: vi.fn(),
|
|
257
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
258
|
-
$timestamp: Date.now(),
|
|
259
|
-
$raw: 'test'
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const result = await command.handle(message, mockApp)
|
|
263
|
-
|
|
264
|
-
expect(action1).toHaveBeenCalled()
|
|
265
|
-
expect(action2).toHaveBeenCalled()
|
|
266
|
-
expect(action3).not.toHaveBeenCalled() // 第二个动作有返回值,所以第三个不会执行
|
|
267
|
-
expect(result).toBe('Second action result')
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
it('应该正确处理异步动作', async () => {
|
|
271
|
-
const asyncAction = vi.fn().mockResolvedValue('Async result')
|
|
272
|
-
|
|
273
|
-
const command = new MessageCommand('async')
|
|
274
|
-
.action(asyncAction)
|
|
275
|
-
|
|
276
|
-
const message: Message = {
|
|
277
|
-
$id: '1',
|
|
278
|
-
$adapter: 'test',
|
|
279
|
-
$bot: 'test-bot',
|
|
280
|
-
$content: [{ type: 'text', data: { text: 'async test' } }],
|
|
281
|
-
$sender: { id: 'user1', name: 'User' },
|
|
282
|
-
$reply: vi.fn(),
|
|
283
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
284
|
-
$timestamp: Date.now(),
|
|
285
|
-
$raw: 'async test'
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const result = await command.handle(message, mockApp)
|
|
289
|
-
|
|
290
|
-
expect(asyncAction).toHaveBeenCalled()
|
|
291
|
-
expect(result).toBe('Async result')
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
it('应该正确传递匹配结果给动作', async () => {
|
|
295
|
-
const actionSpy = vi.fn().mockReturnValue('Got args!')
|
|
296
|
-
|
|
297
|
-
const command = new MessageCommand('echo')
|
|
298
|
-
.action(actionSpy)
|
|
299
|
-
|
|
300
|
-
const message: Message = {
|
|
301
|
-
$id: '1',
|
|
302
|
-
$adapter: 'test',
|
|
303
|
-
$bot: 'test-bot',
|
|
304
|
-
$content: [{ type: 'text', data: { text: 'echo hello world' } }],
|
|
305
|
-
$sender: { id: 'user1', name: 'User' },
|
|
306
|
-
$reply: vi.fn(),
|
|
307
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
308
|
-
$timestamp: Date.now(),
|
|
309
|
-
$raw: 'echo hello world'
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const result = await command.handle(message, mockApp)
|
|
313
|
-
|
|
314
|
-
expect(actionSpy).toHaveBeenCalledWith(
|
|
315
|
-
message,
|
|
316
|
-
expect.objectContaining({
|
|
317
|
-
matched: expect.any(Array),
|
|
318
|
-
params: expect.any(Object)
|
|
319
|
-
})
|
|
320
|
-
)
|
|
321
|
-
expect(result).toBe('Got args!')
|
|
322
|
-
})
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
describe('消息处理(handle)测试', () => {
|
|
326
|
-
it('应该在不匹配时返回undefined', async () => {
|
|
327
|
-
const command = new MessageCommand('hello')
|
|
328
|
-
.action(() => 'Hello!')
|
|
329
|
-
|
|
330
|
-
const message: Message = {
|
|
331
|
-
$id: '1',
|
|
332
|
-
$adapter: 'test',
|
|
333
|
-
$bot: 'test-bot',
|
|
334
|
-
$content: [{ type: 'text', data: { text: 'goodbye' } }],
|
|
335
|
-
$sender: { id: 'user1', name: 'User' },
|
|
336
|
-
$reply: vi.fn(),
|
|
337
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
338
|
-
$timestamp: Date.now(),
|
|
339
|
-
$raw: 'goodbye'
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
const result = await command.handle(message, mockApp)
|
|
343
|
-
expect(result).toBeUndefined()
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
it('应该正确处理空消息内容', async () => {
|
|
347
|
-
const command = new MessageCommand('test')
|
|
348
|
-
.action(() => 'Response')
|
|
349
|
-
|
|
350
|
-
const message: Message = {
|
|
351
|
-
$id: '1',
|
|
352
|
-
$adapter: 'test',
|
|
353
|
-
$bot: 'test-bot',
|
|
354
|
-
$content: [],
|
|
355
|
-
$sender: { id: 'user1', name: 'User' },
|
|
356
|
-
$reply: vi.fn(),
|
|
357
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
358
|
-
$timestamp: Date.now(),
|
|
359
|
-
$raw: ''
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const result = await command.handle(message, mockApp)
|
|
363
|
-
expect(result).toBeUndefined()
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
it('应该正确处理非文本消息', async () => {
|
|
367
|
-
const command = new MessageCommand('test')
|
|
368
|
-
.action(() => 'Response')
|
|
369
|
-
|
|
370
|
-
const message: Message = {
|
|
371
|
-
$id: '1',
|
|
372
|
-
$adapter: 'test',
|
|
373
|
-
$bot: 'test-bot',
|
|
374
|
-
$content: [
|
|
375
|
-
{ type: 'image', data: { url: 'https://example.com/image.png' } }
|
|
376
|
-
],
|
|
377
|
-
$sender: { id: 'user1', name: 'User' },
|
|
378
|
-
$reply: vi.fn(),
|
|
379
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
380
|
-
$timestamp: Date.now(),
|
|
381
|
-
$raw: '[图片]'
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const result = await command.handle(message, mockApp)
|
|
385
|
-
expect(result).toBeUndefined()
|
|
386
|
-
})
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
describe('复合条件测试', () => {
|
|
390
|
-
it('应该同时满足权限和匹配条件', async () => {
|
|
391
|
-
const command = new MessageCommand('admin')
|
|
392
|
-
.permit('adapter(discord)')
|
|
393
|
-
.action(() => 'Admin command executed')
|
|
394
|
-
|
|
395
|
-
// 正确的适配器和匹配的消息
|
|
396
|
-
const validMessage: Message = {
|
|
397
|
-
$id: '1',
|
|
398
|
-
$adapter: 'discord',
|
|
399
|
-
$bot: 'discord-bot',
|
|
400
|
-
$content: [{ type: 'text', data: { text: 'admin panel' } }],
|
|
401
|
-
$sender: { id: 'admin1', name: 'Admin' },
|
|
402
|
-
$reply: vi.fn(),
|
|
403
|
-
$channel: { id: 'admin-channel', type: 'private' },
|
|
404
|
-
$timestamp: Date.now(),
|
|
405
|
-
$raw: 'admin panel'
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// 错误的适配器但匹配的消息
|
|
409
|
-
const wrongAdapterMessage: Message = {
|
|
410
|
-
$id: '2',
|
|
411
|
-
$adapter: 'telegram',
|
|
412
|
-
$bot: 'telegram-bot',
|
|
413
|
-
$content: [{ type: 'text', data: { text: 'admin panel' } }],
|
|
414
|
-
$sender: { id: 'admin2', name: 'Admin' },
|
|
415
|
-
$reply: vi.fn(),
|
|
416
|
-
$channel: { id: 'admin-channel', type: 'private' },
|
|
417
|
-
$timestamp: Date.now(),
|
|
418
|
-
$raw: 'admin panel'
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// 正确的适配器但不匹配的消息
|
|
422
|
-
const nonMatchingMessage: Message = {
|
|
423
|
-
$id: '3',
|
|
424
|
-
$adapter: 'discord',
|
|
425
|
-
$bot: 'discord-bot',
|
|
426
|
-
$content: [{ type: 'text', data: { text: 'hello world' } }],
|
|
427
|
-
$sender: { id: 'user1', name: 'User' },
|
|
428
|
-
$reply: vi.fn(),
|
|
429
|
-
$channel: { id: 'general-channel', type: 'channel' },
|
|
430
|
-
$timestamp: Date.now(),
|
|
431
|
-
$raw: 'hello world'
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const validResult = await command.handle(validMessage, mockApp)
|
|
435
|
-
const wrongAdapterResult = await command.handle(wrongAdapterMessage, mockApp)
|
|
436
|
-
const nonMatchingResult = await command.handle(nonMatchingMessage, mockApp)
|
|
437
|
-
|
|
438
|
-
expect(validResult).toBe('Admin command executed')
|
|
439
|
-
expect(wrongAdapterResult).toBeUndefined()
|
|
440
|
-
expect(nonMatchingResult).toBeUndefined()
|
|
441
|
-
})
|
|
442
|
-
})
|
|
443
|
-
|
|
444
|
-
describe('错误处理测试', () => {
|
|
445
|
-
it('应该正确处理动作中的同步错误', async () => {
|
|
446
|
-
const errorAction = vi.fn().mockImplementation(() => {
|
|
447
|
-
throw new Error('Action failed')
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
const command = new MessageCommand('error')
|
|
451
|
-
.action(errorAction)
|
|
452
|
-
|
|
453
|
-
const message: Message = {
|
|
454
|
-
$id: '1',
|
|
455
|
-
$adapter: 'test',
|
|
456
|
-
$bot: 'test-bot',
|
|
457
|
-
$content: [{ type: 'text', data: { text: 'error test' } }],
|
|
458
|
-
$sender: { id: 'user1', name: 'User' },
|
|
459
|
-
$reply: vi.fn(),
|
|
460
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
461
|
-
$timestamp: Date.now(),
|
|
462
|
-
$raw: 'error test'
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
await expect(command.handle(message, mockApp)).rejects.toThrow('Action failed')
|
|
466
|
-
})
|
|
467
|
-
|
|
468
|
-
it('应该正确处理动作中的异步错误', async () => {
|
|
469
|
-
const asyncErrorAction = vi.fn().mockRejectedValue(new Error('Async action failed'))
|
|
470
|
-
|
|
471
|
-
const command = new MessageCommand('async-error')
|
|
472
|
-
.action(asyncErrorAction)
|
|
473
|
-
|
|
474
|
-
const message: Message = {
|
|
475
|
-
$id: '1',
|
|
476
|
-
$adapter: 'test',
|
|
477
|
-
$bot: 'test-bot',
|
|
478
|
-
$content: [{ type: 'text', data: { text: 'async-error test' } }],
|
|
479
|
-
$sender: { id: 'user1', name: 'User' },
|
|
480
|
-
$reply: vi.fn(),
|
|
481
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
482
|
-
$timestamp: Date.now(),
|
|
483
|
-
$raw: 'async-error test'
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
await expect(command.handle(message, mockApp)).rejects.toThrow('Async action failed')
|
|
487
|
-
})
|
|
488
|
-
})
|
|
489
|
-
|
|
490
|
-
describe('类型系统测试', () => {
|
|
491
|
-
it('应该支持泛型适配器类型约束', () => {
|
|
492
|
-
// 这主要是TypeScript编译时检查
|
|
493
|
-
const discordCommand = new MessageCommand('test')
|
|
494
|
-
.permit('adapter(discord)')
|
|
495
|
-
.action((message) => {
|
|
496
|
-
// message 应该有正确的类型
|
|
497
|
-
expect(message.$adapter).toBe('discord')
|
|
498
|
-
return 'Discord response'
|
|
499
|
-
})
|
|
500
|
-
|
|
501
|
-
expect(discordCommand).toBeInstanceOf(MessageCommand)
|
|
502
|
-
})
|
|
503
|
-
})
|
|
504
|
-
|
|
505
|
-
describe('性能测试', () => {
|
|
506
|
-
it('应该高效处理大量消息检查', async () => {
|
|
507
|
-
const action = vi.fn().mockReturnValue('Response')
|
|
508
|
-
const command = new MessageCommand('perf')
|
|
509
|
-
.permit('adapter(test)')
|
|
510
|
-
.action(action)
|
|
511
|
-
|
|
512
|
-
const messages: Message[] = Array.from({ length: 1000 }, (_, i) => ({
|
|
513
|
-
$id: `msg-${i}`,
|
|
514
|
-
$adapter: i % 2 === 0 ? 'test' : 'other',
|
|
515
|
-
$bot: 'test-bot',
|
|
516
|
-
$content: [{ type: 'text', data: { text: i % 3 === 0 ? 'perf test' : 'other' } }],
|
|
517
|
-
$sender: { id: `user${i}`, name: `User ${i}` },
|
|
518
|
-
$reply: vi.fn(),
|
|
519
|
-
$channel: { id: `channel${i}`, type: 'private' },
|
|
520
|
-
$timestamp: Date.now(),
|
|
521
|
-
$raw: i % 3 === 0 ? 'perf test' : 'other'
|
|
522
|
-
}))
|
|
523
|
-
|
|
524
|
-
const startTime = Date.now()
|
|
525
|
-
const results = await Promise.all(
|
|
526
|
-
messages.map(message => command.handle(message, mockApp))
|
|
527
|
-
)
|
|
528
|
-
const endTime = Date.now()
|
|
529
|
-
|
|
530
|
-
// 验证执行时间在合理范围内 (< 300ms for 1000 messages)
|
|
531
|
-
expect(endTime - startTime).toBeLessThan(300)
|
|
532
|
-
|
|
533
|
-
// 验证正确的消息被处理
|
|
534
|
-
const validResults = results.filter(r => r !== undefined)
|
|
535
|
-
expect(validResults.length).toBeGreaterThan(0)
|
|
536
|
-
expect(action).toHaveBeenCalled()
|
|
537
|
-
})
|
|
538
|
-
})
|
|
539
|
-
|
|
540
|
-
describe('复杂场景测试', () => {
|
|
541
|
-
it('应该支持命令参数解析', async () => {
|
|
542
|
-
const actionSpy = vi.fn((message, matchResult) => {
|
|
543
|
-
return `参数: ${Object.values(matchResult.params).join(', ')}`
|
|
544
|
-
})
|
|
545
|
-
|
|
546
|
-
const command = new MessageCommand('say')
|
|
547
|
-
.action(actionSpy)
|
|
548
|
-
|
|
549
|
-
const message: Message = {
|
|
550
|
-
$id: '1',
|
|
551
|
-
$adapter: 'test',
|
|
552
|
-
$bot: 'test-bot',
|
|
553
|
-
$content: [{ type: 'text', data: { text: 'say hello world from bot' } }],
|
|
554
|
-
$sender: { id: 'user1', name: 'User' },
|
|
555
|
-
$reply: vi.fn(),
|
|
556
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
557
|
-
$timestamp: Date.now(),
|
|
558
|
-
$raw: 'say hello world from bot'
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
const result = await command.handle(message, mockApp)
|
|
562
|
-
|
|
563
|
-
expect(actionSpy).toHaveBeenCalledWith(
|
|
564
|
-
message,
|
|
565
|
-
expect.objectContaining({
|
|
566
|
-
params: expect.any(Object)
|
|
567
|
-
})
|
|
568
|
-
)
|
|
569
|
-
expect(result).toBe('参数: hello world from bot')
|
|
570
|
-
})
|
|
571
|
-
|
|
572
|
-
it('应该支持条件链式处理', async () => {
|
|
573
|
-
const command = new MessageCommand('multi')
|
|
574
|
-
.permit('adapter(discord)')
|
|
575
|
-
.action((message, result) => {
|
|
576
|
-
if (message.$channel.type === 'private') {
|
|
577
|
-
return '私人消息响应'
|
|
578
|
-
}
|
|
579
|
-
return undefined
|
|
580
|
-
})
|
|
581
|
-
.action(() => {
|
|
582
|
-
return '群组消息响应'
|
|
583
|
-
})
|
|
584
|
-
|
|
585
|
-
const privateMessage: Message = {
|
|
586
|
-
$id: '1',
|
|
587
|
-
$adapter: 'discord',
|
|
588
|
-
$bot: 'discord-bot',
|
|
589
|
-
$content: [{ type: 'text', data: { text: 'multi test' } }],
|
|
590
|
-
$sender: { id: 'user1', name: 'User' },
|
|
591
|
-
$reply: vi.fn(),
|
|
592
|
-
$channel: { id: 'dm-channel', type: 'private' },
|
|
593
|
-
$timestamp: Date.now(),
|
|
594
|
-
$raw: 'multi test'
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
const groupMessage: Message = {
|
|
598
|
-
$id: '2',
|
|
599
|
-
$adapter: 'discord',
|
|
600
|
-
$bot: 'discord-bot',
|
|
601
|
-
$content: [{ type: 'text', data: { text: 'multi test' } }],
|
|
602
|
-
$sender: { id: 'user2', name: 'User' },
|
|
603
|
-
$reply: vi.fn(),
|
|
604
|
-
$channel: { id: 'group-channel', type: 'group' },
|
|
605
|
-
$timestamp: Date.now(),
|
|
606
|
-
$raw: 'multi test'
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
const privateResult = await command.handle(privateMessage, mockApp)
|
|
610
|
-
const groupResult = await command.handle(groupMessage, mockApp)
|
|
611
|
-
|
|
612
|
-
expect(privateResult).toBe('私人消息响应')
|
|
613
|
-
expect(groupResult).toBe('群组消息响应')
|
|
614
|
-
})
|
|
615
|
-
})
|
|
616
|
-
|
|
617
|
-
describe('帮助系统测试', () => {
|
|
618
|
-
it('应该正确设置和获取描述信息', () => {
|
|
619
|
-
const command = new MessageCommand('help')
|
|
620
|
-
.desc('这是命令描述', '可以有多行描述')
|
|
621
|
-
|
|
622
|
-
expect(command.helpInfo.desc).toEqual(['这是命令描述', '可以有多行描述'])
|
|
623
|
-
})
|
|
624
|
-
|
|
625
|
-
it('应该正确设置和获取用法信息', () => {
|
|
626
|
-
const command = new MessageCommand('help')
|
|
627
|
-
.usage('help', 'help <command>')
|
|
628
|
-
|
|
629
|
-
expect(command.helpInfo.usage).toEqual(['help', 'help <command>'])
|
|
630
|
-
})
|
|
631
|
-
|
|
632
|
-
it('应该正确设置和获取示例信息', () => {
|
|
633
|
-
const command = new MessageCommand('help')
|
|
634
|
-
.examples('help', 'help echo', 'help admin')
|
|
635
|
-
|
|
636
|
-
expect(command.helpInfo.examples).toEqual(['help', 'help echo', 'help admin'])
|
|
637
|
-
})
|
|
638
|
-
|
|
639
|
-
it('应该支持链式调用设置帮助信息', () => {
|
|
640
|
-
const command = new MessageCommand('test')
|
|
641
|
-
.desc('测试命令', '用于测试功能')
|
|
642
|
-
.usage('test', 'test <arg>')
|
|
643
|
-
.examples('test', 'test hello')
|
|
644
|
-
.action(() => 'Test response')
|
|
645
|
-
|
|
646
|
-
expect(command.helpInfo.pattern).toBe('test')
|
|
647
|
-
expect(command.helpInfo.desc).toEqual(['测试命令', '用于测试功能'])
|
|
648
|
-
expect(command.helpInfo.usage).toEqual(['test', 'test <arg>'])
|
|
649
|
-
expect(command.helpInfo.examples).toEqual(['test', 'test hello'])
|
|
650
|
-
})
|
|
651
|
-
|
|
652
|
-
it('应该正确生成帮助文本', () => {
|
|
653
|
-
const command = new MessageCommand('greet')
|
|
654
|
-
.desc('打招呼命令')
|
|
655
|
-
.usage('greet <name>')
|
|
656
|
-
.examples('greet Alice')
|
|
657
|
-
|
|
658
|
-
const help = command.help
|
|
659
|
-
|
|
660
|
-
expect(help).toContain('greet')
|
|
661
|
-
expect(help).toContain('打招呼命令')
|
|
662
|
-
expect(help).toContain('greet <name>')
|
|
663
|
-
expect(help).toContain('greet Alice')
|
|
664
|
-
})
|
|
665
|
-
|
|
666
|
-
it('应该处理没有帮助信息的情况', () => {
|
|
667
|
-
const command = new MessageCommand('simple')
|
|
668
|
-
|
|
669
|
-
expect(command.helpInfo.desc).toEqual([])
|
|
670
|
-
expect(command.helpInfo.usage).toEqual([])
|
|
671
|
-
expect(command.helpInfo.examples).toEqual([])
|
|
672
|
-
expect(command.help).toBe('simple')
|
|
673
|
-
})
|
|
674
|
-
|
|
675
|
-
it('应该正确返回 helpInfo 对象结构', () => {
|
|
676
|
-
const command = new MessageCommand('info')
|
|
677
|
-
.desc('信息命令')
|
|
678
|
-
.usage('info')
|
|
679
|
-
.examples('info')
|
|
680
|
-
|
|
681
|
-
const helpInfo = command.helpInfo
|
|
682
|
-
|
|
683
|
-
expect(helpInfo).toHaveProperty('pattern')
|
|
684
|
-
expect(helpInfo).toHaveProperty('desc')
|
|
685
|
-
expect(helpInfo).toHaveProperty('usage')
|
|
686
|
-
expect(helpInfo).toHaveProperty('examples')
|
|
687
|
-
expect(typeof helpInfo.pattern).toBe('string')
|
|
688
|
-
expect(Array.isArray(helpInfo.desc)).toBe(true)
|
|
689
|
-
expect(Array.isArray(helpInfo.usage)).toBe(true)
|
|
690
|
-
expect(Array.isArray(helpInfo.examples)).toBe(true)
|
|
691
|
-
})
|
|
692
|
-
})
|
|
693
|
-
|
|
694
|
-
describe('权限系统测试', () => {
|
|
695
|
-
it('应该正确处理权限检查失败', async () => {
|
|
696
|
-
const command = new MessageCommand('admin')
|
|
697
|
-
.permit('adapter(discord)')
|
|
698
|
-
.action(() => 'Admin command')
|
|
699
|
-
|
|
700
|
-
const message: Message = {
|
|
701
|
-
$id: '1',
|
|
702
|
-
$adapter: 'telegram', // 不匹配的适配器
|
|
703
|
-
$bot: 'telegram-bot',
|
|
704
|
-
$content: [{ type: 'text', data: { text: 'admin test' } }],
|
|
705
|
-
$sender: { id: 'user1', name: 'User' },
|
|
706
|
-
$reply: vi.fn(),
|
|
707
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
708
|
-
$timestamp: Date.now(),
|
|
709
|
-
$raw: 'admin test'
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
const result = await command.handle(message, mockApp)
|
|
713
|
-
expect(result).toBeUndefined()
|
|
714
|
-
})
|
|
715
|
-
|
|
716
|
-
it('应该正确处理权限检查通过', async () => {
|
|
717
|
-
const command = new MessageCommand('admin')
|
|
718
|
-
.permit('adapter(discord)')
|
|
719
|
-
.action(() => 'Admin command')
|
|
720
|
-
|
|
721
|
-
const message: Message = {
|
|
722
|
-
$id: '1',
|
|
723
|
-
$adapter: 'discord', // 匹配的适配器
|
|
724
|
-
$bot: 'discord-bot',
|
|
725
|
-
$content: [{ type: 'text', data: { text: 'admin test' } }],
|
|
726
|
-
$sender: { id: 'user1', name: 'User' },
|
|
727
|
-
$reply: vi.fn(),
|
|
728
|
-
$channel: { id: 'channel1', type: 'private' },
|
|
729
|
-
$timestamp: Date.now(),
|
|
730
|
-
$raw: 'admin test'
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
const result = await command.handle(message, mockApp)
|
|
734
|
-
expect(result).toBe('Admin command')
|
|
735
|
-
})
|
|
736
|
-
})
|
|
737
|
-
})
|