@lark-apaas/action-plugin-testing 0.1.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/LICENSE +13 -0
- package/README.md +187 -0
- package/dist/index.cjs +335 -0
- package/dist/index.d.cts +338 -0
- package/dist/index.d.ts +338 -0
- package/dist/index.js +314 -0
- package/package.json +47 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { Logger, PlatformHttpClient, PluginInstance, ActionContext, Plugin, StreamChunk, StreamEvent, StreamDoneEvent, StreamErrorEvent, StreamDoneMetadata, StreamErrorInfo } from '@lark-apaas/action-plugin-core';
|
|
2
|
+
export { Logger } from '@lark-apaas/action-plugin-core';
|
|
3
|
+
import { ZodSchema } from 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 日志条目
|
|
7
|
+
*/
|
|
8
|
+
interface LogEntry {
|
|
9
|
+
level: 'log' | 'error' | 'warn' | 'debug';
|
|
10
|
+
args: unknown[];
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Mock Logger 接口
|
|
15
|
+
*/
|
|
16
|
+
interface MockLogger extends Logger {
|
|
17
|
+
/** 获取所有日志记录 */
|
|
18
|
+
getLogs(): LogEntry[];
|
|
19
|
+
/** 获取指定级别的日志 */
|
|
20
|
+
getLogsByLevel(level: 'log' | 'error' | 'warn' | 'debug'): LogEntry[];
|
|
21
|
+
/** 清空日志记录 */
|
|
22
|
+
clear(): void;
|
|
23
|
+
/** 断言是否包含指定消息 */
|
|
24
|
+
hasLog(level: string, messagePattern: string | RegExp): boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 测试上下文选项
|
|
28
|
+
*/
|
|
29
|
+
interface TestContextOptions {
|
|
30
|
+
userId?: string;
|
|
31
|
+
tenantId?: string;
|
|
32
|
+
appId?: string;
|
|
33
|
+
logger?: 'console' | 'silent' | Logger;
|
|
34
|
+
platformHttpClient?: PlatformHttpClient;
|
|
35
|
+
/** 是否处于调试模式,默认 false */
|
|
36
|
+
isDebug?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* TestRunner 选项
|
|
40
|
+
*/
|
|
41
|
+
interface TestRunnerOptions {
|
|
42
|
+
defaultContext?: Partial<TestContextOptions>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 测试运行器接口
|
|
46
|
+
*/
|
|
47
|
+
interface TestRunner {
|
|
48
|
+
/**
|
|
49
|
+
* 执行 action
|
|
50
|
+
* @param actionName action 名称
|
|
51
|
+
* @param input 输入参数
|
|
52
|
+
* @param contextOverrides 覆盖默认 context 的字段
|
|
53
|
+
*/
|
|
54
|
+
run<TInput, TOutput>(actionName: string, input: TInput, contextOverrides?: Partial<TestContextOptions>): Promise<TOutput>;
|
|
55
|
+
/**
|
|
56
|
+
* 获取底层插件实例
|
|
57
|
+
*/
|
|
58
|
+
getInstance(): PluginInstance;
|
|
59
|
+
/**
|
|
60
|
+
* 获取 action 的 input schema
|
|
61
|
+
*/
|
|
62
|
+
getInputSchema(actionName: string): ZodSchema | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* 获取 action 的 output schema
|
|
65
|
+
*/
|
|
66
|
+
getOutputSchema(actionName: string, input: unknown): ZodSchema | undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 创建测试用的 ActionContext
|
|
71
|
+
*
|
|
72
|
+
* @param options 配置选项
|
|
73
|
+
* @returns ActionContext 实例
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* // 使用默认值
|
|
78
|
+
* const context = createTestContext();
|
|
79
|
+
*
|
|
80
|
+
* // 自定义部分字段
|
|
81
|
+
* const context = createTestContext({
|
|
82
|
+
* userId: 'user-123',
|
|
83
|
+
* logger: 'console',
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
* // 使用自定义 logger
|
|
87
|
+
* const mockLogger = createMockLogger();
|
|
88
|
+
* const context = createTestContext({ logger: mockLogger });
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare function createTestContext(options?: TestContextOptions): ActionContext;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 创建测试运行器
|
|
95
|
+
*
|
|
96
|
+
* 封装插件实例,简化测试调用。
|
|
97
|
+
*
|
|
98
|
+
* @param plugin 插件定义
|
|
99
|
+
* @param config 插件配置(有配置插件必传)
|
|
100
|
+
* @param options 运行器选项
|
|
101
|
+
* @returns TestRunner 实例
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* // 无配置插件
|
|
106
|
+
* const runner = createTestRunner(plugin);
|
|
107
|
+
*
|
|
108
|
+
* // 有配置插件
|
|
109
|
+
* const runner = createTestRunner(plugin, {
|
|
110
|
+
* apiKey: 'test-key',
|
|
111
|
+
* endpoint: 'https://test.api.com',
|
|
112
|
+
* });
|
|
113
|
+
*
|
|
114
|
+
* // 自定义默认 context
|
|
115
|
+
* const runner = createTestRunner(plugin, config, {
|
|
116
|
+
* defaultContext: {
|
|
117
|
+
* userId: 'test-user-123',
|
|
118
|
+
* logger: 'console',
|
|
119
|
+
* },
|
|
120
|
+
* });
|
|
121
|
+
*
|
|
122
|
+
* // 执行测试
|
|
123
|
+
* const result = await runner.run('createUser', { name: 'John' });
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
declare function createTestRunner<TConfig = undefined>(plugin: Plugin<TConfig>, config?: TConfig, options?: TestRunnerOptions): TestRunner;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 创建 Mock Logger
|
|
130
|
+
* 可收集日志记录用于测试断言
|
|
131
|
+
*/
|
|
132
|
+
declare function createMockLogger(): MockLogger;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Stream testing utilities
|
|
136
|
+
* 用于测试流式输出的辅助函数
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 收集流式输出的所有 chunk
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* const chunks = await collectStream(instance.runStream('action', ctx, input));
|
|
145
|
+
* expect(chunks).toHaveLength(3);
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function collectStream<T>(stream: AsyncIterable<T>): Promise<T[]>;
|
|
149
|
+
/**
|
|
150
|
+
* 收集流式输出并提取 data 字段
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const data = await collectStreamData(stream);
|
|
155
|
+
* // [{ content: 'a' }, { content: 'b' }] 而不是 [{ data: { content: 'a' } }, ...]
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
declare function collectStreamData<T>(stream: AsyncIterable<StreamChunk<T>>): Promise<T[]>;
|
|
159
|
+
/**
|
|
160
|
+
* 收集文本流式输出并拼接成完整字符串
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* const text = await collectStreamText(instance.runStream('chat', ctx, input));
|
|
165
|
+
* expect(text).toContain('Hello');
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
declare function collectStreamText(stream: AsyncIterable<StreamChunk<{
|
|
169
|
+
content: string;
|
|
170
|
+
}>>): Promise<string>;
|
|
171
|
+
/**
|
|
172
|
+
* 获取流的第一个 chunk
|
|
173
|
+
*/
|
|
174
|
+
declare function firstChunk<T>(stream: AsyncIterable<T>): Promise<T | undefined>;
|
|
175
|
+
/**
|
|
176
|
+
* 获取流的最后一个 chunk
|
|
177
|
+
*/
|
|
178
|
+
declare function lastChunk<T>(stream: AsyncIterable<T>): Promise<T | undefined>;
|
|
179
|
+
/**
|
|
180
|
+
* 计算流的 chunk 数量(不保存数据)
|
|
181
|
+
*/
|
|
182
|
+
declare function countChunks<T>(stream: AsyncIterable<T>): Promise<number>;
|
|
183
|
+
/**
|
|
184
|
+
* 验证流式输出的结构
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* await assertStreamChunks(stream, [
|
|
189
|
+
* { data: { value: 1 } },
|
|
190
|
+
* { data: { value: 2 } },
|
|
191
|
+
* ]);
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
declare function assertStreamChunks<T>(stream: AsyncIterable<T>, expected: T[]): Promise<void>;
|
|
195
|
+
/**
|
|
196
|
+
* 验证每个 chunk 都满足条件
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* await assertEveryChunk(stream, (chunk) => {
|
|
201
|
+
* return chunk.data.value > 0;
|
|
202
|
+
* });
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
declare function assertEveryChunk<T>(stream: AsyncIterable<T>, predicate: (chunk: T, index: number) => boolean): Promise<void>;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* StreamEvent testing utilities
|
|
209
|
+
* 用于测试 StreamEvent 事件流的辅助函数
|
|
210
|
+
*/
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* StreamEvent 收集结果
|
|
214
|
+
*/
|
|
215
|
+
interface StreamEventResult<T> {
|
|
216
|
+
/** 所有事件 */
|
|
217
|
+
events: StreamEvent<T>[];
|
|
218
|
+
/** 所有 data 事件的数据 */
|
|
219
|
+
data: T[];
|
|
220
|
+
/** done 事件(如果有) */
|
|
221
|
+
done?: StreamDoneEvent;
|
|
222
|
+
/** error 事件(如果有) */
|
|
223
|
+
error?: StreamErrorEvent;
|
|
224
|
+
/** 是否正常完成(有 done 事件且无 error 事件) */
|
|
225
|
+
completed: boolean;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* 收集 StreamEvent 流的所有事件
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```typescript
|
|
232
|
+
* const result = await collectStreamEvents(
|
|
233
|
+
* instance.runStreamWithEvents('chat', ctx, input)
|
|
234
|
+
* );
|
|
235
|
+
*
|
|
236
|
+
* expect(result.completed).toBe(true);
|
|
237
|
+
* expect(result.data).toHaveLength(3);
|
|
238
|
+
* expect(result.done?.metadata.chunks).toBe(3);
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
declare function collectStreamEvents<T>(stream: AsyncIterable<StreamEvent<T>>): Promise<StreamEventResult<T>>;
|
|
242
|
+
/**
|
|
243
|
+
* 只收集 data 事件的数据
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* const data = await collectEventData(
|
|
248
|
+
* instance.runStreamWithEvents('chat', ctx, input)
|
|
249
|
+
* );
|
|
250
|
+
* // [{ content: 'Hello' }, { content: 'World' }]
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
declare function collectEventData<T>(stream: AsyncIterable<StreamEvent<T>>): Promise<T[]>;
|
|
254
|
+
/**
|
|
255
|
+
* 等待流完成并返回 done 事件的 metadata
|
|
256
|
+
* 如果流以 error 结束,则抛出错误
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* const metadata = await waitForDone(
|
|
261
|
+
* instance.runStreamWithEvents('chat', ctx, input)
|
|
262
|
+
* );
|
|
263
|
+
* expect(metadata.chunks).toBe(5);
|
|
264
|
+
* expect(metadata.aggregated).toBeDefined();
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
declare function waitForDone<T>(stream: AsyncIterable<StreamEvent<T>>): Promise<StreamDoneMetadata>;
|
|
268
|
+
/**
|
|
269
|
+
* 等待流出错并返回 error 信息
|
|
270
|
+
* 如果流正常完成,则抛出错误
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* const error = await waitForError(
|
|
275
|
+
* instance.runStreamWithEvents('badAction', ctx, input)
|
|
276
|
+
* );
|
|
277
|
+
* expect(error.code).toBe('INVALID_INPUT');
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
declare function waitForError<T>(stream: AsyncIterable<StreamEvent<T>>): Promise<StreamErrorInfo>;
|
|
281
|
+
/**
|
|
282
|
+
* 断言流正常完成
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```typescript
|
|
286
|
+
* await expectStreamCompletes(
|
|
287
|
+
* instance.runStreamWithEvents('chat', ctx, input)
|
|
288
|
+
* );
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
declare function expectStreamCompletes<T>(stream: AsyncIterable<StreamEvent<T>>): Promise<void>;
|
|
292
|
+
/**
|
|
293
|
+
* 断言流以错误结束
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* await expectStreamErrors(
|
|
298
|
+
* instance.runStreamWithEvents('badAction', ctx, input),
|
|
299
|
+
* 'INVALID_INPUT'
|
|
300
|
+
* );
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
declare function expectStreamErrors<T>(stream: AsyncIterable<StreamEvent<T>>, expectedCode?: string): Promise<void>;
|
|
304
|
+
/**
|
|
305
|
+
* 断言聚合结果
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* await expectAggregatedResult(
|
|
310
|
+
* instance.runStreamWithEvents('chat', ctx, input),
|
|
311
|
+
* { data: { content: 'Hello World' } }
|
|
312
|
+
* );
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
declare function expectAggregatedResult<T, R>(stream: AsyncIterable<StreamEvent<T>>, expected: R): Promise<void>;
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Schema testing utilities
|
|
319
|
+
* 用于测试 schema 验证的辅助函数
|
|
320
|
+
*/
|
|
321
|
+
/**
|
|
322
|
+
* 解析并验证 input
|
|
323
|
+
*/
|
|
324
|
+
declare function parseInput<T>(instance: {
|
|
325
|
+
getInputSchema: (name: string) => {
|
|
326
|
+
parse: (v: unknown) => T;
|
|
327
|
+
} | undefined;
|
|
328
|
+
}, actionName: string, input: unknown): T;
|
|
329
|
+
/**
|
|
330
|
+
* 验证 input 是否会被拒绝
|
|
331
|
+
*/
|
|
332
|
+
declare function expectInputRejected(instance: {
|
|
333
|
+
getInputSchema: (name: string) => {
|
|
334
|
+
parse: (v: unknown) => unknown;
|
|
335
|
+
} | undefined;
|
|
336
|
+
}, actionName: string, input: unknown): void;
|
|
337
|
+
|
|
338
|
+
export { type LogEntry, type MockLogger, type StreamEventResult, type TestContextOptions, type TestRunner, type TestRunnerOptions, assertEveryChunk, assertStreamChunks, collectEventData, collectStream, collectStreamData, collectStreamEvents, collectStreamText, countChunks, createMockLogger, createTestContext, createTestRunner, expectAggregatedResult, expectInputRejected, expectStreamCompletes, expectStreamErrors, firstChunk, lastChunk, parseInput, waitForDone, waitForError };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { Logger, PlatformHttpClient, PluginInstance, ActionContext, Plugin, StreamChunk, StreamEvent, StreamDoneEvent, StreamErrorEvent, StreamDoneMetadata, StreamErrorInfo } from '@lark-apaas/action-plugin-core';
|
|
2
|
+
export { Logger } from '@lark-apaas/action-plugin-core';
|
|
3
|
+
import { ZodSchema } from 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 日志条目
|
|
7
|
+
*/
|
|
8
|
+
interface LogEntry {
|
|
9
|
+
level: 'log' | 'error' | 'warn' | 'debug';
|
|
10
|
+
args: unknown[];
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Mock Logger 接口
|
|
15
|
+
*/
|
|
16
|
+
interface MockLogger extends Logger {
|
|
17
|
+
/** 获取所有日志记录 */
|
|
18
|
+
getLogs(): LogEntry[];
|
|
19
|
+
/** 获取指定级别的日志 */
|
|
20
|
+
getLogsByLevel(level: 'log' | 'error' | 'warn' | 'debug'): LogEntry[];
|
|
21
|
+
/** 清空日志记录 */
|
|
22
|
+
clear(): void;
|
|
23
|
+
/** 断言是否包含指定消息 */
|
|
24
|
+
hasLog(level: string, messagePattern: string | RegExp): boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 测试上下文选项
|
|
28
|
+
*/
|
|
29
|
+
interface TestContextOptions {
|
|
30
|
+
userId?: string;
|
|
31
|
+
tenantId?: string;
|
|
32
|
+
appId?: string;
|
|
33
|
+
logger?: 'console' | 'silent' | Logger;
|
|
34
|
+
platformHttpClient?: PlatformHttpClient;
|
|
35
|
+
/** 是否处于调试模式,默认 false */
|
|
36
|
+
isDebug?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* TestRunner 选项
|
|
40
|
+
*/
|
|
41
|
+
interface TestRunnerOptions {
|
|
42
|
+
defaultContext?: Partial<TestContextOptions>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 测试运行器接口
|
|
46
|
+
*/
|
|
47
|
+
interface TestRunner {
|
|
48
|
+
/**
|
|
49
|
+
* 执行 action
|
|
50
|
+
* @param actionName action 名称
|
|
51
|
+
* @param input 输入参数
|
|
52
|
+
* @param contextOverrides 覆盖默认 context 的字段
|
|
53
|
+
*/
|
|
54
|
+
run<TInput, TOutput>(actionName: string, input: TInput, contextOverrides?: Partial<TestContextOptions>): Promise<TOutput>;
|
|
55
|
+
/**
|
|
56
|
+
* 获取底层插件实例
|
|
57
|
+
*/
|
|
58
|
+
getInstance(): PluginInstance;
|
|
59
|
+
/**
|
|
60
|
+
* 获取 action 的 input schema
|
|
61
|
+
*/
|
|
62
|
+
getInputSchema(actionName: string): ZodSchema | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* 获取 action 的 output schema
|
|
65
|
+
*/
|
|
66
|
+
getOutputSchema(actionName: string, input: unknown): ZodSchema | undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 创建测试用的 ActionContext
|
|
71
|
+
*
|
|
72
|
+
* @param options 配置选项
|
|
73
|
+
* @returns ActionContext 实例
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* // 使用默认值
|
|
78
|
+
* const context = createTestContext();
|
|
79
|
+
*
|
|
80
|
+
* // 自定义部分字段
|
|
81
|
+
* const context = createTestContext({
|
|
82
|
+
* userId: 'user-123',
|
|
83
|
+
* logger: 'console',
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
* // 使用自定义 logger
|
|
87
|
+
* const mockLogger = createMockLogger();
|
|
88
|
+
* const context = createTestContext({ logger: mockLogger });
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
declare function createTestContext(options?: TestContextOptions): ActionContext;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 创建测试运行器
|
|
95
|
+
*
|
|
96
|
+
* 封装插件实例,简化测试调用。
|
|
97
|
+
*
|
|
98
|
+
* @param plugin 插件定义
|
|
99
|
+
* @param config 插件配置(有配置插件必传)
|
|
100
|
+
* @param options 运行器选项
|
|
101
|
+
* @returns TestRunner 实例
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* // 无配置插件
|
|
106
|
+
* const runner = createTestRunner(plugin);
|
|
107
|
+
*
|
|
108
|
+
* // 有配置插件
|
|
109
|
+
* const runner = createTestRunner(plugin, {
|
|
110
|
+
* apiKey: 'test-key',
|
|
111
|
+
* endpoint: 'https://test.api.com',
|
|
112
|
+
* });
|
|
113
|
+
*
|
|
114
|
+
* // 自定义默认 context
|
|
115
|
+
* const runner = createTestRunner(plugin, config, {
|
|
116
|
+
* defaultContext: {
|
|
117
|
+
* userId: 'test-user-123',
|
|
118
|
+
* logger: 'console',
|
|
119
|
+
* },
|
|
120
|
+
* });
|
|
121
|
+
*
|
|
122
|
+
* // 执行测试
|
|
123
|
+
* const result = await runner.run('createUser', { name: 'John' });
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
declare function createTestRunner<TConfig = undefined>(plugin: Plugin<TConfig>, config?: TConfig, options?: TestRunnerOptions): TestRunner;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 创建 Mock Logger
|
|
130
|
+
* 可收集日志记录用于测试断言
|
|
131
|
+
*/
|
|
132
|
+
declare function createMockLogger(): MockLogger;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Stream testing utilities
|
|
136
|
+
* 用于测试流式输出的辅助函数
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 收集流式输出的所有 chunk
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* const chunks = await collectStream(instance.runStream('action', ctx, input));
|
|
145
|
+
* expect(chunks).toHaveLength(3);
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function collectStream<T>(stream: AsyncIterable<T>): Promise<T[]>;
|
|
149
|
+
/**
|
|
150
|
+
* 收集流式输出并提取 data 字段
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const data = await collectStreamData(stream);
|
|
155
|
+
* // [{ content: 'a' }, { content: 'b' }] 而不是 [{ data: { content: 'a' } }, ...]
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
declare function collectStreamData<T>(stream: AsyncIterable<StreamChunk<T>>): Promise<T[]>;
|
|
159
|
+
/**
|
|
160
|
+
* 收集文本流式输出并拼接成完整字符串
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* const text = await collectStreamText(instance.runStream('chat', ctx, input));
|
|
165
|
+
* expect(text).toContain('Hello');
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
declare function collectStreamText(stream: AsyncIterable<StreamChunk<{
|
|
169
|
+
content: string;
|
|
170
|
+
}>>): Promise<string>;
|
|
171
|
+
/**
|
|
172
|
+
* 获取流的第一个 chunk
|
|
173
|
+
*/
|
|
174
|
+
declare function firstChunk<T>(stream: AsyncIterable<T>): Promise<T | undefined>;
|
|
175
|
+
/**
|
|
176
|
+
* 获取流的最后一个 chunk
|
|
177
|
+
*/
|
|
178
|
+
declare function lastChunk<T>(stream: AsyncIterable<T>): Promise<T | undefined>;
|
|
179
|
+
/**
|
|
180
|
+
* 计算流的 chunk 数量(不保存数据)
|
|
181
|
+
*/
|
|
182
|
+
declare function countChunks<T>(stream: AsyncIterable<T>): Promise<number>;
|
|
183
|
+
/**
|
|
184
|
+
* 验证流式输出的结构
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* await assertStreamChunks(stream, [
|
|
189
|
+
* { data: { value: 1 } },
|
|
190
|
+
* { data: { value: 2 } },
|
|
191
|
+
* ]);
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
declare function assertStreamChunks<T>(stream: AsyncIterable<T>, expected: T[]): Promise<void>;
|
|
195
|
+
/**
|
|
196
|
+
* 验证每个 chunk 都满足条件
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* await assertEveryChunk(stream, (chunk) => {
|
|
201
|
+
* return chunk.data.value > 0;
|
|
202
|
+
* });
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
declare function assertEveryChunk<T>(stream: AsyncIterable<T>, predicate: (chunk: T, index: number) => boolean): Promise<void>;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* StreamEvent testing utilities
|
|
209
|
+
* 用于测试 StreamEvent 事件流的辅助函数
|
|
210
|
+
*/
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* StreamEvent 收集结果
|
|
214
|
+
*/
|
|
215
|
+
interface StreamEventResult<T> {
|
|
216
|
+
/** 所有事件 */
|
|
217
|
+
events: StreamEvent<T>[];
|
|
218
|
+
/** 所有 data 事件的数据 */
|
|
219
|
+
data: T[];
|
|
220
|
+
/** done 事件(如果有) */
|
|
221
|
+
done?: StreamDoneEvent;
|
|
222
|
+
/** error 事件(如果有) */
|
|
223
|
+
error?: StreamErrorEvent;
|
|
224
|
+
/** 是否正常完成(有 done 事件且无 error 事件) */
|
|
225
|
+
completed: boolean;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* 收集 StreamEvent 流的所有事件
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```typescript
|
|
232
|
+
* const result = await collectStreamEvents(
|
|
233
|
+
* instance.runStreamWithEvents('chat', ctx, input)
|
|
234
|
+
* );
|
|
235
|
+
*
|
|
236
|
+
* expect(result.completed).toBe(true);
|
|
237
|
+
* expect(result.data).toHaveLength(3);
|
|
238
|
+
* expect(result.done?.metadata.chunks).toBe(3);
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
declare function collectStreamEvents<T>(stream: AsyncIterable<StreamEvent<T>>): Promise<StreamEventResult<T>>;
|
|
242
|
+
/**
|
|
243
|
+
* 只收集 data 事件的数据
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* const data = await collectEventData(
|
|
248
|
+
* instance.runStreamWithEvents('chat', ctx, input)
|
|
249
|
+
* );
|
|
250
|
+
* // [{ content: 'Hello' }, { content: 'World' }]
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
declare function collectEventData<T>(stream: AsyncIterable<StreamEvent<T>>): Promise<T[]>;
|
|
254
|
+
/**
|
|
255
|
+
* 等待流完成并返回 done 事件的 metadata
|
|
256
|
+
* 如果流以 error 结束,则抛出错误
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```typescript
|
|
260
|
+
* const metadata = await waitForDone(
|
|
261
|
+
* instance.runStreamWithEvents('chat', ctx, input)
|
|
262
|
+
* );
|
|
263
|
+
* expect(metadata.chunks).toBe(5);
|
|
264
|
+
* expect(metadata.aggregated).toBeDefined();
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
declare function waitForDone<T>(stream: AsyncIterable<StreamEvent<T>>): Promise<StreamDoneMetadata>;
|
|
268
|
+
/**
|
|
269
|
+
* 等待流出错并返回 error 信息
|
|
270
|
+
* 如果流正常完成,则抛出错误
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```typescript
|
|
274
|
+
* const error = await waitForError(
|
|
275
|
+
* instance.runStreamWithEvents('badAction', ctx, input)
|
|
276
|
+
* );
|
|
277
|
+
* expect(error.code).toBe('INVALID_INPUT');
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
declare function waitForError<T>(stream: AsyncIterable<StreamEvent<T>>): Promise<StreamErrorInfo>;
|
|
281
|
+
/**
|
|
282
|
+
* 断言流正常完成
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```typescript
|
|
286
|
+
* await expectStreamCompletes(
|
|
287
|
+
* instance.runStreamWithEvents('chat', ctx, input)
|
|
288
|
+
* );
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
declare function expectStreamCompletes<T>(stream: AsyncIterable<StreamEvent<T>>): Promise<void>;
|
|
292
|
+
/**
|
|
293
|
+
* 断言流以错误结束
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* await expectStreamErrors(
|
|
298
|
+
* instance.runStreamWithEvents('badAction', ctx, input),
|
|
299
|
+
* 'INVALID_INPUT'
|
|
300
|
+
* );
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
declare function expectStreamErrors<T>(stream: AsyncIterable<StreamEvent<T>>, expectedCode?: string): Promise<void>;
|
|
304
|
+
/**
|
|
305
|
+
* 断言聚合结果
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* await expectAggregatedResult(
|
|
310
|
+
* instance.runStreamWithEvents('chat', ctx, input),
|
|
311
|
+
* { data: { content: 'Hello World' } }
|
|
312
|
+
* );
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
declare function expectAggregatedResult<T, R>(stream: AsyncIterable<StreamEvent<T>>, expected: R): Promise<void>;
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Schema testing utilities
|
|
319
|
+
* 用于测试 schema 验证的辅助函数
|
|
320
|
+
*/
|
|
321
|
+
/**
|
|
322
|
+
* 解析并验证 input
|
|
323
|
+
*/
|
|
324
|
+
declare function parseInput<T>(instance: {
|
|
325
|
+
getInputSchema: (name: string) => {
|
|
326
|
+
parse: (v: unknown) => T;
|
|
327
|
+
} | undefined;
|
|
328
|
+
}, actionName: string, input: unknown): T;
|
|
329
|
+
/**
|
|
330
|
+
* 验证 input 是否会被拒绝
|
|
331
|
+
*/
|
|
332
|
+
declare function expectInputRejected(instance: {
|
|
333
|
+
getInputSchema: (name: string) => {
|
|
334
|
+
parse: (v: unknown) => unknown;
|
|
335
|
+
} | undefined;
|
|
336
|
+
}, actionName: string, input: unknown): void;
|
|
337
|
+
|
|
338
|
+
export { type LogEntry, type MockLogger, type StreamEventResult, type TestContextOptions, type TestRunner, type TestRunnerOptions, assertEveryChunk, assertStreamChunks, collectEventData, collectStream, collectStreamData, collectStreamEvents, collectStreamText, countChunks, createMockLogger, createTestContext, createTestRunner, expectAggregatedResult, expectInputRejected, expectStreamCompletes, expectStreamErrors, firstChunk, lastChunk, parseInput, waitForDone, waitForError };
|