Skip to main content
Glama
project-update.test.ts6.63 kB
/** * Tests for project_update tool */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { handleProjectUpdate } from '../../../src/tools/project/project-update.js'; import { createMockClientWrapper } from '../../mocks/client.js'; import { createProject } from '../../mocks/factories/project.factory.js'; import type { MockFreshBooksClientWrapper } from '../../mocks/client.js'; describe('project_update tool', () => { let mockClientWrapper: MockFreshBooksClientWrapper; beforeEach(() => { mockClientWrapper = createMockClientWrapper(); vi.clearAllMocks(); }); describe('successful updates', () => { it('should update project title', async () => { const project = createProject({ id: 123, title: 'Updated Title' }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, title: 'Updated Title' }, { client: mockClientWrapper as any } ); expect(result.title).toBe('Updated Title'); }); it('should update project description', async () => { const project = createProject({ id: 123, description: 'New description' }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, description: 'New description' }, { client: mockClientWrapper as any } ); expect(result.description).toBe('New description'); }); it('should update billing rate', async () => { const project = createProject({ id: 123, rate: '150.00' }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, rate: '150.00' }, { client: mockClientWrapper as any } ); expect(result.rate).toBe('150.00'); }); it('should mark project as complete', async () => { const project = createProject({ id: 123, complete: true, active: false }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, complete: true }, { client: mockClientWrapper as any } ); expect(result.complete).toBe(true); }); it('should deactivate project', async () => { const project = createProject({ id: 123, active: false }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, active: false }, { client: mockClientWrapper as any } ); expect(result.active).toBe(false); }); it('should update multiple fields', async () => { const project = createProject({ id: 123, title: 'Updated', rate: '200.00', complete: true, }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, title: 'Updated', rate: '200.00', complete: true, }, { client: mockClientWrapper as any } ); expect(result.title).toBe('Updated'); expect(result.rate).toBe('200.00'); expect(result.complete).toBe(true); }); it('should update billing method', async () => { const project = createProject({ id: 123, billingMethod: 'service_rate' }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, billingMethod: 'service_rate' }, { client: mockClientWrapper as any } ); expect(result.billingMethod).toBe('service_rate'); }); it('should update due date', async () => { const dueDate = '2025-12-31T23:59:59Z'; const project = createProject({ id: 123, dueDate }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, dueDate }, { client: mockClientWrapper as any } ); expect(result.dueDate).toBe(dueDate); }); it('should update budget', async () => { const project = createProject({ id: 123, budget: '100000.00' }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, budget: '100000.00' }, { client: mockClientWrapper as any } ); expect(result.budget).toBe('100000.00'); }); }); describe('error handling', () => { it('should handle project not found', async () => { mockClientWrapper.executeWithRetry.mockRejectedValueOnce( new Error('Project not found') ); await expect( handleProjectUpdate( { accountId: 'ABC123', projectId: 99999, title: 'Test' }, { client: mockClientWrapper as any } ) ).rejects.toThrow('Project not found'); }); it('should handle unauthorized error', async () => { mockClientWrapper.executeWithRetry.mockRejectedValueOnce( new Error('Unauthorized') ); await expect( handleProjectUpdate( { accountId: 'ABC123', projectId: 123, title: 'Test' }, { client: mockClientWrapper as any } ) ).rejects.toThrow('Unauthorized'); }); }); describe('edge cases', () => { it('should handle unicode in title', async () => { const project = createProject({ id: 123, title: '日本語 🎨' }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, title: '日本語 🎨' }, { client: mockClientWrapper as any } ); expect(result.title).toBe('日本語 🎨'); }); it('should handle archive and complete together', async () => { const project = createProject({ id: 123, active: false, complete: true }); mockClientWrapper.executeWithRetry.mockResolvedValueOnce({ project }); const result = await handleProjectUpdate( { accountId: 'ABC123', projectId: 123, active: false, complete: true }, { client: mockClientWrapper as any } ); expect(result.active).toBe(false); expect(result.complete).toBe(true); }); }); });

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/Good-Samaritan-Software-LLC/freshbooks-mcp'

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