shawnxixi-cli 1.1.4 → 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.
@@ -35,9 +35,9 @@ export const itemCommands = {
35
35
  const result = await apiService.createItem(name, options.category, options.location, options.expire, options.quantity, options.unit, options.brand, options.barcode, options.price, options.supplier, options.purchaseDate);
36
36
  loading.succeed('物品创建成功');
37
37
  output(result);
38
- } catch (error: any) {
38
+ } catch (error: unknown) {
39
39
  loading.fail('创建物品失败');
40
- outputError(error.message);
40
+ outputError(error instanceof Error ? error.message : String(error));
41
41
  process.exit(1);
42
42
  }
43
43
  },
@@ -65,9 +65,9 @@ export const itemCommands = {
65
65
  } else {
66
66
  output({ message: '暂无物品' });
67
67
  }
68
- } catch (error: any) {
68
+ } catch (error: unknown) {
69
69
  loading.fail('加载物品列表失败');
70
- outputError(error.message);
70
+ outputError(error instanceof Error ? error.message : String(error));
71
71
  process.exit(1);
72
72
  }
73
73
  },
@@ -96,9 +96,9 @@ export const itemCommands = {
96
96
  } else {
97
97
  output({ message: '暂无即将过期的物品' });
98
98
  }
99
- } catch (error: any) {
99
+ } catch (error: unknown) {
100
100
  loading.fail('查找即将过期物品失败');
101
- outputError(error.message);
101
+ outputError(error instanceof Error ? error.message : String(error));
102
102
  process.exit(1);
103
103
  }
104
104
  },
@@ -121,9 +121,9 @@ export const itemCommands = {
121
121
  const result = await apiService.getItemStats();
122
122
  loading.succeed('统计完成');
123
123
  output(result);
124
- } catch (error: any) {
124
+ } catch (error: unknown) {
125
125
  loading.fail('统计失败');
126
- outputError(error.message);
126
+ outputError(error instanceof Error ? error.message : String(error));
127
127
  process.exit(1);
128
128
  }
129
129
  },
@@ -27,35 +27,35 @@ export const recordCommands = {
27
27
  const result = await apiService.createRecord(title, content, options.type, options.tags);
28
28
  loading.succeed('记录创建成功');
29
29
  output(result);
30
- } catch (error: any) {
30
+ } catch (error: unknown) {
31
31
  loading.fail('创建记录失败');
32
- outputError(error.message);
32
+ outputError(error instanceof Error ? error.message : String(error));
33
33
  process.exit(1);
34
34
  }
35
35
  },
36
36
 
37
37
  list: async (options: { type?: string; tags?: string } = {}) => {
38
38
  const loading = createLoading('加载记录列表...');
39
-
39
+
40
40
  try {
41
41
  if (isDryRun()) {
42
42
  loading.stop();
43
43
  dryRunLog('列出记录', options);
44
44
  return;
45
45
  }
46
-
46
+
47
47
  loading.start();
48
48
  const result = await apiService.listRecords(options.type, options.tags);
49
49
  loading.succeed('记录列表加载成功');
50
-
50
+
51
51
  if (result.data && result.data.length > 0) {
52
52
  output(result.data);
53
53
  } else {
54
54
  output({ message: '暂无记录' });
55
55
  }
56
- } catch (error: any) {
56
+ } catch (error: unknown) {
57
57
  loading.fail('加载记录列表失败');
58
- outputError(error.message);
58
+ outputError(error instanceof Error ? error.message : String(error));
59
59
  process.exit(1);
60
60
  }
61
61
  },
@@ -25,15 +25,15 @@ export const taskCommands = {
25
25
  loading.start();
26
26
  const result = await apiService.listTasks(options.status, options.source);
27
27
  loading.succeed('任务列表加载成功');
28
-
28
+
29
29
  if (result.data && result.data.length > 0) {
30
30
  output(result.data);
31
31
  } else {
32
32
  output({ message: '暂无任务' });
33
33
  }
34
- } catch (error: any) {
34
+ } catch (error: unknown) {
35
35
  loading.fail('加载任务列表失败');
36
- outputError(error.message);
36
+ outputError(error instanceof Error ? error.message : String(error));
37
37
  process.exit(1);
38
38
  }
39
39
  },
@@ -44,21 +44,21 @@ export const taskCommands = {
44
44
  */
45
45
  create: async (title: string, options: { source?: string; openclawId?: string; priority?: string } = {}) => {
46
46
  const loading = createLoading('创建任务...');
47
-
47
+
48
48
  try {
49
49
  if (isDryRun()) {
50
50
  loading.stop();
51
51
  dryRunLog('创建任务', { title, ...options });
52
52
  return;
53
53
  }
54
-
54
+
55
55
  loading.start();
56
56
  const result = await apiService.createTask(title, options.source, options.openclawId, options.priority);
57
57
  loading.succeed('任务创建成功');
58
58
  output(result);
59
- } catch (error: any) {
59
+ } catch (error: unknown) {
60
60
  loading.fail('创建任务失败');
61
- outputError(error.message);
61
+ outputError(error instanceof Error ? error.message : String(error));
62
62
  process.exit(1);
63
63
  }
64
64
  },
@@ -69,14 +69,14 @@ export const taskCommands = {
69
69
  */
70
70
  update: async (id: number, options: { status?: string; priority?: string } = {}) => {
71
71
  const loading = createLoading('更新任务...');
72
-
72
+
73
73
  try {
74
74
  if (isDryRun()) {
75
75
  loading.stop();
76
76
  dryRunLog('更新任务', { id, ...options });
77
77
  return;
78
78
  }
79
-
79
+
80
80
  loading.start();
81
81
  const updates: Partial<{status: string; priority: string}> = {};
82
82
  if (options.status) updates.status = options.status;
@@ -84,9 +84,9 @@ export const taskCommands = {
84
84
  const result = await apiService.updateTask(id, updates);
85
85
  loading.succeed('任务更新成功');
86
86
  output(result);
87
- } catch (error: any) {
87
+ } catch (error: unknown) {
88
88
  loading.fail('更新任务失败');
89
- outputError(error.message);
89
+ outputError(error instanceof Error ? error.message : String(error));
90
90
  process.exit(1);
91
91
  }
92
92
  },
@@ -97,21 +97,21 @@ export const taskCommands = {
97
97
  */
98
98
  delete: async (id: number) => {
99
99
  const loading = createLoading('删除任务...');
100
-
100
+
101
101
  try {
102
102
  if (isDryRun()) {
103
103
  loading.stop();
104
104
  dryRunLog('删除任务', { id });
105
105
  return;
106
106
  }
107
-
107
+
108
108
  loading.start();
109
109
  const result = await apiService.deleteTask(id);
110
110
  loading.succeed('任务删除成功');
111
111
  output(result);
112
- } catch (error: any) {
112
+ } catch (error: unknown) {
113
113
  loading.fail('删除任务失败');
114
- outputError(error.message);
114
+ outputError(error instanceof Error ? error.message : String(error));
115
115
  process.exit(1);
116
116
  }
117
117
  },
@@ -39,9 +39,9 @@ export const todoCommands = {
39
39
  const result = await apiService.createTodo(title, options.due, options.priority, options.tags, options.repeat, options.endDate, options.parentId);
40
40
  loading.succeed('待办创建成功');
41
41
  output(result);
42
- } catch (error: any) {
42
+ } catch (error: unknown) {
43
43
  loading.fail('创建待办失败');
44
- outputError(error.message);
44
+ outputError(error instanceof Error ? error.message : String(error));
45
45
  process.exit(1);
46
46
  }
47
47
  },
@@ -52,26 +52,26 @@ export const todoCommands = {
52
52
  */
53
53
  list: async (options: { status?: string; priority?: string; tags?: string } = {}) => {
54
54
  const loading = createLoading('加载待办列表...');
55
-
55
+
56
56
  try {
57
57
  if (isDryRun()) {
58
58
  loading.stop();
59
59
  dryRunLog('列出待办', options);
60
60
  return;
61
61
  }
62
-
62
+
63
63
  loading.start();
64
64
  const result = await apiService.listTodos(options.status, options.priority, options.tags);
65
65
  loading.succeed('待办列表加载成功');
66
-
66
+
67
67
  if (result.data && result.data.length > 0) {
68
68
  output(result.data);
69
69
  } else {
70
70
  output({ message: '暂无待办' });
71
71
  }
72
- } catch (error: any) {
72
+ } catch (error: unknown) {
73
73
  loading.fail('加载待办列表失败');
74
- outputError(error.message);
74
+ outputError(error instanceof Error ? error.message : String(error));
75
75
  process.exit(1);
76
76
  }
77
77
  },
@@ -82,14 +82,14 @@ export const todoCommands = {
82
82
  */
83
83
  update: async (id: number, options: { title?: string; status?: string; priority?: string; due?: string; tags?: string } = {}) => {
84
84
  const loading = createLoading('更新待办...');
85
-
85
+
86
86
  try {
87
87
  if (isDryRun()) {
88
88
  loading.stop();
89
89
  dryRunLog('更新待办', { id, ...options });
90
90
  return;
91
91
  }
92
-
92
+
93
93
  loading.start();
94
94
  const updates: Partial<{title: string; status: string; priority: string; dueDate: string; tags: string}> = {};
95
95
  if (options.title) updates.title = options.title;
@@ -97,13 +97,13 @@ export const todoCommands = {
97
97
  if (options.priority) updates.priority = options.priority;
98
98
  if (options.due) updates.dueDate = options.due;
99
99
  if (options.tags) updates.tags = options.tags;
100
-
100
+
101
101
  const result = await apiService.updateTodo(id, updates);
102
102
  loading.succeed('待办更新成功');
103
103
  output(result);
104
- } catch (error: any) {
104
+ } catch (error: unknown) {
105
105
  loading.fail('更新待办失败');
106
- outputError(error.message);
106
+ outputError(error instanceof Error ? error.message : String(error));
107
107
  process.exit(1);
108
108
  }
109
109
  },
@@ -114,21 +114,21 @@ export const todoCommands = {
114
114
  */
115
115
  done: async (id: number) => {
116
116
  const loading = createLoading('标记待办完成...');
117
-
117
+
118
118
  try {
119
119
  if (isDryRun()) {
120
120
  loading.stop();
121
121
  dryRunLog('标记待办完成', { id });
122
122
  return;
123
123
  }
124
-
124
+
125
125
  loading.start();
126
126
  const result = await apiService.updateTodo(id, { status: 'done' });
127
127
  loading.succeed('待办已完成');
128
128
  output(result);
129
- } catch (error: any) {
129
+ } catch (error: unknown) {
130
130
  loading.fail('标记待办完成失败');
131
- outputError(error.message);
131
+ outputError(error instanceof Error ? error.message : String(error));
132
132
  process.exit(1);
133
133
  }
134
134
  },
@@ -151,9 +151,9 @@ export const todoCommands = {
151
151
  const result = await apiService.deleteTodo(id);
152
152
  loading.succeed('待办已删除');
153
153
  output(result);
154
- } catch (error: any) {
154
+ } catch (error: unknown) {
155
155
  loading.fail('删除待办失败');
156
- outputError(error.message);
156
+ outputError(error instanceof Error ? error.message : String(error));
157
157
  process.exit(1);
158
158
  }
159
159
  },
@@ -176,9 +176,9 @@ export const todoCommands = {
176
176
  const result = await apiService.repeatTodo(id);
177
177
  loading.succeed('下次 occurrence 已生成');
178
178
  output(result);
179
- } catch (error: any) {
179
+ } catch (error: unknown) {
180
180
  loading.fail('处理重复待办失败');
181
- outputError(error.message);
181
+ outputError(error instanceof Error ? error.message : String(error));
182
182
  process.exit(1);
183
183
  }
184
184
  },
@@ -207,9 +207,9 @@ export const todoCommands = {
207
207
  const result = await apiService.createTodo(options.title, undefined, undefined, undefined, undefined, undefined, String(parentId));
208
208
  loading.succeed('子待办创建成功');
209
209
  output(result);
210
- } catch (error: any) {
210
+ } catch (error: unknown) {
211
211
  loading.fail('创建子待办失败');
212
- outputError(error.message);
212
+ outputError(error instanceof Error ? error.message : String(error));
213
213
  process.exit(1);
214
214
  }
215
215
  },
@@ -237,9 +237,9 @@ export const todoCommands = {
237
237
  } else {
238
238
  output({ message: '暂无子待办' });
239
239
  }
240
- } catch (error: any) {
240
+ } catch (error: unknown) {
241
241
  loading.fail('加载子待办列表失败');
242
- outputError(error.message);
242
+ outputError(error instanceof Error ? error.message : String(error));
243
243
  process.exit(1);
244
244
  }
245
245
  },
@@ -17,9 +17,9 @@ export function setGlobalFlags(args: { json?: boolean; quiet?: boolean; dryRun?:
17
17
  /**
18
18
  * 统一输出函数
19
19
  */
20
- export function output(data: any, options: { quiet?: boolean } = {}): void {
20
+ export function output(data: unknown, options: { quiet?: boolean } = {}): void {
21
21
  if (globalQuietFlag || options.quiet) return;
22
-
22
+
23
23
  if (globalJsonFlag) {
24
24
  console.log(JSON.stringify(data, null, 2));
25
25
  } else {
@@ -30,16 +30,16 @@ export function output(data: any, options: { quiet?: boolean } = {}): void {
30
30
  /**
31
31
  * 错误输出
32
32
  */
33
- export function outputError(message: string, ...args: any[]): void {
33
+ export function outputError(message: string, ...args: unknown[]): void {
34
34
  if (!globalQuietFlag) {
35
- console.error(`\x1b[31m ${message}\x1b[0m`, ...args);
35
+ console.error(`\x1b[31m[ERROR]\x1b[0m ${message}`, ...args);
36
36
  }
37
37
  }
38
38
 
39
39
  /**
40
40
  * 成功输出
41
41
  */
42
- export function outputSuccess(message: string, ...args: any[]): void {
42
+ export function outputSuccess(message: string, ...args: unknown[]): void {
43
43
  if (!globalQuietFlag) {
44
44
  console.log(`\x1b[32m✔ ${message}\x1b[0m`, ...args);
45
45
  }
@@ -116,7 +116,7 @@ export function isDryRun(): boolean {
116
116
  return globalDryRunFlag;
117
117
  }
118
118
 
119
- export function dryRunLog(action: string, details?: any): void {
119
+ export function dryRunLog(action: string, details?: unknown): void {
120
120
  if (globalDryRunFlag) {
121
121
  console.log(`\x1b[33m[DRY-RUN]\x1b[0m ${action}`);
122
122
  if (details && !globalQuietFlag) {
@@ -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
+ });