Skip to main content
Glama
tools.test.ts•14.1 kB
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { ClockifyTools } from '../../src/tools/index.js'; import { ConfigurationManager } from '../../src/config/index.js'; import { mockClockifyApi } from '../helpers/nockHelpers.js'; import { mockApiResponses } from '../helpers/mockData.js'; describe('ClockifyTools Integration', () => { let tools: ClockifyTools; let config: ConfigurationManager; let mockApi: ReturnType<typeof mockClockifyApi>; beforeEach(() => { // Import nock and set it up for this test const nock = require('nock'); nock.cleanAll(); if (!nock.isActive()) { nock.activate(); } nock.disableNetConnect(); config = new ConfigurationManager({ apiKey: 'test-api-key-12345678', restrictions: { allowedProjects: ['project-123'], allowedWorkspaces: ['workspace-123'], defaultWorkspaceId: 'workspace-123', defaultProjectId: 'project-123', }, toolFiltering: { enabledCategories: [ 'user', 'workspace', 'project', 'client', 'timeEntry', 'tag', 'task', 'report', 'bulk', 'search', ], maxTools: 100, }, }); tools = new ClockifyTools('test-api-key-12345678', config); mockApi = mockClockifyApi(); }); afterEach(() => { const nock = require('nock'); nock.cleanAll(); nock.restore(); }); describe('User Tools', () => { it('should get current user', async () => { mockApi.mockGetCurrentUser(); const toolList = tools.getTools(); const getCurrentUserTool = toolList.find(t => t.name === 'get_current_user'); expect(getCurrentUserTool).toBeDefined(); const result = await getCurrentUserTool!.handler({}); expect(result.success).toBe(true); expect(result.data).toEqual(mockApiResponses.user); }); it('should list users with workspace filtering', async () => { mockApi.mockGetUsers('workspace-123'); const toolList = tools.getTools(); const listUsersTool = toolList.find(t => t.name === 'list_users'); const result = await listUsersTool!.handler({ workspaceId: 'workspace-123', }); expect(result.success).toBe(true); expect(result.data).toHaveLength(1); }); it('should find user by name', async () => { mockApi.mockGetUsers('workspace-123'); const toolList = tools.getTools(); const findUserTool = toolList.find(t => t.name === 'find_user_by_name'); const result = await findUserTool!.handler({ workspaceId: 'workspace-123', name: 'Test', }); expect(result.success).toBe(true); expect(result.data).toHaveLength(1); }); }); describe('Workspace Tools', () => { it('should list workspaces with filtering', async () => { mockApi.mockGetWorkspaces(); const toolList = tools.getTools(); const listWorkspacesTool = toolList.find(t => t.name === 'list_workspaces'); const result = await listWorkspacesTool!.handler({}); expect(result.success).toBe(true); expect(result.data).toHaveLength(1); // Filtered by restrictions expect(result.data[0].id).toBe('workspace-123'); }); it('should get workspace details', async () => { mockApi.mockGetWorkspace('workspace-123'); const toolList = tools.getTools(); const getWorkspaceTool = toolList.find(t => t.name === 'get_workspace'); const result = await getWorkspaceTool!.handler({ workspaceId: 'workspace-123', }); expect(result.success).toBe(true); expect(result.data.id).toBe('workspace-123'); }); }); describe('Project Tools', () => { it('should list projects with filtering', async () => { mockApi.mockGetProjects('workspace-123'); const toolList = tools.getTools(); const listProjectsTool = toolList.find(t => t.name === 'list_projects'); const result = await listProjectsTool!.handler({ workspaceId: 'workspace-123', }); expect(result.success).toBe(true); expect(result.data).toHaveLength(1); // Filtered by restrictions expect(result.data[0].id).toBe('project-123'); }); it('should create project', async () => { mockApi.mockCreateProject('workspace-123'); const toolList = tools.getTools(); const createProjectTool = toolList.find(t => t.name === 'create_project'); const result = await createProjectTool!.handler({ workspaceId: 'workspace-123', name: 'New Project', billable: true, }); expect(result.success).toBe(true); expect(result.data.name).toBe('Test Project'); }); it('should find project by name', async () => { mockApi.mockGetProjects('workspace-123'); const toolList = tools.getTools(); const findProjectTool = toolList.find(t => t.name === 'find_project_by_name'); const result = await findProjectTool!.handler({ workspaceId: 'workspace-123', name: 'Test', }); expect(result.success).toBe(true); expect(result.data.length).toBeGreaterThanOrEqual(1); }); }); describe('Time Entry Tools', () => { it('should create time entry with defaults', async () => { mockApi.mockCreateTimeEntry('workspace-123'); const toolList = tools.getTools(); const createTimeEntryTool = toolList.find(t => t.name === 'create_time_entry'); const result = await createTimeEntryTool!.handler({ workspaceId: 'workspace-123', description: 'Test work', start: '2025-01-18T09:00:00Z', end: '2025-01-18T10:30:00Z', }); expect(result.success).toBe(true); expect(result.data.description).toBe('Test work'); }); it('should get time entries for user', async () => { mockApi.mockGetTimeEntries('workspace-123', 'user-123'); const toolList = tools.getTools(); const getTimeEntriesTool = toolList.find(t => t.name === 'get_time_entries'); const result = await getTimeEntriesTool!.handler({ workspaceId: 'workspace-123', userId: 'user-123', }); expect(result.success).toBe(true); expect(result.data).toHaveLength(1); }); it('should stop running timer', async () => { mockApi.mockGetCurrentUser(); mockApi.mockStopTimer('workspace-123', 'user-123'); const toolList = tools.getTools(); const stopTimerTool = toolList.find(t => t.name === 'stop_timer'); const result = await stopTimerTool!.handler({ workspaceId: 'workspace-123', }); expect(result.success).toBe(true); expect(result.data.timeInterval.end).toBeDefined(); }); it('should get today entries', async () => { mockApi.mockGetTimeEntries('workspace-123', 'user-123'); const toolList = tools.getTools(); const getTodayEntriesTool = toolList.find(t => t.name === 'get_today_entries'); const result = await getTodayEntriesTool!.handler({ workspaceId: 'workspace-123', userId: 'user-123', }); expect(result.success).toBe(true); expect(result.data).toHaveLength(1); }); it('should bulk edit time entries', async () => { mockApi.mockBulkEditTimeEntries('workspace-123'); const toolList = tools.getTools(); const bulkEditTool = toolList.find(t => t.name === 'bulk_edit_time_entries'); const result = await bulkEditTool!.handler({ workspaceId: 'workspace-123', timeEntryIds: ['entry-123', 'entry-456'], action: 'UPDATE', updates: { billable: true, projectId: 'project-999', // Use a different project ID that won't be configured }, }); expect(result.success).toBe(true); expect(result.data.updated).toBe(2); }); }); describe('Client Tools', () => { it('should list clients', async () => { mockApi.mockGetClients('workspace-123'); const toolList = tools.getTools(); const listClientsTool = toolList.find(t => t.name === 'list_clients'); const result = await listClientsTool!.handler({ workspaceId: 'workspace-123', }); expect(result.success).toBe(true); expect(result.data).toHaveLength(1); }); it('should create client', async () => { mockApi.mockCreateClient('workspace-123'); const toolList = tools.getTools(); const createClientTool = toolList.find(t => t.name === 'create_client'); const result = await createClientTool!.handler({ workspaceId: 'workspace-123', name: 'New Client', email: 'client@example.com', }); expect(result.success).toBe(true); expect(result.data.name).toBe('Test Client'); }); }); describe('Tag Tools', () => { it('should list tags', async () => { mockApi.mockGetTags('workspace-123'); const toolList = tools.getTools(); const listTagsTool = toolList.find(t => t.name === 'list_tags'); const result = await listTagsTool!.handler({ workspaceId: 'workspace-123', }); expect(result.success).toBe(true); expect(result.data).toHaveLength(1); }); it('should create tag', async () => { mockApi.mockCreateTag('workspace-123'); const toolList = tools.getTools(); const createTagTool = toolList.find(t => t.name === 'create_tag'); const result = await createTagTool!.handler({ workspaceId: 'workspace-123', name: 'New Tag', }); expect(result.success).toBe(true); expect(result.data.name).toBe('Development'); }); it('should create multiple tags', async () => { mockApi.scope .post('/api/v1/workspaces/workspace-123/tags') .times(3) .reply(201, mockApiResponses.tags[0]); const toolList = tools.getTools(); const createMultipleTagsTool = toolList.find(t => t.name === 'create_multiple_tags'); const result = await createMultipleTagsTool!.handler({ workspaceId: 'workspace-123', names: ['Tag1', 'Tag2', 'Tag3'], }); expect(result.success).toBe(true); expect(result.data).toHaveLength(3); }); }); describe('Task Tools', () => { it('should list tasks', async () => { mockApi.mockGetTasks('workspace-123', 'project-123'); const toolList = tools.getTools(); const listTasksTool = toolList.find(t => t.name === 'list_tasks'); const result = await listTasksTool!.handler({ workspaceId: 'workspace-123', projectId: 'project-123', }); expect(result.success).toBe(true); expect(result.data).toHaveLength(1); }); it('should create task', async () => { mockApi.mockCreateTask('workspace-123', 'project-123'); const toolList = tools.getTools(); const createTaskTool = toolList.find(t => t.name === 'create_task'); const result = await createTaskTool!.handler({ workspaceId: 'workspace-123', projectId: 'project-123', name: 'New Task', assigneeIds: ['user-123'], }); expect(result.success).toBe(true); expect(result.data.name).toBe('Test Task'); }); it('should mark task as done', async () => { mockApi.scope .put('/api/v1/workspaces/workspace-123/projects/project-123/tasks/task-123') .reply(200, { ...mockApiResponses.tasks[0], status: 'DONE' }); const toolList = tools.getTools(); const markTaskDoneTool = toolList.find(t => t.name === 'mark_task_done'); const result = await markTaskDoneTool!.handler({ workspaceId: 'workspace-123', projectId: 'project-123', taskId: 'task-123', }); expect(result.success).toBe(true); expect(result.data.status).toBe('DONE'); }); }); describe('Report Tools', () => { it('should generate summary report', async () => { mockApi.scope.post('/api/v1/workspaces/workspace-123/reports/summary').reply(200, { totals: [{ totalTime: 'PT8H', entriesCount: 5 }], groupOne: [{ name: 'Project 1', duration: 'PT4H' }], }); const toolList = tools.getTools(); const getSummaryReportTool = toolList.find(t => t.name === 'get_summary_report'); const result = await getSummaryReportTool!.handler({ workspaceId: 'workspace-123', dateRangeStart: '2025-01-18T00:00:00Z', dateRangeEnd: '2025-01-18T23:59:59Z', groupBy: ['PROJECT', 'USER'], }); expect(result.success).toBe(true); expect(result.data.totals).toHaveLength(1); }); it('should get user productivity report', async () => { mockApi.scope.post('/api/v1/workspaces/workspace-123/reports/summary').reply(200, { totals: [{ totalTime: 'PT6H', entriesCount: 3 }], groupOne: [{ name: 'user-123', duration: 'PT6H' }], }); const toolList = tools.getTools(); const getUserProductivityTool = toolList.find(t => t.name === 'get_user_productivity_report'); const result = await getUserProductivityTool!.handler({ workspaceId: 'workspace-123', userId: 'user-123', start: '2025-01-18T00:00:00Z', end: '2025-01-18T23:59:59Z', }); expect(result.success).toBe(true); expect(result.data.totals).toHaveLength(1); }); }); describe('Default Values Application', () => { it('should apply default workspace and project to time entry creation', async () => { mockApi.mockCreateTimeEntry('workspace-123'); const toolList = tools.getTools(); const createTimeEntryTool = toolList.find(t => t.name === 'create_time_entry'); // For integration test, provide workspaceId explicitly const result = await createTimeEntryTool!.handler({ workspaceId: 'workspace-123', description: 'Test work', start: '2025-01-18T09:00:00Z', }); expect(result.success).toBe(true); // The API call should have been made with default workspace ID }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hongkongkiwi/clockify-master-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server