@zhin.js/core 1.0.19 → 1.0.21

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.
@@ -0,0 +1,78 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest'
2
+ import { Prompt } from '../src/prompt'
3
+ import { Plugin } from '../src/plugin'
4
+ import { Schema } from '@zhin.js/schema'
5
+
6
+ describe('Prompt', () => {
7
+ let plugin: Plugin
8
+ let mockEvent: any
9
+ let prompt: Prompt<any>
10
+
11
+ beforeEach(() => {
12
+ plugin = new Plugin('/test/plugin.ts')
13
+
14
+ // 创建模拟事件
15
+ mockEvent = {
16
+ $adapter: 'test-adapter',
17
+ $bot: 'test-bot',
18
+ $channel: { type: 'text', id: 'test-channel' },
19
+ $sender: { id: 'test-user' },
20
+ $raw: 'test message',
21
+ $reply: vi.fn().mockResolvedValue('message-id')
22
+ }
23
+
24
+ prompt = new Prompt(plugin, mockEvent as any)
25
+ })
26
+
27
+ describe('Constructor', () => {
28
+ it('should create Prompt instance', () => {
29
+ expect(prompt).toBeInstanceOf(Prompt)
30
+ })
31
+ })
32
+
33
+ describe('const', () => {
34
+ it('should return constant value', async () => {
35
+ const result = await prompt.const(42)
36
+ expect(result).toBe(42)
37
+ })
38
+
39
+ it('should return constant string', async () => {
40
+ const result = await prompt.const('test-value')
41
+ expect(result).toBe('test-value')
42
+ })
43
+
44
+ it('should return constant object', async () => {
45
+ const obj = { key: 'value' }
46
+ const result = await prompt.const(obj)
47
+ expect(result).toBe(obj)
48
+ })
49
+
50
+ it('should return constant array', async () => {
51
+ const arr = [1, 2, 3]
52
+ const result = await prompt.const(arr)
53
+ expect(result).toBe(arr)
54
+ })
55
+ })
56
+
57
+ describe('Schema error handling', () => {
58
+ it('should throw error for unsupported list inner type', async () => {
59
+ const schema = Schema.list(Schema.object({})).description('不支持的列表类型')
60
+
61
+ await expect(prompt.getValueWithSchema(schema)).rejects.toThrow('unsupported inner type')
62
+ })
63
+
64
+ it('should throw error for unsupported schema type', async () => {
65
+ const schema = Schema.dict(Schema.string()).description('不支持的类型')
66
+
67
+ await expect(prompt.getValueWithSchema(schema)).rejects.toThrow('Unsupported schema input type')
68
+ })
69
+
70
+ it('should throw error for object schema without object definition', async () => {
71
+ const schema = Schema.object({})
72
+ // 删除 object 定义来触发错误
73
+ delete (schema as any).options.object
74
+
75
+ await expect(prompt.getValueWithSchema(schema)).rejects.toThrow('Object schema missing object definition')
76
+ })
77
+ })
78
+ })
@@ -0,0 +1,197 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { segment } from '../src/utils.js';
3
+
4
+ describe('ReDoS Protection Tests', () => {
5
+ describe('segment.from() ReDoS protection', () => {
6
+ it('should handle small inputs (100 attributes) without timeout', () => {
7
+ // 测试少量属性的标签
8
+ const smallInput = '<tag ' + 'a="b" '.repeat(100) + '/>';
9
+ const start = Date.now();
10
+
11
+ try {
12
+ segment.from(smallInput);
13
+ const duration = Date.now() - start;
14
+
15
+ // 应该在合理时间内完成(100ms)
16
+ expect(duration).toBeLessThan(100);
17
+ } catch (error) {
18
+ // 如果抛出错误,也是可以接受的(比如输入太大)
19
+ expect(error).toBeDefined();
20
+ }
21
+ });
22
+
23
+ it('should handle medium inputs (500 attributes) without timeout', () => {
24
+ // 测试中等数量属性的标签
25
+ const mediumInput = '<tag ' + 'a="b" '.repeat(500) + '/>';
26
+ const start = Date.now();
27
+
28
+ try {
29
+ segment.from(mediumInput);
30
+ const duration = Date.now() - start;
31
+
32
+ // 应该在合理时间内完成(500ms)
33
+ expect(duration).toBeLessThan(500);
34
+ } catch (error) {
35
+ // 如果抛出错误,也是可以接受的(比如输入太大)
36
+ expect(error).toBeDefined();
37
+ }
38
+ });
39
+
40
+ it('should handle large inputs (1000 attributes) without timeout', () => {
41
+ // 测试大量属性的标签
42
+ const largeInput = '<tag ' + 'a="b" '.repeat(1000) + '/>';
43
+ const start = Date.now();
44
+
45
+ try {
46
+ segment.from(largeInput);
47
+ const duration = Date.now() - start;
48
+
49
+ // 应该在合理时间内完成(1秒)
50
+ expect(duration).toBeLessThan(1000);
51
+ } catch (error) {
52
+ // 如果抛出错误,也是可以接受的(比如输入太大)
53
+ expect(error).toBeDefined();
54
+ }
55
+ });
56
+
57
+ it('should handle very large inputs (2000 attributes) without timeout', () => {
58
+ // 测试更大量属性的标签
59
+ const veryLargeInput = '<tag ' + 'a="b" '.repeat(2000) + '/>';
60
+ const start = Date.now();
61
+
62
+ try {
63
+ segment.from(veryLargeInput);
64
+ const duration = Date.now() - start;
65
+
66
+ // 应该在合理时间内完成(2秒)
67
+ expect(duration).toBeLessThan(2000);
68
+ } catch (error) {
69
+ // 如果抛出错误,也是可以接受的(比如输入太大)
70
+ expect(error).toBeDefined();
71
+ }
72
+ });
73
+
74
+ it('should reject extremely large templates', () => {
75
+ // 测试超大模板
76
+ const hugeTemplate = '<tag>content</tag>'.repeat(10000);
77
+
78
+ expect(() => {
79
+ segment.from(hugeTemplate);
80
+ }).toThrow('Template too large');
81
+ });
82
+
83
+ it('should handle nested tags without exponential backtracking', () => {
84
+ // 测试嵌套标签
85
+ const nestedInput = '<outer ' + 'attr="val" '.repeat(100) + '><inner>text</inner></outer>';
86
+ const start = Date.now();
87
+
88
+ segment.from(nestedInput);
89
+ const duration = Date.now() - start;
90
+
91
+ // 应该在合理时间内完成
92
+ expect(duration).toBeLessThan(500);
93
+ });
94
+
95
+ it('should handle malformed attributes gracefully', () => {
96
+ // 测试格式错误的属性
97
+ const malformedInput = '<tag a=b c=d e="f"/>';
98
+
99
+ // 不应该崩溃或超时
100
+ const start = Date.now();
101
+ const result = segment.from(malformedInput);
102
+ const duration = Date.now() - start;
103
+
104
+ expect(duration).toBeLessThan(100);
105
+ expect(result).toBeDefined();
106
+ });
107
+
108
+ it('should prevent infinite loops', () => {
109
+ // 测试可能导致无限循环的输入
110
+ const problematicInput = '<tag><tag><tag><tag><tag>';
111
+
112
+ const start = Date.now();
113
+ try {
114
+ segment.from(problematicInput);
115
+ } catch (error) {
116
+ // 可能会抛出错误,这是可以接受的
117
+ }
118
+ const duration = Date.now() - start;
119
+
120
+ // 应该在合理时间内完成或失败
121
+ expect(duration).toBeLessThan(1000);
122
+ });
123
+
124
+ it('should handle complex attribute patterns', () => {
125
+ // 测试复杂的属性模式
126
+ const complexInput = '<tag attr1="value1" attr2=\'value2\' attr3="value with spaces"/>';
127
+
128
+ const start = Date.now();
129
+ const result = segment.from(complexInput);
130
+ const duration = Date.now() - start;
131
+
132
+ expect(duration).toBeLessThan(100);
133
+ expect(Array.isArray(result)).toBe(true);
134
+ });
135
+
136
+ it('should handle repeated patterns efficiently', () => {
137
+ // 测试重复模式
138
+ const repeatedPattern = ('<tag attr="value">content</tag>').repeat(100);
139
+
140
+ const start = Date.now();
141
+ try {
142
+ segment.from(repeatedPattern);
143
+ } catch (error) {
144
+ // 如果输入太大,抛出错误是可以接受的
145
+ }
146
+ const duration = Date.now() - start;
147
+
148
+ expect(duration).toBeLessThan(1000);
149
+ });
150
+
151
+ it('should handle edge cases with special characters', () => {
152
+ // 测试特殊字符
153
+ const specialChars = '<tag attr="value with \\"quotes\\" and \'apostrophes\'">content</tag>';
154
+
155
+ const start = Date.now();
156
+ const result = segment.from(specialChars);
157
+ const duration = Date.now() - start;
158
+
159
+ expect(duration).toBeLessThan(100);
160
+ expect(Array.isArray(result)).toBe(true);
161
+ });
162
+ });
163
+
164
+ describe('Normal functionality should still work', () => {
165
+ it('should parse simple self-closing tags', () => {
166
+ const input = '<image url="test.jpg"/>';
167
+ const result = segment.from(input);
168
+
169
+ expect(Array.isArray(result)).toBe(true);
170
+ expect(result.length).toBeGreaterThan(0);
171
+ });
172
+
173
+ it('should parse tags with content', () => {
174
+ const input = '<text>Hello World</text>';
175
+ const result = segment.from(input);
176
+
177
+ expect(Array.isArray(result)).toBe(true);
178
+ expect(result.length).toBeGreaterThan(0);
179
+ });
180
+
181
+ it('should parse tags with attributes', () => {
182
+ const input = '<at id="123" name="user"/>';
183
+ const result = segment.from(input);
184
+
185
+ expect(Array.isArray(result)).toBe(true);
186
+ expect(result.length).toBeGreaterThan(0);
187
+ });
188
+
189
+ it('should parse nested tags', () => {
190
+ const input = '<quote><text>quoted text</text></quote>';
191
+ const result = segment.from(input);
192
+
193
+ expect(Array.isArray(result)).toBe(true);
194
+ expect(result.length).toBeGreaterThan(0);
195
+ });
196
+ });
197
+ });