shawnxixi-cli 1.1.3 → 1.2.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.
Files changed (39) hide show
  1. package/CLAUDE.md +2 -0
  2. package/dist/commands/biz/calendar.js +2 -2
  3. package/dist/commands/biz/calendar.js.map +1 -1
  4. package/dist/commands/biz/finance.js +4 -4
  5. package/dist/commands/biz/finance.js.map +1 -1
  6. package/dist/commands/biz/health.js +3 -3
  7. package/dist/commands/biz/health.js.map +1 -1
  8. package/dist/commands/biz/item.d.ts +2 -2
  9. package/dist/commands/biz/item.d.ts.map +1 -1
  10. package/dist/commands/biz/item.js +6 -6
  11. package/dist/commands/biz/item.js.map +1 -1
  12. package/dist/commands/biz/record.js +2 -2
  13. package/dist/commands/biz/record.js.map +1 -1
  14. package/dist/commands/biz/task.d.ts.map +1 -1
  15. package/dist/commands/biz/task.js +10 -5
  16. package/dist/commands/biz/task.js.map +1 -1
  17. package/dist/commands/biz/todo.js +8 -8
  18. package/dist/commands/biz/todo.js.map +1 -1
  19. package/dist/index.js +3 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/utils/output.d.ts +4 -4
  22. package/dist/utils/output.d.ts.map +1 -1
  23. package/dist/utils/output.js +1 -1
  24. package/dist/utils/output.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/commands/biz/calendar.ts +10 -10
  27. package/src/commands/biz/finance.ts +11 -11
  28. package/src/commands/biz/health.ts +9 -9
  29. package/src/commands/biz/item.ts +11 -11
  30. package/src/commands/biz/record.ts +7 -7
  31. package/src/commands/biz/task.ts +19 -16
  32. package/src/commands/biz/todo.ts +24 -24
  33. package/src/index.ts +3 -2
  34. package/src/utils/output.ts +6 -6
  35. package/tests/commands/calendar.test.ts +124 -0
  36. package/tests/commands/health.test.ts +160 -0
  37. package/tests/commands/item.test.ts +12 -2
  38. package/tests/commands/record.test.ts +134 -0
  39. package/tests/commands/task.test.ts +180 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Calendar Command Tests
3
+ * Comprehensive CLI integration tests for biz calendar commands
4
+ */
5
+
6
+ import { calendarCommands } from '../../src/commands/biz/calendar';
7
+ import { setGlobalFlags } from '../../src/utils/output';
8
+
9
+ // Mock apiService
10
+ jest.mock('../../src/services/api', () => ({
11
+ apiService: {
12
+ createCalendarEvent: jest.fn().mockResolvedValue({ id: 1, title: '测试日程' }),
13
+ listCalendarEvents: jest.fn().mockResolvedValue({ data: [{ id: 1, title: '测试日程' }] }),
14
+ },
15
+ }));
16
+
17
+ import { apiService } from '../../src/services/api';
18
+
19
+ // Mock process.exit to prevent test from exiting
20
+ const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {}) as any);
21
+
22
+ // Suppress console output during tests
23
+ beforeAll(() => {
24
+ setGlobalFlags({ quiet: true });
25
+ });
26
+
27
+ afterAll(() => {
28
+ setGlobalFlags({ quiet: false });
29
+ mockExit.mockRestore();
30
+ });
31
+
32
+ describe('biz calendar commands', () => {
33
+ beforeEach(() => {
34
+ jest.clearAllMocks();
35
+ });
36
+
37
+ describe('create', () => {
38
+ it('should call apiService.createCalendarEvent with title and required options', async () => {
39
+ await calendarCommands.create('测试日程', { from: '2026-04-12', to: '2026-04-13' });
40
+ expect(apiService.createCalendarEvent).toHaveBeenCalledWith(
41
+ '测试日程',
42
+ '2026-04-12',
43
+ '2026-04-13',
44
+ undefined,
45
+ undefined,
46
+ undefined
47
+ );
48
+ });
49
+
50
+ it('should call apiService.createCalendarEvent with all options', async () => {
51
+ await calendarCommands.create('测试日程', {
52
+ from: '2026-04-12 10:00',
53
+ to: '2026-04-12 11:00',
54
+ desc: '会议讨论',
55
+ location: '会议室A',
56
+ color: '#ff0000',
57
+ });
58
+ expect(apiService.createCalendarEvent).toHaveBeenCalledWith(
59
+ '测试日程',
60
+ '2026-04-12 10:00',
61
+ '2026-04-12 11:00',
62
+ '会议讨论',
63
+ '会议室A',
64
+ '#ff0000'
65
+ );
66
+ });
67
+
68
+ it('should exit with error when from is missing', async () => {
69
+ await calendarCommands.create('测试日程', { to: '2026-04-13' });
70
+ expect(mockExit).toHaveBeenCalledWith(1);
71
+ });
72
+
73
+ it('should exit with error when to is missing', async () => {
74
+ await calendarCommands.create('测试日程', { from: '2026-04-12' });
75
+ expect(mockExit).toHaveBeenCalledWith(1);
76
+ });
77
+
78
+ it('should handle api errors gracefully', async () => {
79
+ (apiService.createCalendarEvent as jest.Mock).mockRejectedValueOnce(new Error('API Error'));
80
+
81
+ await calendarCommands.create('测试日程', { from: '2026-04-12', to: '2026-04-13' });
82
+
83
+ expect(mockExit).toHaveBeenCalledWith(1);
84
+ });
85
+ });
86
+
87
+ describe('list', () => {
88
+ it('should call apiService.listCalendarEvents without filters', async () => {
89
+ await calendarCommands.list();
90
+ expect(apiService.listCalendarEvents).toHaveBeenCalledWith(undefined, undefined);
91
+ });
92
+
93
+ it('should call apiService.listCalendarEvents with from filter', async () => {
94
+ await calendarCommands.list({ from: '2026-04-01' });
95
+ expect(apiService.listCalendarEvents).toHaveBeenCalledWith('2026-04-01', undefined);
96
+ });
97
+
98
+ it('should call apiService.listCalendarEvents with to filter', async () => {
99
+ await calendarCommands.list({ to: '2026-04-30' });
100
+ expect(apiService.listCalendarEvents).toHaveBeenCalledWith(undefined, '2026-04-30');
101
+ });
102
+
103
+ it('should call apiService.listCalendarEvents with both filters', async () => {
104
+ await calendarCommands.list({ from: '2026-04-01', to: '2026-04-30' });
105
+ expect(apiService.listCalendarEvents).toHaveBeenCalledWith('2026-04-01', '2026-04-30');
106
+ });
107
+
108
+ it('should handle empty results', async () => {
109
+ (apiService.listCalendarEvents as jest.Mock).mockResolvedValueOnce({ data: [] });
110
+
111
+ await calendarCommands.list();
112
+
113
+ expect(apiService.listCalendarEvents).toHaveBeenCalled();
114
+ });
115
+
116
+ it('should handle api errors gracefully', async () => {
117
+ (apiService.listCalendarEvents as jest.Mock).mockRejectedValueOnce(new Error('Network Error'));
118
+
119
+ await calendarCommands.list();
120
+
121
+ expect(mockExit).toHaveBeenCalledWith(1);
122
+ });
123
+ });
124
+ });
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Health Command Tests
3
+ * Comprehensive CLI integration tests for biz health commands
4
+ */
5
+
6
+ import { healthCommands } from '../../src/commands/biz/health';
7
+ import { setGlobalFlags } from '../../src/utils/output';
8
+
9
+ // Mock apiService
10
+ jest.mock('../../src/services/api', () => ({
11
+ apiService: {
12
+ logHealthData: jest.fn().mockResolvedValue({ id: 1, dataType: 'heart_rate', value: '72' }),
13
+ listHealthData: jest.fn().mockResolvedValue({ data: [{ id: 1, dataType: 'heart_rate', value: '72' }] }),
14
+ syncHealthData: jest.fn().mockResolvedValue({ synced: true }),
15
+ },
16
+ }));
17
+
18
+ import { apiService } from '../../src/services/api';
19
+
20
+ // Mock process.exit to prevent test from exiting
21
+ const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {}) as any);
22
+
23
+ // Suppress console output during tests
24
+ beforeAll(() => {
25
+ setGlobalFlags({ quiet: true });
26
+ });
27
+
28
+ afterAll(() => {
29
+ setGlobalFlags({ quiet: false });
30
+ mockExit.mockRestore();
31
+ });
32
+
33
+ describe('biz health commands', () => {
34
+ beforeEach(() => {
35
+ jest.clearAllMocks();
36
+ });
37
+
38
+ describe('log', () => {
39
+ it('should call apiService.logHealthData with required options', async () => {
40
+ await healthCommands.log({ dataType: 'heart_rate', value: '72' });
41
+ expect(apiService.logHealthData).toHaveBeenCalledWith(
42
+ 'heart_rate',
43
+ '72',
44
+ undefined,
45
+ undefined
46
+ );
47
+ });
48
+
49
+ it('should call apiService.logHealthData with unit', async () => {
50
+ await healthCommands.log({ dataType: 'heart_rate', value: '72', unit: 'bpm' });
51
+ expect(apiService.logHealthData).toHaveBeenCalledWith(
52
+ 'heart_rate',
53
+ '72',
54
+ 'bpm',
55
+ undefined
56
+ );
57
+ });
58
+
59
+ it('should call apiService.logHealthData with date', async () => {
60
+ await healthCommands.log({ dataType: 'heart_rate', value: '72', date: '2026-04-12' });
61
+ expect(apiService.logHealthData).toHaveBeenCalledWith(
62
+ 'heart_rate',
63
+ '72',
64
+ undefined,
65
+ '2026-04-12'
66
+ );
67
+ });
68
+
69
+ it('should call apiService.logHealthData with all options', async () => {
70
+ await healthCommands.log({
71
+ dataType: 'weight',
72
+ value: '70.5',
73
+ unit: 'kg',
74
+ date: '2026-04-12',
75
+ });
76
+ expect(apiService.logHealthData).toHaveBeenCalledWith(
77
+ 'weight',
78
+ '70.5',
79
+ 'kg',
80
+ '2026-04-12'
81
+ );
82
+ });
83
+
84
+ it('should exit with error when dataType is missing', async () => {
85
+ await healthCommands.log({ value: '72' });
86
+ expect(mockExit).toHaveBeenCalledWith(1);
87
+ });
88
+
89
+ it('should exit with error when value is missing', async () => {
90
+ await healthCommands.log({ dataType: 'heart_rate' });
91
+ expect(mockExit).toHaveBeenCalledWith(1);
92
+ });
93
+
94
+ it('should handle api errors gracefully', async () => {
95
+ (apiService.logHealthData as jest.Mock).mockRejectedValueOnce(new Error('API Error'));
96
+
97
+ await healthCommands.log({ dataType: 'heart_rate', value: '72' });
98
+
99
+ expect(mockExit).toHaveBeenCalledWith(1);
100
+ });
101
+ });
102
+
103
+ describe('list', () => {
104
+ it('should call apiService.listHealthData without filters', async () => {
105
+ await healthCommands.list();
106
+ expect(apiService.listHealthData).toHaveBeenCalledWith(undefined, undefined, undefined);
107
+ });
108
+
109
+ it('should call apiService.listHealthData with dataType filter', async () => {
110
+ await healthCommands.list({ dataType: 'heart_rate' });
111
+ expect(apiService.listHealthData).toHaveBeenCalledWith('heart_rate', undefined, undefined);
112
+ });
113
+
114
+ it('should call apiService.listHealthData with from date', async () => {
115
+ await healthCommands.list({ from: '2026-04-01' });
116
+ expect(apiService.listHealthData).toHaveBeenCalledWith(undefined, '2026-04-01', undefined);
117
+ });
118
+
119
+ it('should call apiService.listHealthData with to date', async () => {
120
+ await healthCommands.list({ to: '2026-04-12' });
121
+ expect(apiService.listHealthData).toHaveBeenCalledWith(undefined, undefined, '2026-04-12');
122
+ });
123
+
124
+ it('should call apiService.listHealthData with all filters', async () => {
125
+ await healthCommands.list({ dataType: 'heart_rate', from: '2026-04-01', to: '2026-04-12' });
126
+ expect(apiService.listHealthData).toHaveBeenCalledWith('heart_rate', '2026-04-01', '2026-04-12');
127
+ });
128
+
129
+ it('should handle empty results', async () => {
130
+ (apiService.listHealthData as jest.Mock).mockResolvedValueOnce({ data: [] });
131
+
132
+ await healthCommands.list();
133
+
134
+ expect(apiService.listHealthData).toHaveBeenCalled();
135
+ });
136
+
137
+ it('should handle api errors gracefully', async () => {
138
+ (apiService.listHealthData as jest.Mock).mockRejectedValueOnce(new Error('Network Error'));
139
+
140
+ await healthCommands.list();
141
+
142
+ expect(mockExit).toHaveBeenCalledWith(1);
143
+ });
144
+ });
145
+
146
+ describe('sync', () => {
147
+ it('should call apiService.syncHealthData', async () => {
148
+ await healthCommands.sync();
149
+ expect(apiService.syncHealthData).toHaveBeenCalled();
150
+ });
151
+
152
+ it('should handle api errors gracefully', async () => {
153
+ (apiService.syncHealthData as jest.Mock).mockRejectedValueOnce(new Error('API Error'));
154
+
155
+ await healthCommands.sync();
156
+
157
+ expect(mockExit).toHaveBeenCalledWith(1);
158
+ });
159
+ });
160
+ });
@@ -145,12 +145,22 @@ describe('biz item commands', () => {
145
145
  describe('list', () => {
146
146
  it('should call apiService.listItems without filters', async () => {
147
147
  await itemCommands.list();
148
- expect(apiService.listItems).toHaveBeenCalledWith(undefined);
148
+ expect(apiService.listItems).toHaveBeenCalledWith(undefined, undefined);
149
149
  });
150
150
 
151
151
  it('should call apiService.listItems with category filter', async () => {
152
152
  await itemCommands.list({ category: 'electronics' });
153
- expect(apiService.listItems).toHaveBeenCalledWith('electronics');
153
+ expect(apiService.listItems).toHaveBeenCalledWith('electronics', undefined);
154
+ });
155
+
156
+ it('should call apiService.listItems with status filter', async () => {
157
+ await itemCommands.list({ status: 'out_of_stock' });
158
+ expect(apiService.listItems).toHaveBeenCalledWith(undefined, 'out_of_stock');
159
+ });
160
+
161
+ it('should call apiService.listItems with both filters', async () => {
162
+ await itemCommands.list({ category: 'electronics', status: 'out_of_stock' });
163
+ expect(apiService.listItems).toHaveBeenCalledWith('electronics', 'out_of_stock');
154
164
  });
155
165
 
156
166
  it('should handle empty results', async () => {
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Record Command Tests
3
+ * Comprehensive CLI integration tests for biz record commands
4
+ */
5
+
6
+ import { recordCommands } from '../../src/commands/biz/record';
7
+ import { setGlobalFlags } from '../../src/utils/output';
8
+
9
+ // Mock apiService
10
+ jest.mock('../../src/services/api', () => ({
11
+ apiService: {
12
+ createRecord: jest.fn().mockResolvedValue({ id: 1, title: '测试记录' }),
13
+ listRecords: jest.fn().mockResolvedValue({ data: [{ id: 1, title: '测试记录' }] }),
14
+ },
15
+ }));
16
+
17
+ import { apiService } from '../../src/services/api';
18
+
19
+ // Mock process.exit to prevent test from exiting
20
+ const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {}) as any);
21
+
22
+ // Suppress console output during tests
23
+ beforeAll(() => {
24
+ setGlobalFlags({ quiet: true });
25
+ });
26
+
27
+ afterAll(() => {
28
+ setGlobalFlags({ quiet: false });
29
+ mockExit.mockRestore();
30
+ });
31
+
32
+ describe('biz record commands', () => {
33
+ beforeEach(() => {
34
+ jest.clearAllMocks();
35
+ });
36
+
37
+ describe('create', () => {
38
+ it('should call apiService.createRecord with title and empty content', async () => {
39
+ await recordCommands.create('测试记录', '');
40
+ expect(apiService.createRecord).toHaveBeenCalledWith(
41
+ '测试记录',
42
+ '',
43
+ undefined,
44
+ undefined
45
+ );
46
+ });
47
+
48
+ it('should call apiService.createRecord with content', async () => {
49
+ await recordCommands.create('测试记录', '这是记录内容');
50
+ expect(apiService.createRecord).toHaveBeenCalledWith(
51
+ '测试记录',
52
+ '这是记录内容',
53
+ undefined,
54
+ undefined
55
+ );
56
+ });
57
+
58
+ it('should call apiService.createRecord with type', async () => {
59
+ await recordCommands.create('测试记录', '', { type: 'diary' });
60
+ expect(apiService.createRecord).toHaveBeenCalledWith(
61
+ '测试记录',
62
+ '',
63
+ 'diary',
64
+ undefined
65
+ );
66
+ });
67
+
68
+ it('should call apiService.createRecord with tags', async () => {
69
+ await recordCommands.create('测试记录', '', { tags: 'work,important' });
70
+ expect(apiService.createRecord).toHaveBeenCalledWith(
71
+ '测试记录',
72
+ '',
73
+ undefined,
74
+ 'work,important'
75
+ );
76
+ });
77
+
78
+ it('should call apiService.createRecord with all options', async () => {
79
+ await recordCommands.create('测试记录', '内容部分', { type: 'note', tags: 'test' });
80
+ expect(apiService.createRecord).toHaveBeenCalledWith(
81
+ '测试记录',
82
+ '内容部分',
83
+ 'note',
84
+ 'test'
85
+ );
86
+ });
87
+
88
+ it('should handle api errors gracefully', async () => {
89
+ (apiService.createRecord as jest.Mock).mockRejectedValueOnce(new Error('API Error'));
90
+
91
+ await recordCommands.create('测试记录', '');
92
+
93
+ expect(mockExit).toHaveBeenCalledWith(1);
94
+ });
95
+ });
96
+
97
+ describe('list', () => {
98
+ it('should call apiService.listRecords without filters', async () => {
99
+ await recordCommands.list();
100
+ expect(apiService.listRecords).toHaveBeenCalledWith(undefined, undefined);
101
+ });
102
+
103
+ it('should call apiService.listRecords with type filter', async () => {
104
+ await recordCommands.list({ type: 'diary' });
105
+ expect(apiService.listRecords).toHaveBeenCalledWith('diary', undefined);
106
+ });
107
+
108
+ it('should call apiService.listRecords with tags filter', async () => {
109
+ await recordCommands.list({ tags: 'work' });
110
+ expect(apiService.listRecords).toHaveBeenCalledWith(undefined, 'work');
111
+ });
112
+
113
+ it('should call apiService.listRecords with both filters', async () => {
114
+ await recordCommands.list({ type: 'pet', tags: 'cat' });
115
+ expect(apiService.listRecords).toHaveBeenCalledWith('pet', 'cat');
116
+ });
117
+
118
+ it('should handle empty results', async () => {
119
+ (apiService.listRecords as jest.Mock).mockResolvedValueOnce({ data: [] });
120
+
121
+ await recordCommands.list();
122
+
123
+ expect(apiService.listRecords).toHaveBeenCalled();
124
+ });
125
+
126
+ it('should handle api errors gracefully', async () => {
127
+ (apiService.listRecords as jest.Mock).mockRejectedValueOnce(new Error('Network Error'));
128
+
129
+ await recordCommands.list();
130
+
131
+ expect(mockExit).toHaveBeenCalledWith(1);
132
+ });
133
+ });
134
+ });
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Task Command Tests
3
+ * Comprehensive CLI integration tests for biz task commands
4
+ */
5
+
6
+ import { taskCommands } from '../../src/commands/biz/task';
7
+ import { setGlobalFlags } from '../../src/utils/output';
8
+
9
+ // Mock apiService
10
+ jest.mock('../../src/services/api', () => ({
11
+ apiService: {
12
+ createTask: jest.fn().mockResolvedValue({ id: 1, title: '测试任务' }),
13
+ listTasks: jest.fn().mockResolvedValue({ data: [{ id: 1, title: '测试任务' }] }),
14
+ updateTask: jest.fn().mockResolvedValue({ id: 1, status: 'done' }),
15
+ deleteTask: jest.fn().mockResolvedValue({ success: true }),
16
+ },
17
+ }));
18
+
19
+ import { apiService } from '../../src/services/api';
20
+
21
+ // Mock process.exit to prevent test from exiting
22
+ const mockExit = jest.spyOn(process, 'exit').mockImplementation((() => {}) as any);
23
+
24
+ // Suppress console output during tests
25
+ beforeAll(() => {
26
+ setGlobalFlags({ quiet: true });
27
+ });
28
+
29
+ afterAll(() => {
30
+ setGlobalFlags({ quiet: false });
31
+ mockExit.mockRestore();
32
+ });
33
+
34
+ describe('biz task commands', () => {
35
+ beforeEach(() => {
36
+ jest.clearAllMocks();
37
+ });
38
+
39
+ describe('list', () => {
40
+ it('should call apiService.listTasks without filters', async () => {
41
+ await taskCommands.list();
42
+ expect(apiService.listTasks).toHaveBeenCalledWith(undefined, undefined);
43
+ });
44
+
45
+ it('should call apiService.listTasks with status filter', async () => {
46
+ await taskCommands.list({ status: 'running' });
47
+ expect(apiService.listTasks).toHaveBeenCalledWith('running', undefined);
48
+ });
49
+
50
+ it('should call apiService.listTasks with source filter', async () => {
51
+ await taskCommands.list({ source: 'openclaw' });
52
+ expect(apiService.listTasks).toHaveBeenCalledWith(undefined, 'openclaw');
53
+ });
54
+
55
+ it('should call apiService.listTasks with both filters', async () => {
56
+ await taskCommands.list({ status: 'pending', source: 'cli' });
57
+ expect(apiService.listTasks).toHaveBeenCalledWith('pending', 'cli');
58
+ });
59
+
60
+ it('should handle empty results', async () => {
61
+ (apiService.listTasks as jest.Mock).mockResolvedValueOnce({ data: [] });
62
+
63
+ await taskCommands.list();
64
+
65
+ expect(apiService.listTasks).toHaveBeenCalled();
66
+ });
67
+
68
+ it('should handle api errors gracefully', async () => {
69
+ (apiService.listTasks as jest.Mock).mockRejectedValueOnce(new Error('Network Error'));
70
+
71
+ await taskCommands.list();
72
+
73
+ expect(mockExit).toHaveBeenCalledWith(1);
74
+ });
75
+ });
76
+
77
+ describe('create', () => {
78
+ it('should call apiService.createTask with title only', async () => {
79
+ await taskCommands.create('测试任务');
80
+ expect(apiService.createTask).toHaveBeenCalledWith(
81
+ '测试任务',
82
+ undefined,
83
+ undefined,
84
+ undefined
85
+ );
86
+ });
87
+
88
+ it('should call apiService.createTask with source', async () => {
89
+ await taskCommands.create('测试任务', { source: 'openclaw' });
90
+ expect(apiService.createTask).toHaveBeenCalledWith(
91
+ '测试任务',
92
+ 'openclaw',
93
+ undefined,
94
+ undefined
95
+ );
96
+ });
97
+
98
+ it('should call apiService.createTask with openclawId', async () => {
99
+ await taskCommands.create('测试任务', { openclawId: '123' });
100
+ expect(apiService.createTask).toHaveBeenCalledWith(
101
+ '测试任务',
102
+ undefined,
103
+ '123',
104
+ undefined
105
+ );
106
+ });
107
+
108
+ it('should call apiService.createTask with priority', async () => {
109
+ await taskCommands.create('测试任务', { priority: 'high' });
110
+ expect(apiService.createTask).toHaveBeenCalledWith(
111
+ '测试任务',
112
+ undefined,
113
+ undefined,
114
+ 'high'
115
+ );
116
+ });
117
+
118
+ it('should call apiService.createTask with all options', async () => {
119
+ await taskCommands.create('测试任务', {
120
+ source: 'openclaw',
121
+ openclawId: '456',
122
+ priority: 'medium',
123
+ });
124
+ expect(apiService.createTask).toHaveBeenCalledWith(
125
+ '测试任务',
126
+ 'openclaw',
127
+ '456',
128
+ 'medium'
129
+ );
130
+ });
131
+
132
+ it('should handle api errors gracefully', async () => {
133
+ (apiService.createTask as jest.Mock).mockRejectedValueOnce(new Error('API Error'));
134
+
135
+ await taskCommands.create('测试任务');
136
+
137
+ expect(mockExit).toHaveBeenCalledWith(1);
138
+ });
139
+ });
140
+
141
+ describe('update', () => {
142
+ it('should call apiService.updateTask with status', async () => {
143
+ await taskCommands.update(1, { status: 'running' });
144
+ expect(apiService.updateTask).toHaveBeenCalledWith(1, { status: 'running' });
145
+ });
146
+
147
+ it('should call apiService.updateTask with priority', async () => {
148
+ await taskCommands.update(1, { priority: 'high' });
149
+ expect(apiService.updateTask).toHaveBeenCalledWith(1, { priority: 'high' });
150
+ });
151
+
152
+ it('should call apiService.updateTask with both updates', async () => {
153
+ await taskCommands.update(1, { status: 'done', priority: 'low' });
154
+ expect(apiService.updateTask).toHaveBeenCalledWith(1, { status: 'done', priority: 'low' });
155
+ });
156
+
157
+ it('should handle api errors gracefully', async () => {
158
+ (apiService.updateTask as jest.Mock).mockRejectedValueOnce(new Error('API Error'));
159
+
160
+ await taskCommands.update(1, { status: 'done' });
161
+
162
+ expect(mockExit).toHaveBeenCalledWith(1);
163
+ });
164
+ });
165
+
166
+ describe('delete', () => {
167
+ it('should call apiService.deleteTask with id', async () => {
168
+ await taskCommands.delete(1);
169
+ expect(apiService.deleteTask).toHaveBeenCalledWith(1);
170
+ });
171
+
172
+ it('should handle api errors gracefully', async () => {
173
+ (apiService.deleteTask as jest.Mock).mockRejectedValueOnce(new Error('API Error'));
174
+
175
+ await taskCommands.delete(1);
176
+
177
+ expect(mockExit).toHaveBeenCalledWith(1);
178
+ });
179
+ });
180
+ });