Files
2026-07-13 12:38:34 +08:00

306 lines
9.2 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest';
import { mockClient } from '../utils/mocks/client.mock';
import { toolMocks } from '../utils/mocks/data.mock';
import { Tool } from '../../src/types/tool.types';
import {
createTestContext,
setupTest,
mockToolExecution,
createSchemaModifier,
createExecutionModifiers,
} from '../utils/toolExecuteUtils';
describe('Tools Modifiers', () => {
const context = createTestContext();
setupTest(context);
describe('Schema Modifiers', () => {
it('should modify the tool schema with the provided modifier', async () => {
const userId = 'test-user';
const slug = 'COMPOSIO_TOOL';
const schemaModifier = createSchemaModifier({
name: 'Modified Name',
description: 'Modified description',
});
// mock client to send back the tool
mockClient.tools.retrieve.mockResolvedValueOnce(toolMocks.rawTool);
const result = await context.tools.getRawComposioToolBySlug(slug, {
modifySchema: schemaModifier,
});
expect(schemaModifier).toHaveBeenCalledTimes(1);
expect(schemaModifier).toHaveBeenCalledWith(
expect.objectContaining({
toolSlug: slug,
toolkitSlug: expect.any(String),
schema: expect.objectContaining({ slug }),
})
);
expect(result.name).toEqual('Modified Name');
expect(result.description).toEqual('Modified description');
});
it('should apply schema modifiers to all tools when getting multiple tools', async () => {
const userId = 'test-user';
const schemaModifier = createSchemaModifier(({ schema }) => ({
...schema,
name: `Modified ${schema.name}`,
}));
mockClient.tools.list.mockResolvedValueOnce({
items: [
{ ...toolMocks.rawTool, slug: 'TOOL1' },
{ ...toolMocks.rawTool, slug: 'TOOL2' },
],
totalPages: 1,
});
const result = await context.tools.getRawComposioTools(
{ tools: ['TOOL1', 'TOOL2'] },
{ modifySchema: schemaModifier }
);
expect(schemaModifier).toHaveBeenCalledTimes(2);
expect(result[0].name).toContain('Modified');
expect(result[1].name).toContain('Modified');
});
});
describe('Execution Modifiers', () => {
it('should apply beforeExecute modifier before executing a tool', async () => {
const slug = 'COMPOSIO_TOOL';
const body = {
userId: 'test-user',
arguments: { limit: 5 },
dangerouslySkipVersionCheck: true,
};
const { beforeExecute } = createExecutionModifiers({
beforeModifications: {
arguments: { limit: 10 },
},
});
await mockToolExecution(context.tools);
await context.tools.execute(slug, body, { beforeExecute });
expect(beforeExecute).toHaveBeenCalledTimes(1);
expect(beforeExecute).toHaveBeenCalledWith(
expect.objectContaining({
toolSlug: slug,
toolkitSlug: expect.any(String),
params: body,
})
);
expect(mockClient.tools.execute).toHaveBeenCalledWith(
slug,
expect.objectContaining({
arguments: { limit: 10 },
}),
undefined
);
});
it('should apply afterExecute modifier after executing a tool', async () => {
const slug = 'COMPOSIO_TOOL';
const body = { userId: 'test-user', arguments: {}, dangerouslySkipVersionCheck: true };
const { afterExecute } = createExecutionModifiers({
afterModifications: {
data: { enhanced: true },
},
});
await mockToolExecution(context.tools);
const result = await context.tools.execute(slug, body, { afterExecute });
expect(afterExecute).toHaveBeenCalledTimes(1);
expect(afterExecute).toHaveBeenCalledWith(
expect.objectContaining({
toolSlug: slug,
toolkitSlug: expect.any(String),
result: expect.objectContaining({
data: expect.any(Object),
successful: expect.any(Boolean),
}),
})
);
expect(result.data).toHaveProperty('enhanced', true);
});
});
describe('Combined Modifiers', () => {
it('should apply both schema and execution modifiers together', async () => {
const userId = 'test-user';
const slug = 'GITHUB_GET_REPOS';
// Mock analytics and logging functions
const trackToolUsage = vi.fn();
const logToolError = vi.fn();
const schemaModifier = createSchemaModifier(({ schema, toolSlug }) => ({
...schema,
description: `Enhanced ${toolSlug} for better context`,
tags: [...(schema.tags || []), 'enhanced', 'modified'],
inputParameters: {
type: 'object' as const,
properties: {
...schema.inputParameters?.properties,
tracking: {
type: 'object' as const,
properties: {
enabled: {
type: 'boolean' as const,
default: true,
},
},
},
},
},
}));
const executionModifiers = {
beforeExecute: vi.fn(({ params, toolSlug }) => {
// Add analytics tracking
trackToolUsage(toolSlug);
return {
...params,
arguments: {
...(params.arguments || {}),
tracking: {
enabled: true,
timestamp: expect.any(String),
},
},
};
}),
afterExecute: vi.fn(({ result, toolSlug }) => {
// Log results and handle errors
if (!result.successful) {
logToolError(toolSlug, result.error);
}
return {
...result,
data: {
...(result.data || {}),
processed: true,
timestamp: expect.any(String),
},
};
}),
};
const getRawComposioToolBySlugSpy = vi.spyOn(context.tools, 'getRawComposioToolBySlug');
const mockTool = {
...(toolMocks.transformedTool as unknown as Tool),
description: 'Enhanced GITHUB_GET_REPOS for better context',
tags: ['enhanced', 'modified'],
inputParameters: {
type: 'object' as const,
properties: {
tracking: {
type: 'object' as const,
properties: {
enabled: {
type: 'boolean' as const,
default: true,
},
},
},
},
},
};
getRawComposioToolBySlugSpy.mockResolvedValueOnce(mockTool);
const createExecuteToolFnSpy = vi.spyOn(context.tools as any, 'createExecuteToolFn');
const executeWithModifiers = async (params: any) => {
// Apply beforeExecute modifier
const modifiedParams = await executionModifiers.beforeExecute({
toolSlug: slug,
toolkitSlug: 'default',
params,
});
// Execute the tool
const response = await mockClient.tools.execute(slug, modifiedParams);
// Apply afterExecute modifier
return executionModifiers.afterExecute({
toolSlug: slug,
toolkitSlug: 'default',
result: response,
});
};
createExecuteToolFnSpy.mockReturnValueOnce(executeWithModifiers);
const tool = await context.tools.get(userId, slug, {
modifySchema: schemaModifier,
...executionModifiers,
});
// Verify schema modification
expect(getRawComposioToolBySlugSpy).toHaveBeenCalledWith(
slug,
{
modifySchema: schemaModifier,
},
undefined
);
expect(mockTool.description).toBe('Enhanced GITHUB_GET_REPOS for better context');
expect(mockTool.tags).toContain('enhanced');
expect(mockTool.tags).toContain('modified');
expect(mockTool.inputParameters).toHaveProperty(['properties', 'tracking']);
// Verify execution function creation
expect(createExecuteToolFnSpy).toHaveBeenCalledWith(
userId,
expect.objectContaining(executionModifiers)
);
// Mock a tool execution to verify the full flow
await mockToolExecution(context.tools);
mockClient.tools.execute.mockResolvedValueOnce({
data: {
results: true,
},
error: null,
successful: true,
log_id: '123',
session_info: {},
});
// Execute the tool using the wrapped execute function
const result = await executeWithModifiers({ query: 'test' });
// Verify analytics tracking was called
expect(trackToolUsage).toHaveBeenCalledWith(slug);
// Verify the execution flow
expect(executionModifiers.beforeExecute).toHaveBeenCalledWith({
toolSlug: slug,
toolkitSlug: 'default',
params: { query: 'test' },
});
expect(executionModifiers.afterExecute).toHaveBeenCalledWith({
toolSlug: slug,
toolkitSlug: 'default',
result: {
data: {
results: true,
},
error: null,
successful: true,
log_id: '123',
session_info: {},
},
});
// Verify no error logging occurred
expect(logToolError).not.toHaveBeenCalled();
});
});
});