kt-template-online-api/test/qqbot/plugins/bangDream/tsugu/operation-pipeline.spec.ts

120 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { TsuguHookRegistry } from '@/qqbot/plugins/bangDream/tsugu/runtime/hook-registry';
import { TsuguOperationPipeline } from '@/qqbot/plugins/bangDream/tsugu/runtime/operation-pipeline';
import type { TsuguOperationDefinition } from '@/qqbot/plugins/bangDream/tsugu/runtime/operation-registry';
const songSearchOperation: TsuguOperationDefinition = {
description: '查询歌曲',
handlerName: 'searchSong',
key: 'bangdream.song.search',
name: '查曲',
onlineCommand: {
aliases: ['查曲'],
cooldownMs: 1500,
remark: '查询歌曲',
},
};
describe('TsuguOperationPipeline', () => {
it('runs ready check, operation resolve, handler, and output hooks', async () => {
const events: string[] = [];
const hookRegistry = new TsuguHookRegistry([
{
afterOutput: (context) => {
events.push(`afterOutput:${context.stage}:${context.imageCount}`);
},
afterResolve: (context) => {
events.push(`afterResolve:${context.stage}:${context.handlerName}`);
},
beforeParse: (context) => {
events.push(`beforeParse:${context.stage}:${context.query}`);
},
beforeRender: (context) => {
events.push(`beforeRender:${context.stage}:${context.handlerName}`);
},
name: 'recorder',
},
]);
const pipeline = new TsuguOperationPipeline({
executeHandler: async (handlerName, input) => {
events.push(`handler:${handlerName}:${input.text}`);
return {
imageCount: 1,
operationKey: 'bangdream.song.search',
query: '夏祭り',
replyText: '[CQ:image,file=base64://base64-song-card]',
source: 'Tsugu BangDream Bot 内置源码',
};
},
hookRegistry,
normalizeError: (error) => `${error}`,
resolveOperation: () => songSearchOperation,
waitForReady: async () => {
events.push('ready');
},
});
const output = await pipeline.run('bangdream.song.search', {
text: '夏祭り',
});
expect(output.imageCount).toBe(1);
expect(events).toEqual([
'beforeParse:start:夏祭り',
'ready',
'afterResolve:operation:searchSong',
'beforeRender:handler:searchSong',
'handler:searchSong:夏祭り',
'afterOutput:output:1',
]);
});
it('normalizes unknown operation errors and emits error hook', async () => {
const errors: string[] = [];
const pipeline = new TsuguOperationPipeline({
executeHandler: jest.fn(),
hookRegistry: new TsuguHookRegistry([
{
name: 'error-recorder',
onError: (context, error) => {
errors.push(`${context.stage}:${error}`);
},
},
]),
normalizeError: (error) =>
error instanceof Error ? error.message : `${error}`,
resolveOperation: () => undefined,
waitForReady: jest.fn().mockResolvedValue(undefined),
});
await expect(
pipeline.run('bangdream.song.search', { text: '夏祭り' }),
).rejects.toThrow('BangDream 插件能力不存在bangdream.song.search');
expect(errors).toEqual([
'operation:BangDream 插件能力不存在bangdream.song.search',
]);
});
it('normalizes handler errors at handler stage', async () => {
const errors: string[] = [];
const pipeline = new TsuguOperationPipeline({
executeHandler: jest.fn().mockRejectedValue('图片渲染失败'),
hookRegistry: new TsuguHookRegistry([
{
name: 'error-recorder',
onError: (context, error) => {
errors.push(`${context.stage}:${context.handlerName}:${error}`);
},
},
]),
normalizeError: (error) => `${error}`,
resolveOperation: () => songSearchOperation,
waitForReady: jest.fn().mockResolvedValue(undefined),
});
await expect(
pipeline.run('bangdream.song.search', { text: '夏祭り' }),
).rejects.toThrow('图片渲染失败');
expect(errors).toEqual(['handler:searchSong:图片渲染失败']);
});
});