File size: 5,416 Bytes
7a1ad33 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | /**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { CompleteTaskTool } from './complete-task.js';
import { type MessageBus } from '../confirmation-bus/message-bus.js';
import { z } from 'zod';
describe('CompleteTaskTool', () => {
let mockMessageBus: MessageBus;
beforeEach(() => {
mockMessageBus = {
publish: vi.fn().mockResolvedValue(undefined),
subscribe: vi.fn(),
unsubscribe: vi.fn(),
} as unknown as MessageBus;
});
describe('Default Configuration (no outputConfig)', () => {
let tool: CompleteTaskTool;
beforeEach(() => {
tool = new CompleteTaskTool(mockMessageBus);
});
it('should have correct metadata', () => {
expect(tool.name).toBe('complete_task');
expect(tool.displayName).toBe('Complete Task');
});
it('should generate correct schema', () => {
const schema = tool.getSchema();
const parameters = schema.parametersJsonSchema as Record<string, unknown>;
const properties = parameters['properties'] as Record<string, unknown>;
expect(properties).toHaveProperty('result');
expect(parameters['required']).toContain('result');
const resultProp = properties['result'] as Record<string, unknown>;
expect(resultProp['type']).toBe('string');
});
it('should validate successfully with result', () => {
const result = tool.validateToolParams({ result: 'Task done' });
expect(result).toBeNull();
});
it('should fail validation if result is missing', () => {
const result = tool.validateToolParams({});
expect(result).toContain("must have required property 'result'");
});
it('should fail validation if result is only whitespace', () => {
const result = tool.validateToolParams({ result: ' ' });
expect(result).toContain(
'Missing required "result" argument. You must provide your findings when calling complete_task.',
);
});
it('should execute and return correct data', async () => {
const invocation = tool.build({ result: 'Success message' });
const result = await invocation.execute({
abortSignal: new AbortController().signal,
});
expect(result.data).toEqual({
taskCompleted: true,
submittedOutput: 'Success message',
});
expect(result.returnDisplay).toBe('Result submitted and task completed.');
});
});
describe('Structured Configuration (with outputConfig)', () => {
const schema = z.object({
report: z.string(),
score: z.number(),
});
const outputConfig = {
outputName: 'my_output',
description: 'The final report',
schema,
};
let tool: CompleteTaskTool<typeof schema>;
beforeEach(() => {
tool = new CompleteTaskTool(mockMessageBus, outputConfig);
});
it('should generate schema based on outputConfig', () => {
const toolSchema = tool.getSchema();
expect(toolSchema.parametersJsonSchema).toHaveProperty(
'properties.my_output',
);
expect(toolSchema.parametersJsonSchema).toHaveProperty(
'properties.my_output.type',
'object',
);
expect(toolSchema.parametersJsonSchema).toHaveProperty(
'properties.my_output.properties.report',
);
expect(toolSchema.parametersJsonSchema).toHaveProperty(
'properties.my_output.properties.score',
);
expect(toolSchema.parametersJsonSchema).toHaveProperty(
'required',
expect.arrayContaining(['my_output']),
);
});
it('should validate successfully with correct structure', () => {
const result = tool.validateToolParams({
my_output: { report: 'All good', score: 100 },
});
expect(result).toBeNull();
});
it('should fail validation if output is missing', () => {
const result = tool.validateToolParams({});
expect(result).toContain("must have required property 'my_output'");
});
it('should fail validation if schema mismatch', () => {
const result = tool.validateToolParams({
my_output: { report: 'All good', score: 'not a number' },
});
expect(result).toContain('must be number');
});
it('should execute and return structured data', async () => {
const outputValue = { report: 'Final findings', score: 42 };
const invocation = tool.build({ my_output: outputValue });
const result = await invocation.execute({
abortSignal: new AbortController().signal,
});
expect(result.data?.['taskCompleted']).toBe(true);
expect(result.data?.['submittedOutput']).toBe(
JSON.stringify(outputValue, null, 2),
);
});
it('should use processOutput if provided', async () => {
const processOutput = (val: z.infer<typeof schema>) =>
`Score was ${val.score}`;
const toolWithProcess = new CompleteTaskTool(
mockMessageBus,
outputConfig,
processOutput,
);
const outputValue = { report: 'Final findings', score: 42 };
const invocation = toolWithProcess.build({ my_output: outputValue });
const result = await invocation.execute({
abortSignal: new AbortController().signal,
});
expect(result.data?.['submittedOutput']).toBe('Score was 42');
});
});
});
|