103 lines
3.1 KiB
TypeScript
103 lines
3.1 KiB
TypeScript
/**
|
|
* Unit tests for blocked-handler
|
|
*
|
|
* Tests blocked state handling including user input callback flow.
|
|
*/
|
|
|
|
import { describe, it, expect, vi } from 'vitest';
|
|
import { handleBlocked } from '../core/piece/engine/blocked-handler.js';
|
|
import type { AgentResponse } from '../core/models/types.js';
|
|
import type { PieceEngineOptions } from '../core/piece/types.js';
|
|
import { makeMovement } from './test-helpers.js';
|
|
|
|
function makeResponse(content: string): AgentResponse {
|
|
return {
|
|
persona: 'tester',
|
|
status: 'blocked',
|
|
content,
|
|
timestamp: new Date(),
|
|
};
|
|
}
|
|
|
|
function makeOptions(overrides: Partial<PieceEngineOptions> = {}): PieceEngineOptions {
|
|
return {
|
|
projectCwd: '/tmp/project',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe('handleBlocked', () => {
|
|
it('should return shouldContinue=false when no onUserInput callback', async () => {
|
|
const result = await handleBlocked(
|
|
makeMovement(),
|
|
makeResponse('blocked message'),
|
|
makeOptions(),
|
|
);
|
|
|
|
expect(result.shouldContinue).toBe(false);
|
|
expect(result.userInput).toBeUndefined();
|
|
});
|
|
|
|
it('should call onUserInput and return user input', async () => {
|
|
const onUserInput = vi.fn().mockResolvedValue('user response');
|
|
const result = await handleBlocked(
|
|
makeMovement(),
|
|
makeResponse('質問: どうしますか?'),
|
|
makeOptions({ onUserInput }),
|
|
);
|
|
|
|
expect(result.shouldContinue).toBe(true);
|
|
expect(result.userInput).toBe('user response');
|
|
expect(onUserInput).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it('should return shouldContinue=false when user cancels (returns null)', async () => {
|
|
const onUserInput = vi.fn().mockResolvedValue(null);
|
|
const result = await handleBlocked(
|
|
makeMovement(),
|
|
makeResponse('blocked'),
|
|
makeOptions({ onUserInput }),
|
|
);
|
|
|
|
expect(result.shouldContinue).toBe(false);
|
|
expect(result.userInput).toBeUndefined();
|
|
});
|
|
|
|
it('should pass extracted prompt in the request', async () => {
|
|
const onUserInput = vi.fn().mockResolvedValue('answer');
|
|
await handleBlocked(
|
|
makeMovement(),
|
|
makeResponse('質問: 環境は何ですか?'),
|
|
makeOptions({ onUserInput }),
|
|
);
|
|
|
|
const request = onUserInput.mock.calls[0]![0];
|
|
expect(request.prompt).toBe('環境は何ですか?');
|
|
});
|
|
|
|
it('should pass the full content as prompt when no pattern matches', async () => {
|
|
const onUserInput = vi.fn().mockResolvedValue('answer');
|
|
const content = 'I need more information to continue';
|
|
await handleBlocked(
|
|
makeMovement(),
|
|
makeResponse(content),
|
|
makeOptions({ onUserInput }),
|
|
);
|
|
|
|
const request = onUserInput.mock.calls[0]![0];
|
|
expect(request.prompt).toBe(content);
|
|
});
|
|
|
|
it('should pass movement and response in the request', async () => {
|
|
const step = makeMovement();
|
|
const response = makeResponse('blocked');
|
|
const onUserInput = vi.fn().mockResolvedValue('answer');
|
|
|
|
await handleBlocked(step, response, makeOptions({ onUserInput }));
|
|
|
|
const request = onUserInput.mock.calls[0]![0];
|
|
expect(request.movement).toBe(step);
|
|
expect(request.response).toBe(response);
|
|
});
|
|
});
|