@newpeak/barista-cli 0.1.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/.eslintrc.json +23 -0
- package/.prettierrc +9 -0
- package/.sisyphus/notepads/liberica-employees/learnings.md +73 -0
- package/AGENTS.md +270 -0
- package/CONTRIBUTING.md +291 -0
- package/README.md +707 -0
- package/bin/barista +6 -0
- package/bin/barista.js +3 -0
- package/docs/ARCHITECTURE.md +184 -0
- package/docs/COMMANDS.md +352 -0
- package/docs/COMMAND_DESIGN_SPEC.md +811 -0
- package/docs/INTEGRATION_NOTES.md +270 -0
- package/docs/commands/REFERENCE.md +297 -0
- package/docs/commands/arabica/auth/index.md +296 -0
- package/docs/commands/liberica/auth/index.md +133 -0
- package/docs/commands/liberica/context/index.md +60 -0
- package/docs/commands/liberica/employees/create.md +185 -0
- package/docs/commands/liberica/employees/disable.md +138 -0
- package/docs/commands/liberica/employees/enable.md +137 -0
- package/docs/commands/liberica/employees/get.md +153 -0
- package/docs/commands/liberica/employees/list.md +168 -0
- package/docs/commands/liberica/employees/update.md +180 -0
- package/docs/commands/liberica/orgs/list.md +62 -0
- package/docs/commands/liberica/positions/list.md +61 -0
- package/docs/commands/liberica/roles/list.md +67 -0
- package/docs/commands/liberica/users/create.md +170 -0
- package/docs/commands/liberica/users/get.md +151 -0
- package/docs/commands/liberica/users/list.md +175 -0
- package/package.json +37 -0
- package/src/commands/arabica/auth/index.ts +277 -0
- package/src/commands/arabica/auth/login.ts +5 -0
- package/src/commands/arabica/auth/logout.ts +5 -0
- package/src/commands/arabica/auth/register.ts +5 -0
- package/src/commands/arabica/auth/status.ts +5 -0
- package/src/commands/arabica/index.ts +23 -0
- package/src/commands/auth.ts +107 -0
- package/src/commands/context.ts +60 -0
- package/src/commands/liberica/auth/index.ts +170 -0
- package/src/commands/liberica/context/index.ts +43 -0
- package/src/commands/liberica/employees/create.ts +275 -0
- package/src/commands/liberica/employees/delete.ts +122 -0
- package/src/commands/liberica/employees/disable.ts +97 -0
- package/src/commands/liberica/employees/enable.ts +97 -0
- package/src/commands/liberica/employees/get.ts +115 -0
- package/src/commands/liberica/employees/index.ts +23 -0
- package/src/commands/liberica/employees/list.ts +131 -0
- package/src/commands/liberica/employees/update.ts +157 -0
- package/src/commands/liberica/index.ts +36 -0
- package/src/commands/liberica/orgs/index.ts +35 -0
- package/src/commands/liberica/positions/index.ts +30 -0
- package/src/commands/liberica/roles/index.ts +59 -0
- package/src/commands/liberica/users/create.ts +132 -0
- package/src/commands/liberica/users/delete.ts +49 -0
- package/src/commands/liberica/users/disable.ts +41 -0
- package/src/commands/liberica/users/enable.ts +30 -0
- package/src/commands/liberica/users/get.ts +46 -0
- package/src/commands/liberica/users/index.ts +27 -0
- package/src/commands/liberica/users/list.ts +68 -0
- package/src/commands/liberica/users/me.ts +42 -0
- package/src/commands/liberica/users/reset-password.ts +42 -0
- package/src/commands/liberica/users/update.ts +48 -0
- package/src/core/api/client.ts +825 -0
- package/src/core/auth/token-manager.ts +183 -0
- package/src/core/config/manager.ts +164 -0
- package/src/index.ts +37 -0
- package/src/types/employee.ts +102 -0
- package/src/types/index.ts +75 -0
- package/src/types/org.ts +25 -0
- package/src/types/position.ts +24 -0
- package/src/types/user.ts +64 -0
- package/tests/unit/commands/arabica/auth.test.ts +230 -0
- package/tests/unit/commands/liberica/auth.test.ts +175 -0
- package/tests/unit/commands/liberica/context.test.ts +98 -0
- package/tests/unit/commands/liberica/employees/create.test.ts +463 -0
- package/tests/unit/commands/liberica/employees/disable.test.ts +82 -0
- package/tests/unit/commands/liberica/employees/enable.test.ts +82 -0
- package/tests/unit/commands/liberica/employees/get.test.ts +111 -0
- package/tests/unit/commands/liberica/employees/list.test.ts +294 -0
- package/tests/unit/commands/liberica/employees/update.test.ts +210 -0
- package/tests/unit/config.test.ts +141 -0
- package/tests/unit/types.test.ts +195 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
vi.mock('chalk', () => ({
|
|
4
|
+
default: {
|
|
5
|
+
bold: vi.fn((text) => text),
|
|
6
|
+
green: vi.fn((text) => text),
|
|
7
|
+
red: vi.fn((text) => text),
|
|
8
|
+
gray: vi.fn((text) => text),
|
|
9
|
+
},
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock('../../../../../src/core/config/manager.js', () => ({
|
|
13
|
+
configManager: {
|
|
14
|
+
getCurrentContext: vi.fn(() => ({
|
|
15
|
+
environment: 'dev' as const,
|
|
16
|
+
service: 'liberica' as const,
|
|
17
|
+
tenant: 'test-tenant',
|
|
18
|
+
})),
|
|
19
|
+
},
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
vi.mock('../../../../../src/core/auth/token-manager.js', () => ({
|
|
23
|
+
tokenManager: {
|
|
24
|
+
getToken: vi.fn(() => Promise.resolve('test-token')),
|
|
25
|
+
},
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
vi.mock('../../../../../src/core/api/client.js', () => ({
|
|
29
|
+
apiClient: {
|
|
30
|
+
getEmployee: vi.fn(),
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
import { createEmployeeGetCommand } from '../../../../../src/commands/liberica/employees/get.js';
|
|
35
|
+
|
|
36
|
+
describe('createEmployeeGetCommand', () => {
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
vi.clearAllMocks();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should create command with name "get"', () => {
|
|
42
|
+
const command = createEmployeeGetCommand();
|
|
43
|
+
expect(command.name()).toBe('get');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should have correct description', () => {
|
|
47
|
+
const command = createEmployeeGetCommand();
|
|
48
|
+
expect(command.description()).toBe('Get employee details by ID');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should have --json option', () => {
|
|
52
|
+
const command = createEmployeeGetCommand();
|
|
53
|
+
const jsonOption = command.options.find((opt) => opt.long === '--json');
|
|
54
|
+
expect(jsonOption).toBeDefined();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('get employee command logic', () => {
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
vi.clearAllMocks();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should call apiClient.getEmployee with correct parameters', async () => {
|
|
64
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
65
|
+
const mockEmployee = {
|
|
66
|
+
employeeId: 12345,
|
|
67
|
+
employeeCode: 'EMP001',
|
|
68
|
+
employeeName: 'John Doe',
|
|
69
|
+
employeeNo: '001',
|
|
70
|
+
statusFlag: 1,
|
|
71
|
+
};
|
|
72
|
+
(apiClient.getEmployee as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
73
|
+
success: true,
|
|
74
|
+
data: mockEmployee,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(apiClient.getEmployee).toBeDefined();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should handle employee not found (404)', async () => {
|
|
81
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
82
|
+
(apiClient.getEmployee as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
83
|
+
success: false,
|
|
84
|
+
error: {
|
|
85
|
+
code: '01001150001',
|
|
86
|
+
message: '查询结果不存在',
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(apiClient.getEmployee).toBeDefined();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should format JSON output correctly', async () => {
|
|
94
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
95
|
+
const mockEmployee = {
|
|
96
|
+
employeeId: 12345,
|
|
97
|
+
employeeCode: 'EMP001',
|
|
98
|
+
employeeName: 'John Doe',
|
|
99
|
+
employeeNo: '001',
|
|
100
|
+
statusFlag: 1,
|
|
101
|
+
};
|
|
102
|
+
(apiClient.getEmployee as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
103
|
+
success: true,
|
|
104
|
+
data: mockEmployee,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const response = await apiClient.getEmployee('dev', 'test-tenant', 12345);
|
|
108
|
+
expect(response.success).toBe(true);
|
|
109
|
+
expect(response.data).toEqual(mockEmployee);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
vi.mock('chalk', () => ({
|
|
4
|
+
default: {
|
|
5
|
+
bold: vi.fn((text) => text),
|
|
6
|
+
green: vi.fn((text) => text),
|
|
7
|
+
red: vi.fn((text) => text),
|
|
8
|
+
gray: vi.fn((text) => text),
|
|
9
|
+
},
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
vi.mock('cli-table3', () => ({
|
|
13
|
+
default: vi.fn(() => ({
|
|
14
|
+
head: vi.fn(),
|
|
15
|
+
push: vi.fn(),
|
|
16
|
+
toString: vi.fn(() => 'mocked-table'),
|
|
17
|
+
})),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock('../../../../../src/core/config/manager.js', () => ({
|
|
21
|
+
configManager: {
|
|
22
|
+
getCurrentContext: vi.fn(() => ({
|
|
23
|
+
environment: 'dev' as const,
|
|
24
|
+
service: 'liberica' as const,
|
|
25
|
+
tenant: 'test-tenant',
|
|
26
|
+
})),
|
|
27
|
+
},
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
vi.mock('../../../../../src/core/auth/token-manager.js', () => ({
|
|
31
|
+
tokenManager: {
|
|
32
|
+
getToken: vi.fn(),
|
|
33
|
+
setToken: vi.fn(),
|
|
34
|
+
deleteToken: vi.fn(),
|
|
35
|
+
},
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
vi.mock('../../../../../src/core/api/client.js', () => ({
|
|
39
|
+
apiClient: {
|
|
40
|
+
listEmployees: vi.fn(),
|
|
41
|
+
},
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
import { createEmployeeListCommand } from '../../../../../src/commands/liberica/employees/list.js';
|
|
45
|
+
|
|
46
|
+
const mockEmployees = [
|
|
47
|
+
{
|
|
48
|
+
employeeId: 1,
|
|
49
|
+
employeeCode: 'E001',
|
|
50
|
+
employeeName: '张三',
|
|
51
|
+
employeePhone: '13800000001',
|
|
52
|
+
orgName: '研发部',
|
|
53
|
+
orgCode: 'RD',
|
|
54
|
+
statusFlag: 1 as const,
|
|
55
|
+
employeeNo: '001',
|
|
56
|
+
employeeEmail: 'zhangsan@example.com',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
employeeId: 2,
|
|
60
|
+
employeeCode: 'E002',
|
|
61
|
+
employeeName: '李四',
|
|
62
|
+
employeePhone: '13800000002',
|
|
63
|
+
orgName: '市场部',
|
|
64
|
+
orgCode: 'MKT',
|
|
65
|
+
statusFlag: 2 as const,
|
|
66
|
+
employeeNo: '002',
|
|
67
|
+
employeeEmail: 'lisi@example.com',
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
describe('createEmployeeListCommand', () => {
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
vi.clearAllMocks();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should create command with name "list"', () => {
|
|
77
|
+
const command = createEmployeeListCommand();
|
|
78
|
+
expect(command.name()).toBe('list');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should have description "List employees with pagination"', () => {
|
|
82
|
+
const command = createEmployeeListCommand();
|
|
83
|
+
expect(command.description()).toBe('List employees with pagination');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should have --page option', () => {
|
|
87
|
+
const command = createEmployeeListCommand();
|
|
88
|
+
const pageOption = command.options.find((opt) => opt.short === '-p' || opt.long === '--page');
|
|
89
|
+
expect(pageOption).toBeDefined();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should have --size option', () => {
|
|
93
|
+
const command = createEmployeeListCommand();
|
|
94
|
+
const sizeOption = command.options.find((opt) => opt.short === '-s' || opt.long === '--size');
|
|
95
|
+
expect(sizeOption).toBeDefined();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should have --status option', () => {
|
|
99
|
+
const command = createEmployeeListCommand();
|
|
100
|
+
const statusOption = command.options.find((opt) => opt.long === '--status');
|
|
101
|
+
expect(statusOption).toBeDefined();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should have --org-id option', () => {
|
|
105
|
+
const command = createEmployeeListCommand();
|
|
106
|
+
const orgIdOption = command.options.find((opt) => opt.long === '--org-id');
|
|
107
|
+
expect(orgIdOption).toBeDefined();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should have --json option', () => {
|
|
111
|
+
const command = createEmployeeListCommand();
|
|
112
|
+
const jsonOption = command.options.find((opt) => opt.long === '--json');
|
|
113
|
+
expect(jsonOption).toBeDefined();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('apiClient.listEmployees', () => {
|
|
117
|
+
it('should be defined', async () => {
|
|
118
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
119
|
+
expect(apiClient.listEmployees).toBeDefined();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should be called with correct default params', async () => {
|
|
123
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
124
|
+
const { configManager } = await import('../../../../../src/core/config/manager.js');
|
|
125
|
+
|
|
126
|
+
(apiClient.listEmployees as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
127
|
+
success: true,
|
|
128
|
+
data: {
|
|
129
|
+
total: 0,
|
|
130
|
+
size: 20,
|
|
131
|
+
current: 1,
|
|
132
|
+
records: [],
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect(apiClient.listEmployees).toBeDefined();
|
|
137
|
+
expect(configManager.getCurrentContext).toBeDefined();
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('command options defaults', () => {
|
|
142
|
+
it('should have page defaulting to "1"', () => {
|
|
143
|
+
const command = createEmployeeListCommand();
|
|
144
|
+
const pageOption = command.options.find((opt) => opt.short === '-p');
|
|
145
|
+
expect(pageOption?.defaultValue).toBe('1');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should have size defaulting to "20"', () => {
|
|
149
|
+
const command = createEmployeeListCommand();
|
|
150
|
+
const sizeOption = command.options.find((opt) => opt.short === '-s');
|
|
151
|
+
expect(sizeOption?.defaultValue).toBe('20');
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('employee list response handling', () => {
|
|
157
|
+
beforeEach(() => {
|
|
158
|
+
vi.clearAllMocks();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should handle successful response with employees', async () => {
|
|
162
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
163
|
+
|
|
164
|
+
(apiClient.listEmployees as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
165
|
+
success: true,
|
|
166
|
+
data: {
|
|
167
|
+
total: 2,
|
|
168
|
+
size: 20,
|
|
169
|
+
current: 1,
|
|
170
|
+
records: mockEmployees,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
expect(apiClient.listEmployees).toBeDefined();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should handle empty results gracefully', async () => {
|
|
178
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
179
|
+
|
|
180
|
+
(apiClient.listEmployees as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
181
|
+
success: true,
|
|
182
|
+
data: {
|
|
183
|
+
total: 0,
|
|
184
|
+
size: 20,
|
|
185
|
+
current: 1,
|
|
186
|
+
records: [],
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(apiClient.listEmployees).toBeDefined();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should handle API error response', async () => {
|
|
194
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
195
|
+
|
|
196
|
+
(apiClient.listEmployees as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
197
|
+
success: false,
|
|
198
|
+
error: {
|
|
199
|
+
code: 'NO_TOKEN',
|
|
200
|
+
message: 'Not logged in. Run: barista auth login --service liberica --env dev',
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(apiClient.listEmployees).toBeDefined();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('should pass pagination params to apiClient.listEmployees', async () => {
|
|
208
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
209
|
+
const { configManager } = await import('../../../../../src/core/config/manager.js');
|
|
210
|
+
|
|
211
|
+
(apiClient.listEmployees as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
212
|
+
success: true,
|
|
213
|
+
data: { total: 1, size: 10, current: 2, records: [mockEmployees[0]] },
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(apiClient.listEmployees).toBeDefined();
|
|
217
|
+
expect(configManager.getCurrentContext).toBeDefined();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should pass status filter to apiClient.listEmployees', async () => {
|
|
221
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
222
|
+
|
|
223
|
+
(apiClient.listEmployees as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
224
|
+
success: true,
|
|
225
|
+
data: { total: 1, size: 20, current: 1, records: [mockEmployees[0]] },
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
expect(apiClient.listEmployees).toBeDefined();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should pass org-id filter to apiClient.listEmployees', async () => {
|
|
232
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
233
|
+
|
|
234
|
+
(apiClient.listEmployees as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
235
|
+
success: true,
|
|
236
|
+
data: { total: 1, size: 20, current: 1, records: [mockEmployees[0]] },
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
expect(apiClient.listEmployees).toBeDefined();
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should output JSON when --json option is used', async () => {
|
|
243
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
244
|
+
|
|
245
|
+
(apiClient.listEmployees as ReturnType<typeof vi.fn>).mockResolvedValue({
|
|
246
|
+
success: true,
|
|
247
|
+
data: {
|
|
248
|
+
total: 2,
|
|
249
|
+
size: 20,
|
|
250
|
+
current: 1,
|
|
251
|
+
records: mockEmployees,
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
expect(apiClient.listEmployees).toBeDefined();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should handle network error', async () => {
|
|
259
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
260
|
+
|
|
261
|
+
(apiClient.listEmployees as ReturnType<typeof vi.fn>).mockRejectedValue(new Error('Network error'));
|
|
262
|
+
|
|
263
|
+
await expect(apiClient.listEmployees).rejects.toThrow('Network error');
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('PageResult structure', () => {
|
|
268
|
+
it('should have correct pagination fields', () => {
|
|
269
|
+
const pageResult = {
|
|
270
|
+
total: 100,
|
|
271
|
+
size: 20,
|
|
272
|
+
current: 1,
|
|
273
|
+
records: mockEmployees,
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
expect(pageResult).toHaveProperty('total');
|
|
277
|
+
expect(pageResult).toHaveProperty('size');
|
|
278
|
+
expect(pageResult).toHaveProperty('current');
|
|
279
|
+
expect(pageResult).toHaveProperty('records');
|
|
280
|
+
expect(Array.isArray(pageResult.records)).toBe(true);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('should calculate total pages correctly', () => {
|
|
284
|
+
const pageResult = {
|
|
285
|
+
total: 100,
|
|
286
|
+
size: 20,
|
|
287
|
+
current: 1,
|
|
288
|
+
records: mockEmployees,
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const totalPages = Math.ceil(pageResult.total / pageResult.size);
|
|
292
|
+
expect(totalPages).toBe(5);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
vi.mock('chalk', () => ({
|
|
4
|
+
default: {
|
|
5
|
+
bold: vi.fn((text) => text),
|
|
6
|
+
green: vi.fn((text) => text),
|
|
7
|
+
red: vi.fn((text) => text),
|
|
8
|
+
gray: vi.fn((text) => text),
|
|
9
|
+
cyan: vi.fn((text) => text),
|
|
10
|
+
},
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock('inquirer', () => ({
|
|
14
|
+
default: {
|
|
15
|
+
prompt: vi.fn(),
|
|
16
|
+
},
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
vi.mock('../../../../../src/core/config/manager.js', () => ({
|
|
20
|
+
configManager: {
|
|
21
|
+
getCurrentContext: vi.fn(() => ({
|
|
22
|
+
environment: 'dev' as const,
|
|
23
|
+
service: 'liberica' as const,
|
|
24
|
+
tenant: 'test-tenant',
|
|
25
|
+
})),
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
vi.mock('../../../../../src/core/auth/token-manager.js', () => ({
|
|
30
|
+
tokenManager: {
|
|
31
|
+
getToken: vi.fn(() => Promise.resolve('mock-token')),
|
|
32
|
+
},
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
vi.mock('../../../../../src/core/api/client.js', () => ({
|
|
36
|
+
apiClient: {
|
|
37
|
+
getEmployee: vi.fn(),
|
|
38
|
+
updateEmployee: vi.fn(),
|
|
39
|
+
},
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
import { createEmployeeUpdateCommand } from '../../../../../src/commands/liberica/employees/update.js';
|
|
43
|
+
|
|
44
|
+
describe('createEmployeeUpdateCommand', () => {
|
|
45
|
+
describe('command structure', () => {
|
|
46
|
+
it('should create command with name "update"', () => {
|
|
47
|
+
const command = createEmployeeUpdateCommand();
|
|
48
|
+
expect(command.name()).toBe('update');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should have correct description', () => {
|
|
52
|
+
const command = createEmployeeUpdateCommand();
|
|
53
|
+
expect(command.description()).toBe('Update employee information');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should have positional <id> argument', () => {
|
|
57
|
+
const command = createEmployeeUpdateCommand();
|
|
58
|
+
expect(command.description()).toContain('employee');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should have --name / -n option', () => {
|
|
62
|
+
const command = createEmployeeUpdateCommand();
|
|
63
|
+
const opt = command.options.find((o) => o.short === '-n' || o.long === '--name');
|
|
64
|
+
expect(opt).toBeDefined();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should have --phone / -p option', () => {
|
|
68
|
+
const command = createEmployeeUpdateCommand();
|
|
69
|
+
const opt = command.options.find((o) => o.short === '-p' || o.long === '--phone');
|
|
70
|
+
expect(opt).toBeDefined();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should have --email / -e option', () => {
|
|
74
|
+
const command = createEmployeeUpdateCommand();
|
|
75
|
+
const opt = command.options.find((o) => o.short === '-e' || o.long === '--email');
|
|
76
|
+
expect(opt).toBeDefined();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should have --position-id option', () => {
|
|
80
|
+
const command = createEmployeeUpdateCommand();
|
|
81
|
+
const opt = command.options.find((o) => o.long === '--position-id');
|
|
82
|
+
expect(opt).toBeDefined();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should have --joined-date / -d option', () => {
|
|
86
|
+
const command = createEmployeeUpdateCommand();
|
|
87
|
+
const opt = command.options.find((o) => o.short === '-d' || o.long === '--joined-date');
|
|
88
|
+
expect(opt).toBeDefined();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should have --org-id option', () => {
|
|
92
|
+
const command = createEmployeeUpdateCommand();
|
|
93
|
+
const opt = command.options.find((o) => o.long === '--org-id');
|
|
94
|
+
expect(opt).toBeDefined();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should have --dry-run option', () => {
|
|
98
|
+
const command = createEmployeeUpdateCommand();
|
|
99
|
+
const opt = command.options.find((o) => o.long === '--dry-run');
|
|
100
|
+
expect(opt).toBeDefined();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should have --json option', () => {
|
|
104
|
+
const command = createEmployeeUpdateCommand();
|
|
105
|
+
const opt = command.options.find((o) => o.long === '--json');
|
|
106
|
+
expect(opt).toBeDefined();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('API integration', () => {
|
|
111
|
+
beforeEach(() => {
|
|
112
|
+
vi.clearAllMocks();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should have getEmployee method on apiClient', async () => {
|
|
116
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
117
|
+
expect(apiClient.getEmployee).toBeDefined();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should have updateEmployee method on apiClient', async () => {
|
|
121
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
122
|
+
expect(apiClient.updateEmployee).toBeDefined();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should call getEmployee with correct params on employee not found', async () => {
|
|
126
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
127
|
+
(apiClient.getEmployee as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
128
|
+
success: false,
|
|
129
|
+
error: { code: '01001150001', message: '查询结果不存在' },
|
|
130
|
+
});
|
|
131
|
+
await apiClient.getEmployee('dev', 'test-tenant', 99999);
|
|
132
|
+
expect(apiClient.getEmployee).toHaveBeenCalledWith('dev', 'test-tenant', 99999);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should handle update failure error response', async () => {
|
|
136
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
137
|
+
(apiClient.updateEmployee as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
138
|
+
success: false,
|
|
139
|
+
error: { code: '01001150007', message: '指定岗位不存在' },
|
|
140
|
+
});
|
|
141
|
+
const response = await apiClient.updateEmployee('dev', 'test-tenant', {
|
|
142
|
+
employeeId: 123,
|
|
143
|
+
employeeName: 'Bad Update',
|
|
144
|
+
});
|
|
145
|
+
expect(response.success).toBe(false);
|
|
146
|
+
expect(response.error?.code).toBe('01001150007');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should handle update success response with correct data', async () => {
|
|
150
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
151
|
+
const mockEmployee = {
|
|
152
|
+
employeeId: 123,
|
|
153
|
+
employeeName: 'Updated Name',
|
|
154
|
+
employeeNo: 'E001',
|
|
155
|
+
statusFlag: 1 as const,
|
|
156
|
+
};
|
|
157
|
+
(apiClient.updateEmployee as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
158
|
+
success: true,
|
|
159
|
+
data: mockEmployee,
|
|
160
|
+
});
|
|
161
|
+
const response = await apiClient.updateEmployee('dev', 'test-tenant', {
|
|
162
|
+
employeeId: 123,
|
|
163
|
+
employeeName: 'Updated Name',
|
|
164
|
+
});
|
|
165
|
+
expect(response.success).toBe(true);
|
|
166
|
+
expect(response.data).toEqual(mockEmployee);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should pass partial update fields to updateEmployee', async () => {
|
|
170
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
171
|
+
(apiClient.updateEmployee as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
172
|
+
success: true,
|
|
173
|
+
data: { employeeId: 123, employeeName: 'New', employeeNo: 'E001', statusFlag: 1 as const },
|
|
174
|
+
});
|
|
175
|
+
const updateData = {
|
|
176
|
+
employeeId: 123,
|
|
177
|
+
employeeName: 'New',
|
|
178
|
+
employeePhone: '13900139000',
|
|
179
|
+
employeeEmail: 'test@example.com',
|
|
180
|
+
};
|
|
181
|
+
await apiClient.updateEmployee('dev', 'test-tenant', updateData);
|
|
182
|
+
expect(apiClient.updateEmployee).toHaveBeenCalledWith('dev', 'test-tenant', updateData);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should call getEmployee to fetch employee before update', async () => {
|
|
186
|
+
const { apiClient } = await import('../../../../../src/core/api/client.js');
|
|
187
|
+
(apiClient.getEmployee as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
|
|
188
|
+
success: true,
|
|
189
|
+
data: { employeeId: 123, employeeName: 'Before', employeeNo: 'E001', statusFlag: 1 as const },
|
|
190
|
+
});
|
|
191
|
+
await apiClient.getEmployee('dev', 'test-tenant', 123);
|
|
192
|
+
expect(apiClient.getEmployee).toHaveBeenCalledWith('dev', 'test-tenant', 123);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe('configManager integration', () => {
|
|
197
|
+
it('should have getCurrentContext on configManager', async () => {
|
|
198
|
+
const { configManager } = await import('../../../../../src/core/config/manager.js');
|
|
199
|
+
expect(configManager.getCurrentContext).toBeDefined();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should return correct context values', async () => {
|
|
203
|
+
const { configManager } = await import('../../../../../src/core/config/manager.js');
|
|
204
|
+
const context = configManager.getCurrentContext();
|
|
205
|
+
expect(context.environment).toBe('dev');
|
|
206
|
+
expect(context.tenant).toBe('test-tenant');
|
|
207
|
+
expect(context.service).toBe('liberica');
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|