Skip to main content
Glama
comments.spec.ts8.16 kB
import { getTaskCommentsTool, createTaskCommentTool, getContextLabelsTool, } from './comments'; import type { MockedFunction } from 'vitest'; import { getTaskComments, createTaskComment } from '../services/tasks/comments'; import { getContextLabels } from '../services/labels/labels'; vi.mock('../services/tasks/comments'); vi.mock('../services/labels/labels'); const mockGetTaskComments = getTaskComments as MockedFunction< typeof getTaskComments >; const mockCreateTaskComment = createTaskComment as MockedFunction< typeof createTaskComment >; const mockGetContextLabels = getContextLabels as MockedFunction< typeof getContextLabels >; describe('Comments', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('getTaskCommentsTool', () => { it('should format get_task_comments response as text with stringified JSON', async () => { // arrange const mockCommentsData = { comments: [ { id: 1, content: 'This is a comment on the task', posted: '2023-01-01T00:00:00Z', posted_uid: 'user123', attachment: null, }, ], total_count: 1, }; mockGetTaskComments.mockResolvedValue(mockCommentsData); // act const response = await getTaskCommentsTool.handler({ task_id: '123' }); // assert expect(response.content[0].type).toBe('text'); expect(response.content[0].text).toBe( JSON.stringify(mockCommentsData, null, 2) ); expect(mockGetTaskComments).toHaveBeenCalledWith('123'); }); it('should properly stringify comment data with attachments', async () => { // arrange const mockCommentsData = { comments: [ { id: 1, content: 'Comment with attachment', posted: '2023-01-01T00:00:00Z', posted_uid: 'user123', attachment: { resource_type: 'file', file_name: 'document.pdf', file_size: 1024, file_url: 'https://todoist.com/file/document.pdf', upload_state: 'completed', }, }, { id: 2, content: 'Simple comment without attachment', posted: '2023-01-02T00:00:00Z', posted_uid: 'user456', attachment: null, }, ], total_count: 2, }; mockGetTaskComments.mockResolvedValue(mockCommentsData); // act const response = await getTaskCommentsTool.handler({ task_id: '123' }); const stringified = response.content[0].text; // assert expect(stringified).toContain('"id": 1'); expect(stringified).toContain('"content": "Comment with attachment"'); expect(stringified).toContain('"posted": "2023-01-01T00:00:00Z"'); expect(stringified).toContain('"posted_uid": "user123"'); expect(stringified).toContain('"file_name": "document.pdf"'); expect(stringified).toContain('"total_count": 2'); }); it('should handle empty comments list', async () => { // arrange const mockEmptyData = { comments: [], total_count: 0, }; mockGetTaskComments.mockResolvedValue(mockEmptyData); // act const response = await getTaskCommentsTool.handler({ task_id: '123' }); const stringified = response.content[0].text; // assert expect(stringified).toContain('"comments": []'); expect(stringified).toContain('"total_count": 0'); }); it('should throw error when task_id is missing', async () => { // arrange const args = {} as { task_id: string }; // act const promise = getTaskCommentsTool.handler(args); // assert await expect(promise).rejects.toThrow('task_id is required'); }); }); describe('createTaskCommentTool', () => { it('should create a new comment on a task successfully', async () => { // arrange const mockTaskId = '123'; const mockContent = 'This is a test comment'; const mockComment = { id: 456, content: 'This is a test comment\n\n*(commented using Claude)*', posted: '2024-01-03T12:00:00Z', posted_uid: 'user1', attachment: null, }; mockCreateTaskComment.mockResolvedValue(mockComment); // act const result = await createTaskCommentTool.handler({ task_id: mockTaskId, content: mockContent, }); // assert expect(result.content).toHaveLength(1); expect(result.content[0].type).toBe('text'); const responseData = JSON.parse(result.content[0].text); expect(responseData.success).toBe(true); expect(responseData.message).toBe('Comment created successfully'); expect(responseData.comment.id).toBe(456); expect(responseData.comment.content).toBe( 'This is a test comment\n\n*(commented using Claude)*' ); expect(responseData.comment.posted_uid).toBe('user1'); expect(mockCreateTaskComment).toHaveBeenCalledWith( mockTaskId, mockContent ); }); it('should handle error when creating comment fails', async () => { // arrange const mockTaskId = '123'; const mockContent = 'This is a test comment'; const mockError = new Error('API Error'); mockCreateTaskComment.mockRejectedValue(mockError); // act const result = await createTaskCommentTool.handler({ task_id: mockTaskId, content: mockContent, }); // assert expect(result.content).toHaveLength(1); expect(result.content[0].type).toBe('text'); const responseData = JSON.parse(result.content[0].text); expect(responseData.success).toBe(false); expect(responseData.message).toBe('Failed to create comment'); expect(responseData.error).toBe('API Error'); expect(mockCreateTaskComment).toHaveBeenCalledWith( mockTaskId, mockContent ); }); }); describe('getContextLabelsTool', () => { it('should return context labels when API call succeeds', async () => { // arrange const mockContextLabels = { labels: [ { id: 1, name: 'context:home', color: 'red', order: 1, is_favorite: false, }, { id: 2, name: 'context:office', color: 'blue', order: 2, is_favorite: true, }, { id: 3, name: 'context:mobile', color: 'green', order: 3, is_favorite: false, }, ], total_count: 3, }; mockGetContextLabels.mockResolvedValue(mockContextLabels); // act const result = await getContextLabelsTool.handler(); // assert expect(result).toEqual({ content: [ { type: 'text', text: JSON.stringify(mockContextLabels, null, 2), }, ], }); expect(mockGetContextLabels).toHaveBeenCalledTimes(1); }); it('should handle empty context labels response', async () => { // arrange const mockEmptyResponse = { labels: [], total_count: 0, }; mockGetContextLabels.mockResolvedValue(mockEmptyResponse); // act const result = await getContextLabelsTool.handler(); // assert expect(result).toEqual({ content: [ { type: 'text', text: JSON.stringify(mockEmptyResponse, null, 2), }, ], }); expect(mockGetContextLabels).toHaveBeenCalledTimes(1); }); it('should handle API errors', async () => { // arrange const errorMessage = 'Failed to get context labels: API Error'; mockGetContextLabels.mockRejectedValue(new Error(errorMessage)); // act const promise = getContextLabelsTool.handler(); // assert await expect(promise).rejects.toThrow(errorMessage); expect(mockGetContextLabels).toHaveBeenCalledTimes(1); }); }); });

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/bkotos/todoist-mcp'

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