@uipath/data-fabric-tool 1.0.4 → 1.195.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/browser.json +3 -0
- package/dist/index.js +20 -43167
- package/dist/tool.js +29174 -28399
- package/package.json +16 -32
- package/src/commands/choice-sets.spec.ts +849 -0
- package/src/commands/choice-sets.ts +690 -0
- package/src/commands/entities.spec.ts +411 -137
- package/src/commands/entities.ts +189 -368
- package/src/commands/files.spec.ts +18 -32
- package/src/commands/files.ts +43 -92
- package/src/commands/records.spec.ts +171 -207
- package/src/commands/records.ts +150 -342
- package/src/index.ts +5 -10
- package/src/tool.ts +6 -0
- package/src/utils/output.spec.ts +78 -0
- package/src/utils/output.ts +51 -0
- package/src/utils/sdk-client.spec.ts +59 -0
- package/src/utils/sdk-client.ts +23 -0
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
vi.mock("../utils/sdk-client", () => ({
|
|
5
|
+
createDataFabricClient: vi.fn(),
|
|
6
|
+
connectOrFail: vi.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
vi.mock("@uipath/common", async (importOriginal) => {
|
|
10
|
+
const actual = await importOriginal<typeof import("@uipath/common")>();
|
|
11
|
+
return {
|
|
12
|
+
...actual,
|
|
13
|
+
OutputFormatter: {
|
|
14
|
+
success: vi.fn(),
|
|
15
|
+
error: vi.fn(),
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
import { OutputFormatter } from "@uipath/common";
|
|
21
|
+
import { connectOrFail } from "../utils/sdk-client";
|
|
22
|
+
import {
|
|
23
|
+
registerChoiceSetsCommand,
|
|
24
|
+
registerChoiceSetValuesCommand,
|
|
25
|
+
} from "./choice-sets";
|
|
26
|
+
|
|
27
|
+
function buildProgram(): Command {
|
|
28
|
+
const program = new Command();
|
|
29
|
+
program.name("test").exitOverride();
|
|
30
|
+
registerChoiceSetsCommand(program);
|
|
31
|
+
return program;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildValuesProgram(): Command {
|
|
35
|
+
const program = new Command();
|
|
36
|
+
program.name("test").exitOverride();
|
|
37
|
+
registerChoiceSetValuesCommand(program);
|
|
38
|
+
return program;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function mockSdk(overrides: Record<string, unknown> = {}) {
|
|
42
|
+
const sdk = {
|
|
43
|
+
entities: {
|
|
44
|
+
choicesets: {
|
|
45
|
+
getAll: vi.fn().mockResolvedValue([]),
|
|
46
|
+
getById: vi.fn().mockResolvedValue({
|
|
47
|
+
items: [],
|
|
48
|
+
totalCount: 0,
|
|
49
|
+
}),
|
|
50
|
+
create: vi.fn().mockResolvedValue("new-choice-set-id"),
|
|
51
|
+
updateById: vi.fn().mockResolvedValue(undefined),
|
|
52
|
+
deleteById: vi.fn().mockResolvedValue(undefined),
|
|
53
|
+
insertValueById: vi.fn().mockResolvedValue({
|
|
54
|
+
id: "v-new",
|
|
55
|
+
name: "travel",
|
|
56
|
+
displayName: "Travel",
|
|
57
|
+
numberId: 1,
|
|
58
|
+
}),
|
|
59
|
+
updateValueById: vi.fn().mockResolvedValue({
|
|
60
|
+
id: "v-1",
|
|
61
|
+
name: "travel",
|
|
62
|
+
displayName: "Business Travel",
|
|
63
|
+
numberId: 1,
|
|
64
|
+
}),
|
|
65
|
+
deleteValuesById: vi.fn().mockResolvedValue(undefined),
|
|
66
|
+
...overrides,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
vi.mocked(connectOrFail).mockResolvedValue(sdk as never);
|
|
71
|
+
return sdk;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe("choice-sets list", () => {
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
vi.resetAllMocks();
|
|
77
|
+
process.exitCode = undefined;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("should register the choice-sets command with list, list-values, create, update, and delete subcommands", () => {
|
|
81
|
+
const program = buildProgram();
|
|
82
|
+
const cmd = program.commands.find((c) => c.name() === "choice-sets");
|
|
83
|
+
expect(cmd).toBeDefined();
|
|
84
|
+
const subNames = cmd?.commands.map((c) => c.name()) ?? [];
|
|
85
|
+
expect(subNames).toContain("list");
|
|
86
|
+
expect(subNames).toContain("list-values");
|
|
87
|
+
expect(subNames).toContain("create");
|
|
88
|
+
expect(subNames).toContain("update");
|
|
89
|
+
expect(subNames).toContain("delete");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should list choice sets and return the verbatim SDK response", async () => {
|
|
93
|
+
const sdk = mockSdk();
|
|
94
|
+
const rawList = [
|
|
95
|
+
{
|
|
96
|
+
id: "cs-uuid-1",
|
|
97
|
+
name: "ExpenseTypes",
|
|
98
|
+
displayName: "Expense Types",
|
|
99
|
+
description: "Categories of expenses",
|
|
100
|
+
folderId: "f-1",
|
|
101
|
+
createdBy: "u-1",
|
|
102
|
+
updatedBy: "u-2",
|
|
103
|
+
createdTime: "2026-01-01T00:00:00Z",
|
|
104
|
+
updatedTime: "2026-01-02T00:00:00Z",
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
vi.mocked(sdk.entities.choicesets.getAll).mockResolvedValue(
|
|
108
|
+
rawList as never,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const program = buildProgram();
|
|
112
|
+
await program.parseAsync(["node", "test", "choice-sets", "list"]);
|
|
113
|
+
|
|
114
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
115
|
+
expect.objectContaining({
|
|
116
|
+
Result: "Success",
|
|
117
|
+
Code: "ChoiceSetList",
|
|
118
|
+
Data: rawList,
|
|
119
|
+
}),
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should return empty list when no choice sets exist", async () => {
|
|
124
|
+
mockSdk();
|
|
125
|
+
const program = buildProgram();
|
|
126
|
+
await program.parseAsync(["node", "test", "choice-sets", "list"]);
|
|
127
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
128
|
+
expect.objectContaining({
|
|
129
|
+
Result: "Success",
|
|
130
|
+
Code: "ChoiceSetList",
|
|
131
|
+
Data: [],
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should output error when list fails", async () => {
|
|
137
|
+
const sdk = mockSdk();
|
|
138
|
+
vi.mocked(sdk.entities.choicesets.getAll).mockRejectedValue(
|
|
139
|
+
new Error("forbidden"),
|
|
140
|
+
);
|
|
141
|
+
const program = buildProgram();
|
|
142
|
+
await program.parseAsync(["node", "test", "choice-sets", "list"]);
|
|
143
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
144
|
+
expect.objectContaining({
|
|
145
|
+
Result: "Failure",
|
|
146
|
+
Message: "Error listing choice sets",
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
expect(process.exitCode).toBe(1);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("should not proceed with list when the client connection fails", async () => {
|
|
153
|
+
const sdk = mockSdk();
|
|
154
|
+
vi.mocked(connectOrFail).mockResolvedValue(undefined);
|
|
155
|
+
const program = buildProgram();
|
|
156
|
+
await program.parseAsync(["node", "test", "choice-sets", "list"]);
|
|
157
|
+
expect(sdk.entities.choicesets.getAll).not.toHaveBeenCalled();
|
|
158
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe("choice-sets list-values", () => {
|
|
163
|
+
beforeEach(() => {
|
|
164
|
+
vi.resetAllMocks();
|
|
165
|
+
process.exitCode = undefined;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should get values for a choice set and return the verbatim SDK response", async () => {
|
|
169
|
+
const sdk = mockSdk();
|
|
170
|
+
const rawResponse = {
|
|
171
|
+
items: [
|
|
172
|
+
{
|
|
173
|
+
id: "v-1",
|
|
174
|
+
name: "travel",
|
|
175
|
+
displayName: "Travel",
|
|
176
|
+
numberId: 1,
|
|
177
|
+
createdTime: "2026-01-01T00:00:00Z",
|
|
178
|
+
updatedTime: "2026-01-02T00:00:00Z",
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
totalCount: 1,
|
|
182
|
+
hasNextPage: false,
|
|
183
|
+
};
|
|
184
|
+
vi.mocked(sdk.entities.choicesets.getById).mockResolvedValue(
|
|
185
|
+
rawResponse as never,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
const program = buildProgram();
|
|
189
|
+
await program.parseAsync([
|
|
190
|
+
"node",
|
|
191
|
+
"test",
|
|
192
|
+
"choice-sets",
|
|
193
|
+
"list-values",
|
|
194
|
+
"cs-1",
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
expect(sdk.entities.choicesets.getById).toHaveBeenCalledWith("cs-1", {
|
|
198
|
+
pageSize: 50,
|
|
199
|
+
});
|
|
200
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
201
|
+
expect.objectContaining({
|
|
202
|
+
Result: "Success",
|
|
203
|
+
Code: "ChoiceSetValues",
|
|
204
|
+
Data: rawResponse,
|
|
205
|
+
}),
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("should pass cursor to choicesets.getById and return the raw response", async () => {
|
|
210
|
+
const sdk = mockSdk();
|
|
211
|
+
const rawResponse = {
|
|
212
|
+
items: [{ id: "v-1", name: "n", displayName: "N", numberId: 1 }],
|
|
213
|
+
totalCount: 100,
|
|
214
|
+
hasNextPage: true,
|
|
215
|
+
nextCursor: "next-cur",
|
|
216
|
+
};
|
|
217
|
+
vi.mocked(sdk.entities.choicesets.getById).mockResolvedValue(
|
|
218
|
+
rawResponse as never,
|
|
219
|
+
);
|
|
220
|
+
const program = buildProgram();
|
|
221
|
+
await program.parseAsync([
|
|
222
|
+
"node",
|
|
223
|
+
"test",
|
|
224
|
+
"choice-sets",
|
|
225
|
+
"list-values",
|
|
226
|
+
"cs-1",
|
|
227
|
+
"--cursor",
|
|
228
|
+
"prev",
|
|
229
|
+
"--limit",
|
|
230
|
+
"10",
|
|
231
|
+
]);
|
|
232
|
+
expect(sdk.entities.choicesets.getById).toHaveBeenCalledWith("cs-1", {
|
|
233
|
+
pageSize: 10,
|
|
234
|
+
cursor: { value: "prev" },
|
|
235
|
+
});
|
|
236
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
237
|
+
expect.objectContaining({
|
|
238
|
+
Data: expect.objectContaining({
|
|
239
|
+
hasNextPage: true,
|
|
240
|
+
nextCursor: "next-cur",
|
|
241
|
+
}),
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("should pass jumpToPage to choicesets.getById when --offset is given", async () => {
|
|
247
|
+
const sdk = mockSdk();
|
|
248
|
+
vi.mocked(sdk.entities.choicesets.getById).mockResolvedValue({
|
|
249
|
+
items: [],
|
|
250
|
+
totalCount: 100,
|
|
251
|
+
hasNextPage: false,
|
|
252
|
+
});
|
|
253
|
+
const program = buildProgram();
|
|
254
|
+
await program.parseAsync([
|
|
255
|
+
"node",
|
|
256
|
+
"test",
|
|
257
|
+
"choice-sets",
|
|
258
|
+
"list-values",
|
|
259
|
+
"cs-1",
|
|
260
|
+
"--offset",
|
|
261
|
+
"20",
|
|
262
|
+
"--limit",
|
|
263
|
+
"10",
|
|
264
|
+
]);
|
|
265
|
+
expect(sdk.entities.choicesets.getById).toHaveBeenCalledWith("cs-1", {
|
|
266
|
+
pageSize: 10,
|
|
267
|
+
jumpToPage: 3,
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it("should error when --offset and --cursor are both given", async () => {
|
|
272
|
+
mockSdk();
|
|
273
|
+
const program = buildProgram();
|
|
274
|
+
await program.parseAsync([
|
|
275
|
+
"node",
|
|
276
|
+
"test",
|
|
277
|
+
"choice-sets",
|
|
278
|
+
"list-values",
|
|
279
|
+
"cs-1",
|
|
280
|
+
"--offset",
|
|
281
|
+
"10",
|
|
282
|
+
"--cursor",
|
|
283
|
+
"x",
|
|
284
|
+
]);
|
|
285
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
286
|
+
expect.objectContaining({
|
|
287
|
+
Result: "Failure",
|
|
288
|
+
Message: "--offset and --cursor are mutually exclusive",
|
|
289
|
+
}),
|
|
290
|
+
);
|
|
291
|
+
expect(process.exitCode).toBe(1);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("should error when --limit is invalid", async () => {
|
|
295
|
+
mockSdk();
|
|
296
|
+
const program = buildProgram();
|
|
297
|
+
await program.parseAsync([
|
|
298
|
+
"node",
|
|
299
|
+
"test",
|
|
300
|
+
"choice-sets",
|
|
301
|
+
"list-values",
|
|
302
|
+
"cs-1",
|
|
303
|
+
"--limit",
|
|
304
|
+
"0",
|
|
305
|
+
]);
|
|
306
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
307
|
+
expect.objectContaining({
|
|
308
|
+
Result: "Failure",
|
|
309
|
+
Message: "Invalid --limit value",
|
|
310
|
+
}),
|
|
311
|
+
);
|
|
312
|
+
expect(process.exitCode).toBe(1);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("should error when --offset is negative", async () => {
|
|
316
|
+
mockSdk();
|
|
317
|
+
const program = buildProgram();
|
|
318
|
+
await program.parseAsync([
|
|
319
|
+
"node",
|
|
320
|
+
"test",
|
|
321
|
+
"choice-sets",
|
|
322
|
+
"list-values",
|
|
323
|
+
"cs-1",
|
|
324
|
+
"--offset",
|
|
325
|
+
"-1",
|
|
326
|
+
]);
|
|
327
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
328
|
+
expect.objectContaining({
|
|
329
|
+
Result: "Failure",
|
|
330
|
+
Message: "Invalid --offset value",
|
|
331
|
+
}),
|
|
332
|
+
);
|
|
333
|
+
expect(process.exitCode).toBe(1);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("should output error when get fails", async () => {
|
|
337
|
+
const sdk = mockSdk();
|
|
338
|
+
vi.mocked(sdk.entities.choicesets.getById).mockRejectedValue(
|
|
339
|
+
new Error("404"),
|
|
340
|
+
);
|
|
341
|
+
const program = buildProgram();
|
|
342
|
+
await program.parseAsync([
|
|
343
|
+
"node",
|
|
344
|
+
"test",
|
|
345
|
+
"choice-sets",
|
|
346
|
+
"list-values",
|
|
347
|
+
"missing",
|
|
348
|
+
]);
|
|
349
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
350
|
+
expect.objectContaining({
|
|
351
|
+
Result: "Failure",
|
|
352
|
+
Message: "Error getting choice set 'missing'",
|
|
353
|
+
}),
|
|
354
|
+
);
|
|
355
|
+
expect(process.exitCode).toBe(1);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it("should not proceed with get when the client connection fails", async () => {
|
|
359
|
+
const sdk = mockSdk();
|
|
360
|
+
vi.mocked(connectOrFail).mockResolvedValue(undefined);
|
|
361
|
+
const program = buildProgram();
|
|
362
|
+
await program.parseAsync([
|
|
363
|
+
"node",
|
|
364
|
+
"test",
|
|
365
|
+
"choice-sets",
|
|
366
|
+
"list-values",
|
|
367
|
+
"cs-1",
|
|
368
|
+
]);
|
|
369
|
+
expect(sdk.entities.choicesets.getById).not.toHaveBeenCalled();
|
|
370
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
describe("choice-sets create", () => {
|
|
375
|
+
beforeEach(() => {
|
|
376
|
+
vi.resetAllMocks();
|
|
377
|
+
process.exitCode = undefined;
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("should create a choice set with options and return the ID", async () => {
|
|
381
|
+
const sdk = mockSdk();
|
|
382
|
+
const program = buildProgram();
|
|
383
|
+
await program.parseAsync([
|
|
384
|
+
"node",
|
|
385
|
+
"test",
|
|
386
|
+
"choice-sets",
|
|
387
|
+
"create",
|
|
388
|
+
"ExpenseTypes",
|
|
389
|
+
"--display-name",
|
|
390
|
+
"Expense Types",
|
|
391
|
+
"--description",
|
|
392
|
+
"Categories",
|
|
393
|
+
"--folder-key",
|
|
394
|
+
"folder-1",
|
|
395
|
+
]);
|
|
396
|
+
|
|
397
|
+
expect(sdk.entities.choicesets.create).toHaveBeenCalledWith(
|
|
398
|
+
"ExpenseTypes",
|
|
399
|
+
{
|
|
400
|
+
displayName: "Expense Types",
|
|
401
|
+
description: "Categories",
|
|
402
|
+
folderKey: "folder-1",
|
|
403
|
+
},
|
|
404
|
+
);
|
|
405
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
406
|
+
expect.objectContaining({
|
|
407
|
+
Result: "Success",
|
|
408
|
+
Code: "ChoiceSetCreated",
|
|
409
|
+
Data: { ID: "new-choice-set-id" },
|
|
410
|
+
}),
|
|
411
|
+
);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it("should create a choice set with no options (undefined opts)", async () => {
|
|
415
|
+
const sdk = mockSdk();
|
|
416
|
+
const program = buildProgram();
|
|
417
|
+
await program.parseAsync([
|
|
418
|
+
"node",
|
|
419
|
+
"test",
|
|
420
|
+
"choice-sets",
|
|
421
|
+
"create",
|
|
422
|
+
"ExpenseTypes",
|
|
423
|
+
]);
|
|
424
|
+
expect(sdk.entities.choicesets.create).toHaveBeenCalledWith(
|
|
425
|
+
"ExpenseTypes",
|
|
426
|
+
undefined,
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("should output error when create fails", async () => {
|
|
431
|
+
const sdk = mockSdk();
|
|
432
|
+
vi.mocked(sdk.entities.choicesets.create).mockRejectedValue(
|
|
433
|
+
new Error("conflict"),
|
|
434
|
+
);
|
|
435
|
+
const program = buildProgram();
|
|
436
|
+
await program.parseAsync([
|
|
437
|
+
"node",
|
|
438
|
+
"test",
|
|
439
|
+
"choice-sets",
|
|
440
|
+
"create",
|
|
441
|
+
"X",
|
|
442
|
+
]);
|
|
443
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
444
|
+
expect.objectContaining({
|
|
445
|
+
Result: "Failure",
|
|
446
|
+
Message: "Error creating choice set",
|
|
447
|
+
}),
|
|
448
|
+
);
|
|
449
|
+
expect(process.exitCode).toBe(1);
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
describe("choice-sets update", () => {
|
|
454
|
+
beforeEach(() => {
|
|
455
|
+
vi.resetAllMocks();
|
|
456
|
+
process.exitCode = undefined;
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it("should update a choice set's metadata", async () => {
|
|
460
|
+
const sdk = mockSdk();
|
|
461
|
+
const program = buildProgram();
|
|
462
|
+
await program.parseAsync([
|
|
463
|
+
"node",
|
|
464
|
+
"test",
|
|
465
|
+
"choice-sets",
|
|
466
|
+
"update",
|
|
467
|
+
"cs-1",
|
|
468
|
+
"--display-name",
|
|
469
|
+
"New Name",
|
|
470
|
+
]);
|
|
471
|
+
expect(sdk.entities.choicesets.updateById).toHaveBeenCalledWith(
|
|
472
|
+
"cs-1",
|
|
473
|
+
{
|
|
474
|
+
displayName: "New Name",
|
|
475
|
+
},
|
|
476
|
+
);
|
|
477
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
478
|
+
expect.objectContaining({
|
|
479
|
+
Result: "Success",
|
|
480
|
+
Code: "ChoiceSetUpdated",
|
|
481
|
+
Data: { ID: "cs-1" },
|
|
482
|
+
}),
|
|
483
|
+
);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it("should error when no update fields are provided", async () => {
|
|
487
|
+
const sdk = mockSdk();
|
|
488
|
+
const program = buildProgram();
|
|
489
|
+
await program.parseAsync([
|
|
490
|
+
"node",
|
|
491
|
+
"test",
|
|
492
|
+
"choice-sets",
|
|
493
|
+
"update",
|
|
494
|
+
"cs-1",
|
|
495
|
+
]);
|
|
496
|
+
expect(sdk.entities.choicesets.updateById).not.toHaveBeenCalled();
|
|
497
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
498
|
+
expect.objectContaining({
|
|
499
|
+
Result: "Failure",
|
|
500
|
+
Message: "No update fields provided",
|
|
501
|
+
}),
|
|
502
|
+
);
|
|
503
|
+
expect(process.exitCode).toBe(1);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it("should output error when update fails", async () => {
|
|
507
|
+
const sdk = mockSdk();
|
|
508
|
+
vi.mocked(sdk.entities.choicesets.updateById).mockRejectedValue(
|
|
509
|
+
new Error("404"),
|
|
510
|
+
);
|
|
511
|
+
const program = buildProgram();
|
|
512
|
+
await program.parseAsync([
|
|
513
|
+
"node",
|
|
514
|
+
"test",
|
|
515
|
+
"choice-sets",
|
|
516
|
+
"update",
|
|
517
|
+
"missing",
|
|
518
|
+
"--description",
|
|
519
|
+
"x",
|
|
520
|
+
]);
|
|
521
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
522
|
+
expect.objectContaining({
|
|
523
|
+
Result: "Failure",
|
|
524
|
+
Message: "Error updating choice set 'missing'",
|
|
525
|
+
}),
|
|
526
|
+
);
|
|
527
|
+
expect(process.exitCode).toBe(1);
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
describe("choice-sets delete", () => {
|
|
532
|
+
beforeEach(() => {
|
|
533
|
+
vi.resetAllMocks();
|
|
534
|
+
process.exitCode = undefined;
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it("should delete a choice set with --confirm and --reason", async () => {
|
|
538
|
+
const sdk = mockSdk();
|
|
539
|
+
const program = buildProgram();
|
|
540
|
+
await program.parseAsync([
|
|
541
|
+
"node",
|
|
542
|
+
"test",
|
|
543
|
+
"choice-sets",
|
|
544
|
+
"delete",
|
|
545
|
+
"cs-1",
|
|
546
|
+
"--confirm",
|
|
547
|
+
"--reason",
|
|
548
|
+
"cleanup",
|
|
549
|
+
]);
|
|
550
|
+
expect(sdk.entities.choicesets.deleteById).toHaveBeenCalledWith("cs-1");
|
|
551
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
552
|
+
expect.objectContaining({
|
|
553
|
+
Result: "Success",
|
|
554
|
+
Code: "ChoiceSetDeleted",
|
|
555
|
+
Data: { ID: "cs-1", Reason: "cleanup" },
|
|
556
|
+
}),
|
|
557
|
+
);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it("should require --confirm", async () => {
|
|
561
|
+
const sdk = mockSdk();
|
|
562
|
+
const program = buildProgram();
|
|
563
|
+
await program.parseAsync([
|
|
564
|
+
"node",
|
|
565
|
+
"test",
|
|
566
|
+
"choice-sets",
|
|
567
|
+
"delete",
|
|
568
|
+
"cs-1",
|
|
569
|
+
"--reason",
|
|
570
|
+
"cleanup",
|
|
571
|
+
]);
|
|
572
|
+
expect(sdk.entities.choicesets.deleteById).not.toHaveBeenCalled();
|
|
573
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
574
|
+
expect.objectContaining({
|
|
575
|
+
Result: "Failure",
|
|
576
|
+
Message: "Confirmation required for destructive operation",
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
expect(process.exitCode).toBe(1);
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it("should require --reason", async () => {
|
|
583
|
+
const sdk = mockSdk();
|
|
584
|
+
const program = buildProgram();
|
|
585
|
+
await program.parseAsync([
|
|
586
|
+
"node",
|
|
587
|
+
"test",
|
|
588
|
+
"choice-sets",
|
|
589
|
+
"delete",
|
|
590
|
+
"cs-1",
|
|
591
|
+
"--confirm",
|
|
592
|
+
]);
|
|
593
|
+
expect(sdk.entities.choicesets.deleteById).not.toHaveBeenCalled();
|
|
594
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
595
|
+
expect.objectContaining({
|
|
596
|
+
Result: "Failure",
|
|
597
|
+
Message: "Reason required for destructive operation",
|
|
598
|
+
}),
|
|
599
|
+
);
|
|
600
|
+
expect(process.exitCode).toBe(1);
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
describe("choice-set-values", () => {
|
|
605
|
+
beforeEach(() => {
|
|
606
|
+
vi.resetAllMocks();
|
|
607
|
+
process.exitCode = undefined;
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it("should register the choice-set-values command with create, update, and delete subcommands", () => {
|
|
611
|
+
const program = buildValuesProgram();
|
|
612
|
+
const cmd = program.commands.find(
|
|
613
|
+
(c) => c.name() === "choice-set-values",
|
|
614
|
+
);
|
|
615
|
+
expect(cmd).toBeDefined();
|
|
616
|
+
const subNames = cmd?.commands.map((c) => c.name()) ?? [];
|
|
617
|
+
expect(subNames).toContain("create");
|
|
618
|
+
expect(subNames).toContain("update");
|
|
619
|
+
expect(subNames).toContain("delete");
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it("should create a value and project the response", async () => {
|
|
623
|
+
const sdk = mockSdk();
|
|
624
|
+
const program = buildValuesProgram();
|
|
625
|
+
await program.parseAsync([
|
|
626
|
+
"node",
|
|
627
|
+
"test",
|
|
628
|
+
"choice-set-values",
|
|
629
|
+
"create",
|
|
630
|
+
"cs-1",
|
|
631
|
+
"travel",
|
|
632
|
+
"--display-name",
|
|
633
|
+
"Travel",
|
|
634
|
+
]);
|
|
635
|
+
expect(sdk.entities.choicesets.insertValueById).toHaveBeenCalledWith(
|
|
636
|
+
"cs-1",
|
|
637
|
+
"travel",
|
|
638
|
+
{ displayName: "Travel" },
|
|
639
|
+
);
|
|
640
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
641
|
+
expect.objectContaining({
|
|
642
|
+
Result: "Success",
|
|
643
|
+
Code: "ChoiceSetValueCreated",
|
|
644
|
+
Data: expect.objectContaining({
|
|
645
|
+
id: "v-new",
|
|
646
|
+
name: "travel",
|
|
647
|
+
displayName: "Travel",
|
|
648
|
+
numberId: 1,
|
|
649
|
+
}),
|
|
650
|
+
}),
|
|
651
|
+
);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
it("should create a value with no display name (undefined opts)", async () => {
|
|
655
|
+
const sdk = mockSdk();
|
|
656
|
+
const program = buildValuesProgram();
|
|
657
|
+
await program.parseAsync([
|
|
658
|
+
"node",
|
|
659
|
+
"test",
|
|
660
|
+
"choice-set-values",
|
|
661
|
+
"create",
|
|
662
|
+
"cs-1",
|
|
663
|
+
"travel",
|
|
664
|
+
]);
|
|
665
|
+
expect(sdk.entities.choicesets.insertValueById).toHaveBeenCalledWith(
|
|
666
|
+
"cs-1",
|
|
667
|
+
"travel",
|
|
668
|
+
undefined,
|
|
669
|
+
);
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it("should output error when value create fails", async () => {
|
|
673
|
+
const sdk = mockSdk();
|
|
674
|
+
vi.mocked(sdk.entities.choicesets.insertValueById).mockRejectedValue(
|
|
675
|
+
new Error("conflict"),
|
|
676
|
+
);
|
|
677
|
+
const program = buildValuesProgram();
|
|
678
|
+
await program.parseAsync([
|
|
679
|
+
"node",
|
|
680
|
+
"test",
|
|
681
|
+
"choice-set-values",
|
|
682
|
+
"create",
|
|
683
|
+
"cs-1",
|
|
684
|
+
"travel",
|
|
685
|
+
]);
|
|
686
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
687
|
+
expect.objectContaining({
|
|
688
|
+
Result: "Failure",
|
|
689
|
+
Message: "Error creating value in choice set 'cs-1'",
|
|
690
|
+
}),
|
|
691
|
+
);
|
|
692
|
+
expect(process.exitCode).toBe(1);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it("should update a value's display name", async () => {
|
|
696
|
+
const sdk = mockSdk();
|
|
697
|
+
const program = buildValuesProgram();
|
|
698
|
+
await program.parseAsync([
|
|
699
|
+
"node",
|
|
700
|
+
"test",
|
|
701
|
+
"choice-set-values",
|
|
702
|
+
"update",
|
|
703
|
+
"cs-1",
|
|
704
|
+
"v-1",
|
|
705
|
+
"Business Travel",
|
|
706
|
+
]);
|
|
707
|
+
expect(sdk.entities.choicesets.updateValueById).toHaveBeenCalledWith(
|
|
708
|
+
"cs-1",
|
|
709
|
+
"v-1",
|
|
710
|
+
"Business Travel",
|
|
711
|
+
);
|
|
712
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
713
|
+
expect.objectContaining({
|
|
714
|
+
Result: "Success",
|
|
715
|
+
Code: "ChoiceSetValueUpdated",
|
|
716
|
+
Data: expect.objectContaining({
|
|
717
|
+
id: "v-1",
|
|
718
|
+
displayName: "Business Travel",
|
|
719
|
+
}),
|
|
720
|
+
}),
|
|
721
|
+
);
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
it("should output error when value update fails", async () => {
|
|
725
|
+
const sdk = mockSdk();
|
|
726
|
+
vi.mocked(sdk.entities.choicesets.updateValueById).mockRejectedValue(
|
|
727
|
+
new Error("404"),
|
|
728
|
+
);
|
|
729
|
+
const program = buildValuesProgram();
|
|
730
|
+
await program.parseAsync([
|
|
731
|
+
"node",
|
|
732
|
+
"test",
|
|
733
|
+
"choice-set-values",
|
|
734
|
+
"update",
|
|
735
|
+
"cs-1",
|
|
736
|
+
"missing",
|
|
737
|
+
"X",
|
|
738
|
+
]);
|
|
739
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
740
|
+
expect.objectContaining({
|
|
741
|
+
Result: "Failure",
|
|
742
|
+
Message: "Error updating value 'missing' in choice set 'cs-1'",
|
|
743
|
+
}),
|
|
744
|
+
);
|
|
745
|
+
expect(process.exitCode).toBe(1);
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
it("should delete values from a comma-separated --ids list", async () => {
|
|
749
|
+
const sdk = mockSdk();
|
|
750
|
+
const program = buildValuesProgram();
|
|
751
|
+
await program.parseAsync([
|
|
752
|
+
"node",
|
|
753
|
+
"test",
|
|
754
|
+
"choice-set-values",
|
|
755
|
+
"delete",
|
|
756
|
+
"cs-1",
|
|
757
|
+
"--ids",
|
|
758
|
+
"v-1, v-2 ,v-3",
|
|
759
|
+
"--confirm",
|
|
760
|
+
"--reason",
|
|
761
|
+
"cleanup",
|
|
762
|
+
]);
|
|
763
|
+
expect(sdk.entities.choicesets.deleteValuesById).toHaveBeenCalledWith(
|
|
764
|
+
"cs-1",
|
|
765
|
+
["v-1", "v-2", "v-3"],
|
|
766
|
+
);
|
|
767
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
768
|
+
expect.objectContaining({
|
|
769
|
+
Result: "Success",
|
|
770
|
+
Code: "ChoiceSetValuesDeleted",
|
|
771
|
+
Data: {
|
|
772
|
+
ChoiceSetId: "cs-1",
|
|
773
|
+
DeletedIds: ["v-1", "v-2", "v-3"],
|
|
774
|
+
Reason: "cleanup",
|
|
775
|
+
},
|
|
776
|
+
}),
|
|
777
|
+
);
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
it("should error when --ids is missing or empty", async () => {
|
|
781
|
+
const sdk = mockSdk();
|
|
782
|
+
const program = buildValuesProgram();
|
|
783
|
+
await program.parseAsync([
|
|
784
|
+
"node",
|
|
785
|
+
"test",
|
|
786
|
+
"choice-set-values",
|
|
787
|
+
"delete",
|
|
788
|
+
"cs-1",
|
|
789
|
+
"--confirm",
|
|
790
|
+
"--reason",
|
|
791
|
+
"cleanup",
|
|
792
|
+
]);
|
|
793
|
+
expect(sdk.entities.choicesets.deleteValuesById).not.toHaveBeenCalled();
|
|
794
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
795
|
+
expect.objectContaining({
|
|
796
|
+
Result: "Failure",
|
|
797
|
+
Message: "No value IDs provided",
|
|
798
|
+
}),
|
|
799
|
+
);
|
|
800
|
+
expect(process.exitCode).toBe(1);
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
it("should require --confirm for value delete", async () => {
|
|
804
|
+
const sdk = mockSdk();
|
|
805
|
+
const program = buildValuesProgram();
|
|
806
|
+
await program.parseAsync([
|
|
807
|
+
"node",
|
|
808
|
+
"test",
|
|
809
|
+
"choice-set-values",
|
|
810
|
+
"delete",
|
|
811
|
+
"cs-1",
|
|
812
|
+
"--ids",
|
|
813
|
+
"v-1",
|
|
814
|
+
"--reason",
|
|
815
|
+
"cleanup",
|
|
816
|
+
]);
|
|
817
|
+
expect(sdk.entities.choicesets.deleteValuesById).not.toHaveBeenCalled();
|
|
818
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
819
|
+
expect.objectContaining({
|
|
820
|
+
Result: "Failure",
|
|
821
|
+
Message: "Confirmation required for destructive operation",
|
|
822
|
+
}),
|
|
823
|
+
);
|
|
824
|
+
expect(process.exitCode).toBe(1);
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
it("should require --reason for value delete", async () => {
|
|
828
|
+
const sdk = mockSdk();
|
|
829
|
+
const program = buildValuesProgram();
|
|
830
|
+
await program.parseAsync([
|
|
831
|
+
"node",
|
|
832
|
+
"test",
|
|
833
|
+
"choice-set-values",
|
|
834
|
+
"delete",
|
|
835
|
+
"cs-1",
|
|
836
|
+
"--ids",
|
|
837
|
+
"v-1",
|
|
838
|
+
"--confirm",
|
|
839
|
+
]);
|
|
840
|
+
expect(sdk.entities.choicesets.deleteValuesById).not.toHaveBeenCalled();
|
|
841
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
842
|
+
expect.objectContaining({
|
|
843
|
+
Result: "Failure",
|
|
844
|
+
Message: "Reason required for destructive operation",
|
|
845
|
+
}),
|
|
846
|
+
);
|
|
847
|
+
expect(process.exitCode).toBe(1);
|
|
848
|
+
});
|
|
849
|
+
});
|