@uipath/data-fabric-tool 1.198.0 → 1.199.0-preview.104
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/tool.js +774 -28
- package/package.json +2 -2
- package/src/commands/choice-sets.spec.ts +107 -4
- package/src/commands/choice-sets.ts +15 -3
- package/src/commands/directory.spec.ts +544 -0
- package/src/commands/directory.ts +325 -0
- package/src/commands/entities.spec.ts +239 -2
- package/src/commands/entities.ts +47 -3
- package/src/commands/records.spec.ts +119 -3
- package/src/commands/records.ts +83 -2
- package/src/commands/roles.spec.ts +153 -0
- package/src/commands/roles.ts +88 -0
- package/src/tool.ts +4 -0
- package/src/utils/validate.spec.ts +555 -0
- package/src/utils/validate.ts +611 -0
- package/tests/access-management.e2e.test.ts +347 -0
- package/tests/choice-sets-lifecycle.e2e.test.ts +741 -0
- package/tests/entities-lifecycle.e2e.test.ts +614 -0
- package/tests/files-management.e2e.test.ts +518 -0
- package/tests/records-management.e2e.test.ts +646 -0
|
@@ -0,0 +1,544 @@
|
|
|
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 { registerDirectoryCommand } from "./directory";
|
|
23
|
+
|
|
24
|
+
function buildProgram(): Command {
|
|
25
|
+
const program = new Command();
|
|
26
|
+
program.name("test").exitOverride();
|
|
27
|
+
registerDirectoryCommand(program);
|
|
28
|
+
return program;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function mockSdk(overrides: Record<string, unknown> = {}) {
|
|
32
|
+
const sdk = {
|
|
33
|
+
entities: {
|
|
34
|
+
directory: {
|
|
35
|
+
list: vi.fn().mockResolvedValue({
|
|
36
|
+
totalCount: 0,
|
|
37
|
+
results: [],
|
|
38
|
+
}),
|
|
39
|
+
assignRoles: vi.fn().mockResolvedValue([]),
|
|
40
|
+
revokeRoles: vi.fn().mockResolvedValue(undefined),
|
|
41
|
+
...overrides,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
vi.mocked(connectOrFail).mockResolvedValue(sdk as never);
|
|
46
|
+
return sdk;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describe("directory list", () => {
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
vi.resetAllMocks();
|
|
52
|
+
process.exitCode = undefined;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should register the directory command with list, assign-roles, and revoke-roles subcommands", () => {
|
|
56
|
+
const program = buildProgram();
|
|
57
|
+
const cmd = program.commands.find((c) => c.name() === "directory");
|
|
58
|
+
expect(cmd).toBeDefined();
|
|
59
|
+
const subNames = cmd?.commands.map((c) => c.name()) ?? [];
|
|
60
|
+
expect(subNames).toContain("list");
|
|
61
|
+
expect(subNames).toContain("assign-roles");
|
|
62
|
+
expect(subNames).toContain("revoke-roles");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should list one page of principals with default paging", async () => {
|
|
66
|
+
const sdk = mockSdk();
|
|
67
|
+
const response = {
|
|
68
|
+
totalCount: 1,
|
|
69
|
+
results: [
|
|
70
|
+
{
|
|
71
|
+
externalId: "p-1",
|
|
72
|
+
name: "automation-users",
|
|
73
|
+
type: "Group",
|
|
74
|
+
roles: [{ id: "r-1", name: "DataWriter" }],
|
|
75
|
+
isUIEnabled: true,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
vi.mocked(sdk.entities.directory.list).mockResolvedValue(
|
|
80
|
+
response as never,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const program = buildProgram();
|
|
84
|
+
await program.parseAsync(["node", "test", "directory", "list"]);
|
|
85
|
+
|
|
86
|
+
expect(sdk.entities.directory.list).toHaveBeenCalledWith({
|
|
87
|
+
skip: 0,
|
|
88
|
+
top: 50,
|
|
89
|
+
});
|
|
90
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
91
|
+
expect.objectContaining({
|
|
92
|
+
Result: "Success",
|
|
93
|
+
Code: "DirectoryList",
|
|
94
|
+
Data: response,
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should forward --limit and --offset", async () => {
|
|
100
|
+
const sdk = mockSdk();
|
|
101
|
+
|
|
102
|
+
const program = buildProgram();
|
|
103
|
+
await program.parseAsync([
|
|
104
|
+
"node",
|
|
105
|
+
"test",
|
|
106
|
+
"directory",
|
|
107
|
+
"list",
|
|
108
|
+
"--limit",
|
|
109
|
+
"10",
|
|
110
|
+
"--offset",
|
|
111
|
+
"20",
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
expect(sdk.entities.directory.list).toHaveBeenCalledWith({
|
|
115
|
+
skip: 20,
|
|
116
|
+
top: 10,
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("should reject a non-positive --limit", async () => {
|
|
121
|
+
const sdk = mockSdk();
|
|
122
|
+
|
|
123
|
+
const program = buildProgram();
|
|
124
|
+
await program.parseAsync([
|
|
125
|
+
"node",
|
|
126
|
+
"test",
|
|
127
|
+
"directory",
|
|
128
|
+
"list",
|
|
129
|
+
"--limit",
|
|
130
|
+
"0",
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
expect(sdk.entities.directory.list).not.toHaveBeenCalled();
|
|
134
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
135
|
+
expect.objectContaining({
|
|
136
|
+
Result: "Failure",
|
|
137
|
+
Message: "Invalid --limit value '0'",
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
expect(process.exitCode).toBe(1);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("should reject a negative --offset", async () => {
|
|
144
|
+
const sdk = mockSdk();
|
|
145
|
+
|
|
146
|
+
const program = buildProgram();
|
|
147
|
+
await program.parseAsync([
|
|
148
|
+
"node",
|
|
149
|
+
"test",
|
|
150
|
+
"directory",
|
|
151
|
+
"list",
|
|
152
|
+
"--offset",
|
|
153
|
+
"-5",
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
expect(sdk.entities.directory.list).not.toHaveBeenCalled();
|
|
157
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
158
|
+
expect.objectContaining({
|
|
159
|
+
Result: "Failure",
|
|
160
|
+
Message: "Invalid --offset value '-5'",
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
expect(process.exitCode).toBe(1);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("should reject a --limit with trailing non-numeric characters", async () => {
|
|
167
|
+
const sdk = mockSdk();
|
|
168
|
+
|
|
169
|
+
const program = buildProgram();
|
|
170
|
+
await program.parseAsync([
|
|
171
|
+
"node",
|
|
172
|
+
"test",
|
|
173
|
+
"directory",
|
|
174
|
+
"list",
|
|
175
|
+
"--limit",
|
|
176
|
+
"10abc",
|
|
177
|
+
]);
|
|
178
|
+
|
|
179
|
+
expect(sdk.entities.directory.list).not.toHaveBeenCalled();
|
|
180
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
181
|
+
expect.objectContaining({
|
|
182
|
+
Result: "Failure",
|
|
183
|
+
Message: "Invalid --limit value '10abc'",
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
expect(process.exitCode).toBe(1);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should reject a fractional --offset", async () => {
|
|
190
|
+
const sdk = mockSdk();
|
|
191
|
+
|
|
192
|
+
const program = buildProgram();
|
|
193
|
+
await program.parseAsync([
|
|
194
|
+
"node",
|
|
195
|
+
"test",
|
|
196
|
+
"directory",
|
|
197
|
+
"list",
|
|
198
|
+
"--offset",
|
|
199
|
+
"1.5",
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
expect(sdk.entities.directory.list).not.toHaveBeenCalled();
|
|
203
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
204
|
+
expect.objectContaining({
|
|
205
|
+
Result: "Failure",
|
|
206
|
+
Message: "Invalid --offset value '1.5'",
|
|
207
|
+
}),
|
|
208
|
+
);
|
|
209
|
+
expect(process.exitCode).toBe(1);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("should fail with exit 1 when the SDK call errors", async () => {
|
|
213
|
+
const sdk = mockSdk();
|
|
214
|
+
vi.mocked(sdk.entities.directory.list).mockRejectedValue(
|
|
215
|
+
new Error("boom"),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const program = buildProgram();
|
|
219
|
+
await program.parseAsync(["node", "test", "directory", "list"]);
|
|
220
|
+
|
|
221
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
222
|
+
expect.objectContaining({
|
|
223
|
+
Result: "Failure",
|
|
224
|
+
Message: "Error listing directory principals",
|
|
225
|
+
}),
|
|
226
|
+
);
|
|
227
|
+
expect(process.exitCode).toBe(1);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("should bail when SDK connection fails", async () => {
|
|
231
|
+
const sdk = mockSdk();
|
|
232
|
+
vi.mocked(connectOrFail).mockResolvedValue(undefined);
|
|
233
|
+
|
|
234
|
+
const program = buildProgram();
|
|
235
|
+
await program.parseAsync(["node", "test", "directory", "list"]);
|
|
236
|
+
|
|
237
|
+
expect(sdk.entities.directory.list).not.toHaveBeenCalled();
|
|
238
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe("directory assign-roles", () => {
|
|
243
|
+
beforeEach(() => {
|
|
244
|
+
vi.resetAllMocks();
|
|
245
|
+
process.exitCode = undefined;
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("should assign roles to multiple principals with preserved defaults", async () => {
|
|
249
|
+
const sdk = mockSdk();
|
|
250
|
+
const results = [
|
|
251
|
+
{ principalId: "p-1", roleIds: ["r-1"] },
|
|
252
|
+
{ principalId: "p-2", roleIds: ["r-1"] },
|
|
253
|
+
];
|
|
254
|
+
vi.mocked(sdk.entities.directory.assignRoles).mockResolvedValue(
|
|
255
|
+
results as never,
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const program = buildProgram();
|
|
259
|
+
await program.parseAsync([
|
|
260
|
+
"node",
|
|
261
|
+
"test",
|
|
262
|
+
"directory",
|
|
263
|
+
"assign-roles",
|
|
264
|
+
"p-1",
|
|
265
|
+
"p-2",
|
|
266
|
+
"--type",
|
|
267
|
+
"Group",
|
|
268
|
+
"--role-ids",
|
|
269
|
+
"r-1",
|
|
270
|
+
]);
|
|
271
|
+
|
|
272
|
+
expect(sdk.entities.directory.assignRoles).toHaveBeenCalledWith(
|
|
273
|
+
["p-1", "p-2"],
|
|
274
|
+
"Group",
|
|
275
|
+
["r-1"],
|
|
276
|
+
{},
|
|
277
|
+
);
|
|
278
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
279
|
+
expect.objectContaining({
|
|
280
|
+
Result: "Success",
|
|
281
|
+
Code: "DirectoryRolesAssigned",
|
|
282
|
+
Data: results,
|
|
283
|
+
}),
|
|
284
|
+
);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("should normalize a case-insensitive --type and split comma-separated --role-ids", async () => {
|
|
288
|
+
const sdk = mockSdk();
|
|
289
|
+
|
|
290
|
+
const program = buildProgram();
|
|
291
|
+
await program.parseAsync([
|
|
292
|
+
"node",
|
|
293
|
+
"test",
|
|
294
|
+
"directory",
|
|
295
|
+
"assign-roles",
|
|
296
|
+
"p-1",
|
|
297
|
+
"--type",
|
|
298
|
+
"user",
|
|
299
|
+
"--role-ids",
|
|
300
|
+
" r-1 , r-2 ",
|
|
301
|
+
]);
|
|
302
|
+
|
|
303
|
+
expect(sdk.entities.directory.assignRoles).toHaveBeenCalledWith(
|
|
304
|
+
["p-1"],
|
|
305
|
+
"User",
|
|
306
|
+
["r-1", "r-2"],
|
|
307
|
+
{},
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it("should pass preserveExisting false with --replace and uiEnabled false with --no-ui-access", async () => {
|
|
312
|
+
const sdk = mockSdk();
|
|
313
|
+
|
|
314
|
+
const program = buildProgram();
|
|
315
|
+
await program.parseAsync([
|
|
316
|
+
"node",
|
|
317
|
+
"test",
|
|
318
|
+
"directory",
|
|
319
|
+
"assign-roles",
|
|
320
|
+
"p-1",
|
|
321
|
+
"--type",
|
|
322
|
+
"Application",
|
|
323
|
+
"--role-ids",
|
|
324
|
+
"r-1",
|
|
325
|
+
"--replace",
|
|
326
|
+
"--no-ui-access",
|
|
327
|
+
]);
|
|
328
|
+
|
|
329
|
+
expect(sdk.entities.directory.assignRoles).toHaveBeenCalledWith(
|
|
330
|
+
["p-1"],
|
|
331
|
+
"Application",
|
|
332
|
+
["r-1"],
|
|
333
|
+
{ preserveExisting: false, uiEnabled: false },
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it("should reject an unknown --type without calling the SDK", async () => {
|
|
338
|
+
const sdk = mockSdk();
|
|
339
|
+
|
|
340
|
+
const program = buildProgram();
|
|
341
|
+
await program.parseAsync([
|
|
342
|
+
"node",
|
|
343
|
+
"test",
|
|
344
|
+
"directory",
|
|
345
|
+
"assign-roles",
|
|
346
|
+
"p-1",
|
|
347
|
+
"--type",
|
|
348
|
+
"Robot",
|
|
349
|
+
"--role-ids",
|
|
350
|
+
"r-1",
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
expect(sdk.entities.directory.assignRoles).not.toHaveBeenCalled();
|
|
354
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
355
|
+
expect.objectContaining({
|
|
356
|
+
Result: "Failure",
|
|
357
|
+
Message: "Invalid --type value 'Robot'",
|
|
358
|
+
}),
|
|
359
|
+
);
|
|
360
|
+
expect(process.exitCode).toBe(1);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("should reject --role-ids with no usable IDs", async () => {
|
|
364
|
+
const sdk = mockSdk();
|
|
365
|
+
|
|
366
|
+
const program = buildProgram();
|
|
367
|
+
await program.parseAsync([
|
|
368
|
+
"node",
|
|
369
|
+
"test",
|
|
370
|
+
"directory",
|
|
371
|
+
"assign-roles",
|
|
372
|
+
"p-1",
|
|
373
|
+
"--type",
|
|
374
|
+
"User",
|
|
375
|
+
"--role-ids",
|
|
376
|
+
" , ",
|
|
377
|
+
]);
|
|
378
|
+
|
|
379
|
+
expect(sdk.entities.directory.assignRoles).not.toHaveBeenCalled();
|
|
380
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
381
|
+
expect.objectContaining({
|
|
382
|
+
Result: "Failure",
|
|
383
|
+
Message: "No role IDs provided",
|
|
384
|
+
}),
|
|
385
|
+
);
|
|
386
|
+
expect(process.exitCode).toBe(1);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it("should fail with exit 1 when the SDK call errors", async () => {
|
|
390
|
+
const sdk = mockSdk();
|
|
391
|
+
vi.mocked(sdk.entities.directory.assignRoles).mockRejectedValue(
|
|
392
|
+
new Error("boom"),
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
const program = buildProgram();
|
|
396
|
+
await program.parseAsync([
|
|
397
|
+
"node",
|
|
398
|
+
"test",
|
|
399
|
+
"directory",
|
|
400
|
+
"assign-roles",
|
|
401
|
+
"p-1",
|
|
402
|
+
"--type",
|
|
403
|
+
"User",
|
|
404
|
+
"--role-ids",
|
|
405
|
+
"r-1",
|
|
406
|
+
]);
|
|
407
|
+
|
|
408
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
409
|
+
expect.objectContaining({
|
|
410
|
+
Result: "Failure",
|
|
411
|
+
Message: "Error assigning roles",
|
|
412
|
+
}),
|
|
413
|
+
);
|
|
414
|
+
expect(process.exitCode).toBe(1);
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
describe("directory revoke-roles", () => {
|
|
419
|
+
beforeEach(() => {
|
|
420
|
+
vi.resetAllMocks();
|
|
421
|
+
process.exitCode = undefined;
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("should require confirmation (no --yes): exit 1, SDK not called", async () => {
|
|
425
|
+
const sdk = mockSdk();
|
|
426
|
+
|
|
427
|
+
const program = buildProgram();
|
|
428
|
+
await program.parseAsync([
|
|
429
|
+
"node",
|
|
430
|
+
"test",
|
|
431
|
+
"directory",
|
|
432
|
+
"revoke-roles",
|
|
433
|
+
"p-1",
|
|
434
|
+
"--reason",
|
|
435
|
+
"offboarding",
|
|
436
|
+
]);
|
|
437
|
+
|
|
438
|
+
expect(sdk.entities.directory.revokeRoles).not.toHaveBeenCalled();
|
|
439
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
440
|
+
expect(process.exitCode).toBe(1);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it("should require --reason", async () => {
|
|
444
|
+
const sdk = mockSdk();
|
|
445
|
+
|
|
446
|
+
const program = buildProgram();
|
|
447
|
+
await program.parseAsync([
|
|
448
|
+
"node",
|
|
449
|
+
"test",
|
|
450
|
+
"directory",
|
|
451
|
+
"revoke-roles",
|
|
452
|
+
"p-1",
|
|
453
|
+
"--yes",
|
|
454
|
+
]);
|
|
455
|
+
|
|
456
|
+
expect(sdk.entities.directory.revokeRoles).not.toHaveBeenCalled();
|
|
457
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
458
|
+
expect.objectContaining({
|
|
459
|
+
Result: "Failure",
|
|
460
|
+
Message: "Reason required for destructive operation",
|
|
461
|
+
}),
|
|
462
|
+
);
|
|
463
|
+
expect(process.exitCode).toBe(1);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it("should revoke roles from multiple principals with --yes and --reason", async () => {
|
|
467
|
+
const sdk = mockSdk();
|
|
468
|
+
|
|
469
|
+
const program = buildProgram();
|
|
470
|
+
await program.parseAsync([
|
|
471
|
+
"node",
|
|
472
|
+
"test",
|
|
473
|
+
"directory",
|
|
474
|
+
"revoke-roles",
|
|
475
|
+
"p-1",
|
|
476
|
+
"p-2",
|
|
477
|
+
"--yes",
|
|
478
|
+
"--reason",
|
|
479
|
+
"offboarding",
|
|
480
|
+
]);
|
|
481
|
+
|
|
482
|
+
expect(sdk.entities.directory.revokeRoles).toHaveBeenCalledWith([
|
|
483
|
+
"p-1",
|
|
484
|
+
"p-2",
|
|
485
|
+
]);
|
|
486
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
487
|
+
expect.objectContaining({
|
|
488
|
+
Result: "Success",
|
|
489
|
+
Code: "DirectoryRolesRevoked",
|
|
490
|
+
Data: {
|
|
491
|
+
PrincipalIds: ["p-1", "p-2"],
|
|
492
|
+
Reason: "offboarding",
|
|
493
|
+
},
|
|
494
|
+
}),
|
|
495
|
+
);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
it("should fail with exit 1 when the SDK call errors", async () => {
|
|
499
|
+
const sdk = mockSdk();
|
|
500
|
+
vi.mocked(sdk.entities.directory.revokeRoles).mockRejectedValue(
|
|
501
|
+
new Error("boom"),
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
const program = buildProgram();
|
|
505
|
+
await program.parseAsync([
|
|
506
|
+
"node",
|
|
507
|
+
"test",
|
|
508
|
+
"directory",
|
|
509
|
+
"revoke-roles",
|
|
510
|
+
"p-1",
|
|
511
|
+
"--yes",
|
|
512
|
+
"--reason",
|
|
513
|
+
"offboarding",
|
|
514
|
+
]);
|
|
515
|
+
|
|
516
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
517
|
+
expect.objectContaining({
|
|
518
|
+
Result: "Failure",
|
|
519
|
+
Message: "Error revoking roles",
|
|
520
|
+
}),
|
|
521
|
+
);
|
|
522
|
+
expect(process.exitCode).toBe(1);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it("should bail when SDK connection fails", async () => {
|
|
526
|
+
const sdk = mockSdk();
|
|
527
|
+
vi.mocked(connectOrFail).mockResolvedValue(undefined);
|
|
528
|
+
|
|
529
|
+
const program = buildProgram();
|
|
530
|
+
await program.parseAsync([
|
|
531
|
+
"node",
|
|
532
|
+
"test",
|
|
533
|
+
"directory",
|
|
534
|
+
"revoke-roles",
|
|
535
|
+
"p-1",
|
|
536
|
+
"--yes",
|
|
537
|
+
"--reason",
|
|
538
|
+
"offboarding",
|
|
539
|
+
]);
|
|
540
|
+
|
|
541
|
+
expect(sdk.entities.directory.revokeRoles).not.toHaveBeenCalled();
|
|
542
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
543
|
+
});
|
|
544
|
+
});
|