@doist/todoist-ai 4.15.1 → 4.16.1
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 +175 -175
- package/dist/index.js +61 -81
- package/dist/main.js +15 -23
- package/dist/mcp-helpers.d.ts +4 -4
- package/dist/mcp-server-6tm7Rhyz.js +2840 -0
- package/dist/todoist-tool.d.ts +2 -2
- package/dist/tool-helpers.d.ts +1 -1
- package/dist/tools/add-comments.d.ts +1 -1
- package/dist/tools/add-comments.d.ts.map +1 -1
- package/dist/tools/add-projects.d.ts +4 -4
- package/dist/tools/add-projects.d.ts.map +1 -1
- package/dist/tools/add-sections.d.ts +1 -1
- package/dist/tools/add-sections.d.ts.map +1 -1
- package/dist/tools/add-tasks.d.ts +4 -4
- package/dist/tools/add-tasks.d.ts.map +1 -1
- package/dist/tools/complete-tasks.d.ts +1 -1
- package/dist/tools/complete-tasks.d.ts.map +1 -1
- package/dist/tools/delete-object.d.ts +3 -3
- package/dist/tools/delete-object.d.ts.map +1 -1
- package/dist/tools/fetch.d.ts +1 -1
- package/dist/tools/find-activity.d.ts +5 -5
- package/dist/tools/find-activity.d.ts.map +1 -1
- package/dist/tools/find-comments.d.ts +2 -2
- package/dist/tools/find-comments.d.ts.map +1 -1
- package/dist/tools/find-completed-tasks.d.ts +3 -3
- package/dist/tools/find-completed-tasks.d.ts.map +1 -1
- package/dist/tools/find-project-collaborators.d.ts +2 -2
- package/dist/tools/find-projects.d.ts +1 -1
- package/dist/tools/find-projects.d.ts.map +1 -1
- package/dist/tools/find-sections.d.ts +1 -1
- package/dist/tools/find-sections.d.ts.map +1 -1
- package/dist/tools/find-tasks-by-date.d.ts +1 -1
- package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
- package/dist/tools/find-tasks.d.ts +3 -3
- package/dist/tools/find-tasks.d.ts.map +1 -1
- package/dist/tools/get-overview.d.ts +1 -1
- package/dist/tools/manage-assignments.d.ts +1 -1
- package/dist/tools/search.d.ts +1 -1
- package/dist/tools/update-comments.d.ts +4 -4
- package/dist/tools/update-comments.d.ts.map +1 -1
- package/dist/tools/update-projects.d.ts +1 -1
- package/dist/tools/update-projects.d.ts.map +1 -1
- package/dist/tools/update-sections.d.ts +4 -4
- package/dist/tools/update-sections.d.ts.map +1 -1
- package/dist/tools/update-tasks.d.ts +7 -7
- package/dist/tools/update-tasks.d.ts.map +1 -1
- package/dist/tools/user-info.d.ts +1 -1
- package/dist/utils/assignment-validator.d.ts +2 -2
- 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 +11 -9
- 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 -533
- 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 -324
- 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 -245
- 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 -79
- package/dist/tools/add-projects.js +0 -63
- package/dist/tools/add-sections.js +0 -61
- package/dist/tools/add-tasks.js +0 -160
- 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 -143
- package/dist/tools/find-completed-tasks.js +0 -161
- package/dist/tools/find-project-collaborators.js +0 -151
- package/dist/tools/find-projects.js +0 -101
- package/dist/tools/find-sections.js +0 -96
- 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 -170
- 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,236 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
import { extractStructuredContent, extractTextContent } from '../../utils/test-helpers.js';
|
|
3
|
-
import { ToolNames } from '../../utils/tool-names.js';
|
|
4
|
-
import { findComments } from '../find-comments.js';
|
|
5
|
-
// Mock the Todoist API
|
|
6
|
-
const mockTodoistApi = {
|
|
7
|
-
getComment: jest.fn(),
|
|
8
|
-
getComments: jest.fn(),
|
|
9
|
-
};
|
|
10
|
-
const { FIND_COMMENTS } = ToolNames;
|
|
11
|
-
const createMockComment = (overrides = {}) => ({
|
|
12
|
-
id: '12345',
|
|
13
|
-
content: 'Test comment content',
|
|
14
|
-
postedAt: '2024-01-01T12:00:00Z',
|
|
15
|
-
postedUid: 'user123',
|
|
16
|
-
taskId: 'task123',
|
|
17
|
-
projectId: undefined,
|
|
18
|
-
fileAttachment: null,
|
|
19
|
-
uidsToNotify: null,
|
|
20
|
-
reactions: null,
|
|
21
|
-
isDeleted: false,
|
|
22
|
-
...overrides,
|
|
23
|
-
});
|
|
24
|
-
describe(`${FIND_COMMENTS} tool`, () => {
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
jest.clearAllMocks();
|
|
27
|
-
});
|
|
28
|
-
describe('finding comments by task', () => {
|
|
29
|
-
it('should find comments for a task', async () => {
|
|
30
|
-
const mockComments = [
|
|
31
|
-
createMockComment({ id: '1', content: 'First comment', taskId: 'task123' }),
|
|
32
|
-
createMockComment({ id: '2', content: 'Second comment', taskId: 'task123' }),
|
|
33
|
-
];
|
|
34
|
-
mockTodoistApi.getComments.mockResolvedValue({
|
|
35
|
-
results: mockComments,
|
|
36
|
-
nextCursor: null,
|
|
37
|
-
});
|
|
38
|
-
const result = await findComments.execute({
|
|
39
|
-
taskId: 'task123',
|
|
40
|
-
}, mockTodoistApi);
|
|
41
|
-
expect(mockTodoistApi.getComments).toHaveBeenCalledWith({
|
|
42
|
-
taskId: 'task123',
|
|
43
|
-
cursor: null,
|
|
44
|
-
limit: 10,
|
|
45
|
-
});
|
|
46
|
-
// Verify result is a concise summary
|
|
47
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
48
|
-
// Verify structured content
|
|
49
|
-
const structuredContent = extractStructuredContent(result);
|
|
50
|
-
expect(structuredContent).toEqual(expect.objectContaining({
|
|
51
|
-
comments: expect.arrayContaining([
|
|
52
|
-
expect.objectContaining({ id: '1', content: 'First comment' }),
|
|
53
|
-
expect.objectContaining({ id: '2', content: 'Second comment' }),
|
|
54
|
-
]),
|
|
55
|
-
searchType: 'task',
|
|
56
|
-
searchId: 'task123',
|
|
57
|
-
hasMore: false,
|
|
58
|
-
totalCount: 2,
|
|
59
|
-
}));
|
|
60
|
-
});
|
|
61
|
-
it('should handle pagination', async () => {
|
|
62
|
-
const mockComments = [createMockComment({ id: '1', content: 'Comment 1' })];
|
|
63
|
-
mockTodoistApi.getComments.mockResolvedValue({
|
|
64
|
-
results: mockComments,
|
|
65
|
-
nextCursor: 'next_page_token',
|
|
66
|
-
});
|
|
67
|
-
const result = await findComments.execute({
|
|
68
|
-
taskId: 'task123',
|
|
69
|
-
limit: 1,
|
|
70
|
-
cursor: 'current_cursor',
|
|
71
|
-
}, mockTodoistApi);
|
|
72
|
-
expect(mockTodoistApi.getComments).toHaveBeenCalledWith({
|
|
73
|
-
taskId: 'task123',
|
|
74
|
-
cursor: 'current_cursor',
|
|
75
|
-
limit: 1,
|
|
76
|
-
});
|
|
77
|
-
// Verify result includes pagination info
|
|
78
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
79
|
-
// Verify structured content includes pagination
|
|
80
|
-
const structuredContent = extractStructuredContent(result);
|
|
81
|
-
expect(structuredContent).toEqual(expect.objectContaining({
|
|
82
|
-
comments: expect.arrayContaining([
|
|
83
|
-
expect.objectContaining({ id: '1', content: 'Comment 1' }),
|
|
84
|
-
]),
|
|
85
|
-
searchType: 'task',
|
|
86
|
-
searchId: 'task123',
|
|
87
|
-
hasMore: true,
|
|
88
|
-
nextCursor: 'next_page_token',
|
|
89
|
-
totalCount: 1,
|
|
90
|
-
}));
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
describe('finding comments by project', () => {
|
|
94
|
-
it('should find comments for a project', async () => {
|
|
95
|
-
const mockComments = [
|
|
96
|
-
createMockComment({
|
|
97
|
-
id: '1',
|
|
98
|
-
content: 'Project comment',
|
|
99
|
-
taskId: undefined,
|
|
100
|
-
projectId: 'project456',
|
|
101
|
-
}),
|
|
102
|
-
];
|
|
103
|
-
mockTodoistApi.getComments.mockResolvedValue({
|
|
104
|
-
results: mockComments,
|
|
105
|
-
nextCursor: null,
|
|
106
|
-
});
|
|
107
|
-
const result = await findComments.execute({
|
|
108
|
-
projectId: 'project456',
|
|
109
|
-
}, mockTodoistApi);
|
|
110
|
-
expect(mockTodoistApi.getComments).toHaveBeenCalledWith({
|
|
111
|
-
projectId: 'project456',
|
|
112
|
-
cursor: null,
|
|
113
|
-
limit: 10,
|
|
114
|
-
});
|
|
115
|
-
// Verify result is a concise summary
|
|
116
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
117
|
-
// Verify structured content
|
|
118
|
-
const structuredContent = extractStructuredContent(result);
|
|
119
|
-
expect(structuredContent).toEqual(expect.objectContaining({
|
|
120
|
-
comments: expect.arrayContaining([
|
|
121
|
-
expect.objectContaining({
|
|
122
|
-
id: '1',
|
|
123
|
-
content: 'Project comment',
|
|
124
|
-
projectId: 'project456',
|
|
125
|
-
}),
|
|
126
|
-
]),
|
|
127
|
-
searchType: 'project',
|
|
128
|
-
searchId: 'project456',
|
|
129
|
-
hasMore: false,
|
|
130
|
-
totalCount: 1,
|
|
131
|
-
}));
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
describe('finding single comment', () => {
|
|
135
|
-
it('should find comment by ID', async () => {
|
|
136
|
-
const mockComment = createMockComment({
|
|
137
|
-
id: 'comment789',
|
|
138
|
-
content: 'Single comment content',
|
|
139
|
-
taskId: 'task123',
|
|
140
|
-
});
|
|
141
|
-
mockTodoistApi.getComment.mockResolvedValue(mockComment);
|
|
142
|
-
const result = await findComments.execute({
|
|
143
|
-
commentId: 'comment789',
|
|
144
|
-
}, mockTodoistApi);
|
|
145
|
-
expect(mockTodoistApi.getComment).toHaveBeenCalledWith('comment789');
|
|
146
|
-
// Verify result is a concise summary
|
|
147
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
148
|
-
// Verify structured content
|
|
149
|
-
const structuredContent = extractStructuredContent(result);
|
|
150
|
-
expect(structuredContent).toEqual(expect.objectContaining({
|
|
151
|
-
comments: expect.arrayContaining([
|
|
152
|
-
expect.objectContaining({
|
|
153
|
-
id: 'comment789',
|
|
154
|
-
content: 'Single comment content',
|
|
155
|
-
taskId: 'task123',
|
|
156
|
-
}),
|
|
157
|
-
]),
|
|
158
|
-
searchType: 'single',
|
|
159
|
-
searchId: 'comment789',
|
|
160
|
-
hasMore: false,
|
|
161
|
-
totalCount: 1,
|
|
162
|
-
}));
|
|
163
|
-
});
|
|
164
|
-
it('should handle comment with attachment', async () => {
|
|
165
|
-
const mockComment = createMockComment({
|
|
166
|
-
id: 'comment789',
|
|
167
|
-
content: 'Comment with file',
|
|
168
|
-
fileAttachment: {
|
|
169
|
-
resourceType: 'file',
|
|
170
|
-
fileName: 'document.pdf',
|
|
171
|
-
fileUrl: 'https://example.com/document.pdf',
|
|
172
|
-
fileType: 'application/pdf',
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
mockTodoistApi.getComment.mockResolvedValue(mockComment);
|
|
176
|
-
const result = await findComments.execute({
|
|
177
|
-
commentId: 'comment789',
|
|
178
|
-
}, mockTodoistApi);
|
|
179
|
-
// Verify result includes attachment info
|
|
180
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
181
|
-
// Verify structured content includes attachment
|
|
182
|
-
const structuredContent = extractStructuredContent(result);
|
|
183
|
-
expect(structuredContent).toEqual(expect.objectContaining({
|
|
184
|
-
comments: expect.arrayContaining([
|
|
185
|
-
expect.objectContaining({
|
|
186
|
-
id: 'comment789',
|
|
187
|
-
content: 'Comment with file',
|
|
188
|
-
fileAttachment: expect.objectContaining({
|
|
189
|
-
resourceType: 'file',
|
|
190
|
-
fileName: 'document.pdf',
|
|
191
|
-
fileUrl: 'https://example.com/document.pdf',
|
|
192
|
-
fileType: 'application/pdf',
|
|
193
|
-
}),
|
|
194
|
-
}),
|
|
195
|
-
]),
|
|
196
|
-
searchType: 'single',
|
|
197
|
-
searchId: 'comment789',
|
|
198
|
-
hasMore: false,
|
|
199
|
-
totalCount: 1,
|
|
200
|
-
}));
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
describe('validation', () => {
|
|
204
|
-
it('should throw error when no search parameter provided', async () => {
|
|
205
|
-
await expect(findComments.execute({}, mockTodoistApi)).rejects.toThrow('Must provide exactly one of: taskId, projectId, or commentId.');
|
|
206
|
-
});
|
|
207
|
-
it('should throw error when multiple search parameters provided', async () => {
|
|
208
|
-
await expect(findComments.execute({
|
|
209
|
-
taskId: 'task123',
|
|
210
|
-
projectId: 'project456',
|
|
211
|
-
}, mockTodoistApi)).rejects.toThrow('Cannot provide multiple search parameters.');
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
describe('empty results', () => {
|
|
215
|
-
it('should handle no comments found', async () => {
|
|
216
|
-
mockTodoistApi.getComments.mockResolvedValue({
|
|
217
|
-
results: [],
|
|
218
|
-
nextCursor: null,
|
|
219
|
-
});
|
|
220
|
-
const result = await findComments.execute({
|
|
221
|
-
taskId: 'task123',
|
|
222
|
-
}, mockTodoistApi);
|
|
223
|
-
// Verify result handles empty case
|
|
224
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
225
|
-
// Verify structured content
|
|
226
|
-
const structuredContent = extractStructuredContent(result);
|
|
227
|
-
expect(structuredContent).toEqual({
|
|
228
|
-
searchType: 'task',
|
|
229
|
-
searchId: 'task123',
|
|
230
|
-
hasMore: false,
|
|
231
|
-
totalCount: 0,
|
|
232
|
-
// comments array is removed when empty
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"find-completed-tasks.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/find-completed-tasks.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
import { createMockTask, extractTextContent } from '../../utils/test-helpers.js';
|
|
3
|
-
import { ToolNames } from '../../utils/tool-names.js';
|
|
4
|
-
import { findCompletedTasks } from '../find-completed-tasks.js';
|
|
5
|
-
// Mock the Todoist API
|
|
6
|
-
const mockTodoistApi = {
|
|
7
|
-
getCompletedTasksByCompletionDate: jest.fn(),
|
|
8
|
-
getCompletedTasksByDueDate: jest.fn(),
|
|
9
|
-
getUser: jest.fn(),
|
|
10
|
-
};
|
|
11
|
-
const { FIND_COMPLETED_TASKS } = ToolNames;
|
|
12
|
-
describe(`${FIND_COMPLETED_TASKS} tool`, () => {
|
|
13
|
-
beforeEach(() => {
|
|
14
|
-
jest.clearAllMocks();
|
|
15
|
-
// Mock default user with UTC timezone
|
|
16
|
-
mockTodoistApi.getUser.mockResolvedValue({
|
|
17
|
-
id: 'test-user-id',
|
|
18
|
-
fullName: 'Test User',
|
|
19
|
-
email: 'test@example.com',
|
|
20
|
-
tzInfo: {
|
|
21
|
-
timezone: 'UTC',
|
|
22
|
-
gmtString: '+00:00',
|
|
23
|
-
hours: 0,
|
|
24
|
-
minutes: 0,
|
|
25
|
-
isDst: 0,
|
|
26
|
-
},
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
describe('getting completed tasks by completion date (default)', () => {
|
|
30
|
-
it('should get completed tasks by completion date', async () => {
|
|
31
|
-
const mockCompletedTasks = [
|
|
32
|
-
createMockTask({
|
|
33
|
-
id: '8485093748',
|
|
34
|
-
content: 'Completed task 1',
|
|
35
|
-
description: 'Task completed yesterday',
|
|
36
|
-
completedAt: '2024-01-01T00:00:00Z',
|
|
37
|
-
labels: ['work'],
|
|
38
|
-
priority: 2,
|
|
39
|
-
url: 'https://todoist.com/showTask?id=8485093748',
|
|
40
|
-
addedAt: '2025-08-13T22:09:56.123456Z',
|
|
41
|
-
due: {
|
|
42
|
-
date: '2025-08-14',
|
|
43
|
-
isRecurring: false,
|
|
44
|
-
lang: 'en',
|
|
45
|
-
string: 'Aug 14',
|
|
46
|
-
timezone: null,
|
|
47
|
-
},
|
|
48
|
-
}),
|
|
49
|
-
];
|
|
50
|
-
mockTodoistApi.getCompletedTasksByCompletionDate.mockResolvedValue({
|
|
51
|
-
items: mockCompletedTasks,
|
|
52
|
-
nextCursor: null,
|
|
53
|
-
});
|
|
54
|
-
const result = await findCompletedTasks.execute({
|
|
55
|
-
getBy: 'completion',
|
|
56
|
-
limit: 50,
|
|
57
|
-
since: '2025-08-10',
|
|
58
|
-
until: '2025-08-15',
|
|
59
|
-
labels: [],
|
|
60
|
-
labelsOperator: 'or',
|
|
61
|
-
}, mockTodoistApi);
|
|
62
|
-
expect(mockTodoistApi.getCompletedTasksByCompletionDate).toHaveBeenCalledWith({
|
|
63
|
-
since: '2025-08-10T00:00:00.000Z',
|
|
64
|
-
until: '2025-08-15T23:59:59.000Z',
|
|
65
|
-
limit: 50,
|
|
66
|
-
});
|
|
67
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
68
|
-
});
|
|
69
|
-
it('should handle explicit completion date query', async () => {
|
|
70
|
-
mockTodoistApi.getCompletedTasksByCompletionDate.mockResolvedValue({
|
|
71
|
-
items: [],
|
|
72
|
-
nextCursor: 'next-cursor',
|
|
73
|
-
});
|
|
74
|
-
const result = await findCompletedTasks.execute({
|
|
75
|
-
getBy: 'completion',
|
|
76
|
-
limit: 100,
|
|
77
|
-
since: '2025-08-01',
|
|
78
|
-
until: '2025-08-31',
|
|
79
|
-
projectId: 'specific-project-id',
|
|
80
|
-
cursor: 'current-cursor',
|
|
81
|
-
labels: [],
|
|
82
|
-
labelsOperator: 'or',
|
|
83
|
-
}, mockTodoistApi);
|
|
84
|
-
expect(mockTodoistApi.getCompletedTasksByCompletionDate).toHaveBeenCalledWith({
|
|
85
|
-
since: '2025-08-01T00:00:00.000Z',
|
|
86
|
-
until: '2025-08-31T23:59:59.000Z',
|
|
87
|
-
projectId: 'specific-project-id',
|
|
88
|
-
limit: 100,
|
|
89
|
-
cursor: 'current-cursor',
|
|
90
|
-
});
|
|
91
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
describe('getting completed tasks by due date', () => {
|
|
95
|
-
it('should get completed tasks by due date', async () => {
|
|
96
|
-
const mockCompletedTasks = [
|
|
97
|
-
createMockTask({
|
|
98
|
-
id: '8485093750',
|
|
99
|
-
content: 'Task completed by due date',
|
|
100
|
-
description: 'This task was due and completed',
|
|
101
|
-
completedAt: '2024-01-01T00:00:00Z',
|
|
102
|
-
labels: ['urgent'],
|
|
103
|
-
priority: 3,
|
|
104
|
-
url: 'https://todoist.com/showTask?id=8485093750',
|
|
105
|
-
addedAt: '2025-08-13T22:09:58.123456Z',
|
|
106
|
-
due: {
|
|
107
|
-
date: '2025-08-15',
|
|
108
|
-
isRecurring: true,
|
|
109
|
-
lang: 'en',
|
|
110
|
-
string: 'every Monday',
|
|
111
|
-
timezone: null,
|
|
112
|
-
},
|
|
113
|
-
}),
|
|
114
|
-
];
|
|
115
|
-
mockTodoistApi.getCompletedTasksByDueDate.mockResolvedValue({
|
|
116
|
-
items: mockCompletedTasks,
|
|
117
|
-
nextCursor: null,
|
|
118
|
-
});
|
|
119
|
-
const result = await findCompletedTasks.execute({
|
|
120
|
-
getBy: 'due',
|
|
121
|
-
limit: 50,
|
|
122
|
-
since: '2025-08-10',
|
|
123
|
-
until: '2025-08-20',
|
|
124
|
-
labels: [],
|
|
125
|
-
labelsOperator: 'or',
|
|
126
|
-
}, mockTodoistApi);
|
|
127
|
-
expect(mockTodoistApi.getCompletedTasksByDueDate).toHaveBeenCalledWith({
|
|
128
|
-
since: '2025-08-10T00:00:00.000Z',
|
|
129
|
-
until: '2025-08-20T23:59:59.000Z',
|
|
130
|
-
limit: 50,
|
|
131
|
-
});
|
|
132
|
-
expect(mockTodoistApi.getCompletedTasksByCompletionDate).not.toHaveBeenCalled();
|
|
133
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
describe('label filtering', () => {
|
|
137
|
-
it.each([
|
|
138
|
-
{
|
|
139
|
-
name: 'single label with OR operator',
|
|
140
|
-
params: {
|
|
141
|
-
getBy: 'completion',
|
|
142
|
-
since: '2025-08-01',
|
|
143
|
-
until: '2025-08-31',
|
|
144
|
-
limit: 50,
|
|
145
|
-
labels: ['work'],
|
|
146
|
-
},
|
|
147
|
-
expectedMethod: 'getCompletedTasksByCompletionDate',
|
|
148
|
-
expectedFilter: '(@work)',
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
name: 'multiple labels with AND operator',
|
|
152
|
-
params: {
|
|
153
|
-
getBy: 'due',
|
|
154
|
-
since: '2025-08-01',
|
|
155
|
-
until: '2025-08-31',
|
|
156
|
-
limit: 50,
|
|
157
|
-
labels: ['work', 'urgent'],
|
|
158
|
-
labelsOperator: 'and',
|
|
159
|
-
},
|
|
160
|
-
expectedMethod: 'getCompletedTasksByDueDate',
|
|
161
|
-
expectedFilter: '(@work & @urgent)',
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
name: 'multiple labels with OR operator',
|
|
165
|
-
params: {
|
|
166
|
-
getBy: 'completion',
|
|
167
|
-
since: '2025-08-10',
|
|
168
|
-
until: '2025-08-20',
|
|
169
|
-
limit: 25,
|
|
170
|
-
labels: ['personal', 'shopping'],
|
|
171
|
-
},
|
|
172
|
-
expectedMethod: 'getCompletedTasksByCompletionDate',
|
|
173
|
-
expectedFilter: '(@personal | @shopping)',
|
|
174
|
-
},
|
|
175
|
-
])('should filter completed tasks by labels: $name', async ({ params, expectedMethod, expectedFilter }) => {
|
|
176
|
-
const mockCompletedTasks = [
|
|
177
|
-
createMockTask({
|
|
178
|
-
id: '8485093748',
|
|
179
|
-
content: 'Completed task with label',
|
|
180
|
-
labels: params.labels,
|
|
181
|
-
completedAt: '2024-01-01T00:00:00Z',
|
|
182
|
-
}),
|
|
183
|
-
];
|
|
184
|
-
const mockResponse = { items: mockCompletedTasks, nextCursor: null };
|
|
185
|
-
const mockMethod = mockTodoistApi[expectedMethod];
|
|
186
|
-
mockMethod.mockResolvedValue(mockResponse);
|
|
187
|
-
const result = await findCompletedTasks.execute(params, mockTodoistApi);
|
|
188
|
-
expect(mockMethod).toHaveBeenCalledWith({
|
|
189
|
-
since: `${params.since}T00:00:00.000Z`,
|
|
190
|
-
until: `${params.until}T23:59:59.000Z`,
|
|
191
|
-
limit: params.limit,
|
|
192
|
-
filterQuery: expectedFilter,
|
|
193
|
-
filterLang: 'en',
|
|
194
|
-
});
|
|
195
|
-
const textContent = extractTextContent(result);
|
|
196
|
-
expect(textContent).toMatchSnapshot();
|
|
197
|
-
});
|
|
198
|
-
it('should handle empty labels array', async () => {
|
|
199
|
-
const params = {
|
|
200
|
-
getBy: 'completion',
|
|
201
|
-
since: '2025-08-01',
|
|
202
|
-
until: '2025-08-31',
|
|
203
|
-
limit: 50,
|
|
204
|
-
labels: [],
|
|
205
|
-
labelsOperator: 'or',
|
|
206
|
-
};
|
|
207
|
-
const mockResponse = { items: [], nextCursor: null };
|
|
208
|
-
mockTodoistApi.getCompletedTasksByCompletionDate.mockResolvedValue(mockResponse);
|
|
209
|
-
await findCompletedTasks.execute(params, mockTodoistApi);
|
|
210
|
-
expect(mockTodoistApi.getCompletedTasksByCompletionDate).toHaveBeenCalledWith({
|
|
211
|
-
since: `${params.since}T00:00:00.000Z`,
|
|
212
|
-
until: `${params.until}T23:59:59.000Z`,
|
|
213
|
-
limit: params.limit,
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
it('should combine other filters with label filters', async () => {
|
|
217
|
-
const params = {
|
|
218
|
-
getBy: 'due',
|
|
219
|
-
since: '2025-08-01',
|
|
220
|
-
until: '2025-08-31',
|
|
221
|
-
limit: 25,
|
|
222
|
-
projectId: 'test-project-id',
|
|
223
|
-
sectionId: 'test-section-id',
|
|
224
|
-
labels: ['important'],
|
|
225
|
-
labelsOperator: 'or',
|
|
226
|
-
};
|
|
227
|
-
const mockTasks = [
|
|
228
|
-
createMockTask({
|
|
229
|
-
content: 'Important completed task',
|
|
230
|
-
labels: ['important'],
|
|
231
|
-
completedAt: '2024-01-01T00:00:00Z',
|
|
232
|
-
}),
|
|
233
|
-
];
|
|
234
|
-
const mockResponse = { items: mockTasks, nextCursor: null };
|
|
235
|
-
mockTodoistApi.getCompletedTasksByDueDate.mockResolvedValue(mockResponse);
|
|
236
|
-
const result = await findCompletedTasks.execute(params, mockTodoistApi);
|
|
237
|
-
expect(mockTodoistApi.getCompletedTasksByDueDate).toHaveBeenCalledWith({
|
|
238
|
-
since: `${params.since}T00:00:00.000Z`,
|
|
239
|
-
until: `${params.until}T23:59:59.000Z`,
|
|
240
|
-
limit: params.limit,
|
|
241
|
-
projectId: params.projectId,
|
|
242
|
-
sectionId: params.sectionId,
|
|
243
|
-
filterQuery: '(@important)',
|
|
244
|
-
filterLang: 'en',
|
|
245
|
-
});
|
|
246
|
-
const textContent = extractTextContent(result);
|
|
247
|
-
expect(textContent).toMatchSnapshot();
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
describe('timezone handling', () => {
|
|
251
|
-
it('should convert user timezone to UTC correctly (Europe/Madrid)', async () => {
|
|
252
|
-
// Mock user with Madrid timezone
|
|
253
|
-
mockTodoistApi.getUser.mockResolvedValue({
|
|
254
|
-
id: 'test-user-id',
|
|
255
|
-
fullName: 'Test User',
|
|
256
|
-
email: 'test@example.com',
|
|
257
|
-
tzInfo: {
|
|
258
|
-
timezone: 'Europe/Madrid',
|
|
259
|
-
gmtString: '+02:00',
|
|
260
|
-
hours: 2,
|
|
261
|
-
minutes: 0,
|
|
262
|
-
isDst: 0,
|
|
263
|
-
},
|
|
264
|
-
});
|
|
265
|
-
const mockCompletedTasks = [
|
|
266
|
-
createMockTask({
|
|
267
|
-
id: '8485093750',
|
|
268
|
-
content: 'Task completed in Madrid timezone',
|
|
269
|
-
completedAt: '2025-10-11T15:30:00Z',
|
|
270
|
-
}),
|
|
271
|
-
];
|
|
272
|
-
mockTodoistApi.getCompletedTasksByCompletionDate.mockResolvedValue({
|
|
273
|
-
items: mockCompletedTasks,
|
|
274
|
-
nextCursor: null,
|
|
275
|
-
});
|
|
276
|
-
const result = await findCompletedTasks.execute({
|
|
277
|
-
getBy: 'completion',
|
|
278
|
-
limit: 50,
|
|
279
|
-
since: '2025-10-11',
|
|
280
|
-
until: '2025-10-11',
|
|
281
|
-
labels: [],
|
|
282
|
-
labelsOperator: 'or',
|
|
283
|
-
}, mockTodoistApi);
|
|
284
|
-
// Should convert Madrid local time to UTC
|
|
285
|
-
// 2025-10-11 00:00:00 +02:00 = 2025-10-10 22:00:00 UTC
|
|
286
|
-
// 2025-10-11 23:59:59 +02:00 = 2025-10-11 21:59:59 UTC
|
|
287
|
-
expect(mockTodoistApi.getCompletedTasksByCompletionDate).toHaveBeenCalledWith({
|
|
288
|
-
since: '2025-10-10T22:00:00.000Z',
|
|
289
|
-
until: '2025-10-11T21:59:59.000Z',
|
|
290
|
-
limit: 50,
|
|
291
|
-
});
|
|
292
|
-
expect(extractTextContent(result)).toMatchSnapshot();
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
describe('error handling', () => {
|
|
296
|
-
it('should propagate completion date API errors', async () => {
|
|
297
|
-
const apiError = new Error('API Error: Invalid date range');
|
|
298
|
-
mockTodoistApi.getCompletedTasksByCompletionDate.mockRejectedValue(apiError);
|
|
299
|
-
await expect(findCompletedTasks.execute(
|
|
300
|
-
// invalid date range
|
|
301
|
-
{
|
|
302
|
-
getBy: 'completion',
|
|
303
|
-
limit: 50,
|
|
304
|
-
since: '2025-08-31',
|
|
305
|
-
until: '2025-08-01',
|
|
306
|
-
labels: [],
|
|
307
|
-
labelsOperator: 'or',
|
|
308
|
-
}, mockTodoistApi)).rejects.toThrow('API Error: Invalid date range');
|
|
309
|
-
});
|
|
310
|
-
it('should propagate due date API errors', async () => {
|
|
311
|
-
const apiError = new Error('API Error: Project not found');
|
|
312
|
-
mockTodoistApi.getCompletedTasksByDueDate.mockRejectedValue(apiError);
|
|
313
|
-
await expect(findCompletedTasks.execute({
|
|
314
|
-
getBy: 'due',
|
|
315
|
-
limit: 50,
|
|
316
|
-
since: '2025-08-01',
|
|
317
|
-
until: '2025-08-31',
|
|
318
|
-
projectId: 'non-existent-project',
|
|
319
|
-
labels: [],
|
|
320
|
-
labelsOperator: 'or',
|
|
321
|
-
}, mockTodoistApi)).rejects.toThrow('API Error: Project not found');
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"find-projects.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/find-projects.test.ts"],"names":[],"mappings":""}
|