@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.
Files changed (122) hide show
  1. package/lib/adapter.d.ts +1 -26
  2. package/lib/adapter.d.ts.map +1 -1
  3. package/lib/adapter.js +20 -117
  4. package/lib/adapter.js.map +1 -1
  5. package/lib/built/adapter-process.d.ts +0 -4
  6. package/lib/built/adapter-process.d.ts.map +1 -1
  7. package/lib/built/adapter-process.js +0 -95
  8. package/lib/built/adapter-process.js.map +1 -1
  9. package/lib/built/agent-preset.d.ts +2 -0
  10. package/lib/built/agent-preset.d.ts.map +1 -1
  11. package/lib/built/agent-preset.js +4 -0
  12. package/lib/built/agent-preset.js.map +1 -1
  13. package/lib/built/command.d.ts +4 -0
  14. package/lib/built/command.d.ts.map +1 -1
  15. package/lib/built/command.js +6 -0
  16. package/lib/built/command.js.map +1 -1
  17. package/lib/built/component.d.ts.map +1 -1
  18. package/lib/built/component.js +1 -0
  19. package/lib/built/component.js.map +1 -1
  20. package/lib/built/dispatcher.d.ts.map +1 -1
  21. package/lib/built/dispatcher.js +0 -13
  22. package/lib/built/dispatcher.js.map +1 -1
  23. package/lib/built/message-filter.d.ts +2 -0
  24. package/lib/built/message-filter.d.ts.map +1 -1
  25. package/lib/built/message-filter.js +5 -0
  26. package/lib/built/message-filter.js.map +1 -1
  27. package/lib/built/skill.d.ts +11 -0
  28. package/lib/built/skill.d.ts.map +1 -1
  29. package/lib/built/skill.js +14 -0
  30. package/lib/built/skill.js.map +1 -1
  31. package/lib/built/tool.d.ts +11 -44
  32. package/lib/built/tool.d.ts.map +1 -1
  33. package/lib/built/tool.js +14 -353
  34. package/lib/built/tool.js.map +1 -1
  35. package/lib/plugin.d.ts +1 -25
  36. package/lib/plugin.d.ts.map +1 -1
  37. package/lib/plugin.js +1 -77
  38. package/lib/plugin.js.map +1 -1
  39. package/lib/types.d.ts +0 -25
  40. package/lib/types.d.ts.map +1 -1
  41. package/package.json +10 -7
  42. package/CHANGELOG.md +0 -561
  43. package/REFACTORING_COMPLETE.md +0 -178
  44. package/REFACTORING_STATUS.md +0 -263
  45. package/src/adapter.ts +0 -275
  46. package/src/ai/index.ts +0 -55
  47. package/src/ai/providers/anthropic.ts +0 -379
  48. package/src/ai/providers/base.ts +0 -175
  49. package/src/ai/providers/index.ts +0 -13
  50. package/src/ai/providers/ollama.ts +0 -302
  51. package/src/ai/providers/openai.ts +0 -174
  52. package/src/ai/types.ts +0 -348
  53. package/src/bot.ts +0 -37
  54. package/src/built/adapter-process.ts +0 -177
  55. package/src/built/agent-preset.ts +0 -136
  56. package/src/built/ai-trigger.ts +0 -259
  57. package/src/built/command.ts +0 -108
  58. package/src/built/common-adapter-tools.ts +0 -242
  59. package/src/built/component.ts +0 -130
  60. package/src/built/config.ts +0 -335
  61. package/src/built/cron.ts +0 -156
  62. package/src/built/database.ts +0 -134
  63. package/src/built/dispatcher.ts +0 -496
  64. package/src/built/login-assist.ts +0 -131
  65. package/src/built/message-filter.ts +0 -390
  66. package/src/built/permission.ts +0 -151
  67. package/src/built/schema-feature.ts +0 -190
  68. package/src/built/skill.ts +0 -221
  69. package/src/built/tool.ts +0 -948
  70. package/src/command.ts +0 -87
  71. package/src/component.ts +0 -565
  72. package/src/cron.ts +0 -4
  73. package/src/errors.ts +0 -46
  74. package/src/feature.ts +0 -7
  75. package/src/index.ts +0 -53
  76. package/src/jsx-dev-runtime.ts +0 -2
  77. package/src/jsx-runtime.ts +0 -12
  78. package/src/jsx.ts +0 -135
  79. package/src/message.ts +0 -48
  80. package/src/models/system-log.ts +0 -20
  81. package/src/models/user.ts +0 -15
  82. package/src/notice.ts +0 -98
  83. package/src/plugin.ts +0 -896
  84. package/src/prompt.ts +0 -293
  85. package/src/request.ts +0 -95
  86. package/src/scheduler/index.ts +0 -19
  87. package/src/scheduler/scheduler.ts +0 -372
  88. package/src/scheduler/types.ts +0 -74
  89. package/src/tool-zod.ts +0 -115
  90. package/src/types-generator.ts +0 -78
  91. package/src/types.ts +0 -505
  92. package/src/utils.ts +0 -227
  93. package/tests/adapter.test.ts +0 -638
  94. package/tests/ai/ai-trigger.test.ts +0 -368
  95. package/tests/ai/providers.integration.test.ts +0 -227
  96. package/tests/ai/setup.ts +0 -308
  97. package/tests/ai/tool.test.ts +0 -800
  98. package/tests/bot.test.ts +0 -151
  99. package/tests/command.test.ts +0 -737
  100. package/tests/component-new.test.ts +0 -361
  101. package/tests/config.test.ts +0 -372
  102. package/tests/cron.test.ts +0 -82
  103. package/tests/dispatcher.test.ts +0 -293
  104. package/tests/errors.test.ts +0 -21
  105. package/tests/expression-evaluation.test.ts +0 -258
  106. package/tests/features-builtin.test.ts +0 -191
  107. package/tests/jsx-runtime.test.ts +0 -45
  108. package/tests/jsx.test.ts +0 -319
  109. package/tests/message-filter.test.ts +0 -566
  110. package/tests/message.test.ts +0 -402
  111. package/tests/notice.test.ts +0 -198
  112. package/tests/plugin.test.ts +0 -779
  113. package/tests/prompt.test.ts +0 -78
  114. package/tests/redos-protection.test.ts +0 -198
  115. package/tests/request.test.ts +0 -221
  116. package/tests/schema.test.ts +0 -248
  117. package/tests/skill-feature.test.ts +0 -179
  118. package/tests/test-utils.ts +0 -59
  119. package/tests/tool-feature.test.ts +0 -254
  120. package/tests/types.test.ts +0 -162
  121. package/tests/utils.test.ts +0 -135
  122. package/tsconfig.json +0 -24
@@ -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
- })