@@ -45,7 +45,14 @@ describe(`${FIND_COMPLETED_TASKS} tool`, () => {
45
45
} )
46
46
47
47
const result = await findCompletedTasks . execute (
48
- { getBy : 'completion' , limit : 50 , since : '2025-08-10' , until : '2025-08-15' } ,
48
+ {
49
+ getBy : 'completion' ,
50
+ limit : 50 ,
51
+ since : '2025-08-10' ,
52
+ until : '2025-08-15' ,
53
+ labels : [ ] ,
54
+ labelsOperator : 'or' as const ,
55
+ } ,
49
56
mockTodoistApi ,
50
57
)
51
58
@@ -72,6 +79,8 @@ describe(`${FIND_COMPLETED_TASKS} tool`, () => {
72
79
until : '2025-08-31' ,
73
80
projectId : 'specific-project-id' ,
74
81
cursor : 'current-cursor' ,
82
+ labels : [ ] ,
83
+ labelsOperator : 'or' as const ,
75
84
} ,
76
85
mockTodoistApi ,
77
86
)
@@ -121,6 +130,8 @@ describe(`${FIND_COMPLETED_TASKS} tool`, () => {
121
130
limit : 50 ,
122
131
since : '2025-08-10' ,
123
132
until : '2025-08-20' ,
133
+ labels : [ ] ,
134
+ labelsOperator : 'or' as const ,
124
135
} ,
125
136
mockTodoistApi ,
126
137
)
@@ -136,6 +147,141 @@ describe(`${FIND_COMPLETED_TASKS} tool`, () => {
136
147
} )
137
148
} )
138
149
150
+ describe ( 'label filtering' , ( ) => {
151
+ it . each ( [
152
+ {
153
+ name : 'single label with OR operator' ,
154
+ params : {
155
+ getBy : 'completion' as const ,
156
+ since : '2025-08-01' ,
157
+ until : '2025-08-31' ,
158
+ limit : 50 ,
159
+ labels : [ 'work' ] ,
160
+ } ,
161
+ expectedMethod : 'getCompletedTasksByCompletionDate' ,
162
+ expectedFilter : '(@work)' ,
163
+ } ,
164
+ {
165
+ name : 'multiple labels with AND operator' ,
166
+ params : {
167
+ getBy : 'due' as const ,
168
+ since : '2025-08-01' ,
169
+ until : '2025-08-31' ,
170
+ limit : 50 ,
171
+ labels : [ 'work' , 'urgent' ] ,
172
+ labelsOperator : 'and' as const ,
173
+ } ,
174
+ expectedMethod : 'getCompletedTasksByDueDate' ,
175
+ expectedFilter : '(@work & @urgent)' ,
176
+ } ,
177
+ {
178
+ name : 'multiple labels with OR operator' ,
179
+ params : {
180
+ getBy : 'completion' as const ,
181
+ since : '2025-08-10' ,
182
+ until : '2025-08-20' ,
183
+ limit : 25 ,
184
+ labels : [ 'personal' , 'shopping' ] ,
185
+ } ,
186
+ expectedMethod : 'getCompletedTasksByCompletionDate' ,
187
+ expectedFilter : '(@personal | @shopping)' ,
188
+ } ,
189
+ ] ) (
190
+ 'should filter completed tasks by labels: $name' ,
191
+ async ( { params, expectedMethod, expectedFilter } ) => {
192
+ const mockCompletedTasks = [
193
+ createMockTask ( {
194
+ id : '8485093748' ,
195
+ content : 'Completed task with label' ,
196
+ labels : params . labels ,
197
+ completedAt : '2024-01-01T00:00:00Z' ,
198
+ } ) ,
199
+ ]
200
+
201
+ const mockResponse = { items : mockCompletedTasks , nextCursor : null }
202
+ const mockMethod = mockTodoistApi [
203
+ expectedMethod as keyof typeof mockTodoistApi
204
+ ] as jest . MockedFunction <
205
+ ( ...args : never [ ] ) => Promise < { items : unknown [ ] ; nextCursor : string | null } >
206
+ >
207
+ mockMethod . mockResolvedValue ( mockResponse )
208
+
209
+ const result = await findCompletedTasks . execute ( params , mockTodoistApi )
210
+
211
+ expect ( mockMethod ) . toHaveBeenCalledWith ( {
212
+ since : params . since ,
213
+ until : params . until ,
214
+ limit : params . limit ,
215
+ filterQuery : expectedFilter ,
216
+ filterLang : 'en' ,
217
+ } )
218
+
219
+ const textContent = extractTextContent ( result )
220
+ expect ( textContent ) . toMatchSnapshot ( )
221
+ } ,
222
+ )
223
+
224
+ it ( 'should handle empty labels array' , async ( ) => {
225
+ const params = {
226
+ getBy : 'completion' as const ,
227
+ since : '2025-08-01' ,
228
+ until : '2025-08-31' ,
229
+ limit : 50 ,
230
+ labels : [ ] ,
231
+ labelsOperator : 'or' as const ,
232
+ }
233
+
234
+ const mockResponse = { items : [ ] , nextCursor : null }
235
+ mockTodoistApi . getCompletedTasksByCompletionDate . mockResolvedValue ( mockResponse )
236
+
237
+ await findCompletedTasks . execute ( params , mockTodoistApi )
238
+
239
+ expect ( mockTodoistApi . getCompletedTasksByCompletionDate ) . toHaveBeenCalledWith ( {
240
+ since : params . since ,
241
+ until : params . until ,
242
+ limit : params . limit ,
243
+ } )
244
+ } )
245
+
246
+ it ( 'should combine other filters with label filters' , async ( ) => {
247
+ const params = {
248
+ getBy : 'due' as const ,
249
+ since : '2025-08-01' ,
250
+ until : '2025-08-31' ,
251
+ limit : 25 ,
252
+ projectId : 'test-project-id' ,
253
+ sectionId : 'test-section-id' ,
254
+ labels : [ 'important' ] ,
255
+ labelsOperator : 'or' as const ,
256
+ }
257
+
258
+ const mockTasks = [
259
+ createMockTask ( {
260
+ content : 'Important completed task' ,
261
+ labels : [ 'important' ] ,
262
+ completedAt : '2024-01-01T00:00:00Z' ,
263
+ } ) ,
264
+ ]
265
+ const mockResponse = { items : mockTasks , nextCursor : null }
266
+ mockTodoistApi . getCompletedTasksByDueDate . mockResolvedValue ( mockResponse )
267
+
268
+ const result = await findCompletedTasks . execute ( params , mockTodoistApi )
269
+
270
+ expect ( mockTodoistApi . getCompletedTasksByDueDate ) . toHaveBeenCalledWith ( {
271
+ since : params . since ,
272
+ until : params . until ,
273
+ limit : params . limit ,
274
+ projectId : params . projectId ,
275
+ sectionId : params . sectionId ,
276
+ filterQuery : '(@important)' ,
277
+ filterLang : 'en' ,
278
+ } )
279
+
280
+ const textContent = extractTextContent ( result )
281
+ expect ( textContent ) . toMatchSnapshot ( )
282
+ } )
283
+ } )
284
+
139
285
describe ( 'error handling' , ( ) => {
140
286
it ( 'should propagate completion date API errors' , async ( ) => {
141
287
const apiError = new Error ( 'API Error: Invalid date range' )
@@ -144,7 +290,14 @@ describe(`${FIND_COMPLETED_TASKS} tool`, () => {
144
290
await expect (
145
291
findCompletedTasks . execute (
146
292
// invalid date range
147
- { getBy : 'completion' , limit : 50 , since : '2025-08-31' , until : '2025-08-01' } ,
293
+ {
294
+ getBy : 'completion' ,
295
+ limit : 50 ,
296
+ since : '2025-08-31' ,
297
+ until : '2025-08-01' ,
298
+ labels : [ ] ,
299
+ labelsOperator : 'or' as const ,
300
+ } ,
148
301
mockTodoistApi ,
149
302
) ,
150
303
) . rejects . toThrow ( 'API Error: Invalid date range' )
@@ -162,6 +315,8 @@ describe(`${FIND_COMPLETED_TASKS} tool`, () => {
162
315
since : '2025-08-01' ,
163
316
until : '2025-08-31' ,
164
317
projectId : 'non-existent-project' ,
318
+ labels : [ ] ,
319
+ labelsOperator : 'or' as const ,
165
320
} ,
166
321
mockTodoistApi ,
167
322
) ,
0 commit comments