@uipath/data-fabric-tool 0.9.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/dist/index.js +57923 -0
- package/dist/tool.js +57914 -0
- package/package.json +34 -0
- package/src/commands/entities.spec.ts +970 -0
- package/src/commands/entities.ts +564 -0
- package/src/commands/files.spec.ts +344 -0
- package/src/commands/files.ts +339 -0
- package/src/commands/records.spec.ts +2106 -0
- package/src/commands/records.ts +875 -0
- package/src/index.ts +17 -0
- package/src/tool.ts +19 -0
- package/src/utils/input.ts +32 -0
- package/src/utils/sdk-client.ts +23 -0
- package/tsconfig.json +9 -0
- package/vitest.config.ts +1 -0
|
@@ -0,0 +1,2106 @@
|
|
|
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
|
+
}));
|
|
7
|
+
|
|
8
|
+
vi.mock("@uipath/common", async (importOriginal) => {
|
|
9
|
+
const actual = await importOriginal<typeof import("@uipath/common")>();
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
OutputFormatter: {
|
|
13
|
+
success: vi.fn(),
|
|
14
|
+
error: vi.fn(),
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
vi.mock("@uipath/filesystem", () => ({
|
|
20
|
+
getFileSystem: vi.fn(() => ({
|
|
21
|
+
readFile: vi.fn().mockResolvedValue(null),
|
|
22
|
+
})),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
import { OutputFormatter } from "@uipath/common";
|
|
26
|
+
import { createDataFabricClient } from "../utils/sdk-client";
|
|
27
|
+
import { registerRecordsCommand } from "./records";
|
|
28
|
+
|
|
29
|
+
function buildProgram(): Command {
|
|
30
|
+
const program = new Command();
|
|
31
|
+
program.name("test").exitOverride();
|
|
32
|
+
registerRecordsCommand(program);
|
|
33
|
+
return program;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function mockSdk(overrides: Record<string, unknown> = {}) {
|
|
37
|
+
const sdk = {
|
|
38
|
+
entities: {
|
|
39
|
+
getAllRecords: vi.fn().mockResolvedValue({
|
|
40
|
+
items: [],
|
|
41
|
+
totalCount: 0,
|
|
42
|
+
}),
|
|
43
|
+
getRecordById: vi.fn().mockResolvedValue({ Id: "rec-1" }),
|
|
44
|
+
insertRecordById: vi.fn().mockResolvedValue({ Id: "rec-2" }),
|
|
45
|
+
insertRecordsById: vi.fn().mockResolvedValue({
|
|
46
|
+
successRecords: [],
|
|
47
|
+
failureRecords: [],
|
|
48
|
+
}),
|
|
49
|
+
updateRecordById: vi.fn().mockResolvedValue({ Id: "rec-1" }),
|
|
50
|
+
updateRecordsById: vi.fn().mockResolvedValue({
|
|
51
|
+
successRecords: [],
|
|
52
|
+
failureRecords: [],
|
|
53
|
+
}),
|
|
54
|
+
deleteRecordsById: vi.fn().mockResolvedValue({
|
|
55
|
+
successRecords: ["rec-1"],
|
|
56
|
+
failureRecords: [],
|
|
57
|
+
}),
|
|
58
|
+
queryRecordsById: vi.fn().mockResolvedValue({
|
|
59
|
+
items: [],
|
|
60
|
+
totalCount: 0,
|
|
61
|
+
}),
|
|
62
|
+
importRecordsById: vi.fn().mockResolvedValue({
|
|
63
|
+
insertedRecords: 0,
|
|
64
|
+
totalRecords: 0,
|
|
65
|
+
}),
|
|
66
|
+
...overrides,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
vi.mocked(createDataFabricClient).mockResolvedValue(sdk as never);
|
|
70
|
+
return sdk;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe("records list", () => {
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
vi.resetAllMocks();
|
|
76
|
+
process.exitCode = undefined;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should register the records command", () => {
|
|
80
|
+
const program = buildProgram();
|
|
81
|
+
const cmd = program.commands.find((c) => c.name() === "records");
|
|
82
|
+
expect(cmd).toBeDefined();
|
|
83
|
+
expect(cmd?.commands.map((c) => c.name())).toContain("list");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should list records successfully", async () => {
|
|
87
|
+
const sdk = mockSdk();
|
|
88
|
+
vi.mocked(sdk.entities.getAllRecords).mockResolvedValue({
|
|
89
|
+
items: [{ Id: "rec-1", name: "test" }],
|
|
90
|
+
totalCount: 1,
|
|
91
|
+
hasNextPage: false,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const program = buildProgram();
|
|
95
|
+
await program.parseAsync([
|
|
96
|
+
"node",
|
|
97
|
+
"test",
|
|
98
|
+
"records",
|
|
99
|
+
"list",
|
|
100
|
+
"entity-id",
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
expect(sdk.entities.getAllRecords).toHaveBeenCalledWith("entity-id", {
|
|
104
|
+
pageSize: 50,
|
|
105
|
+
});
|
|
106
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
107
|
+
expect.objectContaining({
|
|
108
|
+
Result: "Success",
|
|
109
|
+
Code: "RecordList",
|
|
110
|
+
Data: expect.objectContaining({
|
|
111
|
+
TotalCount: 1,
|
|
112
|
+
HasNextPage: false,
|
|
113
|
+
}),
|
|
114
|
+
}),
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should pass cursor to SDK and include NextCursor in output", async () => {
|
|
119
|
+
const sdk = mockSdk();
|
|
120
|
+
vi.mocked(sdk.entities.getAllRecords).mockResolvedValue({
|
|
121
|
+
items: [{ Id: "rec-2" }],
|
|
122
|
+
totalCount: 100,
|
|
123
|
+
hasNextPage: true,
|
|
124
|
+
nextCursor: "cursor-abc",
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const program = buildProgram();
|
|
128
|
+
await program.parseAsync([
|
|
129
|
+
"node",
|
|
130
|
+
"test",
|
|
131
|
+
"records",
|
|
132
|
+
"list",
|
|
133
|
+
"entity-id",
|
|
134
|
+
"--cursor",
|
|
135
|
+
"cursor-xyz",
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
expect(sdk.entities.getAllRecords).toHaveBeenCalledWith("entity-id", {
|
|
139
|
+
pageSize: 50,
|
|
140
|
+
cursor: { value: "cursor-xyz" },
|
|
141
|
+
});
|
|
142
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
143
|
+
expect.objectContaining({
|
|
144
|
+
Data: expect.objectContaining({
|
|
145
|
+
HasNextPage: true,
|
|
146
|
+
NextCursor: "cursor-abc",
|
|
147
|
+
}),
|
|
148
|
+
}),
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("should return empty list when no records exist", async () => {
|
|
153
|
+
mockSdk();
|
|
154
|
+
|
|
155
|
+
const program = buildProgram();
|
|
156
|
+
await program.parseAsync([
|
|
157
|
+
"node",
|
|
158
|
+
"test",
|
|
159
|
+
"records",
|
|
160
|
+
"list",
|
|
161
|
+
"entity-id",
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
165
|
+
expect.objectContaining({
|
|
166
|
+
Result: "Success",
|
|
167
|
+
Code: "RecordList",
|
|
168
|
+
Data: expect.objectContaining({ TotalCount: 0, Records: [] }),
|
|
169
|
+
}),
|
|
170
|
+
);
|
|
171
|
+
expect(process.exitCode).not.toBe(1);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it("should output error when list fails", async () => {
|
|
175
|
+
const sdk = mockSdk();
|
|
176
|
+
vi.mocked(sdk.entities.getAllRecords).mockRejectedValue(
|
|
177
|
+
new Error("timeout"),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
const program = buildProgram();
|
|
181
|
+
await program.parseAsync([
|
|
182
|
+
"node",
|
|
183
|
+
"test",
|
|
184
|
+
"records",
|
|
185
|
+
"list",
|
|
186
|
+
"entity-id",
|
|
187
|
+
]);
|
|
188
|
+
|
|
189
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
190
|
+
expect.objectContaining({ Result: "Failure" }),
|
|
191
|
+
);
|
|
192
|
+
expect(process.exitCode).toBe(1);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should error when --limit is not a number", async () => {
|
|
196
|
+
mockSdk();
|
|
197
|
+
const program = buildProgram();
|
|
198
|
+
await program.parseAsync([
|
|
199
|
+
"node",
|
|
200
|
+
"test",
|
|
201
|
+
"records",
|
|
202
|
+
"list",
|
|
203
|
+
"entity-id",
|
|
204
|
+
"--limit",
|
|
205
|
+
"abc",
|
|
206
|
+
]);
|
|
207
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
208
|
+
expect.objectContaining({
|
|
209
|
+
Result: "Failure",
|
|
210
|
+
Message: "Invalid --limit value",
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
213
|
+
expect(process.exitCode).toBe(1);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("should error when --limit is zero", async () => {
|
|
217
|
+
mockSdk();
|
|
218
|
+
const program = buildProgram();
|
|
219
|
+
await program.parseAsync([
|
|
220
|
+
"node",
|
|
221
|
+
"test",
|
|
222
|
+
"records",
|
|
223
|
+
"list",
|
|
224
|
+
"entity-id",
|
|
225
|
+
"--limit",
|
|
226
|
+
"0",
|
|
227
|
+
]);
|
|
228
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
229
|
+
expect.objectContaining({
|
|
230
|
+
Result: "Failure",
|
|
231
|
+
Message: "Invalid --limit value",
|
|
232
|
+
}),
|
|
233
|
+
);
|
|
234
|
+
expect(process.exitCode).toBe(1);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe("records list client error", () => {
|
|
239
|
+
beforeEach(() => {
|
|
240
|
+
vi.resetAllMocks();
|
|
241
|
+
process.exitCode = undefined;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should error when SDK connection fails on list", async () => {
|
|
245
|
+
vi.mocked(createDataFabricClient).mockRejectedValue(
|
|
246
|
+
new Error("Not logged in"),
|
|
247
|
+
);
|
|
248
|
+
const program = buildProgram();
|
|
249
|
+
await program.parseAsync([
|
|
250
|
+
"node",
|
|
251
|
+
"test",
|
|
252
|
+
"records",
|
|
253
|
+
"list",
|
|
254
|
+
"entity-id",
|
|
255
|
+
]);
|
|
256
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
257
|
+
expect.objectContaining({
|
|
258
|
+
Result: "Failure",
|
|
259
|
+
Message: "Error connecting to Data Fabric",
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
262
|
+
expect(process.exitCode).toBe(1);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("should handle list result without items/totalCount (uses defaults)", async () => {
|
|
266
|
+
const sdk = mockSdk({
|
|
267
|
+
getAllRecords: vi
|
|
268
|
+
.fn()
|
|
269
|
+
.mockResolvedValue({ items: [], hasNextPage: false }),
|
|
270
|
+
});
|
|
271
|
+
const program = buildProgram();
|
|
272
|
+
await program.parseAsync([
|
|
273
|
+
"node",
|
|
274
|
+
"test",
|
|
275
|
+
"records",
|
|
276
|
+
"list",
|
|
277
|
+
"entity-id",
|
|
278
|
+
]);
|
|
279
|
+
expect(sdk.entities.getAllRecords).toHaveBeenCalled();
|
|
280
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
281
|
+
expect.objectContaining({
|
|
282
|
+
Data: expect.objectContaining({
|
|
283
|
+
TotalCount: 0,
|
|
284
|
+
Records: [],
|
|
285
|
+
HasNextPage: false,
|
|
286
|
+
}),
|
|
287
|
+
}),
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe("records get", () => {
|
|
293
|
+
beforeEach(() => {
|
|
294
|
+
vi.resetAllMocks();
|
|
295
|
+
process.exitCode = undefined;
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("should get record successfully", async () => {
|
|
299
|
+
const sdk = mockSdk();
|
|
300
|
+
vi.mocked(sdk.entities.getRecordById).mockResolvedValue({
|
|
301
|
+
Id: "rec-1",
|
|
302
|
+
amount: 100,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const program = buildProgram();
|
|
306
|
+
await program.parseAsync([
|
|
307
|
+
"node",
|
|
308
|
+
"test",
|
|
309
|
+
"records",
|
|
310
|
+
"get",
|
|
311
|
+
"entity-id",
|
|
312
|
+
"rec-1",
|
|
313
|
+
]);
|
|
314
|
+
|
|
315
|
+
expect(sdk.entities.getRecordById).toHaveBeenCalledWith(
|
|
316
|
+
"entity-id",
|
|
317
|
+
"rec-1",
|
|
318
|
+
);
|
|
319
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
320
|
+
expect.objectContaining({
|
|
321
|
+
Result: "Success",
|
|
322
|
+
Code: "RecordDetails",
|
|
323
|
+
}),
|
|
324
|
+
);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it("should output error when record not found (SDK throws)", async () => {
|
|
328
|
+
const sdk = mockSdk();
|
|
329
|
+
vi.mocked(sdk.entities.getRecordById).mockRejectedValue(
|
|
330
|
+
new Error("Not found"),
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
const program = buildProgram();
|
|
334
|
+
await program.parseAsync([
|
|
335
|
+
"node",
|
|
336
|
+
"test",
|
|
337
|
+
"records",
|
|
338
|
+
"get",
|
|
339
|
+
"entity-id",
|
|
340
|
+
"bad-id",
|
|
341
|
+
]);
|
|
342
|
+
|
|
343
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
344
|
+
expect.objectContaining({ Result: "Failure" }),
|
|
345
|
+
);
|
|
346
|
+
expect(process.exitCode).toBe(1);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("should output error when record returns null (not found)", async () => {
|
|
350
|
+
const sdk = mockSdk();
|
|
351
|
+
vi.mocked(sdk.entities.getRecordById).mockResolvedValue(null as never);
|
|
352
|
+
|
|
353
|
+
const program = buildProgram();
|
|
354
|
+
await program.parseAsync([
|
|
355
|
+
"node",
|
|
356
|
+
"test",
|
|
357
|
+
"records",
|
|
358
|
+
"get",
|
|
359
|
+
"entity-id",
|
|
360
|
+
"missing-id",
|
|
361
|
+
]);
|
|
362
|
+
|
|
363
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
364
|
+
expect.objectContaining({
|
|
365
|
+
Result: "Failure",
|
|
366
|
+
Message: "Record 'missing-id' not found in entity 'entity-id'",
|
|
367
|
+
}),
|
|
368
|
+
);
|
|
369
|
+
expect(process.exitCode).toBe(1);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
describe("records get client error", () => {
|
|
374
|
+
beforeEach(() => {
|
|
375
|
+
vi.resetAllMocks();
|
|
376
|
+
process.exitCode = undefined;
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("should error when SDK connection fails on get", async () => {
|
|
380
|
+
vi.mocked(createDataFabricClient).mockRejectedValue(
|
|
381
|
+
new Error("Not logged in"),
|
|
382
|
+
);
|
|
383
|
+
const program = buildProgram();
|
|
384
|
+
await program.parseAsync([
|
|
385
|
+
"node",
|
|
386
|
+
"test",
|
|
387
|
+
"records",
|
|
388
|
+
"get",
|
|
389
|
+
"entity-id",
|
|
390
|
+
"rec-1",
|
|
391
|
+
]);
|
|
392
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
393
|
+
expect.objectContaining({
|
|
394
|
+
Result: "Failure",
|
|
395
|
+
Message: "Error connecting to Data Fabric",
|
|
396
|
+
}),
|
|
397
|
+
);
|
|
398
|
+
expect(process.exitCode).toBe(1);
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
describe("records insert", () => {
|
|
403
|
+
beforeEach(() => {
|
|
404
|
+
vi.resetAllMocks();
|
|
405
|
+
process.exitCode = undefined;
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it("should insert single record via --body", async () => {
|
|
409
|
+
const sdk = mockSdk();
|
|
410
|
+
vi.mocked(sdk.entities.insertRecordById).mockResolvedValue({
|
|
411
|
+
Id: "new-rec",
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
const program = buildProgram();
|
|
415
|
+
await program.parseAsync([
|
|
416
|
+
"node",
|
|
417
|
+
"test",
|
|
418
|
+
"records",
|
|
419
|
+
"insert",
|
|
420
|
+
"entity-id",
|
|
421
|
+
"--body",
|
|
422
|
+
'{"amount":100}',
|
|
423
|
+
]);
|
|
424
|
+
|
|
425
|
+
expect(sdk.entities.insertRecordById).toHaveBeenCalledWith(
|
|
426
|
+
"entity-id",
|
|
427
|
+
expect.objectContaining({ amount: 100 }),
|
|
428
|
+
);
|
|
429
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
430
|
+
expect.objectContaining({
|
|
431
|
+
Result: "Success",
|
|
432
|
+
Code: "RecordInserted",
|
|
433
|
+
}),
|
|
434
|
+
);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it("should output error when no data provided", async () => {
|
|
438
|
+
mockSdk();
|
|
439
|
+
|
|
440
|
+
const program = buildProgram();
|
|
441
|
+
await program.parseAsync([
|
|
442
|
+
"node",
|
|
443
|
+
"test",
|
|
444
|
+
"records",
|
|
445
|
+
"insert",
|
|
446
|
+
"entity-id",
|
|
447
|
+
]);
|
|
448
|
+
|
|
449
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
450
|
+
expect.objectContaining({ Result: "Failure" }),
|
|
451
|
+
);
|
|
452
|
+
expect(process.exitCode).toBe(1);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it("should insert multiple records via --body array", async () => {
|
|
456
|
+
const sdk = mockSdk();
|
|
457
|
+
vi.mocked(sdk.entities.insertRecordsById).mockResolvedValue({
|
|
458
|
+
successRecords: [{ Id: "rec-1" }, { Id: "rec-2" }],
|
|
459
|
+
failureRecords: [],
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
const program = buildProgram();
|
|
463
|
+
await program.parseAsync([
|
|
464
|
+
"node",
|
|
465
|
+
"test",
|
|
466
|
+
"records",
|
|
467
|
+
"insert",
|
|
468
|
+
"entity-id",
|
|
469
|
+
"--body",
|
|
470
|
+
'[{"amount":100},{"amount":200}]',
|
|
471
|
+
]);
|
|
472
|
+
|
|
473
|
+
expect(sdk.entities.insertRecordsById).toHaveBeenCalledWith(
|
|
474
|
+
"entity-id",
|
|
475
|
+
expect.arrayContaining([
|
|
476
|
+
expect.objectContaining({ amount: 100 }),
|
|
477
|
+
expect.objectContaining({ amount: 200 }),
|
|
478
|
+
]),
|
|
479
|
+
);
|
|
480
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
481
|
+
expect.objectContaining({
|
|
482
|
+
Result: "Success",
|
|
483
|
+
Code: "RecordsBatchInserted",
|
|
484
|
+
Data: expect.objectContaining({ SuccessCount: 2 }),
|
|
485
|
+
}),
|
|
486
|
+
);
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it("should error when single insert API fails", async () => {
|
|
490
|
+
const sdk = mockSdk();
|
|
491
|
+
vi.mocked(sdk.entities.insertRecordById).mockRejectedValue(
|
|
492
|
+
new Error("insert failed"),
|
|
493
|
+
);
|
|
494
|
+
const program = buildProgram();
|
|
495
|
+
await program.parseAsync([
|
|
496
|
+
"node",
|
|
497
|
+
"test",
|
|
498
|
+
"records",
|
|
499
|
+
"insert",
|
|
500
|
+
"entity-id",
|
|
501
|
+
"--body",
|
|
502
|
+
'{"amount":100}',
|
|
503
|
+
]);
|
|
504
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
505
|
+
expect.objectContaining({
|
|
506
|
+
Result: "Failure",
|
|
507
|
+
Message: "Error inserting record",
|
|
508
|
+
}),
|
|
509
|
+
);
|
|
510
|
+
expect(process.exitCode).toBe(1);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it("should error when SDK connection fails on insert", async () => {
|
|
514
|
+
vi.mocked(createDataFabricClient).mockRejectedValue(
|
|
515
|
+
new Error("Not logged in"),
|
|
516
|
+
);
|
|
517
|
+
const program = buildProgram();
|
|
518
|
+
await program.parseAsync([
|
|
519
|
+
"node",
|
|
520
|
+
"test",
|
|
521
|
+
"records",
|
|
522
|
+
"insert",
|
|
523
|
+
"entity-id",
|
|
524
|
+
"--body",
|
|
525
|
+
'{"amount":100}',
|
|
526
|
+
]);
|
|
527
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
528
|
+
expect.objectContaining({
|
|
529
|
+
Result: "Failure",
|
|
530
|
+
Message: "Error connecting to Data Fabric",
|
|
531
|
+
}),
|
|
532
|
+
);
|
|
533
|
+
expect(process.exitCode).toBe(1);
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it("should handle batch insert result without successRecords/failureRecords", async () => {
|
|
537
|
+
const sdk = mockSdk({
|
|
538
|
+
insertRecordsById: vi.fn().mockResolvedValue({}),
|
|
539
|
+
});
|
|
540
|
+
const program = buildProgram();
|
|
541
|
+
await program.parseAsync([
|
|
542
|
+
"node",
|
|
543
|
+
"test",
|
|
544
|
+
"records",
|
|
545
|
+
"insert",
|
|
546
|
+
"entity-id",
|
|
547
|
+
"--body",
|
|
548
|
+
'[{"amount":100},{"amount":200}]',
|
|
549
|
+
]);
|
|
550
|
+
expect(sdk.entities.insertRecordsById).toHaveBeenCalled();
|
|
551
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
552
|
+
expect.objectContaining({
|
|
553
|
+
Data: expect.objectContaining({
|
|
554
|
+
SuccessCount: 0,
|
|
555
|
+
FailureCount: 0,
|
|
556
|
+
}),
|
|
557
|
+
}),
|
|
558
|
+
);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it("should error when batch insert fails", async () => {
|
|
562
|
+
const sdk = mockSdk();
|
|
563
|
+
vi.mocked(sdk.entities.insertRecordsById).mockRejectedValue(
|
|
564
|
+
new Error("batch failed"),
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
const program = buildProgram();
|
|
568
|
+
await program.parseAsync([
|
|
569
|
+
"node",
|
|
570
|
+
"test",
|
|
571
|
+
"records",
|
|
572
|
+
"insert",
|
|
573
|
+
"entity-id",
|
|
574
|
+
"--body",
|
|
575
|
+
'[{"amount":100},{"amount":200}]',
|
|
576
|
+
]);
|
|
577
|
+
|
|
578
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
579
|
+
expect.objectContaining({ Result: "Failure" }),
|
|
580
|
+
);
|
|
581
|
+
expect(process.exitCode).toBe(1);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it("should error on partial batch insert failure (FailureCount > 0)", async () => {
|
|
585
|
+
const sdk = mockSdk();
|
|
586
|
+
vi.mocked(sdk.entities.insertRecordsById).mockResolvedValue({
|
|
587
|
+
successRecords: [{ Id: "rec-1" }],
|
|
588
|
+
failureRecords: [{ error: "duplicate" }],
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
const program = buildProgram();
|
|
592
|
+
await program.parseAsync([
|
|
593
|
+
"node",
|
|
594
|
+
"test",
|
|
595
|
+
"records",
|
|
596
|
+
"insert",
|
|
597
|
+
"entity-id",
|
|
598
|
+
"--body",
|
|
599
|
+
'[{"amount":100},{"amount":200}]',
|
|
600
|
+
]);
|
|
601
|
+
|
|
602
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
603
|
+
expect.objectContaining({
|
|
604
|
+
Result: "Success",
|
|
605
|
+
Code: "RecordsBatchInserted",
|
|
606
|
+
Data: expect.objectContaining({
|
|
607
|
+
FailureCount: 1,
|
|
608
|
+
FailureRecords: [{ error: "duplicate" }],
|
|
609
|
+
}),
|
|
610
|
+
}),
|
|
611
|
+
);
|
|
612
|
+
expect(process.exitCode).toBe(1);
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
describe("records update", () => {
|
|
617
|
+
beforeEach(() => {
|
|
618
|
+
vi.resetAllMocks();
|
|
619
|
+
process.exitCode = undefined;
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it("should update a single record via --body", async () => {
|
|
623
|
+
const sdk = mockSdk();
|
|
624
|
+
vi.mocked(sdk.entities.updateRecordById).mockResolvedValue({
|
|
625
|
+
Id: "rec-1",
|
|
626
|
+
amount: 200,
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
const program = buildProgram();
|
|
630
|
+
await program.parseAsync([
|
|
631
|
+
"node",
|
|
632
|
+
"test",
|
|
633
|
+
"records",
|
|
634
|
+
"update",
|
|
635
|
+
"entity-id",
|
|
636
|
+
"--body",
|
|
637
|
+
'{"Id":"rec-1","amount":200}',
|
|
638
|
+
]);
|
|
639
|
+
|
|
640
|
+
expect(sdk.entities.updateRecordById).toHaveBeenCalledWith(
|
|
641
|
+
"entity-id",
|
|
642
|
+
"rec-1",
|
|
643
|
+
expect.objectContaining({ Id: "rec-1", amount: 200 }),
|
|
644
|
+
);
|
|
645
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
646
|
+
expect.objectContaining({
|
|
647
|
+
Result: "Success",
|
|
648
|
+
Code: "RecordUpdated",
|
|
649
|
+
}),
|
|
650
|
+
);
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
it("should error when single record is missing Id", async () => {
|
|
654
|
+
mockSdk();
|
|
655
|
+
|
|
656
|
+
const program = buildProgram();
|
|
657
|
+
await program.parseAsync([
|
|
658
|
+
"node",
|
|
659
|
+
"test",
|
|
660
|
+
"records",
|
|
661
|
+
"update",
|
|
662
|
+
"entity-id",
|
|
663
|
+
"--body",
|
|
664
|
+
'{"amount":200}',
|
|
665
|
+
]);
|
|
666
|
+
|
|
667
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
668
|
+
expect.objectContaining({
|
|
669
|
+
Result: "Failure",
|
|
670
|
+
Message: "Record must include an 'Id' field",
|
|
671
|
+
}),
|
|
672
|
+
);
|
|
673
|
+
expect(process.exitCode).toBe(1);
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it("should update batch records via --body", async () => {
|
|
677
|
+
const sdk = mockSdk();
|
|
678
|
+
vi.mocked(sdk.entities.updateRecordsById).mockResolvedValue({
|
|
679
|
+
successRecords: [{ Id: "rec-1" }, { Id: "rec-2" }],
|
|
680
|
+
failureRecords: [],
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
const program = buildProgram();
|
|
684
|
+
await program.parseAsync([
|
|
685
|
+
"node",
|
|
686
|
+
"test",
|
|
687
|
+
"records",
|
|
688
|
+
"update",
|
|
689
|
+
"entity-id",
|
|
690
|
+
"--body",
|
|
691
|
+
'[{"Id":"rec-1","amount":200},{"Id":"rec-2","amount":300}]',
|
|
692
|
+
]);
|
|
693
|
+
|
|
694
|
+
expect(sdk.entities.updateRecordsById).toHaveBeenCalledWith(
|
|
695
|
+
"entity-id",
|
|
696
|
+
expect.arrayContaining([
|
|
697
|
+
expect.objectContaining({ Id: "rec-1" }),
|
|
698
|
+
expect.objectContaining({ Id: "rec-2" }),
|
|
699
|
+
]),
|
|
700
|
+
);
|
|
701
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
702
|
+
expect.objectContaining({
|
|
703
|
+
Result: "Success",
|
|
704
|
+
Code: "RecordsBatchUpdated",
|
|
705
|
+
Data: expect.objectContaining({ SuccessCount: 2 }),
|
|
706
|
+
}),
|
|
707
|
+
);
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
it("should error when batch record is missing Id", async () => {
|
|
711
|
+
mockSdk();
|
|
712
|
+
|
|
713
|
+
const program = buildProgram();
|
|
714
|
+
await program.parseAsync([
|
|
715
|
+
"node",
|
|
716
|
+
"test",
|
|
717
|
+
"records",
|
|
718
|
+
"update",
|
|
719
|
+
"entity-id",
|
|
720
|
+
"--body",
|
|
721
|
+
'[{"Id":"rec-1"},{"amount":300}]',
|
|
722
|
+
]);
|
|
723
|
+
|
|
724
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
725
|
+
expect.objectContaining({
|
|
726
|
+
Result: "Failure",
|
|
727
|
+
Message: "All records must include an 'Id' field",
|
|
728
|
+
}),
|
|
729
|
+
);
|
|
730
|
+
expect(process.exitCode).toBe(1);
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
it("should output error when update API fails", async () => {
|
|
734
|
+
const sdk = mockSdk();
|
|
735
|
+
vi.mocked(sdk.entities.updateRecordById).mockRejectedValue(
|
|
736
|
+
new Error("update failed"),
|
|
737
|
+
);
|
|
738
|
+
|
|
739
|
+
const program = buildProgram();
|
|
740
|
+
await program.parseAsync([
|
|
741
|
+
"node",
|
|
742
|
+
"test",
|
|
743
|
+
"records",
|
|
744
|
+
"update",
|
|
745
|
+
"entity-id",
|
|
746
|
+
"--body",
|
|
747
|
+
'{"Id":"rec-1","amount":200}',
|
|
748
|
+
]);
|
|
749
|
+
|
|
750
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
751
|
+
expect.objectContaining({ Result: "Failure" }),
|
|
752
|
+
);
|
|
753
|
+
expect(process.exitCode).toBe(1);
|
|
754
|
+
});
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
describe("records update parse error", () => {
|
|
758
|
+
beforeEach(() => {
|
|
759
|
+
vi.resetAllMocks();
|
|
760
|
+
process.exitCode = undefined;
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
it("should error when --body is invalid JSON on update", async () => {
|
|
764
|
+
mockSdk();
|
|
765
|
+
const program = buildProgram();
|
|
766
|
+
await program.parseAsync([
|
|
767
|
+
"node",
|
|
768
|
+
"test",
|
|
769
|
+
"records",
|
|
770
|
+
"update",
|
|
771
|
+
"entity-id",
|
|
772
|
+
"--body",
|
|
773
|
+
"not-valid-json",
|
|
774
|
+
]);
|
|
775
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
776
|
+
expect.objectContaining({
|
|
777
|
+
Result: "Failure",
|
|
778
|
+
Message: "Error parsing input data",
|
|
779
|
+
}),
|
|
780
|
+
);
|
|
781
|
+
expect(process.exitCode).toBe(1);
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
describe("records update client/batch errors", () => {
|
|
786
|
+
beforeEach(() => {
|
|
787
|
+
vi.resetAllMocks();
|
|
788
|
+
process.exitCode = undefined;
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it("should error when SDK connection fails on update", async () => {
|
|
792
|
+
vi.mocked(createDataFabricClient).mockRejectedValue(
|
|
793
|
+
new Error("Not logged in"),
|
|
794
|
+
);
|
|
795
|
+
const program = buildProgram();
|
|
796
|
+
await program.parseAsync([
|
|
797
|
+
"node",
|
|
798
|
+
"test",
|
|
799
|
+
"records",
|
|
800
|
+
"update",
|
|
801
|
+
"entity-id",
|
|
802
|
+
"--body",
|
|
803
|
+
'{"Id":"rec-1"}',
|
|
804
|
+
]);
|
|
805
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
806
|
+
expect.objectContaining({
|
|
807
|
+
Result: "Failure",
|
|
808
|
+
Message: "Error connecting to Data Fabric",
|
|
809
|
+
}),
|
|
810
|
+
);
|
|
811
|
+
expect(process.exitCode).toBe(1);
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
it("should handle batch update result without successRecords/failureRecords", async () => {
|
|
815
|
+
const sdk = mockSdk({
|
|
816
|
+
updateRecordsById: vi.fn().mockResolvedValue({}),
|
|
817
|
+
});
|
|
818
|
+
const program = buildProgram();
|
|
819
|
+
await program.parseAsync([
|
|
820
|
+
"node",
|
|
821
|
+
"test",
|
|
822
|
+
"records",
|
|
823
|
+
"update",
|
|
824
|
+
"entity-id",
|
|
825
|
+
"--body",
|
|
826
|
+
'[{"Id":"rec-1"},{"Id":"rec-2"}]',
|
|
827
|
+
]);
|
|
828
|
+
expect(sdk.entities.updateRecordsById).toHaveBeenCalled();
|
|
829
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
830
|
+
expect.objectContaining({
|
|
831
|
+
Data: expect.objectContaining({
|
|
832
|
+
SuccessCount: 0,
|
|
833
|
+
FailureCount: 0,
|
|
834
|
+
}),
|
|
835
|
+
}),
|
|
836
|
+
);
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
it("should error when batch update API fails", async () => {
|
|
840
|
+
const sdk = mockSdk();
|
|
841
|
+
vi.mocked(sdk.entities.updateRecordsById).mockRejectedValue(
|
|
842
|
+
new Error("batch update failed"),
|
|
843
|
+
);
|
|
844
|
+
const program = buildProgram();
|
|
845
|
+
await program.parseAsync([
|
|
846
|
+
"node",
|
|
847
|
+
"test",
|
|
848
|
+
"records",
|
|
849
|
+
"update",
|
|
850
|
+
"entity-id",
|
|
851
|
+
"--body",
|
|
852
|
+
'[{"Id":"rec-1"},{"Id":"rec-2"}]',
|
|
853
|
+
]);
|
|
854
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
855
|
+
expect.objectContaining({
|
|
856
|
+
Result: "Failure",
|
|
857
|
+
Message: "Error updating records",
|
|
858
|
+
}),
|
|
859
|
+
);
|
|
860
|
+
expect(process.exitCode).toBe(1);
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
it("should error on partial batch update failure (FailureCount > 0)", async () => {
|
|
864
|
+
const sdk = mockSdk();
|
|
865
|
+
vi.mocked(sdk.entities.updateRecordsById).mockResolvedValue({
|
|
866
|
+
successRecords: [{ Id: "rec-1" }],
|
|
867
|
+
failureRecords: [{ error: "conflict" }],
|
|
868
|
+
});
|
|
869
|
+
const program = buildProgram();
|
|
870
|
+
await program.parseAsync([
|
|
871
|
+
"node",
|
|
872
|
+
"test",
|
|
873
|
+
"records",
|
|
874
|
+
"update",
|
|
875
|
+
"entity-id",
|
|
876
|
+
"--body",
|
|
877
|
+
'[{"Id":"rec-1"},{"Id":"rec-2"}]',
|
|
878
|
+
]);
|
|
879
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
880
|
+
expect.objectContaining({
|
|
881
|
+
Result: "Success",
|
|
882
|
+
Code: "RecordsBatchUpdated",
|
|
883
|
+
Data: expect.objectContaining({
|
|
884
|
+
FailureCount: 1,
|
|
885
|
+
FailureRecords: [{ error: "conflict" }],
|
|
886
|
+
}),
|
|
887
|
+
}),
|
|
888
|
+
);
|
|
889
|
+
expect(process.exitCode).toBe(1);
|
|
890
|
+
});
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
describe("records delete", () => {
|
|
894
|
+
beforeEach(() => {
|
|
895
|
+
vi.resetAllMocks();
|
|
896
|
+
process.exitCode = undefined;
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
it("should delete records successfully", async () => {
|
|
900
|
+
const sdk = mockSdk();
|
|
901
|
+
|
|
902
|
+
const program = buildProgram();
|
|
903
|
+
await program.parseAsync([
|
|
904
|
+
"node",
|
|
905
|
+
"test",
|
|
906
|
+
"records",
|
|
907
|
+
"delete",
|
|
908
|
+
"entity-id",
|
|
909
|
+
"rec-1",
|
|
910
|
+
"rec-2",
|
|
911
|
+
]);
|
|
912
|
+
|
|
913
|
+
expect(sdk.entities.deleteRecordsById).toHaveBeenCalledWith(
|
|
914
|
+
"entity-id",
|
|
915
|
+
["rec-1", "rec-2"],
|
|
916
|
+
);
|
|
917
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
918
|
+
expect.objectContaining({
|
|
919
|
+
Result: "Success",
|
|
920
|
+
Code: "RecordsDeleted",
|
|
921
|
+
}),
|
|
922
|
+
);
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
it("should handle delete result without successRecords/failureRecords", async () => {
|
|
926
|
+
const sdk = mockSdk({
|
|
927
|
+
deleteRecordsById: vi.fn().mockResolvedValue({}),
|
|
928
|
+
});
|
|
929
|
+
const program = buildProgram();
|
|
930
|
+
await program.parseAsync([
|
|
931
|
+
"node",
|
|
932
|
+
"test",
|
|
933
|
+
"records",
|
|
934
|
+
"delete",
|
|
935
|
+
"entity-id",
|
|
936
|
+
"rec-1",
|
|
937
|
+
]);
|
|
938
|
+
expect(sdk.entities.deleteRecordsById).toHaveBeenCalled();
|
|
939
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
940
|
+
expect.objectContaining({
|
|
941
|
+
Data: expect.objectContaining({
|
|
942
|
+
SuccessCount: 0,
|
|
943
|
+
FailureCount: 0,
|
|
944
|
+
}),
|
|
945
|
+
}),
|
|
946
|
+
);
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
it("should output error when delete fails", async () => {
|
|
950
|
+
const sdk = mockSdk();
|
|
951
|
+
vi.mocked(sdk.entities.deleteRecordsById).mockRejectedValue(
|
|
952
|
+
new Error("forbidden"),
|
|
953
|
+
);
|
|
954
|
+
|
|
955
|
+
const program = buildProgram();
|
|
956
|
+
await program.parseAsync([
|
|
957
|
+
"node",
|
|
958
|
+
"test",
|
|
959
|
+
"records",
|
|
960
|
+
"delete",
|
|
961
|
+
"entity-id",
|
|
962
|
+
"rec-1",
|
|
963
|
+
]);
|
|
964
|
+
|
|
965
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
966
|
+
expect.objectContaining({ Result: "Failure" }),
|
|
967
|
+
);
|
|
968
|
+
expect(process.exitCode).toBe(1);
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
it("should error on partial batch delete failure (FailureCount > 0)", async () => {
|
|
972
|
+
const sdk = mockSdk();
|
|
973
|
+
vi.mocked(sdk.entities.deleteRecordsById).mockResolvedValue({
|
|
974
|
+
successRecords: [{ id: "rec-1" }],
|
|
975
|
+
failureRecords: [{ error: "not found" }],
|
|
976
|
+
});
|
|
977
|
+
const program = buildProgram();
|
|
978
|
+
await program.parseAsync([
|
|
979
|
+
"node",
|
|
980
|
+
"test",
|
|
981
|
+
"records",
|
|
982
|
+
"delete",
|
|
983
|
+
"entity-id",
|
|
984
|
+
"rec-1",
|
|
985
|
+
"rec-2",
|
|
986
|
+
]);
|
|
987
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
988
|
+
expect.objectContaining({
|
|
989
|
+
Result: "Success",
|
|
990
|
+
Code: "RecordsDeleted",
|
|
991
|
+
Data: expect.objectContaining({
|
|
992
|
+
FailureCount: 1,
|
|
993
|
+
FailureRecords: [{ error: "not found" }],
|
|
994
|
+
}),
|
|
995
|
+
}),
|
|
996
|
+
);
|
|
997
|
+
expect(process.exitCode).toBe(1);
|
|
998
|
+
});
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
describe("records delete client error", () => {
|
|
1002
|
+
beforeEach(() => {
|
|
1003
|
+
vi.resetAllMocks();
|
|
1004
|
+
process.exitCode = undefined;
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
it("should error when SDK connection fails on delete", async () => {
|
|
1008
|
+
vi.mocked(createDataFabricClient).mockRejectedValue(
|
|
1009
|
+
new Error("Not logged in"),
|
|
1010
|
+
);
|
|
1011
|
+
const program = buildProgram();
|
|
1012
|
+
await program.parseAsync([
|
|
1013
|
+
"node",
|
|
1014
|
+
"test",
|
|
1015
|
+
"records",
|
|
1016
|
+
"delete",
|
|
1017
|
+
"entity-id",
|
|
1018
|
+
"rec-1",
|
|
1019
|
+
]);
|
|
1020
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1021
|
+
expect.objectContaining({
|
|
1022
|
+
Result: "Failure",
|
|
1023
|
+
Message: "Error connecting to Data Fabric",
|
|
1024
|
+
}),
|
|
1025
|
+
);
|
|
1026
|
+
expect(process.exitCode).toBe(1);
|
|
1027
|
+
});
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
describe("records insert via --file", () => {
|
|
1031
|
+
beforeEach(() => {
|
|
1032
|
+
vi.resetAllMocks();
|
|
1033
|
+
process.exitCode = undefined;
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
it("should insert record from JSON file", async () => {
|
|
1037
|
+
const sdk = mockSdk();
|
|
1038
|
+
const { getFileSystem } = await import("@uipath/filesystem");
|
|
1039
|
+
vi.mocked(getFileSystem).mockReturnValue({
|
|
1040
|
+
readFile: vi.fn().mockResolvedValue('{"amount":100}'),
|
|
1041
|
+
path: { basename: vi.fn().mockReturnValue("rec.json") },
|
|
1042
|
+
} as never);
|
|
1043
|
+
|
|
1044
|
+
const program = buildProgram();
|
|
1045
|
+
await program.parseAsync([
|
|
1046
|
+
"node",
|
|
1047
|
+
"test",
|
|
1048
|
+
"records",
|
|
1049
|
+
"insert",
|
|
1050
|
+
"entity-id",
|
|
1051
|
+
"--file",
|
|
1052
|
+
"rec.json",
|
|
1053
|
+
]);
|
|
1054
|
+
|
|
1055
|
+
expect(sdk.entities.insertRecordById).toHaveBeenCalledWith(
|
|
1056
|
+
"entity-id",
|
|
1057
|
+
expect.objectContaining({ amount: 100 }),
|
|
1058
|
+
);
|
|
1059
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1060
|
+
expect.objectContaining({
|
|
1061
|
+
Result: "Success",
|
|
1062
|
+
Code: "RecordInserted",
|
|
1063
|
+
}),
|
|
1064
|
+
);
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
it("should error when file cannot be read", async () => {
|
|
1068
|
+
mockSdk();
|
|
1069
|
+
const { getFileSystem } = await import("@uipath/filesystem");
|
|
1070
|
+
vi.mocked(getFileSystem).mockReturnValue({
|
|
1071
|
+
readFile: vi.fn().mockResolvedValue(null),
|
|
1072
|
+
path: { basename: vi.fn().mockReturnValue("rec.json") },
|
|
1073
|
+
} as never);
|
|
1074
|
+
|
|
1075
|
+
const program = buildProgram();
|
|
1076
|
+
await program.parseAsync([
|
|
1077
|
+
"node",
|
|
1078
|
+
"test",
|
|
1079
|
+
"records",
|
|
1080
|
+
"insert",
|
|
1081
|
+
"entity-id",
|
|
1082
|
+
"--file",
|
|
1083
|
+
"missing.json",
|
|
1084
|
+
]);
|
|
1085
|
+
|
|
1086
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1087
|
+
expect.objectContaining({ Result: "Failure" }),
|
|
1088
|
+
);
|
|
1089
|
+
expect(process.exitCode).toBe(1);
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
it("should error when JSON body is not an object", async () => {
|
|
1093
|
+
mockSdk();
|
|
1094
|
+
|
|
1095
|
+
const program = buildProgram();
|
|
1096
|
+
await program.parseAsync([
|
|
1097
|
+
"node",
|
|
1098
|
+
"test",
|
|
1099
|
+
"records",
|
|
1100
|
+
"insert",
|
|
1101
|
+
"entity-id",
|
|
1102
|
+
"--body",
|
|
1103
|
+
'"just a string"',
|
|
1104
|
+
]);
|
|
1105
|
+
|
|
1106
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1107
|
+
expect.objectContaining({ Result: "Failure" }),
|
|
1108
|
+
);
|
|
1109
|
+
expect(process.exitCode).toBe(1);
|
|
1110
|
+
});
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
describe("records query", () => {
|
|
1114
|
+
beforeEach(() => {
|
|
1115
|
+
vi.resetAllMocks();
|
|
1116
|
+
process.exitCode = undefined;
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
it("should query records without filters (no --body)", async () => {
|
|
1120
|
+
const sdk = mockSdk();
|
|
1121
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1122
|
+
items: [{ Id: "rec-1", title: "test" }],
|
|
1123
|
+
totalCount: 1,
|
|
1124
|
+
hasNextPage: false,
|
|
1125
|
+
});
|
|
1126
|
+
|
|
1127
|
+
const program = buildProgram();
|
|
1128
|
+
await program.parseAsync([
|
|
1129
|
+
"node",
|
|
1130
|
+
"test",
|
|
1131
|
+
"records",
|
|
1132
|
+
"query",
|
|
1133
|
+
"entity-id",
|
|
1134
|
+
]);
|
|
1135
|
+
|
|
1136
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1137
|
+
"entity-id",
|
|
1138
|
+
expect.objectContaining({ pageSize: 50 }),
|
|
1139
|
+
);
|
|
1140
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1141
|
+
expect.objectContaining({
|
|
1142
|
+
Result: "Success",
|
|
1143
|
+
Code: "RecordQuery",
|
|
1144
|
+
Data: expect.objectContaining({
|
|
1145
|
+
TotalCount: 1,
|
|
1146
|
+
HasNextPage: false,
|
|
1147
|
+
}),
|
|
1148
|
+
}),
|
|
1149
|
+
);
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
it("should query records with filterGroup via --body", async () => {
|
|
1153
|
+
const sdk = mockSdk();
|
|
1154
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1155
|
+
items: [{ Id: "rec-1", amount: 200 }],
|
|
1156
|
+
totalCount: 1,
|
|
1157
|
+
hasNextPage: false,
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
const filterBody = JSON.stringify({
|
|
1161
|
+
filterGroup: {
|
|
1162
|
+
logicalOperator: "And",
|
|
1163
|
+
queryFilters: [
|
|
1164
|
+
{
|
|
1165
|
+
fieldName: "amount",
|
|
1166
|
+
operator: "GreaterThan",
|
|
1167
|
+
value: 100,
|
|
1168
|
+
},
|
|
1169
|
+
],
|
|
1170
|
+
},
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
const program = buildProgram();
|
|
1174
|
+
await program.parseAsync([
|
|
1175
|
+
"node",
|
|
1176
|
+
"test",
|
|
1177
|
+
"records",
|
|
1178
|
+
"query",
|
|
1179
|
+
"entity-id",
|
|
1180
|
+
"--body",
|
|
1181
|
+
filterBody,
|
|
1182
|
+
]);
|
|
1183
|
+
|
|
1184
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1185
|
+
"entity-id",
|
|
1186
|
+
expect.objectContaining({
|
|
1187
|
+
filterGroup: expect.objectContaining({
|
|
1188
|
+
logicalOperator: "And",
|
|
1189
|
+
}),
|
|
1190
|
+
pageSize: 50,
|
|
1191
|
+
}),
|
|
1192
|
+
);
|
|
1193
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1194
|
+
expect.objectContaining({
|
|
1195
|
+
Result: "Success",
|
|
1196
|
+
Code: "RecordQuery",
|
|
1197
|
+
}),
|
|
1198
|
+
);
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
it("should query records with sortOptions via --body", async () => {
|
|
1202
|
+
const sdk = mockSdk();
|
|
1203
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1204
|
+
items: [{ Id: "rec-2" }, { Id: "rec-1" }],
|
|
1205
|
+
totalCount: 2,
|
|
1206
|
+
hasNextPage: false,
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
const sortBody = JSON.stringify({
|
|
1210
|
+
sortOptions: [{ fieldName: "amount", isAscending: false }],
|
|
1211
|
+
});
|
|
1212
|
+
|
|
1213
|
+
const program = buildProgram();
|
|
1214
|
+
await program.parseAsync([
|
|
1215
|
+
"node",
|
|
1216
|
+
"test",
|
|
1217
|
+
"records",
|
|
1218
|
+
"query",
|
|
1219
|
+
"entity-id",
|
|
1220
|
+
"--body",
|
|
1221
|
+
sortBody,
|
|
1222
|
+
]);
|
|
1223
|
+
|
|
1224
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1225
|
+
"entity-id",
|
|
1226
|
+
expect.objectContaining({
|
|
1227
|
+
sortOptions: [{ fieldName: "amount", isAscending: false }],
|
|
1228
|
+
pageSize: 50,
|
|
1229
|
+
}),
|
|
1230
|
+
);
|
|
1231
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1232
|
+
expect.objectContaining({
|
|
1233
|
+
Result: "Success",
|
|
1234
|
+
Code: "RecordQuery",
|
|
1235
|
+
Data: expect.objectContaining({ TotalCount: 2 }),
|
|
1236
|
+
}),
|
|
1237
|
+
);
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
it("should query records with filterGroup and sortOptions combined", async () => {
|
|
1241
|
+
const sdk = mockSdk();
|
|
1242
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1243
|
+
items: [{ Id: "rec-1" }],
|
|
1244
|
+
totalCount: 1,
|
|
1245
|
+
hasNextPage: false,
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
const combinedBody = JSON.stringify({
|
|
1249
|
+
filterGroup: {
|
|
1250
|
+
logicalOperator: "And",
|
|
1251
|
+
queryFilters: [
|
|
1252
|
+
{
|
|
1253
|
+
fieldName: "status",
|
|
1254
|
+
operator: "Equals",
|
|
1255
|
+
value: "Active",
|
|
1256
|
+
},
|
|
1257
|
+
],
|
|
1258
|
+
},
|
|
1259
|
+
sortOptions: [{ fieldName: "createdAt", isAscending: true }],
|
|
1260
|
+
selectedFields: ["Id", "status", "createdAt"],
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
const program = buildProgram();
|
|
1264
|
+
await program.parseAsync([
|
|
1265
|
+
"node",
|
|
1266
|
+
"test",
|
|
1267
|
+
"records",
|
|
1268
|
+
"query",
|
|
1269
|
+
"entity-id",
|
|
1270
|
+
"--body",
|
|
1271
|
+
combinedBody,
|
|
1272
|
+
]);
|
|
1273
|
+
|
|
1274
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1275
|
+
"entity-id",
|
|
1276
|
+
expect.objectContaining({
|
|
1277
|
+
filterGroup: expect.any(Object),
|
|
1278
|
+
sortOptions: expect.any(Array),
|
|
1279
|
+
selectedFields: ["Id", "status", "createdAt"],
|
|
1280
|
+
pageSize: 50,
|
|
1281
|
+
}),
|
|
1282
|
+
);
|
|
1283
|
+
});
|
|
1284
|
+
|
|
1285
|
+
it("should pass custom --limit and --cursor to queryRecordsById", async () => {
|
|
1286
|
+
const sdk = mockSdk();
|
|
1287
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1288
|
+
items: [{ Id: "rec-3" }],
|
|
1289
|
+
totalCount: 100,
|
|
1290
|
+
hasNextPage: true,
|
|
1291
|
+
nextCursor: "next-cursor-xyz",
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
const program = buildProgram();
|
|
1295
|
+
await program.parseAsync([
|
|
1296
|
+
"node",
|
|
1297
|
+
"test",
|
|
1298
|
+
"records",
|
|
1299
|
+
"query",
|
|
1300
|
+
"entity-id",
|
|
1301
|
+
"--limit",
|
|
1302
|
+
"10",
|
|
1303
|
+
"--cursor",
|
|
1304
|
+
"prev-cursor",
|
|
1305
|
+
]);
|
|
1306
|
+
|
|
1307
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1308
|
+
"entity-id",
|
|
1309
|
+
expect.objectContaining({
|
|
1310
|
+
pageSize: 10,
|
|
1311
|
+
cursor: { value: "prev-cursor" },
|
|
1312
|
+
}),
|
|
1313
|
+
);
|
|
1314
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1315
|
+
expect.objectContaining({
|
|
1316
|
+
Data: expect.objectContaining({
|
|
1317
|
+
HasNextPage: true,
|
|
1318
|
+
NextCursor: "next-cursor-xyz",
|
|
1319
|
+
}),
|
|
1320
|
+
}),
|
|
1321
|
+
);
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
it("should error when --limit is invalid on query", async () => {
|
|
1325
|
+
mockSdk();
|
|
1326
|
+
const program = buildProgram();
|
|
1327
|
+
await program.parseAsync([
|
|
1328
|
+
"node",
|
|
1329
|
+
"test",
|
|
1330
|
+
"records",
|
|
1331
|
+
"query",
|
|
1332
|
+
"entity-id",
|
|
1333
|
+
"--limit",
|
|
1334
|
+
"bad",
|
|
1335
|
+
]);
|
|
1336
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1337
|
+
expect.objectContaining({
|
|
1338
|
+
Result: "Failure",
|
|
1339
|
+
Message: "Invalid --limit value",
|
|
1340
|
+
}),
|
|
1341
|
+
);
|
|
1342
|
+
expect(process.exitCode).toBe(1);
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
it("should error when --body is invalid JSON on query", async () => {
|
|
1346
|
+
mockSdk();
|
|
1347
|
+
const program = buildProgram();
|
|
1348
|
+
await program.parseAsync([
|
|
1349
|
+
"node",
|
|
1350
|
+
"test",
|
|
1351
|
+
"records",
|
|
1352
|
+
"query",
|
|
1353
|
+
"entity-id",
|
|
1354
|
+
"--body",
|
|
1355
|
+
"not-json",
|
|
1356
|
+
]);
|
|
1357
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1358
|
+
expect.objectContaining({
|
|
1359
|
+
Result: "Failure",
|
|
1360
|
+
Message: "Error parsing query options",
|
|
1361
|
+
}),
|
|
1362
|
+
);
|
|
1363
|
+
expect(process.exitCode).toBe(1);
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
it("should error when query body is an array instead of object", async () => {
|
|
1367
|
+
mockSdk();
|
|
1368
|
+
const program = buildProgram();
|
|
1369
|
+
await program.parseAsync([
|
|
1370
|
+
"node",
|
|
1371
|
+
"test",
|
|
1372
|
+
"records",
|
|
1373
|
+
"query",
|
|
1374
|
+
"entity-id",
|
|
1375
|
+
"--body",
|
|
1376
|
+
'[{"filterGroup":{}}]',
|
|
1377
|
+
]);
|
|
1378
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1379
|
+
expect.objectContaining({
|
|
1380
|
+
Result: "Failure",
|
|
1381
|
+
Message: "Query options must be a JSON object",
|
|
1382
|
+
}),
|
|
1383
|
+
);
|
|
1384
|
+
expect(process.exitCode).toBe(1);
|
|
1385
|
+
});
|
|
1386
|
+
|
|
1387
|
+
it("should error when SDK connection fails on query", async () => {
|
|
1388
|
+
vi.mocked(createDataFabricClient).mockRejectedValue(
|
|
1389
|
+
new Error("Not logged in"),
|
|
1390
|
+
);
|
|
1391
|
+
const program = buildProgram();
|
|
1392
|
+
await program.parseAsync([
|
|
1393
|
+
"node",
|
|
1394
|
+
"test",
|
|
1395
|
+
"records",
|
|
1396
|
+
"query",
|
|
1397
|
+
"entity-id",
|
|
1398
|
+
]);
|
|
1399
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1400
|
+
expect.objectContaining({
|
|
1401
|
+
Result: "Failure",
|
|
1402
|
+
Message: "Error connecting to Data Fabric",
|
|
1403
|
+
}),
|
|
1404
|
+
);
|
|
1405
|
+
expect(process.exitCode).toBe(1);
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
it("should error when query API fails", async () => {
|
|
1409
|
+
const sdk = mockSdk();
|
|
1410
|
+
vi.mocked(sdk.entities.queryRecordsById).mockRejectedValue(
|
|
1411
|
+
new Error("query failed"),
|
|
1412
|
+
);
|
|
1413
|
+
const program = buildProgram();
|
|
1414
|
+
await program.parseAsync([
|
|
1415
|
+
"node",
|
|
1416
|
+
"test",
|
|
1417
|
+
"records",
|
|
1418
|
+
"query",
|
|
1419
|
+
"entity-id",
|
|
1420
|
+
]);
|
|
1421
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1422
|
+
expect.objectContaining({
|
|
1423
|
+
Result: "Failure",
|
|
1424
|
+
Message: "Error querying records",
|
|
1425
|
+
}),
|
|
1426
|
+
);
|
|
1427
|
+
expect(process.exitCode).toBe(1);
|
|
1428
|
+
});
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
describe("records import", () => {
|
|
1432
|
+
beforeEach(() => {
|
|
1433
|
+
vi.resetAllMocks();
|
|
1434
|
+
process.exitCode = undefined;
|
|
1435
|
+
});
|
|
1436
|
+
|
|
1437
|
+
it("should error when no --file is provided", async () => {
|
|
1438
|
+
mockSdk();
|
|
1439
|
+
const program = buildProgram();
|
|
1440
|
+
await program.parseAsync([
|
|
1441
|
+
"node",
|
|
1442
|
+
"test",
|
|
1443
|
+
"records",
|
|
1444
|
+
"import",
|
|
1445
|
+
"entity-id",
|
|
1446
|
+
]);
|
|
1447
|
+
|
|
1448
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1449
|
+
expect.objectContaining({
|
|
1450
|
+
Result: "Failure",
|
|
1451
|
+
Message: "A CSV file path is required",
|
|
1452
|
+
}),
|
|
1453
|
+
);
|
|
1454
|
+
expect(process.exitCode).toBe(1);
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
it("should import records from CSV successfully", async () => {
|
|
1458
|
+
const sdk = mockSdk();
|
|
1459
|
+
vi.mocked(sdk.entities.importRecordsById).mockResolvedValue({
|
|
1460
|
+
insertedRecords: 2,
|
|
1461
|
+
totalRecords: 2,
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
const { getFileSystem } = await import("@uipath/filesystem");
|
|
1465
|
+
const csvBytes = new TextEncoder().encode(
|
|
1466
|
+
"title,amount\ntest,100\nfoo,200",
|
|
1467
|
+
);
|
|
1468
|
+
vi.mocked(getFileSystem).mockReturnValue({
|
|
1469
|
+
readFile: vi.fn().mockResolvedValue(csvBytes),
|
|
1470
|
+
path: { basename: vi.fn().mockReturnValue("data.csv") },
|
|
1471
|
+
} as never);
|
|
1472
|
+
|
|
1473
|
+
const program = buildProgram();
|
|
1474
|
+
await program.parseAsync([
|
|
1475
|
+
"node",
|
|
1476
|
+
"test",
|
|
1477
|
+
"records",
|
|
1478
|
+
"import",
|
|
1479
|
+
"entity-id",
|
|
1480
|
+
"--file",
|
|
1481
|
+
"data.csv",
|
|
1482
|
+
]);
|
|
1483
|
+
|
|
1484
|
+
expect(sdk.entities.importRecordsById).toHaveBeenCalledWith(
|
|
1485
|
+
"entity-id",
|
|
1486
|
+
expect.any(File),
|
|
1487
|
+
);
|
|
1488
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1489
|
+
expect.objectContaining({
|
|
1490
|
+
Result: "Success",
|
|
1491
|
+
Code: "RecordsImported",
|
|
1492
|
+
Data: expect.objectContaining({
|
|
1493
|
+
InsertedRecords: 2,
|
|
1494
|
+
TotalRecords: 2,
|
|
1495
|
+
}),
|
|
1496
|
+
}),
|
|
1497
|
+
);
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
it("should include ErrorFileLink in output when import has partial errors", async () => {
|
|
1501
|
+
const sdk = mockSdk();
|
|
1502
|
+
vi.mocked(sdk.entities.importRecordsById).mockResolvedValue({
|
|
1503
|
+
insertedRecords: 1,
|
|
1504
|
+
totalRecords: 2,
|
|
1505
|
+
errorFileLink: "https://example.com/errors.csv",
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
const { getFileSystem } = await import("@uipath/filesystem");
|
|
1509
|
+
vi.mocked(getFileSystem).mockReturnValue({
|
|
1510
|
+
readFile: vi
|
|
1511
|
+
.fn()
|
|
1512
|
+
.mockResolvedValue(
|
|
1513
|
+
new TextEncoder().encode("title\nvalid\ninvalid"),
|
|
1514
|
+
),
|
|
1515
|
+
path: { basename: vi.fn().mockReturnValue("data.csv") },
|
|
1516
|
+
} as never);
|
|
1517
|
+
|
|
1518
|
+
const program = buildProgram();
|
|
1519
|
+
await program.parseAsync([
|
|
1520
|
+
"node",
|
|
1521
|
+
"test",
|
|
1522
|
+
"records",
|
|
1523
|
+
"import",
|
|
1524
|
+
"entity-id",
|
|
1525
|
+
"--file",
|
|
1526
|
+
"data.csv",
|
|
1527
|
+
]);
|
|
1528
|
+
|
|
1529
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1530
|
+
expect.objectContaining({
|
|
1531
|
+
Result: "Success",
|
|
1532
|
+
Code: "RecordsImported",
|
|
1533
|
+
Data: expect.objectContaining({
|
|
1534
|
+
InsertedRecords: 1,
|
|
1535
|
+
TotalRecords: 2,
|
|
1536
|
+
ErrorFileLink: "https://example.com/errors.csv",
|
|
1537
|
+
}),
|
|
1538
|
+
}),
|
|
1539
|
+
);
|
|
1540
|
+
});
|
|
1541
|
+
|
|
1542
|
+
it("should not include ErrorFileLink when import is fully successful", async () => {
|
|
1543
|
+
const sdk = mockSdk();
|
|
1544
|
+
vi.mocked(sdk.entities.importRecordsById).mockResolvedValue({
|
|
1545
|
+
insertedRecords: 3,
|
|
1546
|
+
totalRecords: 3,
|
|
1547
|
+
});
|
|
1548
|
+
|
|
1549
|
+
const { getFileSystem } = await import("@uipath/filesystem");
|
|
1550
|
+
vi.mocked(getFileSystem).mockReturnValue({
|
|
1551
|
+
readFile: vi
|
|
1552
|
+
.fn()
|
|
1553
|
+
.mockResolvedValue(new TextEncoder().encode("title\na\nb\nc")),
|
|
1554
|
+
path: { basename: vi.fn().mockReturnValue("data.csv") },
|
|
1555
|
+
} as never);
|
|
1556
|
+
|
|
1557
|
+
const program = buildProgram();
|
|
1558
|
+
await program.parseAsync([
|
|
1559
|
+
"node",
|
|
1560
|
+
"test",
|
|
1561
|
+
"records",
|
|
1562
|
+
"import",
|
|
1563
|
+
"entity-id",
|
|
1564
|
+
"--file",
|
|
1565
|
+
"data.csv",
|
|
1566
|
+
]);
|
|
1567
|
+
|
|
1568
|
+
const call = vi.mocked(OutputFormatter.success).mock.calls[0][0] as {
|
|
1569
|
+
Data: Record<string, unknown>;
|
|
1570
|
+
};
|
|
1571
|
+
expect(call.Data).not.toHaveProperty("ErrorFileLink");
|
|
1572
|
+
expect(process.exitCode).not.toBe(1);
|
|
1573
|
+
});
|
|
1574
|
+
|
|
1575
|
+
it("should error when CSV file cannot be read", async () => {
|
|
1576
|
+
mockSdk();
|
|
1577
|
+
|
|
1578
|
+
const { getFileSystem } = await import("@uipath/filesystem");
|
|
1579
|
+
vi.mocked(getFileSystem).mockReturnValue({
|
|
1580
|
+
readFile: vi.fn().mockResolvedValue(null),
|
|
1581
|
+
} as never);
|
|
1582
|
+
|
|
1583
|
+
const program = buildProgram();
|
|
1584
|
+
await program.parseAsync([
|
|
1585
|
+
"node",
|
|
1586
|
+
"test",
|
|
1587
|
+
"records",
|
|
1588
|
+
"import",
|
|
1589
|
+
"entity-id",
|
|
1590
|
+
"--file",
|
|
1591
|
+
"missing.csv",
|
|
1592
|
+
]);
|
|
1593
|
+
|
|
1594
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1595
|
+
expect.objectContaining({
|
|
1596
|
+
Result: "Failure",
|
|
1597
|
+
Message: "Error reading CSV file",
|
|
1598
|
+
}),
|
|
1599
|
+
);
|
|
1600
|
+
expect(process.exitCode).toBe(1);
|
|
1601
|
+
});
|
|
1602
|
+
|
|
1603
|
+
it("should error when import API fails", async () => {
|
|
1604
|
+
const sdk = mockSdk();
|
|
1605
|
+
vi.mocked(sdk.entities.importRecordsById).mockRejectedValue(
|
|
1606
|
+
new Error("Import failed"),
|
|
1607
|
+
);
|
|
1608
|
+
|
|
1609
|
+
const { getFileSystem } = await import("@uipath/filesystem");
|
|
1610
|
+
vi.mocked(getFileSystem).mockReturnValue({
|
|
1611
|
+
readFile: vi
|
|
1612
|
+
.fn()
|
|
1613
|
+
.mockResolvedValue(new TextEncoder().encode("title\ntest")),
|
|
1614
|
+
path: { basename: vi.fn().mockReturnValue("data.csv") },
|
|
1615
|
+
} as never);
|
|
1616
|
+
|
|
1617
|
+
const program = buildProgram();
|
|
1618
|
+
await program.parseAsync([
|
|
1619
|
+
"node",
|
|
1620
|
+
"test",
|
|
1621
|
+
"records",
|
|
1622
|
+
"import",
|
|
1623
|
+
"entity-id",
|
|
1624
|
+
"--file",
|
|
1625
|
+
"data.csv",
|
|
1626
|
+
]);
|
|
1627
|
+
|
|
1628
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1629
|
+
expect.objectContaining({
|
|
1630
|
+
Result: "Failure",
|
|
1631
|
+
Message: "Error importing records",
|
|
1632
|
+
}),
|
|
1633
|
+
);
|
|
1634
|
+
expect(process.exitCode).toBe(1);
|
|
1635
|
+
});
|
|
1636
|
+
|
|
1637
|
+
it("should error when SDK connection fails on import", async () => {
|
|
1638
|
+
vi.mocked(createDataFabricClient).mockRejectedValue(
|
|
1639
|
+
new Error("Not logged in"),
|
|
1640
|
+
);
|
|
1641
|
+
|
|
1642
|
+
const { getFileSystem } = await import("@uipath/filesystem");
|
|
1643
|
+
vi.mocked(getFileSystem).mockReturnValue({
|
|
1644
|
+
readFile: vi
|
|
1645
|
+
.fn()
|
|
1646
|
+
.mockResolvedValue(new TextEncoder().encode("title\ntest")),
|
|
1647
|
+
} as never);
|
|
1648
|
+
|
|
1649
|
+
const program = buildProgram();
|
|
1650
|
+
await program.parseAsync([
|
|
1651
|
+
"node",
|
|
1652
|
+
"test",
|
|
1653
|
+
"records",
|
|
1654
|
+
"import",
|
|
1655
|
+
"entity-id",
|
|
1656
|
+
"--file",
|
|
1657
|
+
"data.csv",
|
|
1658
|
+
]);
|
|
1659
|
+
|
|
1660
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1661
|
+
expect.objectContaining({
|
|
1662
|
+
Result: "Failure",
|
|
1663
|
+
Message: "Error connecting to Data Fabric",
|
|
1664
|
+
}),
|
|
1665
|
+
);
|
|
1666
|
+
expect(process.exitCode).toBe(1);
|
|
1667
|
+
});
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1670
|
+
describe("records query — pagination", () => {
|
|
1671
|
+
beforeEach(() => {
|
|
1672
|
+
vi.resetAllMocks();
|
|
1673
|
+
process.exitCode = undefined;
|
|
1674
|
+
});
|
|
1675
|
+
|
|
1676
|
+
it("should return all records without pagination when no limit/cursor given", async () => {
|
|
1677
|
+
const sdk = mockSdk();
|
|
1678
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1679
|
+
items: [{ Id: "r1" }, { Id: "r2" }, { Id: "r3" }],
|
|
1680
|
+
totalCount: 3,
|
|
1681
|
+
hasNextPage: false,
|
|
1682
|
+
});
|
|
1683
|
+
|
|
1684
|
+
const program = buildProgram();
|
|
1685
|
+
await program.parseAsync([
|
|
1686
|
+
"node",
|
|
1687
|
+
"test",
|
|
1688
|
+
"records",
|
|
1689
|
+
"query",
|
|
1690
|
+
"entity-id",
|
|
1691
|
+
]);
|
|
1692
|
+
|
|
1693
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1694
|
+
"entity-id",
|
|
1695
|
+
expect.objectContaining({ pageSize: 50 }),
|
|
1696
|
+
);
|
|
1697
|
+
const call = vi.mocked(OutputFormatter.success).mock
|
|
1698
|
+
.calls[0][0] as unknown as {
|
|
1699
|
+
Data: {
|
|
1700
|
+
TotalCount: number;
|
|
1701
|
+
Records: unknown[];
|
|
1702
|
+
HasNextPage: boolean;
|
|
1703
|
+
};
|
|
1704
|
+
};
|
|
1705
|
+
expect(call.Data.TotalCount).toBe(3);
|
|
1706
|
+
expect(call.Data.Records).toHaveLength(3);
|
|
1707
|
+
expect(call.Data.HasNextPage).toBe(false);
|
|
1708
|
+
});
|
|
1709
|
+
|
|
1710
|
+
it("should return first page with cursor when paginated", async () => {
|
|
1711
|
+
const sdk = mockSdk();
|
|
1712
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1713
|
+
items: [{ Id: "r1" }, { Id: "r2" }],
|
|
1714
|
+
totalCount: 5,
|
|
1715
|
+
hasNextPage: true,
|
|
1716
|
+
nextCursor: "cursor-page2",
|
|
1717
|
+
});
|
|
1718
|
+
|
|
1719
|
+
const program = buildProgram();
|
|
1720
|
+
await program.parseAsync([
|
|
1721
|
+
"node",
|
|
1722
|
+
"test",
|
|
1723
|
+
"records",
|
|
1724
|
+
"query",
|
|
1725
|
+
"entity-id",
|
|
1726
|
+
"--limit",
|
|
1727
|
+
"2",
|
|
1728
|
+
]);
|
|
1729
|
+
|
|
1730
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1731
|
+
"entity-id",
|
|
1732
|
+
expect.objectContaining({ pageSize: 2 }),
|
|
1733
|
+
);
|
|
1734
|
+
const call = vi.mocked(OutputFormatter.success).mock
|
|
1735
|
+
.calls[0][0] as unknown as {
|
|
1736
|
+
Data: {
|
|
1737
|
+
TotalCount: number;
|
|
1738
|
+
Records: unknown[];
|
|
1739
|
+
HasNextPage: boolean;
|
|
1740
|
+
NextCursor: string;
|
|
1741
|
+
};
|
|
1742
|
+
};
|
|
1743
|
+
expect(call.Data.TotalCount).toBe(5);
|
|
1744
|
+
expect(call.Data.Records).toHaveLength(2);
|
|
1745
|
+
expect(call.Data.HasNextPage).toBe(true);
|
|
1746
|
+
expect(call.Data.NextCursor).toBe("cursor-page2");
|
|
1747
|
+
});
|
|
1748
|
+
|
|
1749
|
+
it("should pass cursor to second page request", async () => {
|
|
1750
|
+
const sdk = mockSdk();
|
|
1751
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1752
|
+
items: [{ Id: "r3" }, { Id: "r4" }],
|
|
1753
|
+
totalCount: 5,
|
|
1754
|
+
hasNextPage: true,
|
|
1755
|
+
nextCursor: "cursor-page3",
|
|
1756
|
+
});
|
|
1757
|
+
|
|
1758
|
+
const program = buildProgram();
|
|
1759
|
+
await program.parseAsync([
|
|
1760
|
+
"node",
|
|
1761
|
+
"test",
|
|
1762
|
+
"records",
|
|
1763
|
+
"query",
|
|
1764
|
+
"entity-id",
|
|
1765
|
+
"--limit",
|
|
1766
|
+
"2",
|
|
1767
|
+
"--cursor",
|
|
1768
|
+
"cursor-page2",
|
|
1769
|
+
]);
|
|
1770
|
+
|
|
1771
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1772
|
+
"entity-id",
|
|
1773
|
+
expect.objectContaining({
|
|
1774
|
+
pageSize: 2,
|
|
1775
|
+
cursor: { value: "cursor-page2" },
|
|
1776
|
+
}),
|
|
1777
|
+
);
|
|
1778
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1779
|
+
expect.objectContaining({
|
|
1780
|
+
Data: expect.objectContaining({
|
|
1781
|
+
HasNextPage: true,
|
|
1782
|
+
NextCursor: "cursor-page3",
|
|
1783
|
+
}),
|
|
1784
|
+
}),
|
|
1785
|
+
);
|
|
1786
|
+
});
|
|
1787
|
+
|
|
1788
|
+
it("should apply filter AND sort together with pagination", async () => {
|
|
1789
|
+
const sdk = mockSdk();
|
|
1790
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1791
|
+
items: [{ Id: "r1", status: "active" }],
|
|
1792
|
+
totalCount: 10,
|
|
1793
|
+
hasNextPage: true,
|
|
1794
|
+
nextCursor: "cur-next",
|
|
1795
|
+
});
|
|
1796
|
+
|
|
1797
|
+
const body = JSON.stringify({
|
|
1798
|
+
filterGroup: {
|
|
1799
|
+
queryFilters: [
|
|
1800
|
+
{ fieldName: "status", operator: "=", value: "active" },
|
|
1801
|
+
],
|
|
1802
|
+
},
|
|
1803
|
+
sortOptions: [{ fieldName: "createTime", isDescending: true }],
|
|
1804
|
+
});
|
|
1805
|
+
|
|
1806
|
+
const program = buildProgram();
|
|
1807
|
+
await program.parseAsync([
|
|
1808
|
+
"node",
|
|
1809
|
+
"test",
|
|
1810
|
+
"records",
|
|
1811
|
+
"query",
|
|
1812
|
+
"entity-id",
|
|
1813
|
+
"--body",
|
|
1814
|
+
body,
|
|
1815
|
+
"--limit",
|
|
1816
|
+
"1",
|
|
1817
|
+
]);
|
|
1818
|
+
|
|
1819
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1820
|
+
"entity-id",
|
|
1821
|
+
expect.objectContaining({
|
|
1822
|
+
filterGroup: expect.objectContaining({
|
|
1823
|
+
queryFilters: [
|
|
1824
|
+
{ fieldName: "status", operator: "=", value: "active" },
|
|
1825
|
+
],
|
|
1826
|
+
}),
|
|
1827
|
+
sortOptions: [{ fieldName: "createTime", isDescending: true }],
|
|
1828
|
+
pageSize: 1,
|
|
1829
|
+
}),
|
|
1830
|
+
);
|
|
1831
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1832
|
+
expect.objectContaining({
|
|
1833
|
+
Data: expect.objectContaining({
|
|
1834
|
+
TotalCount: 10,
|
|
1835
|
+
HasNextPage: true,
|
|
1836
|
+
NextCursor: "cur-next",
|
|
1837
|
+
}),
|
|
1838
|
+
}),
|
|
1839
|
+
);
|
|
1840
|
+
});
|
|
1841
|
+
|
|
1842
|
+
it("should omit NextCursor when hasNextPage is false", async () => {
|
|
1843
|
+
const sdk = mockSdk();
|
|
1844
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1845
|
+
items: [{ Id: "r1" }],
|
|
1846
|
+
totalCount: 1,
|
|
1847
|
+
hasNextPage: false,
|
|
1848
|
+
});
|
|
1849
|
+
|
|
1850
|
+
const program = buildProgram();
|
|
1851
|
+
await program.parseAsync([
|
|
1852
|
+
"node",
|
|
1853
|
+
"test",
|
|
1854
|
+
"records",
|
|
1855
|
+
"query",
|
|
1856
|
+
"entity-id",
|
|
1857
|
+
]);
|
|
1858
|
+
|
|
1859
|
+
const call = vi.mocked(OutputFormatter.success).mock.calls[0][0] as {
|
|
1860
|
+
Data: Record<string, unknown>;
|
|
1861
|
+
};
|
|
1862
|
+
expect(call.Data).not.toHaveProperty("NextCursor");
|
|
1863
|
+
});
|
|
1864
|
+
|
|
1865
|
+
it("should normalise cursor object {value} returned by SDK to a plain string", async () => {
|
|
1866
|
+
const sdk = mockSdk();
|
|
1867
|
+
// SDK may return PaginationCursor = { value: string } instead of a raw string
|
|
1868
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1869
|
+
items: [{ Id: "r1" }, { Id: "r2" }],
|
|
1870
|
+
totalCount: 4,
|
|
1871
|
+
hasNextPage: true,
|
|
1872
|
+
nextCursor: { value: "cursor-as-object" } as unknown as string,
|
|
1873
|
+
});
|
|
1874
|
+
|
|
1875
|
+
const program = buildProgram();
|
|
1876
|
+
await program.parseAsync([
|
|
1877
|
+
"node",
|
|
1878
|
+
"test",
|
|
1879
|
+
"records",
|
|
1880
|
+
"query",
|
|
1881
|
+
"entity-id",
|
|
1882
|
+
"--limit",
|
|
1883
|
+
"2",
|
|
1884
|
+
]);
|
|
1885
|
+
|
|
1886
|
+
const call = vi.mocked(OutputFormatter.success).mock
|
|
1887
|
+
.calls[0][0] as unknown as {
|
|
1888
|
+
Data: { NextCursor: unknown };
|
|
1889
|
+
};
|
|
1890
|
+
// Must be a plain string, not the object
|
|
1891
|
+
expect(typeof call.Data.NextCursor).toBe("string");
|
|
1892
|
+
expect(call.Data.NextCursor).toBe("cursor-as-object");
|
|
1893
|
+
});
|
|
1894
|
+
|
|
1895
|
+
it("should use default limit 50 when --cursor is given without --limit", async () => {
|
|
1896
|
+
const sdk = mockSdk();
|
|
1897
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1898
|
+
items: [{ Id: "r51" }],
|
|
1899
|
+
totalCount: 51,
|
|
1900
|
+
hasNextPage: false,
|
|
1901
|
+
});
|
|
1902
|
+
|
|
1903
|
+
const program = buildProgram();
|
|
1904
|
+
await program.parseAsync([
|
|
1905
|
+
"node",
|
|
1906
|
+
"test",
|
|
1907
|
+
"records",
|
|
1908
|
+
"query",
|
|
1909
|
+
"entity-id",
|
|
1910
|
+
"--cursor",
|
|
1911
|
+
"existing-cursor",
|
|
1912
|
+
]);
|
|
1913
|
+
|
|
1914
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1915
|
+
"entity-id",
|
|
1916
|
+
expect.objectContaining({
|
|
1917
|
+
pageSize: 50,
|
|
1918
|
+
cursor: { value: "existing-cursor" },
|
|
1919
|
+
}),
|
|
1920
|
+
);
|
|
1921
|
+
});
|
|
1922
|
+
|
|
1923
|
+
it("should handle last page correctly — no NextCursor even if SDK emits undefined", async () => {
|
|
1924
|
+
const sdk = mockSdk();
|
|
1925
|
+
vi.mocked(sdk.entities.queryRecordsById).mockResolvedValue({
|
|
1926
|
+
items: [{ Id: "r5" }],
|
|
1927
|
+
totalCount: 5,
|
|
1928
|
+
hasNextPage: false,
|
|
1929
|
+
nextCursor: undefined,
|
|
1930
|
+
});
|
|
1931
|
+
|
|
1932
|
+
const program = buildProgram();
|
|
1933
|
+
await program.parseAsync([
|
|
1934
|
+
"node",
|
|
1935
|
+
"test",
|
|
1936
|
+
"records",
|
|
1937
|
+
"query",
|
|
1938
|
+
"entity-id",
|
|
1939
|
+
"--limit",
|
|
1940
|
+
"4",
|
|
1941
|
+
"--cursor",
|
|
1942
|
+
"cursor-page2",
|
|
1943
|
+
]);
|
|
1944
|
+
|
|
1945
|
+
expect(sdk.entities.queryRecordsById).toHaveBeenCalledWith(
|
|
1946
|
+
"entity-id",
|
|
1947
|
+
expect.objectContaining({
|
|
1948
|
+
pageSize: 4,
|
|
1949
|
+
cursor: { value: "cursor-page2" },
|
|
1950
|
+
}),
|
|
1951
|
+
);
|
|
1952
|
+
const call = vi.mocked(OutputFormatter.success).mock.calls[0][0] as {
|
|
1953
|
+
Data: Record<string, unknown>;
|
|
1954
|
+
};
|
|
1955
|
+
expect(call.Data.HasNextPage).toBe(false);
|
|
1956
|
+
expect(call.Data).not.toHaveProperty("NextCursor");
|
|
1957
|
+
});
|
|
1958
|
+
});
|
|
1959
|
+
|
|
1960
|
+
describe("records — negative scenarios", () => {
|
|
1961
|
+
beforeEach(() => {
|
|
1962
|
+
vi.resetAllMocks();
|
|
1963
|
+
process.exitCode = undefined;
|
|
1964
|
+
});
|
|
1965
|
+
|
|
1966
|
+
it("entities delete is not a registered command", async () => {
|
|
1967
|
+
const { registerEntitiesCommand } = await import("./entities");
|
|
1968
|
+
const { Command } = await import("commander");
|
|
1969
|
+
const prog = new Command().exitOverride();
|
|
1970
|
+
registerEntitiesCommand(prog);
|
|
1971
|
+
const cmd = prog.commands.find((c) => c.name() === "entities");
|
|
1972
|
+
const subNames = cmd?.commands.map((c) => c.name());
|
|
1973
|
+
expect(subNames).not.toContain("delete");
|
|
1974
|
+
});
|
|
1975
|
+
|
|
1976
|
+
it("records query — entity not found returns error", async () => {
|
|
1977
|
+
const sdk = mockSdk();
|
|
1978
|
+
vi.mocked(sdk.entities.queryRecordsById).mockRejectedValue(
|
|
1979
|
+
new Error("Entity not found"),
|
|
1980
|
+
);
|
|
1981
|
+
|
|
1982
|
+
const program = buildProgram();
|
|
1983
|
+
await program.parseAsync([
|
|
1984
|
+
"node",
|
|
1985
|
+
"test",
|
|
1986
|
+
"records",
|
|
1987
|
+
"query",
|
|
1988
|
+
"nonexistent-entity-id",
|
|
1989
|
+
]);
|
|
1990
|
+
|
|
1991
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1992
|
+
expect.objectContaining({
|
|
1993
|
+
Result: "Failure",
|
|
1994
|
+
Message: "Error querying records",
|
|
1995
|
+
}),
|
|
1996
|
+
);
|
|
1997
|
+
expect(process.exitCode).toBe(1);
|
|
1998
|
+
});
|
|
1999
|
+
|
|
2000
|
+
it("records insert — entity not found returns error", async () => {
|
|
2001
|
+
const sdk = mockSdk();
|
|
2002
|
+
vi.mocked(sdk.entities.insertRecordById).mockRejectedValue(
|
|
2003
|
+
new Error("Entity not found"),
|
|
2004
|
+
);
|
|
2005
|
+
|
|
2006
|
+
const program = buildProgram();
|
|
2007
|
+
await program.parseAsync([
|
|
2008
|
+
"node",
|
|
2009
|
+
"test",
|
|
2010
|
+
"records",
|
|
2011
|
+
"insert",
|
|
2012
|
+
"nonexistent-entity-id",
|
|
2013
|
+
"--body",
|
|
2014
|
+
'{"field":"value"}',
|
|
2015
|
+
]);
|
|
2016
|
+
|
|
2017
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2018
|
+
expect.objectContaining({
|
|
2019
|
+
Result: "Failure",
|
|
2020
|
+
Message: "Error inserting record",
|
|
2021
|
+
}),
|
|
2022
|
+
);
|
|
2023
|
+
expect(process.exitCode).toBe(1);
|
|
2024
|
+
});
|
|
2025
|
+
|
|
2026
|
+
it("records delete — record not found returns error", async () => {
|
|
2027
|
+
const sdk = mockSdk();
|
|
2028
|
+
vi.mocked(sdk.entities.deleteRecordsById).mockRejectedValue(
|
|
2029
|
+
new Error("Record not found"),
|
|
2030
|
+
);
|
|
2031
|
+
|
|
2032
|
+
const program = buildProgram();
|
|
2033
|
+
await program.parseAsync([
|
|
2034
|
+
"node",
|
|
2035
|
+
"test",
|
|
2036
|
+
"records",
|
|
2037
|
+
"delete",
|
|
2038
|
+
"entity-id",
|
|
2039
|
+
"nonexistent-record-id",
|
|
2040
|
+
]);
|
|
2041
|
+
|
|
2042
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2043
|
+
expect.objectContaining({
|
|
2044
|
+
Result: "Failure",
|
|
2045
|
+
Message: "Error deleting records",
|
|
2046
|
+
}),
|
|
2047
|
+
);
|
|
2048
|
+
expect(process.exitCode).toBe(1);
|
|
2049
|
+
});
|
|
2050
|
+
|
|
2051
|
+
it("records get — record not found returns error", async () => {
|
|
2052
|
+
const sdk = mockSdk();
|
|
2053
|
+
vi.mocked(sdk.entities.getRecordById).mockRejectedValue(
|
|
2054
|
+
new Error("404 Not Found"),
|
|
2055
|
+
);
|
|
2056
|
+
|
|
2057
|
+
const program = buildProgram();
|
|
2058
|
+
await program.parseAsync([
|
|
2059
|
+
"node",
|
|
2060
|
+
"test",
|
|
2061
|
+
"records",
|
|
2062
|
+
"get",
|
|
2063
|
+
"entity-id",
|
|
2064
|
+
"nonexistent-record",
|
|
2065
|
+
]);
|
|
2066
|
+
|
|
2067
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2068
|
+
expect.objectContaining({ Result: "Failure" }),
|
|
2069
|
+
);
|
|
2070
|
+
expect(process.exitCode).toBe(1);
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
it("records import — entity not found returns error", async () => {
|
|
2074
|
+
const sdk = mockSdk();
|
|
2075
|
+
vi.mocked(sdk.entities.importRecordsById).mockRejectedValue(
|
|
2076
|
+
new Error("Entity not found"),
|
|
2077
|
+
);
|
|
2078
|
+
|
|
2079
|
+
const { getFileSystem } = await import("@uipath/filesystem");
|
|
2080
|
+
vi.mocked(getFileSystem).mockReturnValue({
|
|
2081
|
+
readFile: vi
|
|
2082
|
+
.fn()
|
|
2083
|
+
.mockResolvedValue(new TextEncoder().encode("title\ntest")),
|
|
2084
|
+
path: { basename: vi.fn().mockReturnValue("data.csv") },
|
|
2085
|
+
} as never);
|
|
2086
|
+
|
|
2087
|
+
const program = buildProgram();
|
|
2088
|
+
await program.parseAsync([
|
|
2089
|
+
"node",
|
|
2090
|
+
"test",
|
|
2091
|
+
"records",
|
|
2092
|
+
"import",
|
|
2093
|
+
"nonexistent-entity-id",
|
|
2094
|
+
"--file",
|
|
2095
|
+
"data.csv",
|
|
2096
|
+
]);
|
|
2097
|
+
|
|
2098
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2099
|
+
expect.objectContaining({
|
|
2100
|
+
Result: "Failure",
|
|
2101
|
+
Message: "Error importing records",
|
|
2102
|
+
}),
|
|
2103
|
+
);
|
|
2104
|
+
expect(process.exitCode).toBe(1);
|
|
2105
|
+
});
|
|
2106
|
+
});
|