@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.
Files changed (167) hide show
  1. package/dist/filter-helpers.d.ts +1 -1
  2. package/dist/index.d.ts +175 -175
  3. package/dist/index.js +61 -81
  4. package/dist/main.js +15 -23
  5. package/dist/mcp-helpers.d.ts +4 -4
  6. package/dist/mcp-server-6tm7Rhyz.js +2840 -0
  7. package/dist/todoist-tool.d.ts +2 -2
  8. package/dist/tool-helpers.d.ts +1 -1
  9. package/dist/tools/add-comments.d.ts +1 -1
  10. package/dist/tools/add-comments.d.ts.map +1 -1
  11. package/dist/tools/add-projects.d.ts +4 -4
  12. package/dist/tools/add-projects.d.ts.map +1 -1
  13. package/dist/tools/add-sections.d.ts +1 -1
  14. package/dist/tools/add-sections.d.ts.map +1 -1
  15. package/dist/tools/add-tasks.d.ts +4 -4
  16. package/dist/tools/add-tasks.d.ts.map +1 -1
  17. package/dist/tools/complete-tasks.d.ts +1 -1
  18. package/dist/tools/complete-tasks.d.ts.map +1 -1
  19. package/dist/tools/delete-object.d.ts +3 -3
  20. package/dist/tools/delete-object.d.ts.map +1 -1
  21. package/dist/tools/fetch.d.ts +1 -1
  22. package/dist/tools/find-activity.d.ts +5 -5
  23. package/dist/tools/find-activity.d.ts.map +1 -1
  24. package/dist/tools/find-comments.d.ts +2 -2
  25. package/dist/tools/find-comments.d.ts.map +1 -1
  26. package/dist/tools/find-completed-tasks.d.ts +3 -3
  27. package/dist/tools/find-completed-tasks.d.ts.map +1 -1
  28. package/dist/tools/find-project-collaborators.d.ts +2 -2
  29. package/dist/tools/find-projects.d.ts +1 -1
  30. package/dist/tools/find-projects.d.ts.map +1 -1
  31. package/dist/tools/find-sections.d.ts +1 -1
  32. package/dist/tools/find-sections.d.ts.map +1 -1
  33. package/dist/tools/find-tasks-by-date.d.ts +1 -1
  34. package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
  35. package/dist/tools/find-tasks.d.ts +3 -3
  36. package/dist/tools/find-tasks.d.ts.map +1 -1
  37. package/dist/tools/get-overview.d.ts +1 -1
  38. package/dist/tools/manage-assignments.d.ts +1 -1
  39. package/dist/tools/search.d.ts +1 -1
  40. package/dist/tools/update-comments.d.ts +4 -4
  41. package/dist/tools/update-comments.d.ts.map +1 -1
  42. package/dist/tools/update-projects.d.ts +1 -1
  43. package/dist/tools/update-projects.d.ts.map +1 -1
  44. package/dist/tools/update-sections.d.ts +4 -4
  45. package/dist/tools/update-sections.d.ts.map +1 -1
  46. package/dist/tools/update-tasks.d.ts +7 -7
  47. package/dist/tools/update-tasks.d.ts.map +1 -1
  48. package/dist/tools/user-info.d.ts +1 -1
  49. package/dist/utils/assignment-validator.d.ts +2 -2
  50. package/dist/utils/response-builders.d.ts +1 -3
  51. package/dist/utils/response-builders.d.ts.map +1 -1
  52. package/dist/utils/test-helpers.d.ts +1 -1
  53. package/dist/utils/user-resolver.d.ts +1 -1
  54. package/package.json +11 -9
  55. package/dist/filter-helpers.js +0 -79
  56. package/dist/mcp-helpers.js +0 -71
  57. package/dist/mcp-server.js +0 -142
  58. package/dist/todoist-tool.js +0 -1
  59. package/dist/tool-helpers.js +0 -125
  60. package/dist/tool-helpers.test.d.ts +0 -2
  61. package/dist/tool-helpers.test.d.ts.map +0 -1
  62. package/dist/tool-helpers.test.js +0 -223
  63. package/dist/tools/__tests__/add-comments.test.d.ts +0 -2
  64. package/dist/tools/__tests__/add-comments.test.d.ts.map +0 -1
  65. package/dist/tools/__tests__/add-comments.test.js +0 -241
  66. package/dist/tools/__tests__/add-projects.test.d.ts +0 -2
  67. package/dist/tools/__tests__/add-projects.test.d.ts.map +0 -1
  68. package/dist/tools/__tests__/add-projects.test.js +0 -174
  69. package/dist/tools/__tests__/add-sections.test.d.ts +0 -2
  70. package/dist/tools/__tests__/add-sections.test.d.ts.map +0 -1
  71. package/dist/tools/__tests__/add-sections.test.js +0 -185
  72. package/dist/tools/__tests__/add-tasks.test.d.ts +0 -2
  73. package/dist/tools/__tests__/add-tasks.test.d.ts.map +0 -1
  74. package/dist/tools/__tests__/add-tasks.test.js +0 -533
  75. package/dist/tools/__tests__/assignment-integration.test.d.ts +0 -2
  76. package/dist/tools/__tests__/assignment-integration.test.d.ts.map +0 -1
  77. package/dist/tools/__tests__/assignment-integration.test.js +0 -428
  78. package/dist/tools/__tests__/complete-tasks.test.d.ts +0 -2
  79. package/dist/tools/__tests__/complete-tasks.test.d.ts.map +0 -1
  80. package/dist/tools/__tests__/complete-tasks.test.js +0 -206
  81. package/dist/tools/__tests__/delete-object.test.d.ts +0 -2
  82. package/dist/tools/__tests__/delete-object.test.d.ts.map +0 -1
  83. package/dist/tools/__tests__/delete-object.test.js +0 -110
  84. package/dist/tools/__tests__/fetch.test.d.ts +0 -2
  85. package/dist/tools/__tests__/fetch.test.d.ts.map +0 -1
  86. package/dist/tools/__tests__/fetch.test.js +0 -279
  87. package/dist/tools/__tests__/find-activity.test.d.ts +0 -2
  88. package/dist/tools/__tests__/find-activity.test.d.ts.map +0 -1
  89. package/dist/tools/__tests__/find-activity.test.js +0 -229
  90. package/dist/tools/__tests__/find-comments.test.d.ts +0 -2
  91. package/dist/tools/__tests__/find-comments.test.d.ts.map +0 -1
  92. package/dist/tools/__tests__/find-comments.test.js +0 -236
  93. package/dist/tools/__tests__/find-completed-tasks.test.d.ts +0 -2
  94. package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +0 -1
  95. package/dist/tools/__tests__/find-completed-tasks.test.js +0 -324
  96. package/dist/tools/__tests__/find-projects.test.d.ts +0 -2
  97. package/dist/tools/__tests__/find-projects.test.d.ts.map +0 -1
  98. package/dist/tools/__tests__/find-projects.test.js +0 -154
  99. package/dist/tools/__tests__/find-sections.test.d.ts +0 -2
  100. package/dist/tools/__tests__/find-sections.test.d.ts.map +0 -1
  101. package/dist/tools/__tests__/find-sections.test.js +0 -245
  102. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +0 -2
  103. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +0 -1
  104. package/dist/tools/__tests__/find-tasks-by-date.test.js +0 -528
  105. package/dist/tools/__tests__/find-tasks.test.d.ts +0 -2
  106. package/dist/tools/__tests__/find-tasks.test.d.ts.map +0 -1
  107. package/dist/tools/__tests__/find-tasks.test.js +0 -771
  108. package/dist/tools/__tests__/get-overview.test.d.ts +0 -2
  109. package/dist/tools/__tests__/get-overview.test.d.ts.map +0 -1
  110. package/dist/tools/__tests__/get-overview.test.js +0 -225
  111. package/dist/tools/__tests__/search.test.d.ts +0 -2
  112. package/dist/tools/__tests__/search.test.d.ts.map +0 -1
  113. package/dist/tools/__tests__/search.test.js +0 -206
  114. package/dist/tools/__tests__/update-comments.test.d.ts +0 -2
  115. package/dist/tools/__tests__/update-comments.test.d.ts.map +0 -1
  116. package/dist/tools/__tests__/update-comments.test.js +0 -294
  117. package/dist/tools/__tests__/update-projects.test.d.ts +0 -2
  118. package/dist/tools/__tests__/update-projects.test.d.ts.map +0 -1
  119. package/dist/tools/__tests__/update-projects.test.js +0 -217
  120. package/dist/tools/__tests__/update-sections.test.d.ts +0 -2
  121. package/dist/tools/__tests__/update-sections.test.d.ts.map +0 -1
  122. package/dist/tools/__tests__/update-sections.test.js +0 -169
  123. package/dist/tools/__tests__/update-tasks.test.d.ts +0 -2
  124. package/dist/tools/__tests__/update-tasks.test.d.ts.map +0 -1
  125. package/dist/tools/__tests__/update-tasks.test.js +0 -788
  126. package/dist/tools/__tests__/user-info.test.d.ts +0 -2
  127. package/dist/tools/__tests__/user-info.test.d.ts.map +0 -1
  128. package/dist/tools/__tests__/user-info.test.js +0 -139
  129. package/dist/tools/add-comments.js +0 -79
  130. package/dist/tools/add-projects.js +0 -63
  131. package/dist/tools/add-sections.js +0 -61
  132. package/dist/tools/add-tasks.js +0 -160
  133. package/dist/tools/complete-tasks.js +0 -68
  134. package/dist/tools/delete-object.js +0 -79
  135. package/dist/tools/fetch.js +0 -102
  136. package/dist/tools/find-activity.js +0 -221
  137. package/dist/tools/find-comments.js +0 -143
  138. package/dist/tools/find-completed-tasks.js +0 -161
  139. package/dist/tools/find-project-collaborators.js +0 -151
  140. package/dist/tools/find-projects.js +0 -101
  141. package/dist/tools/find-sections.js +0 -96
  142. package/dist/tools/find-tasks-by-date.js +0 -198
  143. package/dist/tools/find-tasks.js +0 -329
  144. package/dist/tools/get-overview.js +0 -249
  145. package/dist/tools/manage-assignments.js +0 -337
  146. package/dist/tools/search.js +0 -65
  147. package/dist/tools/update-comments.js +0 -82
  148. package/dist/tools/update-projects.js +0 -84
  149. package/dist/tools/update-sections.js +0 -70
  150. package/dist/tools/update-tasks.js +0 -170
  151. package/dist/tools/user-info.js +0 -142
  152. package/dist/utils/assignment-validator.js +0 -253
  153. package/dist/utils/constants.js +0 -45
  154. package/dist/utils/duration-parser.js +0 -96
  155. package/dist/utils/duration-parser.test.d.ts +0 -2
  156. package/dist/utils/duration-parser.test.d.ts.map +0 -1
  157. package/dist/utils/duration-parser.test.js +0 -147
  158. package/dist/utils/labels.js +0 -18
  159. package/dist/utils/priorities.js +0 -20
  160. package/dist/utils/response-builders.js +0 -210
  161. package/dist/utils/sanitize-data.js +0 -37
  162. package/dist/utils/sanitize-data.test.d.ts +0 -2
  163. package/dist/utils/sanitize-data.test.d.ts.map +0 -1
  164. package/dist/utils/sanitize-data.test.js +0 -93
  165. package/dist/utils/test-helpers.js +0 -237
  166. package/dist/utils/tool-names.js +0 -40
  167. package/dist/utils/user-resolver.js +0 -179
@@ -1,528 +0,0 @@
1
- import { jest } from '@jest/globals';
2
- import { getTasksByFilter } from '../../tool-helpers.js';
3
- import { createMappedTask, createMockUser, extractStructuredContent, extractTextContent, TEST_ERRORS, TEST_IDS, } from '../../utils/test-helpers.js';
4
- import { ToolNames } from '../../utils/tool-names.js';
5
- import { resolveUserNameToId } from '../../utils/user-resolver.js';
6
- import { findTasksByDate } from '../find-tasks-by-date.js';
7
- // Mock only getTasksByFilter, use actual implementations for everything else
8
- jest.mock('../../tool-helpers', () => {
9
- const actual = jest.requireActual('../../tool-helpers');
10
- return {
11
- ...actual,
12
- getTasksByFilter: jest.fn(),
13
- };
14
- });
15
- // Mock user resolver
16
- jest.mock('../../utils/user-resolver', () => ({
17
- resolveUserNameToId: jest.fn(),
18
- }));
19
- const mockGetTasksByFilter = getTasksByFilter;
20
- const mockResolveUserNameToId = resolveUserNameToId;
21
- // Mock the Todoist API (not directly used by find-tasks-by-date, but needed for type)
22
- const mockTodoistApi = {
23
- getUser: jest.fn(),
24
- };
25
- // Mock the Todoist User
26
- const mockTodoistUser = createMockUser();
27
- // Mock date-fns functions to make tests deterministic
28
- jest.mock('date-fns', () => ({
29
- addDays: jest.fn((date, amount) => {
30
- const d = new Date(date);
31
- d.setDate(d.getDate() + amount);
32
- return d;
33
- }),
34
- formatISO: jest.fn((date, options) => {
35
- if (typeof date === 'string') {
36
- return date; // Return string dates as-is
37
- }
38
- if (options?.representation === 'date') {
39
- return date.toISOString().split('T')[0];
40
- }
41
- return date.toISOString();
42
- }),
43
- }));
44
- const { FIND_TASKS_BY_DATE, UPDATE_TASKS } = ToolNames;
45
- describe(`${FIND_TASKS_BY_DATE} tool`, () => {
46
- beforeEach(() => {
47
- jest.clearAllMocks();
48
- mockTodoistApi.getUser.mockResolvedValue(mockTodoistUser);
49
- // Mock current date to make tests deterministic
50
- jest.spyOn(Date, 'now').mockReturnValue(new Date('2025-08-15T10:00:00Z').getTime());
51
- });
52
- afterEach(() => {
53
- jest.restoreAllMocks();
54
- });
55
- describe('listing tasks by date range', () => {
56
- it('only returns tasks for the startDate when daysCount is 1', async () => {
57
- const mockTasks = [
58
- createMappedTask({ content: 'Task for specific date', dueDate: '2025-08-20' }),
59
- ];
60
- const mockResponse = { tasks: mockTasks, nextCursor: null };
61
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
62
- const result = await findTasksByDate.execute({ startDate: '2025-08-20', limit: 50, daysCount: 1 }, mockTodoistApi);
63
- // Verify the query uses daysCount=1 by checking the end date calculation
64
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
65
- client: mockTodoistApi,
66
- query: '(due after: 2025-08-20 | due: 2025-08-20) & due before: 2025-08-21 & !assigned to: others',
67
- cursor: undefined,
68
- limit: 50,
69
- });
70
- const textContent = extractTextContent(result);
71
- expect(textContent).toMatchSnapshot();
72
- });
73
- it('should get tasks for today when startDate is "today" (includes overdue)', async () => {
74
- const mockTasks = [createMappedTask({ content: 'Today task', dueDate: '2025-08-15' })];
75
- const mockResponse = { tasks: mockTasks, nextCursor: null };
76
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
77
- const result = await findTasksByDate.execute({ startDate: 'today', limit: 50, daysCount: 7 }, mockTodoistApi);
78
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
79
- client: mockTodoistApi,
80
- query: '(today | overdue) & !assigned to: others',
81
- cursor: undefined,
82
- limit: 50,
83
- });
84
- // Verify result is a concise summary
85
- expect(extractTextContent(result)).toMatchSnapshot();
86
- });
87
- it.each([
88
- {
89
- name: 'specific date',
90
- params: { startDate: '2025-08-20', limit: 50, daysCount: 7 },
91
- tasks: [createMappedTask({ content: 'Specific date task', dueDate: '2025-08-20' })],
92
- cursor: null,
93
- },
94
- {
95
- name: 'multiple days with pagination',
96
- params: {
97
- startDate: '2025-08-20',
98
- daysCount: 3,
99
- limit: 20,
100
- cursor: 'current-cursor',
101
- },
102
- tasks: [
103
- createMappedTask({
104
- id: TEST_IDS.TASK_2,
105
- content: 'Multi-day task 1',
106
- dueDate: '2025-08-20',
107
- }),
108
- createMappedTask({
109
- id: TEST_IDS.TASK_3,
110
- content: 'Multi-day task 2',
111
- dueDate: '2025-08-21',
112
- }),
113
- ],
114
- cursor: 'next-page-cursor',
115
- },
116
- ])('should handle $name', async ({ params, tasks, cursor }) => {
117
- const mockResponse = { tasks, nextCursor: cursor };
118
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
119
- const result = await findTasksByDate.execute(params, mockTodoistApi);
120
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
121
- client: mockTodoistApi,
122
- query: expect.stringContaining('2025-08-20'),
123
- cursor: params.cursor || undefined,
124
- limit: params.limit,
125
- });
126
- // Verify result is a concise summary
127
- expect(extractTextContent(result)).toMatchSnapshot();
128
- });
129
- });
130
- describe('pagination and limits', () => {
131
- it.each([
132
- {
133
- name: 'pagination parameters',
134
- params: {
135
- startDate: 'today',
136
- limit: 25,
137
- daysCount: 7,
138
- cursor: 'pagination-cursor',
139
- },
140
- expectedCursor: 'pagination-cursor',
141
- expectedLimit: 25,
142
- },
143
- {
144
- name: 'default values',
145
- params: { startDate: '2025-08-15', limit: 50, daysCount: 7 },
146
- expectedCursor: undefined,
147
- expectedLimit: 50,
148
- },
149
- ])('should handle $name', async ({ params, expectedCursor, expectedLimit }) => {
150
- const mockResponse = { tasks: [], nextCursor: null };
151
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
152
- await findTasksByDate.execute(params, mockTodoistApi);
153
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
154
- client: mockTodoistApi,
155
- query: expect.any(String),
156
- cursor: expectedCursor,
157
- limit: expectedLimit,
158
- });
159
- });
160
- });
161
- describe('edge cases', () => {
162
- it.each([
163
- { name: 'empty results', daysCount: 7, shouldReturnResult: true },
164
- { name: 'maximum daysCount', daysCount: 30, shouldReturnResult: false },
165
- { name: 'minimum daysCount', daysCount: 1, shouldReturnResult: false },
166
- ])('should handle $name', async ({ daysCount, shouldReturnResult }) => {
167
- const mockResponse = { tasks: [], nextCursor: null };
168
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
169
- const startDate = daysCount === 7 ? 'today' : '2025-08-15';
170
- const result = await findTasksByDate.execute({ startDate, limit: 50, daysCount }, mockTodoistApi);
171
- expect(mockGetTasksByFilter).toHaveBeenCalledTimes(1);
172
- if (shouldReturnResult) {
173
- // Verify result is a concise summary
174
- expect(extractTextContent(result)).toMatchSnapshot();
175
- }
176
- });
177
- });
178
- describe('next steps logic', () => {
179
- it('should suggest appropriate actions when hasOverdue is true', async () => {
180
- const mockTasks = [
181
- createMappedTask({
182
- id: TEST_IDS.TASK_1,
183
- content: 'Overdue task from list',
184
- dueDate: '2025-08-10', // Past date - creates hasOverdue context
185
- }),
186
- ];
187
- const mockResponse = { tasks: mockTasks, nextCursor: null };
188
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
189
- const result = await findTasksByDate.execute({
190
- startDate: '2025-08-15',
191
- limit: 10,
192
- daysCount: 1,
193
- }, mockTodoistApi);
194
- const textContent = extractTextContent(result);
195
- expect(textContent).toMatchSnapshot();
196
- expect(textContent).toContain(`Use ${UPDATE_TASKS} to modify priorities or due dates`);
197
- });
198
- it('should suggest today-focused actions when startDate is today', async () => {
199
- const mockTasks = [
200
- createMappedTask({
201
- id: TEST_IDS.TASK_1,
202
- content: "Today's task",
203
- dueDate: '2025-08-15', // Today's date based on our mock
204
- }),
205
- ];
206
- const mockResponse = { tasks: mockTasks, nextCursor: null };
207
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
208
- const result = await findTasksByDate.execute({ startDate: 'today', limit: 10, daysCount: 1 }, mockTodoistApi);
209
- const textContent = extractTextContent(result);
210
- expect(textContent).toMatchSnapshot();
211
- expect(textContent).toContain(`Use ${UPDATE_TASKS} to modify priorities or due dates`);
212
- });
213
- it('should provide helpful suggestions for empty today results', async () => {
214
- const mockResponse = { tasks: [], nextCursor: null };
215
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
216
- const result = await findTasksByDate.execute({ startDate: 'today', limit: 10, daysCount: 1 }, mockTodoistApi);
217
- const textContent = extractTextContent(result);
218
- expect(textContent).toMatchSnapshot();
219
- expect(textContent).toContain('Great job! No tasks for today or overdue');
220
- });
221
- it('should provide helpful suggestions for empty date range results', async () => {
222
- const mockResponse = { tasks: [], nextCursor: null };
223
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
224
- const result = await findTasksByDate.execute({
225
- startDate: '2025-08-20',
226
- limit: 10,
227
- daysCount: 1,
228
- }, mockTodoistApi);
229
- const textContent = extractTextContent(result);
230
- expect(textContent).toMatchSnapshot();
231
- expect(textContent).toContain("Expand date range with larger 'daysCount'");
232
- expect(textContent).toContain("Check today's tasks with startDate='today'");
233
- });
234
- });
235
- describe('label filtering', () => {
236
- it.each([
237
- {
238
- name: 'single label with OR operator',
239
- params: {
240
- startDate: 'today',
241
- daysCount: 1,
242
- limit: 50,
243
- labels: ['work'],
244
- },
245
- expectedQueryPattern: '(today | overdue) & ((@work)) & !assigned to: others', // Will be combined with date query
246
- },
247
- {
248
- name: 'multiple labels with AND operator',
249
- params: {
250
- startDate: 'today',
251
- daysCount: 1,
252
- limit: 50,
253
- labels: ['work', 'urgent'],
254
- labelsOperator: 'and',
255
- },
256
- expectedQueryPattern: '(today | overdue) & ((@work & @urgent)) & !assigned to: others',
257
- },
258
- {
259
- name: 'multiple labels with OR operator',
260
- params: {
261
- startDate: '2025-08-20',
262
- daysCount: 3,
263
- limit: 50,
264
- labels: ['personal', 'shopping'],
265
- labelsOperator: 'or',
266
- },
267
- expectedQueryPattern: '((@personal | @shopping))',
268
- },
269
- ])('should filter tasks by labels: $name', async ({ params, expectedQueryPattern }) => {
270
- const mockTasks = [
271
- createMappedTask({
272
- id: TEST_IDS.TASK_1,
273
- content: 'Task with work label',
274
- labels: ['work'],
275
- dueDate: '2025-08-20',
276
- }),
277
- ];
278
- const mockResponse = { tasks: mockTasks, nextCursor: null };
279
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
280
- const result = await findTasksByDate.execute(params, mockTodoistApi);
281
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
282
- client: mockTodoistApi,
283
- query: expect.stringContaining('(@'),
284
- cursor: undefined,
285
- limit: 50,
286
- });
287
- // For today specifically, check the exact pattern
288
- if (params.startDate === 'today') {
289
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
290
- client: mockTodoistApi,
291
- query: expectedQueryPattern,
292
- cursor: undefined,
293
- limit: 50,
294
- });
295
- }
296
- const structuredContent = extractStructuredContent(result);
297
- expect(structuredContent.appliedFilters).toEqual(expect.objectContaining({
298
- labels: params.labels,
299
- ...(params.labelsOperator ? { labelsOperator: params.labelsOperator } : {}),
300
- }));
301
- });
302
- it('should handle empty labels array', async () => {
303
- const params = {
304
- startDate: 'today',
305
- daysCount: 1,
306
- limit: 50,
307
- };
308
- const mockResponse = { tasks: [], nextCursor: null };
309
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
310
- await findTasksByDate.execute(params, mockTodoistApi);
311
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
312
- client: mockTodoistApi,
313
- query: expect.not.stringContaining('@'),
314
- cursor: undefined,
315
- limit: 50,
316
- });
317
- });
318
- it('should combine date filters with label filters', async () => {
319
- const params = {
320
- startDate: '2025-08-15',
321
- daysCount: 1,
322
- limit: 25,
323
- labels: ['important'],
324
- };
325
- const mockTasks = [
326
- createMappedTask({
327
- content: 'Important task for specific date',
328
- labels: ['important'],
329
- dueDate: '2025-08-15',
330
- }),
331
- ];
332
- const mockResponse = { tasks: mockTasks, nextCursor: null };
333
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
334
- const result = await findTasksByDate.execute(params, mockTodoistApi);
335
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
336
- client: mockTodoistApi,
337
- query: expect.stringContaining('due after:') &&
338
- expect.stringContaining('(@important)'),
339
- cursor: undefined,
340
- limit: 25,
341
- });
342
- const textContent = extractTextContent(result);
343
- expect(textContent).toMatchSnapshot();
344
- });
345
- });
346
- describe('responsible user filtering', () => {
347
- it('should filter results to show only unassigned tasks or tasks assigned to current user', async () => {
348
- // Backend filtering: API should only return unassigned + assigned to me
349
- const mockTasks = [
350
- createMappedTask({
351
- id: TEST_IDS.TASK_1,
352
- content: 'My task',
353
- dueDate: '2025-08-15',
354
- responsibleUid: TEST_IDS.USER_ID, // Assigned to current user
355
- }),
356
- createMappedTask({
357
- id: TEST_IDS.TASK_2,
358
- content: 'Unassigned task',
359
- dueDate: '2025-08-15',
360
- responsibleUid: null, // Unassigned
361
- }),
362
- ];
363
- const mockResponse = { tasks: mockTasks, nextCursor: null };
364
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
365
- const result = await findTasksByDate.execute({
366
- startDate: 'today',
367
- daysCount: 1,
368
- limit: 50,
369
- responsibleUserFiltering: 'unassignedOrMe',
370
- }, mockTodoistApi);
371
- // Verify the query includes the assignment filter
372
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
373
- client: mockTodoistApi,
374
- query: '(today | overdue) & !assigned to: others',
375
- cursor: undefined,
376
- limit: 50,
377
- });
378
- const structuredContent = extractStructuredContent(result);
379
- // Should only return tasks 1 and 2, not task 3
380
- expect(structuredContent.tasks).toHaveLength(2);
381
- expect(structuredContent.tasks.map((t) => t.id)).toEqual([
382
- TEST_IDS.TASK_1,
383
- TEST_IDS.TASK_2,
384
- ]);
385
- });
386
- it('should filter overdue results to show only unassigned tasks or tasks assigned to current user', async () => {
387
- // Backend filtering: API should only return unassigned + assigned to me
388
- const mockTasks = [
389
- createMappedTask({
390
- id: TEST_IDS.TASK_1,
391
- content: 'My overdue task',
392
- dueDate: '2025-08-10',
393
- responsibleUid: TEST_IDS.USER_ID, // Assigned to current user
394
- }),
395
- createMappedTask({
396
- id: TEST_IDS.TASK_2,
397
- content: 'Unassigned overdue task',
398
- dueDate: '2025-08-10',
399
- responsibleUid: null, // Unassigned
400
- }),
401
- ];
402
- const mockResponse = { tasks: mockTasks, nextCursor: null };
403
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
404
- const result = await findTasksByDate.execute({
405
- overdueOption: 'overdue-only',
406
- daysCount: 1,
407
- limit: 50,
408
- responsibleUserFiltering: 'unassignedOrMe',
409
- }, mockTodoistApi);
410
- // Verify the query includes the assignment filter
411
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
412
- client: mockTodoistApi,
413
- query: 'overdue & !assigned to: others',
414
- cursor: undefined,
415
- limit: 50,
416
- });
417
- const structuredContent = extractStructuredContent(result);
418
- // Should only return tasks 1 and 2, not task 3
419
- expect(structuredContent.tasks).toHaveLength(2);
420
- expect(structuredContent.tasks.map((t) => t.id)).toEqual([
421
- TEST_IDS.TASK_1,
422
- TEST_IDS.TASK_2,
423
- ]);
424
- });
425
- });
426
- describe('responsibleUser parameter', () => {
427
- it('should filter tasks by specific user email', async () => {
428
- mockResolveUserNameToId.mockResolvedValue({
429
- userId: 'user-123',
430
- displayName: 'John Doe',
431
- email: 'john@example.com',
432
- });
433
- const mockTasks = [
434
- createMappedTask({
435
- id: TEST_IDS.TASK_1,
436
- content: 'Task assigned to John',
437
- dueDate: '2025-08-15',
438
- responsibleUid: 'user-123',
439
- }),
440
- ];
441
- const mockResponse = { tasks: mockTasks, nextCursor: null };
442
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
443
- const result = await findTasksByDate.execute({
444
- startDate: 'today',
445
- daysCount: 1,
446
- limit: 50,
447
- responsibleUser: 'john@example.com',
448
- }, mockTodoistApi);
449
- expect(mockResolveUserNameToId).toHaveBeenCalledWith(mockTodoistApi, 'john@example.com');
450
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
451
- client: mockTodoistApi,
452
- query: '(today | overdue) & assigned to: john@example.com',
453
- cursor: undefined,
454
- limit: 50,
455
- });
456
- const textContent = extractTextContent(result);
457
- expect(textContent).toContain('assigned to john@example.com');
458
- expect(textContent).toMatchSnapshot();
459
- });
460
- it('should throw error when user cannot be resolved', async () => {
461
- mockResolveUserNameToId.mockResolvedValue(null);
462
- await expect(findTasksByDate.execute({
463
- startDate: 'today',
464
- daysCount: 1,
465
- limit: 50,
466
- responsibleUser: 'nonexistent@example.com',
467
- }, mockTodoistApi)).rejects.toThrow('Could not find user: "nonexistent@example.com". Make sure the user is a collaborator on a shared project.');
468
- });
469
- it('should combine responsibleUser with labels and date filters', async () => {
470
- mockResolveUserNameToId.mockResolvedValue({
471
- userId: 'user-789',
472
- displayName: 'Bob Wilson',
473
- email: 'bob@example.com',
474
- });
475
- const mockTasks = [
476
- createMappedTask({
477
- id: TEST_IDS.TASK_1,
478
- content: 'Important task for Bob',
479
- dueDate: '2025-08-20',
480
- responsibleUid: 'user-789',
481
- labels: ['urgent'],
482
- }),
483
- ];
484
- const mockResponse = { tasks: mockTasks, nextCursor: null };
485
- mockGetTasksByFilter.mockResolvedValue(mockResponse);
486
- await findTasksByDate.execute({
487
- startDate: '2025-08-20',
488
- daysCount: 1,
489
- limit: 50,
490
- responsibleUser: 'bob@example.com',
491
- labels: ['urgent'],
492
- }, mockTodoistApi);
493
- expect(mockGetTasksByFilter).toHaveBeenCalledWith({
494
- client: mockTodoistApi,
495
- query: expect.stringContaining('2025-08-20'),
496
- cursor: undefined,
497
- limit: 50,
498
- });
499
- const call = mockGetTasksByFilter.mock.calls[0]?.[0];
500
- expect(call?.query).toContain('(@urgent)');
501
- expect(call?.query).toContain('assigned to: bob@example.com');
502
- });
503
- });
504
- describe('error handling', () => {
505
- it.each([
506
- {
507
- error: TEST_ERRORS.INVALID_FILTER,
508
- params: { startDate: 'today', limit: 50, daysCount: 7 },
509
- },
510
- {
511
- error: TEST_ERRORS.API_RATE_LIMIT,
512
- params: { startDate: 'today', limit: 50, daysCount: 7 },
513
- },
514
- {
515
- error: TEST_ERRORS.INVALID_CURSOR,
516
- params: {
517
- startDate: '2025-08-15',
518
- limit: 50,
519
- daysCount: 7,
520
- cursor: 'invalid-cursor',
521
- },
522
- },
523
- ])('should propagate $error', async ({ error, params }) => {
524
- mockGetTasksByFilter.mockRejectedValue(new Error(error));
525
- await expect(findTasksByDate.execute(params, mockTodoistApi)).rejects.toThrow(error);
526
- });
527
- });
528
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=find-tasks.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"find-tasks.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/find-tasks.test.ts"],"names":[],"mappings":""}