@doist/todoist-ai 2.2.2 → 3.0.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/README.md +11 -3
- package/dist/index.d.ts +496 -255
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +41 -29
- package/dist/mcp-helpers.d.ts +25 -3
- package/dist/mcp-helpers.d.ts.map +1 -1
- package/dist/mcp-helpers.js +37 -19
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +32 -28
- package/dist/tools/__tests__/add-tasks.test.d.ts +2 -0
- package/dist/tools/__tests__/add-tasks.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{tasks-add-multiple.test.js → add-tasks.test.js} +85 -81
- package/dist/tools/__tests__/complete-tasks.test.d.ts +2 -0
- package/dist/tools/__tests__/complete-tasks.test.d.ts.map +1 -0
- package/dist/tools/__tests__/complete-tasks.test.js +206 -0
- package/dist/tools/__tests__/delete-object.test.d.ts +2 -0
- package/dist/tools/__tests__/delete-object.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{delete-one.test.js → delete-object.test.js} +42 -22
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts +2 -0
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{tasks-list-completed.test.js → find-completed-tasks.test.js} +13 -36
- package/dist/tools/__tests__/find-projects.test.d.ts +2 -0
- package/dist/tools/__tests__/find-projects.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{projects-list.test.js → find-projects.test.js} +55 -39
- package/dist/tools/__tests__/find-sections.test.d.ts +2 -0
- package/dist/tools/__tests__/find-sections.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{sections-search.test.js → find-sections.test.js} +64 -50
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +2 -0
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{tasks-list-by-date.test.js → find-tasks-by-date.test.js} +96 -14
- package/dist/tools/__tests__/find-tasks.test.d.ts +2 -0
- package/dist/tools/__tests__/find-tasks.test.d.ts.map +1 -0
- package/dist/tools/__tests__/find-tasks.test.js +334 -0
- package/dist/tools/__tests__/get-overview.test.d.ts +2 -0
- package/dist/tools/__tests__/get-overview.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{overview.test.js → get-overview.test.js} +77 -13
- package/dist/tools/__tests__/manage-projects.test.d.ts +2 -0
- package/dist/tools/__tests__/manage-projects.test.d.ts.map +1 -0
- package/dist/tools/__tests__/{projects-manage.test.js → manage-projects.test.js} +33 -30
- package/dist/tools/__tests__/manage-sections.test.d.ts +2 -0
- package/dist/tools/__tests__/manage-sections.test.d.ts.map +1 -0
- package/dist/tools/__tests__/manage-sections.test.js +162 -0
- package/dist/tools/__tests__/update-tasks.test.d.ts +2 -0
- package/dist/tools/__tests__/update-tasks.test.d.ts.map +1 -0
- package/dist/tools/__tests__/update-tasks.test.js +645 -0
- package/dist/tools/{tasks-add-multiple.d.ts → add-tasks.d.ts} +36 -16
- package/dist/tools/add-tasks.d.ts.map +1 -0
- package/dist/tools/{tasks-add-multiple.js → add-tasks.js} +39 -4
- package/dist/tools/complete-tasks.d.ts +40 -0
- package/dist/tools/complete-tasks.d.ts.map +1 -0
- package/dist/tools/complete-tasks.js +68 -0
- package/dist/tools/delete-object.d.ts +38 -0
- package/dist/tools/delete-object.d.ts.map +1 -0
- package/dist/tools/delete-object.js +69 -0
- package/dist/tools/find-completed-tasks.d.ts +74 -0
- package/dist/tools/find-completed-tasks.d.ts.map +1 -0
- package/dist/tools/find-completed-tasks.js +112 -0
- package/dist/tools/find-projects.d.ts +53 -0
- package/dist/tools/find-projects.d.ts.map +1 -0
- package/dist/tools/find-projects.js +101 -0
- package/dist/tools/find-sections.d.ts +42 -0
- package/dist/tools/find-sections.d.ts.map +1 -0
- package/dist/tools/find-sections.js +96 -0
- package/dist/tools/find-tasks-by-date.d.ts +59 -0
- package/dist/tools/find-tasks-by-date.d.ts.map +1 -0
- package/dist/tools/find-tasks-by-date.js +121 -0
- package/dist/tools/find-tasks.d.ts +65 -0
- package/dist/tools/find-tasks.d.ts.map +1 -0
- package/dist/tools/find-tasks.js +182 -0
- package/dist/tools/get-overview.d.ts +67 -0
- package/dist/tools/get-overview.d.ts.map +1 -0
- package/dist/tools/{overview.js → get-overview.js} +66 -19
- package/dist/tools/manage-projects.d.ts +35 -0
- package/dist/tools/manage-projects.d.ts.map +1 -0
- package/dist/tools/manage-projects.js +63 -0
- package/dist/tools/manage-sections.d.ts +38 -0
- package/dist/tools/manage-sections.d.ts.map +1 -0
- package/dist/tools/manage-sections.js +78 -0
- package/dist/tools/update-tasks.d.ts +94 -0
- package/dist/tools/update-tasks.d.ts.map +1 -0
- package/dist/tools/update-tasks.js +120 -0
- package/dist/utils/constants.d.ts +35 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +37 -0
- package/dist/utils/response-builders.d.ts +88 -0
- package/dist/utils/response-builders.d.ts.map +1 -0
- package/dist/utils/response-builders.js +202 -0
- package/dist/{tools → utils}/test-helpers.d.ts +16 -0
- package/dist/utils/test-helpers.d.ts.map +1 -0
- package/dist/{tools → utils}/test-helpers.js +51 -0
- package/dist/utils/tool-names.d.ts +23 -0
- package/dist/utils/tool-names.d.ts.map +1 -0
- package/dist/utils/tool-names.js +25 -0
- package/package.json +1 -1
- package/dist/tools/__tests__/delete-one.test.d.ts +0 -2
- package/dist/tools/__tests__/delete-one.test.d.ts.map +0 -1
- package/dist/tools/__tests__/overview.test.d.ts +0 -2
- package/dist/tools/__tests__/overview.test.d.ts.map +0 -1
- package/dist/tools/__tests__/projects-list.test.d.ts +0 -2
- package/dist/tools/__tests__/projects-list.test.d.ts.map +0 -1
- package/dist/tools/__tests__/projects-manage.test.d.ts +0 -2
- package/dist/tools/__tests__/projects-manage.test.d.ts.map +0 -1
- package/dist/tools/__tests__/sections-manage.test.d.ts +0 -2
- package/dist/tools/__tests__/sections-manage.test.d.ts.map +0 -1
- package/dist/tools/__tests__/sections-manage.test.js +0 -138
- package/dist/tools/__tests__/sections-search.test.d.ts +0 -2
- package/dist/tools/__tests__/sections-search.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-add-multiple.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-add-multiple.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-complete-multiple.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-complete-multiple.test.js +0 -146
- package/dist/tools/__tests__/tasks-list-by-date.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-list-by-date.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-list-completed.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-list-completed.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-list-for-container.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-list-for-container.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-list-for-container.test.js +0 -232
- package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-organize-multiple.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-organize-multiple.test.js +0 -245
- package/dist/tools/__tests__/tasks-search.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-search.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-search.test.js +0 -106
- package/dist/tools/__tests__/tasks-update-one.test.d.ts +0 -2
- package/dist/tools/__tests__/tasks-update-one.test.d.ts.map +0 -1
- package/dist/tools/__tests__/tasks-update-one.test.js +0 -251
- package/dist/tools/delete-one.d.ts +0 -17
- package/dist/tools/delete-one.d.ts.map +0 -1
- package/dist/tools/delete-one.js +0 -25
- package/dist/tools/overview.d.ts +0 -14
- package/dist/tools/overview.d.ts.map +0 -1
- package/dist/tools/projects-list.d.ts +0 -29
- package/dist/tools/projects-list.d.ts.map +0 -1
- package/dist/tools/projects-list.js +0 -39
- package/dist/tools/projects-manage.d.ts +0 -24
- package/dist/tools/projects-manage.d.ts.map +0 -1
- package/dist/tools/projects-manage.js +0 -26
- package/dist/tools/sections-manage.d.ts +0 -23
- package/dist/tools/sections-manage.d.ts.map +0 -1
- package/dist/tools/sections-manage.js +0 -37
- package/dist/tools/sections-search.d.ts +0 -18
- package/dist/tools/sections-search.d.ts.map +0 -1
- package/dist/tools/sections-search.js +0 -27
- package/dist/tools/tasks-add-multiple.d.ts.map +0 -1
- package/dist/tools/tasks-complete-multiple.d.ts +0 -16
- package/dist/tools/tasks-complete-multiple.d.ts.map +0 -1
- package/dist/tools/tasks-complete-multiple.js +0 -23
- package/dist/tools/tasks-list-by-date.d.ts +0 -34
- package/dist/tools/tasks-list-by-date.d.ts.map +0 -1
- package/dist/tools/tasks-list-by-date.js +0 -53
- package/dist/tools/tasks-list-completed.d.ts +0 -44
- package/dist/tools/tasks-list-completed.d.ts.map +0 -1
- package/dist/tools/tasks-list-completed.js +0 -49
- package/dist/tools/tasks-list-for-container.d.ts +0 -34
- package/dist/tools/tasks-list-for-container.d.ts.map +0 -1
- package/dist/tools/tasks-list-for-container.js +0 -48
- package/dist/tools/tasks-organize-multiple.d.ts +0 -37
- package/dist/tools/tasks-organize-multiple.d.ts.map +0 -1
- package/dist/tools/tasks-organize-multiple.js +0 -34
- package/dist/tools/tasks-search.d.ts +0 -32
- package/dist/tools/tasks-search.d.ts.map +0 -1
- package/dist/tools/tasks-search.js +0 -30
- package/dist/tools/tasks-update-one.d.ts +0 -29
- package/dist/tools/tasks-update-one.d.ts.map +0 -1
- package/dist/tools/tasks-update-one.js +0 -63
- package/dist/tools/test-helpers.d.ts.map +0 -1
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
import { sectionsManage } from '../sections-manage.js';
|
|
3
|
-
import { TEST_IDS, createMockSection } from '../test-helpers.js';
|
|
4
|
-
// Mock the Todoist API
|
|
5
|
-
const mockTodoistApi = {
|
|
6
|
-
addSection: jest.fn(),
|
|
7
|
-
updateSection: jest.fn(),
|
|
8
|
-
};
|
|
9
|
-
describe('sections-manage tool', () => {
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
jest.clearAllMocks();
|
|
12
|
-
});
|
|
13
|
-
describe('creating a new section', () => {
|
|
14
|
-
it('should create a section and return result', async () => {
|
|
15
|
-
const mockApiResponse = createMockSection({
|
|
16
|
-
id: TEST_IDS.SECTION_1,
|
|
17
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
18
|
-
name: 'test-abc123def456-section',
|
|
19
|
-
});
|
|
20
|
-
mockTodoistApi.addSection.mockResolvedValue(mockApiResponse);
|
|
21
|
-
const result = await sectionsManage.execute({
|
|
22
|
-
name: 'test-abc123def456-section',
|
|
23
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
24
|
-
}, mockTodoistApi);
|
|
25
|
-
// Verify API was called correctly
|
|
26
|
-
expect(mockTodoistApi.addSection).toHaveBeenCalledWith({
|
|
27
|
-
name: 'test-abc123def456-section',
|
|
28
|
-
projectId: TEST_IDS.PROJECT_TEST,
|
|
29
|
-
});
|
|
30
|
-
// Verify result matches API response
|
|
31
|
-
expect(result).toEqual(mockApiResponse);
|
|
32
|
-
});
|
|
33
|
-
it('should handle different section properties from API', async () => {
|
|
34
|
-
const mockApiResponse = createMockSection({
|
|
35
|
-
id: TEST_IDS.SECTION_2,
|
|
36
|
-
projectId: 'project-789',
|
|
37
|
-
sectionOrder: 2,
|
|
38
|
-
name: 'My Section Name',
|
|
39
|
-
});
|
|
40
|
-
mockTodoistApi.addSection.mockResolvedValue(mockApiResponse);
|
|
41
|
-
const result = await sectionsManage.execute({
|
|
42
|
-
name: 'My Section Name',
|
|
43
|
-
projectId: 'project-789',
|
|
44
|
-
}, mockTodoistApi);
|
|
45
|
-
expect(mockTodoistApi.addSection).toHaveBeenCalledWith({
|
|
46
|
-
name: 'My Section Name',
|
|
47
|
-
projectId: 'project-789',
|
|
48
|
-
});
|
|
49
|
-
expect(result).toEqual(mockApiResponse);
|
|
50
|
-
});
|
|
51
|
-
it('should return error when projectId is missing for new section', async () => {
|
|
52
|
-
const result = await sectionsManage.execute({
|
|
53
|
-
name: 'test-section',
|
|
54
|
-
}, mockTodoistApi);
|
|
55
|
-
// Should not call API when projectId is missing
|
|
56
|
-
expect(mockTodoistApi.addSection).not.toHaveBeenCalled();
|
|
57
|
-
// Should return error content
|
|
58
|
-
expect(result).toEqual({
|
|
59
|
-
content: [
|
|
60
|
-
{
|
|
61
|
-
type: 'text',
|
|
62
|
-
text: 'Error: projectId is required when creating a new section (when id is not provided).',
|
|
63
|
-
},
|
|
64
|
-
],
|
|
65
|
-
isError: true,
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
describe('updating an existing section', () => {
|
|
70
|
-
it('should update a section when id is provided', async () => {
|
|
71
|
-
const mockApiResponse = {
|
|
72
|
-
id: 'existing-section-123',
|
|
73
|
-
projectId: '6cfCcrrCFg2xP94Q',
|
|
74
|
-
sectionOrder: 1,
|
|
75
|
-
userId: 'test-user',
|
|
76
|
-
addedAt: '2024-01-01T00:00:00Z',
|
|
77
|
-
updatedAt: '2024-01-01T00:00:00Z',
|
|
78
|
-
archivedAt: null,
|
|
79
|
-
isArchived: false,
|
|
80
|
-
isDeleted: false,
|
|
81
|
-
isCollapsed: false,
|
|
82
|
-
name: 'Updated Section Name',
|
|
83
|
-
};
|
|
84
|
-
mockTodoistApi.updateSection.mockResolvedValue(mockApiResponse);
|
|
85
|
-
const result = await sectionsManage.execute({
|
|
86
|
-
id: 'existing-section-123',
|
|
87
|
-
name: 'Updated Section Name',
|
|
88
|
-
}, mockTodoistApi);
|
|
89
|
-
expect(mockTodoistApi.updateSection).toHaveBeenCalledWith('existing-section-123', {
|
|
90
|
-
name: 'Updated Section Name',
|
|
91
|
-
});
|
|
92
|
-
expect(result).toEqual(mockApiResponse);
|
|
93
|
-
});
|
|
94
|
-
it('should update section without requiring projectId', async () => {
|
|
95
|
-
const mockApiResponse = {
|
|
96
|
-
id: 'section-update-test',
|
|
97
|
-
projectId: 'original-project-id',
|
|
98
|
-
sectionOrder: 3,
|
|
99
|
-
userId: 'test-user',
|
|
100
|
-
addedAt: '2024-01-01T00:00:00Z',
|
|
101
|
-
updatedAt: '2024-01-01T00:00:00Z',
|
|
102
|
-
archivedAt: null,
|
|
103
|
-
isArchived: false,
|
|
104
|
-
isDeleted: false,
|
|
105
|
-
isCollapsed: false,
|
|
106
|
-
name: 'Section New Name',
|
|
107
|
-
};
|
|
108
|
-
mockTodoistApi.updateSection.mockResolvedValue(mockApiResponse);
|
|
109
|
-
const result = await sectionsManage.execute({
|
|
110
|
-
id: 'section-update-test',
|
|
111
|
-
name: 'Section New Name',
|
|
112
|
-
// Note: projectId not provided for update
|
|
113
|
-
}, mockTodoistApi);
|
|
114
|
-
expect(mockTodoistApi.updateSection).toHaveBeenCalledWith('section-update-test', {
|
|
115
|
-
name: 'Section New Name',
|
|
116
|
-
});
|
|
117
|
-
expect(result).toEqual(mockApiResponse);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
describe('error handling', () => {
|
|
121
|
-
it('should propagate API errors for section creation', async () => {
|
|
122
|
-
const apiError = new Error('API Error: Section name is required');
|
|
123
|
-
mockTodoistApi.addSection.mockRejectedValue(apiError);
|
|
124
|
-
await expect(sectionsManage.execute({
|
|
125
|
-
name: '',
|
|
126
|
-
projectId: '6cfCcrrCFg2xP94Q',
|
|
127
|
-
}, mockTodoistApi)).rejects.toThrow('API Error: Section name is required');
|
|
128
|
-
});
|
|
129
|
-
it('should propagate API errors for section updates', async () => {
|
|
130
|
-
const apiError = new Error('API Error: Section not found');
|
|
131
|
-
mockTodoistApi.updateSection.mockRejectedValue(apiError);
|
|
132
|
-
await expect(sectionsManage.execute({
|
|
133
|
-
id: 'non-existent-section',
|
|
134
|
-
name: 'Updated Name',
|
|
135
|
-
}, mockTodoistApi)).rejects.toThrow('API Error: Section not found');
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sections-search.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/sections-search.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-add-multiple.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/tasks-add-multiple.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-complete-multiple.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/tasks-complete-multiple.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
import { tasksCompleteMultiple } from '../tasks-complete-multiple.js';
|
|
3
|
-
// Mock the Todoist API
|
|
4
|
-
const mockTodoistApi = {
|
|
5
|
-
closeTask: jest.fn(),
|
|
6
|
-
};
|
|
7
|
-
describe('tasks-complete-multiple tool', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
jest.clearAllMocks();
|
|
10
|
-
});
|
|
11
|
-
describe('completing multiple tasks', () => {
|
|
12
|
-
it('should complete all tasks successfully', async () => {
|
|
13
|
-
mockTodoistApi.closeTask.mockResolvedValue(true);
|
|
14
|
-
const result = await tasksCompleteMultiple.execute({ ids: ['task-1', 'task-2', 'task-3'] }, mockTodoistApi);
|
|
15
|
-
// Verify API was called for each task
|
|
16
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledTimes(3);
|
|
17
|
-
expect(mockTodoistApi.closeTask).toHaveBeenNthCalledWith(1, 'task-1');
|
|
18
|
-
expect(mockTodoistApi.closeTask).toHaveBeenNthCalledWith(2, 'task-2');
|
|
19
|
-
expect(mockTodoistApi.closeTask).toHaveBeenNthCalledWith(3, 'task-3');
|
|
20
|
-
// Verify all tasks were completed successfully
|
|
21
|
-
expect(result).toEqual({
|
|
22
|
-
success: true,
|
|
23
|
-
completed: ['task-1', 'task-2', 'task-3'],
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
it('should complete single task', async () => {
|
|
27
|
-
mockTodoistApi.closeTask.mockResolvedValue(true);
|
|
28
|
-
const result = await tasksCompleteMultiple.execute({ ids: ['8485093748'] }, mockTodoistApi);
|
|
29
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledTimes(1);
|
|
30
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledWith('8485093748');
|
|
31
|
-
expect(result).toEqual({
|
|
32
|
-
success: true,
|
|
33
|
-
completed: ['8485093748'],
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
it('should handle partial failures gracefully', async () => {
|
|
37
|
-
// Mock first and third tasks to succeed, second to fail
|
|
38
|
-
mockTodoistApi.closeTask
|
|
39
|
-
.mockResolvedValueOnce(true) // task-1 succeeds
|
|
40
|
-
.mockRejectedValueOnce(new Error('Task not found')) // task-2 fails
|
|
41
|
-
.mockResolvedValueOnce(true); // task-3 succeeds
|
|
42
|
-
const result = await tasksCompleteMultiple.execute({ ids: ['task-1', 'task-2', 'task-3'] }, mockTodoistApi);
|
|
43
|
-
// Verify API was called for all tasks despite failure
|
|
44
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledTimes(3);
|
|
45
|
-
expect(mockTodoistApi.closeTask).toHaveBeenNthCalledWith(1, 'task-1');
|
|
46
|
-
expect(mockTodoistApi.closeTask).toHaveBeenNthCalledWith(2, 'task-2');
|
|
47
|
-
expect(mockTodoistApi.closeTask).toHaveBeenNthCalledWith(3, 'task-3');
|
|
48
|
-
// Verify only successful completions are reported
|
|
49
|
-
expect(result).toEqual({
|
|
50
|
-
success: true,
|
|
51
|
-
completed: ['task-1', 'task-3'], // task-2 excluded due to failure
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
it('should handle all tasks failing', async () => {
|
|
55
|
-
const apiError = new Error('API Error: Network timeout');
|
|
56
|
-
mockTodoistApi.closeTask.mockRejectedValue(apiError);
|
|
57
|
-
const result = await tasksCompleteMultiple.execute({ ids: ['task-1', 'task-2'] }, mockTodoistApi);
|
|
58
|
-
// Verify API was attempted for all tasks
|
|
59
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledTimes(2);
|
|
60
|
-
// Verify no tasks were completed but still returns success
|
|
61
|
-
expect(result).toEqual({
|
|
62
|
-
success: true,
|
|
63
|
-
completed: [], // no tasks completed
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
it('should continue processing remaining tasks after failures', async () => {
|
|
67
|
-
// Mock various failure scenarios
|
|
68
|
-
mockTodoistApi.closeTask
|
|
69
|
-
.mockRejectedValueOnce(new Error('Task already completed'))
|
|
70
|
-
.mockRejectedValueOnce(new Error('Task not found'))
|
|
71
|
-
.mockResolvedValueOnce(true) // task-3 succeeds
|
|
72
|
-
.mockRejectedValueOnce(new Error('Permission denied'))
|
|
73
|
-
.mockResolvedValueOnce(true); // task-5 succeeds
|
|
74
|
-
const result = await tasksCompleteMultiple.execute({ ids: ['task-1', 'task-2', 'task-3', 'task-4', 'task-5'] }, mockTodoistApi);
|
|
75
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledTimes(5);
|
|
76
|
-
// Only tasks 3 and 5 should be in completed list
|
|
77
|
-
expect(result).toEqual({
|
|
78
|
-
success: true,
|
|
79
|
-
completed: ['task-3', 'task-5'],
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
it('should handle different types of API errors', async () => {
|
|
83
|
-
mockTodoistApi.closeTask
|
|
84
|
-
.mockRejectedValueOnce(new Error('Task not found'))
|
|
85
|
-
.mockRejectedValueOnce(new Error('Task already completed'))
|
|
86
|
-
.mockRejectedValueOnce(new Error('Permission denied'))
|
|
87
|
-
.mockRejectedValueOnce(new Error('Rate limit exceeded'));
|
|
88
|
-
const result = await tasksCompleteMultiple.execute({ ids: ['not-found', 'already-done', 'no-permission', 'rate-limited'] }, mockTodoistApi);
|
|
89
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledTimes(4);
|
|
90
|
-
// All should fail, but the tool should handle it gracefully
|
|
91
|
-
expect(result).toEqual({
|
|
92
|
-
success: true,
|
|
93
|
-
completed: [],
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
describe('mixed success and failure scenarios', () => {
|
|
98
|
-
it('should handle realistic mixed scenario', async () => {
|
|
99
|
-
// Simulate a realistic scenario with some tasks completing and others failing
|
|
100
|
-
mockTodoistApi.closeTask
|
|
101
|
-
.mockResolvedValueOnce(true) // regular task completion
|
|
102
|
-
.mockResolvedValueOnce(true) // another successful completion
|
|
103
|
-
.mockRejectedValueOnce(new Error('Task already completed')) // duplicate completion
|
|
104
|
-
.mockResolvedValueOnce(true) // successful completion
|
|
105
|
-
.mockRejectedValueOnce(new Error('Task not found')); // deleted task
|
|
106
|
-
const result = await tasksCompleteMultiple.execute({
|
|
107
|
-
ids: [
|
|
108
|
-
'8485093748', // regular task
|
|
109
|
-
'8485093749', // regular task
|
|
110
|
-
'8485093750', // already completed
|
|
111
|
-
'8485093751', // regular task
|
|
112
|
-
'8485093752', // deleted task
|
|
113
|
-
],
|
|
114
|
-
}, mockTodoistApi);
|
|
115
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledTimes(5);
|
|
116
|
-
expect(result).toEqual({
|
|
117
|
-
success: true,
|
|
118
|
-
completed: ['8485093748', '8485093749', '8485093751'],
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
describe('edge cases', () => {
|
|
123
|
-
it('should handle empty task completion (minimum one task required by schema)', async () => {
|
|
124
|
-
// Note: This test documents that the schema requires at least one task,
|
|
125
|
-
// so this scenario shouldn't occur in practice due to validation
|
|
126
|
-
mockTodoistApi.closeTask.mockResolvedValue(true);
|
|
127
|
-
const result = await tasksCompleteMultiple.execute({ ids: ['single-task'] }, mockTodoistApi);
|
|
128
|
-
expect(result).toEqual({
|
|
129
|
-
success: true,
|
|
130
|
-
completed: ['single-task'],
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
it('should handle tasks with special ID formats', async () => {
|
|
134
|
-
mockTodoistApi.closeTask.mockResolvedValue(true);
|
|
135
|
-
const result = await tasksCompleteMultiple.execute({ ids: ['proj_123_task_456', 'task-with-dashes', '1234567890'] }, mockTodoistApi);
|
|
136
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledTimes(3);
|
|
137
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledWith('proj_123_task_456');
|
|
138
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledWith('task-with-dashes');
|
|
139
|
-
expect(mockTodoistApi.closeTask).toHaveBeenCalledWith('1234567890');
|
|
140
|
-
expect(result).toEqual({
|
|
141
|
-
success: true,
|
|
142
|
-
completed: ['proj_123_task_456', 'task-with-dashes', '1234567890'],
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-list-by-date.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/tasks-list-by-date.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-list-completed.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/tasks-list-completed.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-list-for-container.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/tasks-list-for-container.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
import { tasksListForContainer } from '../tasks-list-for-container.js';
|
|
3
|
-
import { TEST_IDS, createMockApiResponse, createMockTask } from '../test-helpers.js';
|
|
4
|
-
// Mock the Todoist API
|
|
5
|
-
const mockTodoistApi = {
|
|
6
|
-
getTasks: jest.fn(),
|
|
7
|
-
};
|
|
8
|
-
describe('tasks-list-for-container tool', () => {
|
|
9
|
-
beforeEach(() => {
|
|
10
|
-
jest.clearAllMocks();
|
|
11
|
-
});
|
|
12
|
-
describe('container types', () => {
|
|
13
|
-
it.each([
|
|
14
|
-
{
|
|
15
|
-
name: 'project',
|
|
16
|
-
type: 'project',
|
|
17
|
-
id: TEST_IDS.PROJECT_TEST,
|
|
18
|
-
expectedParam: { projectId: TEST_IDS.PROJECT_TEST },
|
|
19
|
-
tasks: [createMockTask({ content: 'Project task' })],
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
name: 'section',
|
|
23
|
-
type: 'section',
|
|
24
|
-
id: TEST_IDS.SECTION_1,
|
|
25
|
-
expectedParam: { sectionId: TEST_IDS.SECTION_1 },
|
|
26
|
-
tasks: [createMockTask({ content: 'Section task' })],
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
name: 'parent task',
|
|
30
|
-
type: 'parent',
|
|
31
|
-
id: TEST_IDS.TASK_1,
|
|
32
|
-
expectedParam: { parentId: TEST_IDS.TASK_1 },
|
|
33
|
-
tasks: [createMockTask({ content: 'Subtask' })],
|
|
34
|
-
},
|
|
35
|
-
])('should get tasks for $name', async ({ type, id, expectedParam, tasks }) => {
|
|
36
|
-
mockTodoistApi.getTasks.mockResolvedValue(createMockApiResponse(tasks));
|
|
37
|
-
const result = await tasksListForContainer.execute({ type, id, limit: 10 }, mockTodoistApi);
|
|
38
|
-
expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
|
|
39
|
-
limit: 10,
|
|
40
|
-
cursor: null,
|
|
41
|
-
...expectedParam,
|
|
42
|
-
});
|
|
43
|
-
expect(result.tasks).toHaveLength(1);
|
|
44
|
-
expect(result.nextCursor).toBeNull();
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
describe('getting tasks for a section', () => {
|
|
48
|
-
it('should get tasks for a specific section', async () => {
|
|
49
|
-
const mockTasks = [
|
|
50
|
-
createMockTask({
|
|
51
|
-
id: '8485093751',
|
|
52
|
-
content: 'Section task 1',
|
|
53
|
-
description: 'Task in specific section',
|
|
54
|
-
labels: ['urgent'],
|
|
55
|
-
childOrder: 1,
|
|
56
|
-
priority: 3,
|
|
57
|
-
projectId: '6cfCcrrCFg2xP94Q',
|
|
58
|
-
sectionId: 'section-123',
|
|
59
|
-
url: 'https://todoist.com/showTask?id=8485093751',
|
|
60
|
-
addedAt: '2025-08-13T22:09:59.123456Z',
|
|
61
|
-
due: {
|
|
62
|
-
date: '2025-08-16',
|
|
63
|
-
isRecurring: true,
|
|
64
|
-
lang: 'en',
|
|
65
|
-
string: 'every week',
|
|
66
|
-
timezone: null,
|
|
67
|
-
},
|
|
68
|
-
}),
|
|
69
|
-
];
|
|
70
|
-
mockTodoistApi.getTasks.mockResolvedValue({ results: mockTasks, nextCursor: null });
|
|
71
|
-
const result = await tasksListForContainer.execute({ type: 'section', id: 'section-123', limit: 15 }, mockTodoistApi);
|
|
72
|
-
expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
|
|
73
|
-
limit: 15,
|
|
74
|
-
cursor: null,
|
|
75
|
-
sectionId: 'section-123',
|
|
76
|
-
});
|
|
77
|
-
expect(result).toEqual({
|
|
78
|
-
tasks: [
|
|
79
|
-
expect.objectContaining({
|
|
80
|
-
id: '8485093751',
|
|
81
|
-
content: 'Section task 1',
|
|
82
|
-
description: 'Task in specific section',
|
|
83
|
-
dueDate: '2025-08-16',
|
|
84
|
-
recurring: 'every week',
|
|
85
|
-
priority: 3,
|
|
86
|
-
sectionId: 'section-123',
|
|
87
|
-
labels: ['urgent'],
|
|
88
|
-
}),
|
|
89
|
-
],
|
|
90
|
-
nextCursor: null,
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
it('should handle empty section', async () => {
|
|
94
|
-
mockTodoistApi.getTasks.mockResolvedValue({ results: [], nextCursor: null });
|
|
95
|
-
const result = await tasksListForContainer.execute({ type: 'section', id: 'empty-section-id', limit: 10 }, mockTodoistApi);
|
|
96
|
-
expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
|
|
97
|
-
limit: 10,
|
|
98
|
-
cursor: null,
|
|
99
|
-
sectionId: 'empty-section-id',
|
|
100
|
-
});
|
|
101
|
-
expect(result).toEqual({ tasks: [], nextCursor: null });
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
describe('getting subtasks for a parent task', () => {
|
|
105
|
-
it('should get subtasks for a specific parent task', async () => {
|
|
106
|
-
const mockSubtasks = [
|
|
107
|
-
createMockTask({
|
|
108
|
-
id: '8485093752',
|
|
109
|
-
content: 'Subtask 1',
|
|
110
|
-
description: 'First subtask',
|
|
111
|
-
childOrder: 1,
|
|
112
|
-
priority: 1,
|
|
113
|
-
projectId: '6cfCcrrCFg2xP94Q',
|
|
114
|
-
parentId: 'parent-task-123',
|
|
115
|
-
url: 'https://todoist.com/showTask?id=8485093752',
|
|
116
|
-
addedAt: '2025-08-13T22:10:00.123456Z',
|
|
117
|
-
}),
|
|
118
|
-
createMockTask({
|
|
119
|
-
id: '8485093753',
|
|
120
|
-
content: 'Subtask 2',
|
|
121
|
-
description: 'Second subtask',
|
|
122
|
-
labels: ['follow-up'],
|
|
123
|
-
childOrder: 2,
|
|
124
|
-
priority: 2,
|
|
125
|
-
projectId: '6cfCcrrCFg2xP94Q',
|
|
126
|
-
parentId: 'parent-task-123',
|
|
127
|
-
url: 'https://todoist.com/showTask?id=8485093753',
|
|
128
|
-
addedAt: '2025-08-13T22:10:01.123456Z',
|
|
129
|
-
due: {
|
|
130
|
-
date: '2025-08-18',
|
|
131
|
-
isRecurring: false,
|
|
132
|
-
lang: 'en',
|
|
133
|
-
string: 'Aug 18',
|
|
134
|
-
timezone: null,
|
|
135
|
-
},
|
|
136
|
-
}),
|
|
137
|
-
];
|
|
138
|
-
mockTodoistApi.getTasks.mockResolvedValue({ results: mockSubtasks, nextCursor: null });
|
|
139
|
-
const result = await tasksListForContainer.execute({ type: 'parent', id: 'parent-task-123', limit: 10 }, mockTodoistApi);
|
|
140
|
-
expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
|
|
141
|
-
parentId: 'parent-task-123',
|
|
142
|
-
cursor: null,
|
|
143
|
-
limit: 10,
|
|
144
|
-
});
|
|
145
|
-
expect(result).toEqual({
|
|
146
|
-
tasks: [
|
|
147
|
-
expect.objectContaining({
|
|
148
|
-
id: '8485093752',
|
|
149
|
-
content: 'Subtask 1',
|
|
150
|
-
description: 'First subtask',
|
|
151
|
-
parentId: 'parent-task-123',
|
|
152
|
-
labels: [],
|
|
153
|
-
}),
|
|
154
|
-
expect.objectContaining({
|
|
155
|
-
id: '8485093753',
|
|
156
|
-
content: 'Subtask 2',
|
|
157
|
-
description: 'Second subtask',
|
|
158
|
-
dueDate: '2025-08-18',
|
|
159
|
-
parentId: 'parent-task-123',
|
|
160
|
-
priority: 2,
|
|
161
|
-
labels: ['follow-up'],
|
|
162
|
-
}),
|
|
163
|
-
],
|
|
164
|
-
nextCursor: null,
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
it('should handle parent task with no subtasks', async () => {
|
|
168
|
-
mockTodoistApi.getTasks.mockResolvedValue({ results: [], nextCursor: null });
|
|
169
|
-
const result = await tasksListForContainer.execute({ type: 'parent', id: 'parent-with-no-subtasks', limit: 10 }, mockTodoistApi);
|
|
170
|
-
expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
|
|
171
|
-
parentId: 'parent-with-no-subtasks',
|
|
172
|
-
cursor: null,
|
|
173
|
-
limit: 10,
|
|
174
|
-
});
|
|
175
|
-
expect(result).toEqual({ tasks: [], nextCursor: null });
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
describe('pagination and limits', () => {
|
|
179
|
-
it('should handle custom limit and cursor', async () => {
|
|
180
|
-
mockTodoistApi.getTasks.mockResolvedValue({ results: [], nextCursor: 'next-cursor' });
|
|
181
|
-
const result = await tasksListForContainer.execute({ type: 'section', id: 'test-section', limit: 25, cursor: 'current-cursor' }, mockTodoistApi);
|
|
182
|
-
expect(mockTodoistApi.getTasks).toHaveBeenCalledWith({
|
|
183
|
-
limit: 25,
|
|
184
|
-
cursor: 'current-cursor',
|
|
185
|
-
sectionId: 'test-section',
|
|
186
|
-
});
|
|
187
|
-
expect(result.nextCursor).toBe('next-cursor');
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
describe('container type validation', () => {
|
|
191
|
-
it('should handle all container types correctly', async () => {
|
|
192
|
-
mockTodoistApi.getTasks.mockResolvedValue({ results: [], nextCursor: null });
|
|
193
|
-
// Test project container
|
|
194
|
-
await tasksListForContainer.execute({ type: 'project', id: 'proj-1', limit: 10 }, mockTodoistApi);
|
|
195
|
-
expect(mockTodoistApi.getTasks).toHaveBeenLastCalledWith(expect.objectContaining({ projectId: 'proj-1' }));
|
|
196
|
-
// Test section container
|
|
197
|
-
await tasksListForContainer.execute({ type: 'section', id: 'sect-1', limit: 10 }, mockTodoistApi);
|
|
198
|
-
expect(mockTodoistApi.getTasks).toHaveBeenLastCalledWith(expect.objectContaining({ sectionId: 'sect-1' }));
|
|
199
|
-
// Test parent container
|
|
200
|
-
await tasksListForContainer.execute({ type: 'parent', id: 'parent-1', limit: 10 }, mockTodoistApi);
|
|
201
|
-
expect(mockTodoistApi.getTasks).toHaveBeenLastCalledWith(expect.objectContaining({ parentId: 'parent-1' }));
|
|
202
|
-
expect(mockTodoistApi.getTasks).toHaveBeenCalledTimes(3);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
describe('error handling', () => {
|
|
206
|
-
it('should propagate API errors for project queries', async () => {
|
|
207
|
-
const apiError = new Error('API Error: Project not found');
|
|
208
|
-
mockTodoistApi.getTasks.mockRejectedValue(apiError);
|
|
209
|
-
await expect(tasksListForContainer.execute({ type: 'project', id: 'non-existent-project', limit: 10 }, mockTodoistApi)).rejects.toThrow('API Error: Project not found');
|
|
210
|
-
});
|
|
211
|
-
it('should propagate API errors for section queries', async () => {
|
|
212
|
-
const apiError = new Error('API Error: Section not found');
|
|
213
|
-
mockTodoistApi.getTasks.mockRejectedValue(apiError);
|
|
214
|
-
await expect(tasksListForContainer.execute({ type: 'section', id: 'non-existent-section', limit: 10 }, mockTodoistApi)).rejects.toThrow('API Error: Section not found');
|
|
215
|
-
});
|
|
216
|
-
it('should propagate API errors for parent task queries', async () => {
|
|
217
|
-
const apiError = new Error('API Error: Parent task not found');
|
|
218
|
-
mockTodoistApi.getTasks.mockRejectedValue(apiError);
|
|
219
|
-
await expect(tasksListForContainer.execute({ type: 'parent', id: 'non-existent-parent', limit: 10 }, mockTodoistApi)).rejects.toThrow('API Error: Parent task not found');
|
|
220
|
-
});
|
|
221
|
-
it('should handle permission errors', async () => {
|
|
222
|
-
const permissionError = new Error('API Error: Insufficient permissions');
|
|
223
|
-
mockTodoistApi.getTasks.mockRejectedValue(permissionError);
|
|
224
|
-
await expect(tasksListForContainer.execute({ type: 'project', id: 'restricted-project', limit: 10 }, mockTodoistApi)).rejects.toThrow('API Error: Insufficient permissions');
|
|
225
|
-
});
|
|
226
|
-
it('should handle cursor validation errors', async () => {
|
|
227
|
-
const cursorError = new Error('API Error: Invalid cursor');
|
|
228
|
-
mockTodoistApi.getTasks.mockRejectedValue(cursorError);
|
|
229
|
-
await expect(tasksListForContainer.execute({ type: 'project', id: 'test-project', cursor: 'invalid-cursor', limit: 10 }, mockTodoistApi)).rejects.toThrow('API Error: Invalid cursor');
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-organize-multiple.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/tasks-organize-multiple.test.ts"],"names":[],"mappings":""}
|