@doist/todoist-ai 4.16.0 → 4.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/filter-helpers.d.ts +1 -1
- package/dist/index.d.ts +1044 -196
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -81
- package/dist/main.js +15 -23
- package/dist/mcp-helpers.d.ts +5 -5
- package/dist/mcp-helpers.d.ts.map +1 -1
- package/dist/mcp-server-BADReNAy.js +3092 -0
- package/dist/todoist-tool.d.ts +9 -3
- package/dist/todoist-tool.d.ts.map +1 -1
- package/dist/tool-helpers.d.ts +1 -1
- package/dist/tools/add-comments.d.ts +69 -3
- package/dist/tools/add-comments.d.ts.map +1 -1
- package/dist/tools/add-projects.d.ts +34 -3
- package/dist/tools/add-projects.d.ts.map +1 -1
- package/dist/tools/add-sections.d.ts +14 -1
- package/dist/tools/add-sections.d.ts.map +1 -1
- package/dist/tools/add-tasks.d.ts +65 -10
- package/dist/tools/add-tasks.d.ts.map +1 -1
- package/dist/tools/complete-tasks.d.ts +20 -1
- package/dist/tools/complete-tasks.d.ts.map +1 -1
- package/dist/tools/delete-object.d.ts +16 -3
- package/dist/tools/delete-object.d.ts.map +1 -1
- package/dist/tools/fetch.d.ts +8 -1
- package/dist/tools/fetch.d.ts.map +1 -1
- package/dist/tools/find-activity.d.ts +44 -7
- package/dist/tools/find-activity.d.ts.map +1 -1
- package/dist/tools/find-comments.d.ts +69 -3
- package/dist/tools/find-comments.d.ts.map +1 -1
- package/dist/tools/find-completed-tasks.d.ts +63 -5
- package/dist/tools/find-completed-tasks.d.ts.map +1 -1
- package/dist/tools/find-project-collaborators.d.ts +33 -2
- package/dist/tools/find-project-collaborators.d.ts.map +1 -1
- package/dist/tools/find-projects.d.ts +35 -1
- package/dist/tools/find-projects.d.ts.map +1 -1
- package/dist/tools/find-sections.d.ts +15 -1
- package/dist/tools/find-sections.d.ts.map +1 -1
- package/dist/tools/find-tasks-by-date.d.ts +61 -3
- package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
- package/dist/tools/find-tasks.d.ts +63 -5
- package/dist/tools/find-tasks.d.ts.map +1 -1
- package/dist/tools/get-overview.d.ts +24 -1
- package/dist/tools/get-overview.d.ts.map +1 -1
- package/dist/tools/manage-assignments.d.ts +39 -2
- package/dist/tools/manage-assignments.d.ts.map +1 -1
- package/dist/tools/search.d.ts +17 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/update-comments.d.ts +76 -3
- package/dist/tools/update-comments.d.ts.map +1 -1
- package/dist/tools/update-projects.d.ts +43 -1
- package/dist/tools/update-projects.d.ts.map +1 -1
- package/dist/tools/update-sections.d.ts +17 -3
- package/dist/tools/update-sections.d.ts.map +1 -1
- package/dist/tools/update-tasks.d.ts +79 -13
- package/dist/tools/update-tasks.d.ts.map +1 -1
- package/dist/tools/user-info.d.ts +19 -1
- package/dist/tools/user-info.d.ts.map +1 -1
- package/dist/utils/assignment-validator.d.ts +2 -2
- package/dist/utils/output-schemas.d.ts +233 -0
- package/dist/utils/output-schemas.d.ts.map +1 -0
- package/dist/utils/response-builders.d.ts +1 -3
- package/dist/utils/response-builders.d.ts.map +1 -1
- package/dist/utils/test-helpers.d.ts +1 -1
- package/dist/utils/user-resolver.d.ts +1 -1
- package/package.json +10 -8
- package/dist/filter-helpers.js +0 -79
- package/dist/mcp-helpers.js +0 -71
- package/dist/mcp-server.js +0 -142
- package/dist/todoist-tool.js +0 -1
- package/dist/tool-helpers.js +0 -125
- package/dist/tool-helpers.test.d.ts +0 -2
- package/dist/tool-helpers.test.d.ts.map +0 -1
- package/dist/tool-helpers.test.js +0 -223
- package/dist/tools/__tests__/add-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/add-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-comments.test.js +0 -241
- package/dist/tools/__tests__/add-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/add-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-projects.test.js +0 -174
- package/dist/tools/__tests__/add-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/add-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-sections.test.js +0 -185
- package/dist/tools/__tests__/add-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/add-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-tasks.test.js +0 -606
- package/dist/tools/__tests__/assignment-integration.test.d.ts +0 -2
- package/dist/tools/__tests__/assignment-integration.test.d.ts.map +0 -1
- package/dist/tools/__tests__/assignment-integration.test.js +0 -428
- package/dist/tools/__tests__/complete-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/complete-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/complete-tasks.test.js +0 -206
- package/dist/tools/__tests__/delete-object.test.d.ts +0 -2
- package/dist/tools/__tests__/delete-object.test.d.ts.map +0 -1
- package/dist/tools/__tests__/delete-object.test.js +0 -110
- package/dist/tools/__tests__/fetch.test.d.ts +0 -2
- package/dist/tools/__tests__/fetch.test.d.ts.map +0 -1
- package/dist/tools/__tests__/fetch.test.js +0 -279
- package/dist/tools/__tests__/find-activity.test.d.ts +0 -2
- package/dist/tools/__tests__/find-activity.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-activity.test.js +0 -229
- package/dist/tools/__tests__/find-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/find-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-comments.test.js +0 -236
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-completed-tasks.test.js +0 -423
- package/dist/tools/__tests__/find-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/find-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-projects.test.js +0 -154
- package/dist/tools/__tests__/find-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/find-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-sections.test.js +0 -313
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +0 -2
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-tasks-by-date.test.js +0 -528
- package/dist/tools/__tests__/find-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/find-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-tasks.test.js +0 -771
- package/dist/tools/__tests__/get-overview.test.d.ts +0 -2
- package/dist/tools/__tests__/get-overview.test.d.ts.map +0 -1
- package/dist/tools/__tests__/get-overview.test.js +0 -225
- package/dist/tools/__tests__/search.test.d.ts +0 -2
- package/dist/tools/__tests__/search.test.d.ts.map +0 -1
- package/dist/tools/__tests__/search.test.js +0 -206
- package/dist/tools/__tests__/update-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/update-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-comments.test.js +0 -294
- package/dist/tools/__tests__/update-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/update-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-projects.test.js +0 -217
- package/dist/tools/__tests__/update-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/update-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-sections.test.js +0 -169
- package/dist/tools/__tests__/update-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/update-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-tasks.test.js +0 -788
- package/dist/tools/__tests__/user-info.test.d.ts +0 -2
- package/dist/tools/__tests__/user-info.test.d.ts.map +0 -1
- package/dist/tools/__tests__/user-info.test.js +0 -139
- package/dist/tools/add-comments.js +0 -89
- package/dist/tools/add-projects.js +0 -63
- package/dist/tools/add-sections.js +0 -74
- package/dist/tools/add-tasks.js +0 -169
- package/dist/tools/complete-tasks.js +0 -68
- package/dist/tools/delete-object.js +0 -79
- package/dist/tools/fetch.js +0 -102
- package/dist/tools/find-activity.js +0 -221
- package/dist/tools/find-comments.js +0 -148
- package/dist/tools/find-completed-tasks.js +0 -168
- package/dist/tools/find-project-collaborators.js +0 -151
- package/dist/tools/find-projects.js +0 -101
- package/dist/tools/find-sections.js +0 -101
- package/dist/tools/find-tasks-by-date.js +0 -198
- package/dist/tools/find-tasks.js +0 -329
- package/dist/tools/get-overview.js +0 -249
- package/dist/tools/manage-assignments.js +0 -337
- package/dist/tools/search.js +0 -65
- package/dist/tools/update-comments.js +0 -82
- package/dist/tools/update-projects.js +0 -84
- package/dist/tools/update-sections.js +0 -70
- package/dist/tools/update-tasks.js +0 -179
- package/dist/tools/user-info.js +0 -142
- package/dist/utils/assignment-validator.js +0 -253
- package/dist/utils/constants.js +0 -45
- package/dist/utils/duration-parser.js +0 -96
- package/dist/utils/duration-parser.test.d.ts +0 -2
- package/dist/utils/duration-parser.test.d.ts.map +0 -1
- package/dist/utils/duration-parser.test.js +0 -147
- package/dist/utils/labels.js +0 -18
- package/dist/utils/priorities.js +0 -20
- package/dist/utils/response-builders.js +0 -210
- package/dist/utils/sanitize-data.js +0 -37
- package/dist/utils/sanitize-data.test.d.ts +0 -2
- package/dist/utils/sanitize-data.test.d.ts.map +0 -1
- package/dist/utils/sanitize-data.test.js +0 -93
- package/dist/utils/test-helpers.js +0 -237
- package/dist/utils/tool-names.js +0 -40
- package/dist/utils/user-resolver.js +0 -179
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get-overview.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/get-overview.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
import { createMockProject, createMockSection, createMockTask, extractStructuredContent, extractTextContent, TEST_ERRORS, TEST_IDS, } from '../../utils/test-helpers.js';
|
|
3
|
-
import { ToolNames } from '../../utils/tool-names.js';
|
|
4
|
-
import { getOverview } from '../get-overview.js';
|
|
5
|
-
// Mock the Todoist API
|
|
6
|
-
const mockTodoistApi = {
|
|
7
|
-
getProjects: jest.fn(),
|
|
8
|
-
getProject: jest.fn(),
|
|
9
|
-
getSections: jest.fn(),
|
|
10
|
-
getTasks: jest.fn(),
|
|
11
|
-
};
|
|
12
|
-
const { GET_OVERVIEW } = ToolNames;
|
|
13
|
-
describe(`${GET_OVERVIEW} tool`, () => {
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
jest.clearAllMocks();
|
|
16
|
-
});
|
|
17
|
-
describe('account overview (no projectId)', () => {
|
|
18
|
-
it('should generate account overview with projects and sections', async () => {
|
|
19
|
-
const mockProjects = [
|
|
20
|
-
createMockProject({
|
|
21
|
-
id: TEST_IDS.PROJECT_INBOX,
|
|
22
|
-
name: 'Inbox',
|
|
23
|
-
color: 'grey',
|
|
24
|
-
inboxProject: true,
|
|
25
|
-
childOrder: 0,
|
|
26
|
-
}),
|
|
27
|
-
createMockProject({
|
|
28
|
-
id: TEST_IDS.PROJECT_TEST,
|
|
29
|
-
name: 'test-abc123def456-project',
|
|
30
|
-
childOrder: 1,
|
|
31
|
-
}),
|
|
32
|
-
];
|
|
33
|
-
const mockSections = [
|
|
34
|
-
createMockSection({
|
|
35
|
-
id: TEST_IDS.SECTION_1,
|
|
36
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
37
|
-
name: 'test-section',
|
|
38
|
-
}),
|
|
39
|
-
];
|
|
40
|
-
mockTodoistApi.getProjects.mockResolvedValue({
|
|
41
|
-
results: mockProjects,
|
|
42
|
-
nextCursor: null,
|
|
43
|
-
});
|
|
44
|
-
mockTodoistApi.getSections.mockImplementation(({ projectId }) => {
|
|
45
|
-
if (projectId === TEST_IDS.PROJECT_TEST) {
|
|
46
|
-
return Promise.resolve({ results: mockSections, nextCursor: null });
|
|
47
|
-
}
|
|
48
|
-
return Promise.resolve({ results: [], nextCursor: null });
|
|
49
|
-
});
|
|
50
|
-
const result = await getOverview.execute({}, mockTodoistApi);
|
|
51
|
-
expect(mockTodoistApi.getProjects).toHaveBeenCalledWith({});
|
|
52
|
-
expect(mockTodoistApi.getSections).toHaveBeenCalledTimes(2); // Once for each project
|
|
53
|
-
// Test text content with snapshot
|
|
54
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
55
|
-
// Test structured content sanity checks
|
|
56
|
-
const structuredContent = extractStructuredContent(result);
|
|
57
|
-
expect(structuredContent).toEqual(expect.objectContaining({
|
|
58
|
-
type: 'account_overview',
|
|
59
|
-
inbox: expect.objectContaining({
|
|
60
|
-
id: TEST_IDS.PROJECT_INBOX,
|
|
61
|
-
name: 'Inbox',
|
|
62
|
-
// sections array removed if empty
|
|
63
|
-
}),
|
|
64
|
-
projects: expect.any(Array),
|
|
65
|
-
totalProjects: 2,
|
|
66
|
-
totalSections: 1,
|
|
67
|
-
hasNestedProjects: false,
|
|
68
|
-
}));
|
|
69
|
-
expect(structuredContent.projects).toHaveLength(1); // Only non-inbox projects
|
|
70
|
-
});
|
|
71
|
-
it('should handle empty projects list', async () => {
|
|
72
|
-
mockTodoistApi.getProjects.mockResolvedValue({ results: [], nextCursor: null });
|
|
73
|
-
const result = await getOverview.execute({}, mockTodoistApi);
|
|
74
|
-
// Test text content with snapshot
|
|
75
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
76
|
-
// Test structured content sanity checks
|
|
77
|
-
const structuredContent = extractStructuredContent(result);
|
|
78
|
-
expect(structuredContent).toEqual({
|
|
79
|
-
type: 'account_overview',
|
|
80
|
-
// projects array is removed when empty
|
|
81
|
-
totalProjects: 0,
|
|
82
|
-
totalSections: 0,
|
|
83
|
-
hasNestedProjects: false,
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
describe('project overview (with projectId)', () => {
|
|
88
|
-
it('should generate detailed project overview with tasks', async () => {
|
|
89
|
-
const mockProject = createMockProject({
|
|
90
|
-
id: TEST_IDS.PROJECT_TEST,
|
|
91
|
-
name: 'test-abc123def456-project',
|
|
92
|
-
});
|
|
93
|
-
const mockSections = [
|
|
94
|
-
createMockSection({
|
|
95
|
-
id: TEST_IDS.SECTION_1,
|
|
96
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
97
|
-
name: 'To Do',
|
|
98
|
-
}),
|
|
99
|
-
createMockSection({
|
|
100
|
-
id: TEST_IDS.SECTION_2,
|
|
101
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
102
|
-
sectionOrder: 2,
|
|
103
|
-
name: 'In Progress',
|
|
104
|
-
}),
|
|
105
|
-
];
|
|
106
|
-
const mockTasks = [
|
|
107
|
-
createMockTask({
|
|
108
|
-
id: TEST_IDS.TASK_1,
|
|
109
|
-
content: 'Task without section',
|
|
110
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
111
|
-
deadline: {
|
|
112
|
-
date: '2025-08-15',
|
|
113
|
-
lang: 'en',
|
|
114
|
-
},
|
|
115
|
-
responsibleUid: TEST_IDS.USER_ID,
|
|
116
|
-
assignedByUid: TEST_IDS.USER_ID,
|
|
117
|
-
}),
|
|
118
|
-
createMockTask({
|
|
119
|
-
id: TEST_IDS.TASK_2,
|
|
120
|
-
content: 'Task in To Do section',
|
|
121
|
-
description: 'Important task',
|
|
122
|
-
labels: ['work'],
|
|
123
|
-
priority: 2,
|
|
124
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
125
|
-
sectionId: TEST_IDS.SECTION_1,
|
|
126
|
-
}),
|
|
127
|
-
createMockTask({
|
|
128
|
-
id: TEST_IDS.TASK_3,
|
|
129
|
-
content: 'Subtask of important task',
|
|
130
|
-
childOrder: 2,
|
|
131
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
132
|
-
sectionId: TEST_IDS.SECTION_1,
|
|
133
|
-
parentId: TEST_IDS.TASK_2,
|
|
134
|
-
}),
|
|
135
|
-
];
|
|
136
|
-
mockTodoistApi.getProject.mockResolvedValue(mockProject);
|
|
137
|
-
mockTodoistApi.getSections.mockResolvedValue({
|
|
138
|
-
results: mockSections,
|
|
139
|
-
nextCursor: null,
|
|
140
|
-
});
|
|
141
|
-
mockTodoistApi.getTasks.mockResolvedValue({
|
|
142
|
-
results: mockTasks,
|
|
143
|
-
nextCursor: null,
|
|
144
|
-
});
|
|
145
|
-
const result = await getOverview.execute({ projectId: TEST_IDS.PROJECT_TEST }, mockTodoistApi);
|
|
146
|
-
expect(mockTodoistApi.getProject).toHaveBeenCalledWith(TEST_IDS.PROJECT_TEST);
|
|
147
|
-
expect(mockTodoistApi.getSections).toHaveBeenCalledWith({
|
|
148
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
149
|
-
});
|
|
150
|
-
expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
|
|
151
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
152
|
-
limit: 50,
|
|
153
|
-
cursor: undefined,
|
|
154
|
-
});
|
|
155
|
-
// Test text content with snapshot
|
|
156
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
157
|
-
// Test structured content sanity checks
|
|
158
|
-
const structuredContent = extractStructuredContent(result);
|
|
159
|
-
expect(structuredContent).toEqual(expect.objectContaining({
|
|
160
|
-
type: 'project_overview',
|
|
161
|
-
project: expect.objectContaining({
|
|
162
|
-
id: TEST_IDS.PROJECT_TEST,
|
|
163
|
-
name: 'test-abc123def456-project',
|
|
164
|
-
}),
|
|
165
|
-
sections: expect.any(Array),
|
|
166
|
-
tasks: expect.any(Array),
|
|
167
|
-
stats: expect.objectContaining({
|
|
168
|
-
totalTasks: 3,
|
|
169
|
-
totalSections: 2,
|
|
170
|
-
tasksWithoutSection: 1,
|
|
171
|
-
}),
|
|
172
|
-
}));
|
|
173
|
-
expect(structuredContent.sections).toHaveLength(2);
|
|
174
|
-
expect(structuredContent.tasks).toHaveLength(3);
|
|
175
|
-
});
|
|
176
|
-
it('should handle project with no tasks', async () => {
|
|
177
|
-
const mockProject = createMockProject({
|
|
178
|
-
id: 'empty-project-id',
|
|
179
|
-
name: 'Empty Project',
|
|
180
|
-
color: 'blue',
|
|
181
|
-
});
|
|
182
|
-
mockTodoistApi.getProject.mockResolvedValue(mockProject);
|
|
183
|
-
mockTodoistApi.getSections.mockResolvedValue({ results: [], nextCursor: null });
|
|
184
|
-
mockTodoistApi.getTasks.mockResolvedValue({ results: [], nextCursor: null });
|
|
185
|
-
const result = await getOverview.execute({ projectId: 'empty-project-id' }, mockTodoistApi);
|
|
186
|
-
// Test text content with snapshot
|
|
187
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
188
|
-
// Test structured content sanity checks
|
|
189
|
-
const structuredContent = extractStructuredContent(result);
|
|
190
|
-
expect(structuredContent).toEqual({
|
|
191
|
-
type: 'project_overview',
|
|
192
|
-
project: expect.objectContaining({
|
|
193
|
-
id: 'empty-project-id',
|
|
194
|
-
name: 'Empty Project',
|
|
195
|
-
}),
|
|
196
|
-
// sections and tasks arrays are removed when empty
|
|
197
|
-
stats: expect.objectContaining({
|
|
198
|
-
totalTasks: 0,
|
|
199
|
-
totalSections: 0,
|
|
200
|
-
tasksWithoutSection: 0,
|
|
201
|
-
}),
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
describe('error handling', () => {
|
|
206
|
-
it.each([
|
|
207
|
-
{
|
|
208
|
-
scenario: 'project retrieval',
|
|
209
|
-
error: 'API Error: Project not found',
|
|
210
|
-
params: { projectId: 'non-existent-project' },
|
|
211
|
-
mockMethod: 'getProject',
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
scenario: 'projects list',
|
|
215
|
-
error: TEST_ERRORS.API_UNAUTHORIZED,
|
|
216
|
-
params: {},
|
|
217
|
-
mockMethod: 'getProjects',
|
|
218
|
-
},
|
|
219
|
-
])('should propagate API errors for $scenario', async ({ error, params, mockMethod }) => {
|
|
220
|
-
const apiError = new Error(error);
|
|
221
|
-
mockTodoistApi[mockMethod].mockRejectedValue(apiError);
|
|
222
|
-
await expect(getOverview.execute(params, mockTodoistApi)).rejects.toThrow(error);
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"search.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/search.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
import { getTasksByFilter } from '../../tool-helpers.js';
|
|
3
|
-
import { createMappedTask, createMockApiResponse, createMockProject, TEST_IDS, } from '../../utils/test-helpers.js';
|
|
4
|
-
import { ToolNames } from '../../utils/tool-names.js';
|
|
5
|
-
import { search } from '../search.js';
|
|
6
|
-
jest.mock('../../tool-helpers', () => {
|
|
7
|
-
return {
|
|
8
|
-
getTasksByFilter: jest.fn(),
|
|
9
|
-
};
|
|
10
|
-
});
|
|
11
|
-
const { SEARCH } = ToolNames;
|
|
12
|
-
const mockGetTasksByFilter = getTasksByFilter;
|
|
13
|
-
// Mock the Todoist API
|
|
14
|
-
const mockTodoistApi = {
|
|
15
|
-
getProjects: jest.fn(),
|
|
16
|
-
};
|
|
17
|
-
describe(`${SEARCH} tool`, () => {
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
jest.clearAllMocks();
|
|
20
|
-
});
|
|
21
|
-
describe('searching tasks and projects', () => {
|
|
22
|
-
it('should search both tasks and projects and return combined results', async () => {
|
|
23
|
-
const mockTasks = [
|
|
24
|
-
createMappedTask({
|
|
25
|
-
id: TEST_IDS.TASK_1,
|
|
26
|
-
content: 'Important meeting task',
|
|
27
|
-
}),
|
|
28
|
-
createMappedTask({
|
|
29
|
-
id: TEST_IDS.TASK_2,
|
|
30
|
-
content: 'Another important item',
|
|
31
|
-
}),
|
|
32
|
-
];
|
|
33
|
-
const mockProjects = [
|
|
34
|
-
createMockProject({
|
|
35
|
-
id: TEST_IDS.PROJECT_WORK,
|
|
36
|
-
name: 'Important Work Project',
|
|
37
|
-
}),
|
|
38
|
-
createMockProject({
|
|
39
|
-
id: TEST_IDS.PROJECT_TEST,
|
|
40
|
-
name: 'Test Project',
|
|
41
|
-
}),
|
|
42
|
-
];
|
|
43
|
-
mockGetTasksByFilter.mockResolvedValue({ tasks: mockTasks, nextCursor: null });
|
|
44
|
-
mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
|
|
45
|
-
const result = await search.execute({ query: 'important' }, mockTodoistApi);
|
|
46
|
-
// Verify both API calls were made
|
|
47
|
-
expect(mockGetTasksByFilter).toHaveBeenCalledWith({
|
|
48
|
-
client: mockTodoistApi,
|
|
49
|
-
query: 'search: important',
|
|
50
|
-
limit: 100, // TASKS_MAX
|
|
51
|
-
cursor: undefined,
|
|
52
|
-
});
|
|
53
|
-
expect(mockTodoistApi.getProjects).toHaveBeenCalledWith({
|
|
54
|
-
limit: 100, // PROJECTS_MAX
|
|
55
|
-
});
|
|
56
|
-
// Verify result structure
|
|
57
|
-
expect(result.content).toHaveLength(1);
|
|
58
|
-
expect(result.content[0]?.type).toBe('text');
|
|
59
|
-
// Parse the JSON response
|
|
60
|
-
const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
|
|
61
|
-
expect(jsonResponse).toHaveProperty('results');
|
|
62
|
-
expect(jsonResponse.results).toHaveLength(3); // 2 tasks + 1 project matching "important"
|
|
63
|
-
// Verify task results
|
|
64
|
-
expect(jsonResponse.results[0]).toEqual({
|
|
65
|
-
id: `task:${TEST_IDS.TASK_1}`,
|
|
66
|
-
title: 'Important meeting task',
|
|
67
|
-
url: `https://app.todoist.com/app/task/${TEST_IDS.TASK_1}`,
|
|
68
|
-
});
|
|
69
|
-
expect(jsonResponse.results[1]).toEqual({
|
|
70
|
-
id: `task:${TEST_IDS.TASK_2}`,
|
|
71
|
-
title: 'Another important item',
|
|
72
|
-
url: `https://app.todoist.com/app/task/${TEST_IDS.TASK_2}`,
|
|
73
|
-
});
|
|
74
|
-
// Verify project result (only "Important Work Project" matches)
|
|
75
|
-
expect(jsonResponse.results[2]).toEqual({
|
|
76
|
-
id: `project:${TEST_IDS.PROJECT_WORK}`,
|
|
77
|
-
title: 'Important Work Project',
|
|
78
|
-
url: `https://app.todoist.com/app/project/${TEST_IDS.PROJECT_WORK}`,
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
it('should return only matching tasks when no projects match', async () => {
|
|
82
|
-
const mockTasks = [
|
|
83
|
-
createMappedTask({
|
|
84
|
-
id: TEST_IDS.TASK_1,
|
|
85
|
-
content: 'Unique task content',
|
|
86
|
-
}),
|
|
87
|
-
];
|
|
88
|
-
const mockProjects = [
|
|
89
|
-
createMockProject({
|
|
90
|
-
id: TEST_IDS.PROJECT_WORK,
|
|
91
|
-
name: 'Work Project',
|
|
92
|
-
}),
|
|
93
|
-
];
|
|
94
|
-
mockGetTasksByFilter.mockResolvedValue({ tasks: mockTasks, nextCursor: null });
|
|
95
|
-
mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
|
|
96
|
-
const result = await search.execute({ query: 'unique' }, mockTodoistApi);
|
|
97
|
-
const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
|
|
98
|
-
expect(jsonResponse.results).toHaveLength(1);
|
|
99
|
-
expect(jsonResponse.results[0].id).toBe(`task:${TEST_IDS.TASK_1}`);
|
|
100
|
-
});
|
|
101
|
-
it('should return only matching projects when no tasks match', async () => {
|
|
102
|
-
const mockProjects = [
|
|
103
|
-
createMockProject({
|
|
104
|
-
id: TEST_IDS.PROJECT_WORK,
|
|
105
|
-
name: 'Special Project Name',
|
|
106
|
-
}),
|
|
107
|
-
createMockProject({
|
|
108
|
-
id: TEST_IDS.PROJECT_TEST,
|
|
109
|
-
name: 'Another Project',
|
|
110
|
-
}),
|
|
111
|
-
];
|
|
112
|
-
mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
|
|
113
|
-
mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
|
|
114
|
-
const result = await search.execute({ query: 'special' }, mockTodoistApi);
|
|
115
|
-
const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
|
|
116
|
-
expect(jsonResponse.results).toHaveLength(1);
|
|
117
|
-
expect(jsonResponse.results[0]).toEqual({
|
|
118
|
-
id: `project:${TEST_IDS.PROJECT_WORK}`,
|
|
119
|
-
title: 'Special Project Name',
|
|
120
|
-
url: `https://app.todoist.com/app/project/${TEST_IDS.PROJECT_WORK}`,
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
it('should return empty results when nothing matches', async () => {
|
|
124
|
-
mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
|
|
125
|
-
mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse([]));
|
|
126
|
-
const result = await search.execute({ query: 'nonexistent' }, mockTodoistApi);
|
|
127
|
-
const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
|
|
128
|
-
expect(jsonResponse.results).toHaveLength(0);
|
|
129
|
-
});
|
|
130
|
-
it('should perform case-insensitive project filtering', async () => {
|
|
131
|
-
const mockProjects = [
|
|
132
|
-
createMockProject({
|
|
133
|
-
id: TEST_IDS.PROJECT_WORK,
|
|
134
|
-
name: 'Important Work',
|
|
135
|
-
}),
|
|
136
|
-
];
|
|
137
|
-
mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
|
|
138
|
-
mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
|
|
139
|
-
const result = await search.execute({ query: 'IMPORTANT' }, mockTodoistApi);
|
|
140
|
-
const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
|
|
141
|
-
expect(jsonResponse.results).toHaveLength(1);
|
|
142
|
-
expect(jsonResponse.results[0].title).toBe('Important Work');
|
|
143
|
-
});
|
|
144
|
-
it('should handle partial matches in project names', async () => {
|
|
145
|
-
const mockProjects = [
|
|
146
|
-
createMockProject({ id: 'project-1', name: 'Development Tasks' }),
|
|
147
|
-
createMockProject({ id: 'project-2', name: 'Developer Resources' }),
|
|
148
|
-
createMockProject({ id: 'project-3', name: 'Marketing' }),
|
|
149
|
-
];
|
|
150
|
-
mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
|
|
151
|
-
mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
|
|
152
|
-
const result = await search.execute({ query: 'develop' }, mockTodoistApi);
|
|
153
|
-
const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
|
|
154
|
-
expect(jsonResponse.results).toHaveLength(2);
|
|
155
|
-
expect(jsonResponse.results[0].title).toBe('Development Tasks');
|
|
156
|
-
expect(jsonResponse.results[1].title).toBe('Developer Resources');
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
describe('error handling', () => {
|
|
160
|
-
it('should return error response for task search failure', async () => {
|
|
161
|
-
mockGetTasksByFilter.mockRejectedValue(new Error('Task search failed'));
|
|
162
|
-
mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse([]));
|
|
163
|
-
const result = await search.execute({ query: 'test' }, mockTodoistApi);
|
|
164
|
-
expect(result.isError).toBe(true);
|
|
165
|
-
expect(result.content[0]?.text).toBe('Task search failed');
|
|
166
|
-
});
|
|
167
|
-
it('should return error response for project search failure', async () => {
|
|
168
|
-
mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
|
|
169
|
-
mockTodoistApi.getProjects.mockRejectedValue(new Error('Project search failed'));
|
|
170
|
-
const result = await search.execute({ query: 'test' }, mockTodoistApi);
|
|
171
|
-
expect(result.isError).toBe(true);
|
|
172
|
-
expect(result.content[0]?.text).toBe('Project search failed');
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
describe('OpenAI MCP spec compliance', () => {
|
|
176
|
-
it('should return exactly one content item with type "text"', async () => {
|
|
177
|
-
mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
|
|
178
|
-
mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse([]));
|
|
179
|
-
const result = await search.execute({ query: 'test' }, mockTodoistApi);
|
|
180
|
-
expect(result.content).toHaveLength(1);
|
|
181
|
-
expect(result.content[0]?.type).toBe('text');
|
|
182
|
-
});
|
|
183
|
-
it('should return valid JSON string in text field', async () => {
|
|
184
|
-
mockGetTasksByFilter.mockResolvedValue({ tasks: [], nextCursor: null });
|
|
185
|
-
mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse([]));
|
|
186
|
-
const result = await search.execute({ query: 'test' }, mockTodoistApi);
|
|
187
|
-
expect(() => JSON.parse(result.content[0]?.text ?? '{}')).not.toThrow();
|
|
188
|
-
});
|
|
189
|
-
it('should include required fields (id, title, url) in each result', async () => {
|
|
190
|
-
const mockTasks = [createMappedTask({ id: TEST_IDS.TASK_1, content: 'Test' })];
|
|
191
|
-
const mockProjects = [createMockProject({ id: TEST_IDS.PROJECT_WORK, name: 'Test' })];
|
|
192
|
-
mockGetTasksByFilter.mockResolvedValue({ tasks: mockTasks, nextCursor: null });
|
|
193
|
-
mockTodoistApi.getProjects.mockResolvedValue(createMockApiResponse(mockProjects));
|
|
194
|
-
const result = await search.execute({ query: 'test' }, mockTodoistApi);
|
|
195
|
-
const jsonResponse = JSON.parse(result.content[0]?.text ?? '{}');
|
|
196
|
-
for (const item of jsonResponse.results) {
|
|
197
|
-
expect(item).toHaveProperty('id');
|
|
198
|
-
expect(item).toHaveProperty('title');
|
|
199
|
-
expect(item).toHaveProperty('url');
|
|
200
|
-
expect(typeof item.id).toBe('string');
|
|
201
|
-
expect(typeof item.title).toBe('string');
|
|
202
|
-
expect(typeof item.url).toBe('string');
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"update-comments.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/update-comments.test.ts"],"names":[],"mappings":""}
|