306 lines
9.2 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|