@uipath/tasks-tool 1.201.0-preview.115 → 1.201.0-preview.121
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 +2 -1
- package/dist/folder-picker-interactive-2kj361mt.js +1850 -0
- package/dist/folder-resolver-htdmzdhy.js +92 -0
- package/dist/index.js +5 -3
- package/dist/tool-0n9mj887.js +2448 -0
- package/dist/tool-rsk1xt86.js +6026 -0
- package/dist/{tool-vw6gha5d.js → tool-ww671age.js} +7469 -10469
- package/dist/tool.js +3 -2
- package/package.json +2 -2
- package/src/commands/_shared/catalog-options.spec.ts +114 -0
- package/src/commands/_shared/catalog-options.ts +122 -0
- package/src/commands/_shared/connect.spec.ts +52 -0
- package/src/commands/_shared/connect.ts +31 -0
- package/src/commands/_shared/constants.ts +1 -0
- package/src/commands/_shared/folder-picker-interactive.spec.ts +92 -0
- package/src/commands/_shared/folder-picker-interactive.ts +76 -0
- package/src/commands/_shared/folder-picker.spec.ts +159 -0
- package/src/commands/_shared/folder-picker.ts +113 -0
- package/src/commands/_shared/folder-resolver.spec.ts +152 -0
- package/src/commands/_shared/folder-resolver.ts +113 -0
- package/src/commands/_shared/parse.spec.ts +69 -0
- package/src/commands/_shared/parse.ts +43 -0
- package/src/commands/catalogs.ts +277 -0
- package/src/commands/comments.ts +160 -0
- package/src/commands/data.ts +137 -0
- package/src/commands/labels.ts +93 -0
- package/src/commands/metadata.ts +139 -0
- package/src/commands/tasks.spec.ts +1557 -2
- package/src/commands/tasks.ts +191 -280
- package/src/tool.ts +10 -0
- package/src/utils/sdk-client.ts +5 -0
- package/tests/assignable-users.e2e.test.ts +58 -4
- package/tests/catalogs.e2e.test.ts +393 -0
- package/tests/comments.e2e.test.ts +223 -0
- package/tests/complete.e2e.test.ts +9 -9
- package/tests/data.e2e.test.ts +187 -0
- package/tests/get.e2e.test.ts +4 -2
- package/tests/labels.e2e.test.ts +184 -0
- package/tests/list.e2e.test.ts +110 -0
- package/tests/metadata.e2e.test.ts +186 -0
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { registerCatalogsCommand } from "./catalogs";
|
|
4
|
+
import { registerCommentsCommand } from "./comments";
|
|
5
|
+
import { registerDataCommand } from "./data";
|
|
6
|
+
import { registerLabelsCommand } from "./labels";
|
|
7
|
+
import { registerMetadataCommand } from "./metadata";
|
|
3
8
|
import { registerTasksCommand } from "./tasks";
|
|
4
9
|
|
|
5
10
|
vi.mock("../utils/sdk-client", () => ({
|
|
6
11
|
createTasksClient: vi.fn(),
|
|
12
|
+
createTaskCatalogsClient: vi.fn(),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
vi.mock("./_shared/folder-resolver", () => ({
|
|
16
|
+
resolveFolderIdByKey: vi.fn(),
|
|
17
|
+
resolveFolderIdByPath: vi.fn(),
|
|
7
18
|
}));
|
|
8
19
|
|
|
9
20
|
vi.mock("@uipath/common", async (importOriginal) => {
|
|
@@ -18,12 +29,24 @@ vi.mock("@uipath/common", async (importOriginal) => {
|
|
|
18
29
|
});
|
|
19
30
|
|
|
20
31
|
import { OutputFormatter } from "@uipath/common";
|
|
21
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
createTaskCatalogsClient,
|
|
34
|
+
createTasksClient,
|
|
35
|
+
} from "../utils/sdk-client";
|
|
36
|
+
import {
|
|
37
|
+
resolveFolderIdByKey,
|
|
38
|
+
resolveFolderIdByPath,
|
|
39
|
+
} from "./_shared/folder-resolver";
|
|
22
40
|
|
|
23
41
|
function buildProgram(): Command {
|
|
24
42
|
const program = new Command();
|
|
25
43
|
program.name("test").exitOverride();
|
|
26
44
|
registerTasksCommand(program);
|
|
45
|
+
registerCatalogsCommand(program);
|
|
46
|
+
registerCommentsCommand(program);
|
|
47
|
+
registerLabelsCommand(program);
|
|
48
|
+
registerMetadataCommand(program);
|
|
49
|
+
registerDataCommand(program);
|
|
27
50
|
return program;
|
|
28
51
|
}
|
|
29
52
|
|
|
@@ -37,6 +60,12 @@ function mockSdk(overrides: Record<string, unknown> = {}) {
|
|
|
37
60
|
unassign: vi.fn().mockResolvedValue({ success: true, data: [] }),
|
|
38
61
|
complete: vi.fn().mockResolvedValue({ success: true }),
|
|
39
62
|
getUsers: vi.fn().mockResolvedValue([]),
|
|
63
|
+
getComments: vi.fn().mockResolvedValue([]),
|
|
64
|
+
createComment: vi.fn().mockResolvedValue({}),
|
|
65
|
+
saveTags: vi.fn().mockResolvedValue(undefined),
|
|
66
|
+
editMetadata: vi.fn().mockResolvedValue(undefined),
|
|
67
|
+
getDataById: vi.fn().mockResolvedValue({}),
|
|
68
|
+
saveData: vi.fn().mockResolvedValue(undefined),
|
|
40
69
|
...overrides,
|
|
41
70
|
},
|
|
42
71
|
};
|
|
@@ -44,6 +73,19 @@ function mockSdk(overrides: Record<string, unknown> = {}) {
|
|
|
44
73
|
return sdk;
|
|
45
74
|
}
|
|
46
75
|
|
|
76
|
+
// Task catalogs live on their own service (createTaskCatalogsClient), not sdk.tasks.
|
|
77
|
+
function mockCatalogs(overrides: Record<string, unknown> = {}) {
|
|
78
|
+
const catalogs = {
|
|
79
|
+
getAll: vi.fn().mockResolvedValue([]),
|
|
80
|
+
getById: vi.fn().mockResolvedValue({}),
|
|
81
|
+
create: vi.fn().mockResolvedValue({}),
|
|
82
|
+
updateById: vi.fn().mockResolvedValue(undefined),
|
|
83
|
+
...overrides,
|
|
84
|
+
};
|
|
85
|
+
vi.mocked(createTaskCatalogsClient).mockReturnValue(catalogs as never);
|
|
86
|
+
return catalogs;
|
|
87
|
+
}
|
|
88
|
+
|
|
47
89
|
describe("tasks command registration", () => {
|
|
48
90
|
it("should register all commands on program", () => {
|
|
49
91
|
const program = buildProgram();
|
|
@@ -55,6 +97,34 @@ describe("tasks command registration", () => {
|
|
|
55
97
|
expect(subcommands).toContain("unassign");
|
|
56
98
|
expect(subcommands).toContain("complete");
|
|
57
99
|
expect(subcommands).toContain("users");
|
|
100
|
+
expect(subcommands).toContain("catalogs");
|
|
101
|
+
expect(subcommands).toContain("comments");
|
|
102
|
+
expect(subcommands).toContain("labels");
|
|
103
|
+
expect(subcommands).toContain("metadata");
|
|
104
|
+
expect(subcommands).toContain("data");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should register the catalogs subcommands", () => {
|
|
108
|
+
const program = buildProgram();
|
|
109
|
+
const catalogs = program.commands.find((c) => c.name() === "catalogs");
|
|
110
|
+
const subcommands = catalogs?.commands.map((c) => c.name()) ?? [];
|
|
111
|
+
expect(subcommands).toEqual(
|
|
112
|
+
expect.arrayContaining(["list", "get", "create", "update"]),
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should register the comments subcommands", () => {
|
|
117
|
+
const program = buildProgram();
|
|
118
|
+
const comments = program.commands.find((c) => c.name() === "comments");
|
|
119
|
+
const subcommands = comments?.commands.map((c) => c.name()) ?? [];
|
|
120
|
+
expect(subcommands).toEqual(expect.arrayContaining(["list", "add"]));
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should register the data subcommands", () => {
|
|
124
|
+
const program = buildProgram();
|
|
125
|
+
const data = program.commands.find((c) => c.name() === "data");
|
|
126
|
+
const subcommands = data?.commands.map((c) => c.name()) ?? [];
|
|
127
|
+
expect(subcommands).toEqual(expect.arrayContaining(["get", "save"]));
|
|
58
128
|
});
|
|
59
129
|
});
|
|
60
130
|
|
|
@@ -862,3 +932,1488 @@ describe("tasks users", () => {
|
|
|
862
932
|
expect(process.exitCode).toBe(1);
|
|
863
933
|
});
|
|
864
934
|
});
|
|
935
|
+
|
|
936
|
+
describe("tasks catalogs list", () => {
|
|
937
|
+
beforeEach(() => {
|
|
938
|
+
vi.resetAllMocks();
|
|
939
|
+
process.exitCode = undefined;
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
it("should list catalogs for a folder", async () => {
|
|
943
|
+
const catalogData = [
|
|
944
|
+
{ id: 1, name: "Invoices" },
|
|
945
|
+
{ id: 2, name: "Receipts" },
|
|
946
|
+
];
|
|
947
|
+
mockSdk();
|
|
948
|
+
const catalogs = mockCatalogs({
|
|
949
|
+
getAll: vi.fn().mockResolvedValue(catalogData),
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
const program = buildProgram();
|
|
953
|
+
await program.parseAsync([
|
|
954
|
+
"node",
|
|
955
|
+
"test",
|
|
956
|
+
"catalogs",
|
|
957
|
+
"list",
|
|
958
|
+
"--folder-id",
|
|
959
|
+
"42",
|
|
960
|
+
]);
|
|
961
|
+
|
|
962
|
+
expect(catalogs.getAll).toHaveBeenCalledWith(
|
|
963
|
+
expect.objectContaining({ folderId: 42, pageSize: 100 }),
|
|
964
|
+
);
|
|
965
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
966
|
+
expect.objectContaining({
|
|
967
|
+
Result: "Success",
|
|
968
|
+
Code: "TaskCatalogList",
|
|
969
|
+
Data: catalogData,
|
|
970
|
+
}),
|
|
971
|
+
);
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it("should apply the --limit option", async () => {
|
|
975
|
+
mockSdk();
|
|
976
|
+
mockCatalogs({
|
|
977
|
+
getAll: vi
|
|
978
|
+
.fn()
|
|
979
|
+
.mockResolvedValue([{ id: 1 }, { id: 2 }, { id: 3 }]),
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
const program = buildProgram();
|
|
983
|
+
await program.parseAsync([
|
|
984
|
+
"node",
|
|
985
|
+
"test",
|
|
986
|
+
"catalogs",
|
|
987
|
+
"list",
|
|
988
|
+
"--folder-id",
|
|
989
|
+
"42",
|
|
990
|
+
"--limit",
|
|
991
|
+
"2",
|
|
992
|
+
]);
|
|
993
|
+
|
|
994
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
995
|
+
expect.objectContaining({
|
|
996
|
+
Code: "TaskCatalogList",
|
|
997
|
+
Data: [{ id: 1 }, { id: 2 }],
|
|
998
|
+
}),
|
|
999
|
+
);
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
it("should reject an invalid --folder-id", async () => {
|
|
1003
|
+
mockSdk();
|
|
1004
|
+
mockCatalogs();
|
|
1005
|
+
|
|
1006
|
+
const program = buildProgram();
|
|
1007
|
+
await program.parseAsync([
|
|
1008
|
+
"node",
|
|
1009
|
+
"test",
|
|
1010
|
+
"catalogs",
|
|
1011
|
+
"list",
|
|
1012
|
+
"--folder-id",
|
|
1013
|
+
"abc",
|
|
1014
|
+
]);
|
|
1015
|
+
|
|
1016
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1017
|
+
expect.objectContaining({
|
|
1018
|
+
Result: "Failure",
|
|
1019
|
+
Message: "Invalid --folder-id: abc",
|
|
1020
|
+
}),
|
|
1021
|
+
);
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
it("should propagate API errors", async () => {
|
|
1025
|
+
mockSdk();
|
|
1026
|
+
mockCatalogs({
|
|
1027
|
+
getAll: vi.fn().mockRejectedValue(new Error("boom")),
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
const program = buildProgram();
|
|
1031
|
+
await program.parseAsync([
|
|
1032
|
+
"node",
|
|
1033
|
+
"test",
|
|
1034
|
+
"catalogs",
|
|
1035
|
+
"list",
|
|
1036
|
+
"--folder-id",
|
|
1037
|
+
"42",
|
|
1038
|
+
]);
|
|
1039
|
+
|
|
1040
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1041
|
+
expect.objectContaining({
|
|
1042
|
+
Result: "Failure",
|
|
1043
|
+
Message: "Error listing task catalogs",
|
|
1044
|
+
}),
|
|
1045
|
+
);
|
|
1046
|
+
});
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
describe("tasks catalogs get", () => {
|
|
1050
|
+
beforeEach(() => {
|
|
1051
|
+
vi.resetAllMocks();
|
|
1052
|
+
process.exitCode = undefined;
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
it("should get a catalog by id", async () => {
|
|
1056
|
+
const catalog = { id: 7, name: "Invoices" };
|
|
1057
|
+
mockSdk();
|
|
1058
|
+
const catalogs = mockCatalogs({
|
|
1059
|
+
getById: vi.fn().mockResolvedValue(catalog),
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
const program = buildProgram();
|
|
1063
|
+
await program.parseAsync([
|
|
1064
|
+
"node",
|
|
1065
|
+
"test",
|
|
1066
|
+
"catalogs",
|
|
1067
|
+
"get",
|
|
1068
|
+
"7",
|
|
1069
|
+
"--folder-id",
|
|
1070
|
+
"42",
|
|
1071
|
+
]);
|
|
1072
|
+
|
|
1073
|
+
expect(catalogs.getById).toHaveBeenCalledWith(7, {
|
|
1074
|
+
folderId: 42,
|
|
1075
|
+
});
|
|
1076
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1077
|
+
expect.objectContaining({
|
|
1078
|
+
Code: "TaskCatalogDetails",
|
|
1079
|
+
Data: catalog,
|
|
1080
|
+
}),
|
|
1081
|
+
);
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
it("should reject an invalid <id>", async () => {
|
|
1085
|
+
mockSdk();
|
|
1086
|
+
mockCatalogs();
|
|
1087
|
+
|
|
1088
|
+
const program = buildProgram();
|
|
1089
|
+
await program.parseAsync([
|
|
1090
|
+
"node",
|
|
1091
|
+
"test",
|
|
1092
|
+
"catalogs",
|
|
1093
|
+
"get",
|
|
1094
|
+
"0",
|
|
1095
|
+
"--folder-id",
|
|
1096
|
+
"42",
|
|
1097
|
+
]);
|
|
1098
|
+
|
|
1099
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1100
|
+
expect.objectContaining({
|
|
1101
|
+
Result: "Failure",
|
|
1102
|
+
Message: "Invalid <id>: 0",
|
|
1103
|
+
}),
|
|
1104
|
+
);
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
it("should propagate API errors", async () => {
|
|
1108
|
+
mockSdk();
|
|
1109
|
+
mockCatalogs({
|
|
1110
|
+
getById: vi.fn().mockRejectedValue(new Error("not found")),
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
const program = buildProgram();
|
|
1114
|
+
await program.parseAsync([
|
|
1115
|
+
"node",
|
|
1116
|
+
"test",
|
|
1117
|
+
"catalogs",
|
|
1118
|
+
"get",
|
|
1119
|
+
"7",
|
|
1120
|
+
"--folder-id",
|
|
1121
|
+
"42",
|
|
1122
|
+
]);
|
|
1123
|
+
|
|
1124
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1125
|
+
expect.objectContaining({
|
|
1126
|
+
Result: "Failure",
|
|
1127
|
+
Message: "Error getting task catalog '7'",
|
|
1128
|
+
}),
|
|
1129
|
+
);
|
|
1130
|
+
});
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
describe("tasks catalogs create", () => {
|
|
1134
|
+
beforeEach(() => {
|
|
1135
|
+
vi.resetAllMocks();
|
|
1136
|
+
process.exitCode = undefined;
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
it("should create a catalog", async () => {
|
|
1140
|
+
const created = { id: 9, name: "New Catalog" };
|
|
1141
|
+
mockSdk();
|
|
1142
|
+
const catalogs = mockCatalogs({
|
|
1143
|
+
create: vi.fn().mockResolvedValue(created),
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
const program = buildProgram();
|
|
1147
|
+
await program.parseAsync([
|
|
1148
|
+
"node",
|
|
1149
|
+
"test",
|
|
1150
|
+
"catalogs",
|
|
1151
|
+
"create",
|
|
1152
|
+
"--folder-id",
|
|
1153
|
+
"42",
|
|
1154
|
+
"--name",
|
|
1155
|
+
"New Catalog",
|
|
1156
|
+
"--description",
|
|
1157
|
+
"desc",
|
|
1158
|
+
]);
|
|
1159
|
+
|
|
1160
|
+
expect(catalogs.create).toHaveBeenCalledWith("New Catalog", {
|
|
1161
|
+
folderId: 42,
|
|
1162
|
+
description: "desc",
|
|
1163
|
+
});
|
|
1164
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1165
|
+
expect.objectContaining({
|
|
1166
|
+
Code: "TaskCatalogCreated",
|
|
1167
|
+
Data: created,
|
|
1168
|
+
}),
|
|
1169
|
+
);
|
|
1170
|
+
});
|
|
1171
|
+
|
|
1172
|
+
it("should fail when --name is missing", async () => {
|
|
1173
|
+
mockSdk();
|
|
1174
|
+
|
|
1175
|
+
const program = buildProgram();
|
|
1176
|
+
await expect(
|
|
1177
|
+
program.parseAsync([
|
|
1178
|
+
"node",
|
|
1179
|
+
"test",
|
|
1180
|
+
"catalogs",
|
|
1181
|
+
"create",
|
|
1182
|
+
"--folder-id",
|
|
1183
|
+
"42",
|
|
1184
|
+
]),
|
|
1185
|
+
).rejects.toThrow();
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
it("should pass retention and encryption options", async () => {
|
|
1189
|
+
mockSdk();
|
|
1190
|
+
const catalogs = mockCatalogs();
|
|
1191
|
+
|
|
1192
|
+
const program = buildProgram();
|
|
1193
|
+
await program.parseAsync([
|
|
1194
|
+
"node",
|
|
1195
|
+
"test",
|
|
1196
|
+
"catalogs",
|
|
1197
|
+
"create",
|
|
1198
|
+
"--folder-id",
|
|
1199
|
+
"42",
|
|
1200
|
+
"--name",
|
|
1201
|
+
"Invoices",
|
|
1202
|
+
"--encrypted",
|
|
1203
|
+
"--retention-action",
|
|
1204
|
+
"Archive",
|
|
1205
|
+
"--retention-period",
|
|
1206
|
+
"30",
|
|
1207
|
+
"--retention-bucket-id",
|
|
1208
|
+
"7",
|
|
1209
|
+
]);
|
|
1210
|
+
|
|
1211
|
+
expect(catalogs.create).toHaveBeenCalledWith("Invoices", {
|
|
1212
|
+
folderId: 42,
|
|
1213
|
+
encrypted: true,
|
|
1214
|
+
retentionAction: "Archive",
|
|
1215
|
+
retentionPeriod: 30,
|
|
1216
|
+
retentionBucketId: 7,
|
|
1217
|
+
});
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
it("should reject an invalid --retention-action", async () => {
|
|
1221
|
+
mockSdk();
|
|
1222
|
+
const catalogs = mockCatalogs();
|
|
1223
|
+
|
|
1224
|
+
const program = buildProgram();
|
|
1225
|
+
await program.parseAsync([
|
|
1226
|
+
"node",
|
|
1227
|
+
"test",
|
|
1228
|
+
"catalogs",
|
|
1229
|
+
"create",
|
|
1230
|
+
"--folder-id",
|
|
1231
|
+
"42",
|
|
1232
|
+
"--name",
|
|
1233
|
+
"Invoices",
|
|
1234
|
+
"--retention-action",
|
|
1235
|
+
"Nope",
|
|
1236
|
+
]);
|
|
1237
|
+
|
|
1238
|
+
expect(catalogs.create).not.toHaveBeenCalled();
|
|
1239
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1240
|
+
expect.objectContaining({
|
|
1241
|
+
Result: "Failure",
|
|
1242
|
+
Message: "Invalid --retention-action 'Nope'",
|
|
1243
|
+
}),
|
|
1244
|
+
);
|
|
1245
|
+
});
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
describe("tasks catalogs update", () => {
|
|
1249
|
+
beforeEach(() => {
|
|
1250
|
+
vi.resetAllMocks();
|
|
1251
|
+
process.exitCode = undefined;
|
|
1252
|
+
});
|
|
1253
|
+
|
|
1254
|
+
it("should update a catalog", async () => {
|
|
1255
|
+
mockSdk();
|
|
1256
|
+
const catalogs = mockCatalogs();
|
|
1257
|
+
|
|
1258
|
+
const program = buildProgram();
|
|
1259
|
+
await program.parseAsync([
|
|
1260
|
+
"node",
|
|
1261
|
+
"test",
|
|
1262
|
+
"catalogs",
|
|
1263
|
+
"update",
|
|
1264
|
+
"9",
|
|
1265
|
+
"--folder-id",
|
|
1266
|
+
"42",
|
|
1267
|
+
"--name",
|
|
1268
|
+
"Renamed",
|
|
1269
|
+
]);
|
|
1270
|
+
|
|
1271
|
+
expect(catalogs.updateById).toHaveBeenCalledWith(9, {
|
|
1272
|
+
folderId: 42,
|
|
1273
|
+
name: "Renamed",
|
|
1274
|
+
});
|
|
1275
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1276
|
+
expect.objectContaining({ Code: "TaskCatalogUpdated" }),
|
|
1277
|
+
);
|
|
1278
|
+
});
|
|
1279
|
+
|
|
1280
|
+
it("should reject an update with no mutable fields", async () => {
|
|
1281
|
+
mockSdk();
|
|
1282
|
+
const catalogs = mockCatalogs();
|
|
1283
|
+
|
|
1284
|
+
const program = buildProgram();
|
|
1285
|
+
await program.parseAsync([
|
|
1286
|
+
"node",
|
|
1287
|
+
"test",
|
|
1288
|
+
"catalogs",
|
|
1289
|
+
"update",
|
|
1290
|
+
"9",
|
|
1291
|
+
"--folder-id",
|
|
1292
|
+
"42",
|
|
1293
|
+
]);
|
|
1294
|
+
|
|
1295
|
+
expect(catalogs.updateById).not.toHaveBeenCalled();
|
|
1296
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1297
|
+
expect.objectContaining({ Message: "No catalog changes provided" }),
|
|
1298
|
+
);
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
it("should reject an empty --retention-action instead of a no-op update", async () => {
|
|
1302
|
+
mockSdk();
|
|
1303
|
+
const catalogs = mockCatalogs();
|
|
1304
|
+
|
|
1305
|
+
const program = buildProgram();
|
|
1306
|
+
await program.parseAsync([
|
|
1307
|
+
"node",
|
|
1308
|
+
"test",
|
|
1309
|
+
"catalogs",
|
|
1310
|
+
"update",
|
|
1311
|
+
"9",
|
|
1312
|
+
"--folder-id",
|
|
1313
|
+
"42",
|
|
1314
|
+
"--retention-action",
|
|
1315
|
+
"",
|
|
1316
|
+
]);
|
|
1317
|
+
|
|
1318
|
+
expect(catalogs.updateById).not.toHaveBeenCalled();
|
|
1319
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1320
|
+
expect.objectContaining({
|
|
1321
|
+
Message: "Invalid --retention-action ''",
|
|
1322
|
+
}),
|
|
1323
|
+
);
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1326
|
+
it("should propagate API errors", async () => {
|
|
1327
|
+
mockSdk();
|
|
1328
|
+
mockCatalogs({
|
|
1329
|
+
updateById: vi.fn().mockRejectedValue(new Error("nope")),
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1332
|
+
const program = buildProgram();
|
|
1333
|
+
await program.parseAsync([
|
|
1334
|
+
"node",
|
|
1335
|
+
"test",
|
|
1336
|
+
"catalogs",
|
|
1337
|
+
"update",
|
|
1338
|
+
"9",
|
|
1339
|
+
"--folder-id",
|
|
1340
|
+
"42",
|
|
1341
|
+
"--name",
|
|
1342
|
+
"Renamed",
|
|
1343
|
+
]);
|
|
1344
|
+
|
|
1345
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1346
|
+
expect.objectContaining({
|
|
1347
|
+
Result: "Failure",
|
|
1348
|
+
Message: "Error updating task catalog '9'",
|
|
1349
|
+
}),
|
|
1350
|
+
);
|
|
1351
|
+
});
|
|
1352
|
+
});
|
|
1353
|
+
|
|
1354
|
+
describe("tasks comments", () => {
|
|
1355
|
+
beforeEach(() => {
|
|
1356
|
+
vi.resetAllMocks();
|
|
1357
|
+
process.exitCode = undefined;
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
it("should list notes for a task", async () => {
|
|
1361
|
+
const noteData = [
|
|
1362
|
+
{ id: 1, text: "First" },
|
|
1363
|
+
{ id: 2, text: "Second" },
|
|
1364
|
+
];
|
|
1365
|
+
const sdk = mockSdk({
|
|
1366
|
+
getComments: vi.fn().mockResolvedValue(noteData),
|
|
1367
|
+
});
|
|
1368
|
+
|
|
1369
|
+
const program = buildProgram();
|
|
1370
|
+
await program.parseAsync([
|
|
1371
|
+
"node",
|
|
1372
|
+
"test",
|
|
1373
|
+
"comments",
|
|
1374
|
+
"list",
|
|
1375
|
+
"5001",
|
|
1376
|
+
"--folder-id",
|
|
1377
|
+
"42",
|
|
1378
|
+
]);
|
|
1379
|
+
|
|
1380
|
+
expect(sdk.tasks.getComments).toHaveBeenCalledWith(
|
|
1381
|
+
5001,
|
|
1382
|
+
expect.objectContaining({ folderId: 42, pageSize: 100 }),
|
|
1383
|
+
);
|
|
1384
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1385
|
+
expect.objectContaining({
|
|
1386
|
+
Result: "Success",
|
|
1387
|
+
Code: "TaskCommentList",
|
|
1388
|
+
Data: noteData,
|
|
1389
|
+
}),
|
|
1390
|
+
);
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
it("should apply the --limit option", async () => {
|
|
1394
|
+
mockSdk({
|
|
1395
|
+
getComments: vi
|
|
1396
|
+
.fn()
|
|
1397
|
+
.mockResolvedValue([{ id: 1 }, { id: 2 }, { id: 3 }]),
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
const program = buildProgram();
|
|
1401
|
+
await program.parseAsync([
|
|
1402
|
+
"node",
|
|
1403
|
+
"test",
|
|
1404
|
+
"comments",
|
|
1405
|
+
"list",
|
|
1406
|
+
"5001",
|
|
1407
|
+
"--folder-id",
|
|
1408
|
+
"42",
|
|
1409
|
+
"--limit",
|
|
1410
|
+
"2",
|
|
1411
|
+
]);
|
|
1412
|
+
|
|
1413
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1414
|
+
expect.objectContaining({
|
|
1415
|
+
Code: "TaskCommentList",
|
|
1416
|
+
Data: [{ id: 1 }, { id: 2 }],
|
|
1417
|
+
}),
|
|
1418
|
+
);
|
|
1419
|
+
});
|
|
1420
|
+
|
|
1421
|
+
it("should reject an invalid <task-id>", async () => {
|
|
1422
|
+
mockSdk();
|
|
1423
|
+
|
|
1424
|
+
const program = buildProgram();
|
|
1425
|
+
await program.parseAsync([
|
|
1426
|
+
"node",
|
|
1427
|
+
"test",
|
|
1428
|
+
"comments",
|
|
1429
|
+
"list",
|
|
1430
|
+
"abc",
|
|
1431
|
+
"--folder-id",
|
|
1432
|
+
"42",
|
|
1433
|
+
]);
|
|
1434
|
+
|
|
1435
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1436
|
+
expect.objectContaining({
|
|
1437
|
+
Result: "Failure",
|
|
1438
|
+
Message: "Invalid <task-id>: abc",
|
|
1439
|
+
}),
|
|
1440
|
+
);
|
|
1441
|
+
});
|
|
1442
|
+
});
|
|
1443
|
+
|
|
1444
|
+
describe("tasks add-comment", () => {
|
|
1445
|
+
beforeEach(() => {
|
|
1446
|
+
vi.resetAllMocks();
|
|
1447
|
+
process.exitCode = undefined;
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1450
|
+
it("should create a note", async () => {
|
|
1451
|
+
const created = { id: 9, text: "Escalated" };
|
|
1452
|
+
const sdk = mockSdk({
|
|
1453
|
+
createComment: vi.fn().mockResolvedValue(created),
|
|
1454
|
+
});
|
|
1455
|
+
|
|
1456
|
+
const program = buildProgram();
|
|
1457
|
+
await program.parseAsync([
|
|
1458
|
+
"node",
|
|
1459
|
+
"test",
|
|
1460
|
+
"comments",
|
|
1461
|
+
"add",
|
|
1462
|
+
"5001",
|
|
1463
|
+
"--folder-id",
|
|
1464
|
+
"42",
|
|
1465
|
+
"--text",
|
|
1466
|
+
"Escalated",
|
|
1467
|
+
]);
|
|
1468
|
+
|
|
1469
|
+
expect(sdk.tasks.createComment).toHaveBeenCalledWith(
|
|
1470
|
+
5001,
|
|
1471
|
+
"Escalated",
|
|
1472
|
+
{
|
|
1473
|
+
folderId: 42,
|
|
1474
|
+
},
|
|
1475
|
+
);
|
|
1476
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1477
|
+
expect.objectContaining({
|
|
1478
|
+
Code: "TaskCommentCreated",
|
|
1479
|
+
Data: created,
|
|
1480
|
+
}),
|
|
1481
|
+
);
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
it("should reject empty --text", async () => {
|
|
1485
|
+
const sdk = mockSdk();
|
|
1486
|
+
|
|
1487
|
+
const program = buildProgram();
|
|
1488
|
+
await program.parseAsync([
|
|
1489
|
+
"node",
|
|
1490
|
+
"test",
|
|
1491
|
+
"comments",
|
|
1492
|
+
"add",
|
|
1493
|
+
"5001",
|
|
1494
|
+
"--folder-id",
|
|
1495
|
+
"42",
|
|
1496
|
+
"--text",
|
|
1497
|
+
"",
|
|
1498
|
+
]);
|
|
1499
|
+
|
|
1500
|
+
expect(sdk.tasks.createComment).not.toHaveBeenCalled();
|
|
1501
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1502
|
+
expect.objectContaining({
|
|
1503
|
+
Result: "Failure",
|
|
1504
|
+
Message: "--text cannot be empty",
|
|
1505
|
+
}),
|
|
1506
|
+
);
|
|
1507
|
+
});
|
|
1508
|
+
|
|
1509
|
+
it("should propagate API errors", async () => {
|
|
1510
|
+
mockSdk({
|
|
1511
|
+
createComment: vi.fn().mockRejectedValue(new Error("nope")),
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
const program = buildProgram();
|
|
1515
|
+
await program.parseAsync([
|
|
1516
|
+
"node",
|
|
1517
|
+
"test",
|
|
1518
|
+
"comments",
|
|
1519
|
+
"add",
|
|
1520
|
+
"5001",
|
|
1521
|
+
"--folder-id",
|
|
1522
|
+
"42",
|
|
1523
|
+
"--text",
|
|
1524
|
+
"hi",
|
|
1525
|
+
]);
|
|
1526
|
+
|
|
1527
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1528
|
+
expect.objectContaining({
|
|
1529
|
+
Result: "Failure",
|
|
1530
|
+
Message: "Error adding comment to task '5001'",
|
|
1531
|
+
}),
|
|
1532
|
+
);
|
|
1533
|
+
});
|
|
1534
|
+
});
|
|
1535
|
+
|
|
1536
|
+
describe("tasks labels", () => {
|
|
1537
|
+
beforeEach(() => {
|
|
1538
|
+
vi.resetAllMocks();
|
|
1539
|
+
process.exitCode = undefined;
|
|
1540
|
+
});
|
|
1541
|
+
|
|
1542
|
+
it("should set tags", async () => {
|
|
1543
|
+
const sdk = mockSdk();
|
|
1544
|
+
|
|
1545
|
+
const program = buildProgram();
|
|
1546
|
+
await program.parseAsync([
|
|
1547
|
+
"node",
|
|
1548
|
+
"test",
|
|
1549
|
+
"labels",
|
|
1550
|
+
"5001",
|
|
1551
|
+
"--folder-id",
|
|
1552
|
+
"42",
|
|
1553
|
+
"--labels",
|
|
1554
|
+
'[{"name":"urgent"}]',
|
|
1555
|
+
]);
|
|
1556
|
+
|
|
1557
|
+
expect(sdk.tasks.saveTags).toHaveBeenCalledWith(
|
|
1558
|
+
5001,
|
|
1559
|
+
[{ name: "urgent" }],
|
|
1560
|
+
{ folderId: 42 },
|
|
1561
|
+
);
|
|
1562
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1563
|
+
expect.objectContaining({ Code: "TaskLabelsSaved" }),
|
|
1564
|
+
);
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
it("should clear tags with an empty array", async () => {
|
|
1568
|
+
const sdk = mockSdk();
|
|
1569
|
+
|
|
1570
|
+
const program = buildProgram();
|
|
1571
|
+
await program.parseAsync([
|
|
1572
|
+
"node",
|
|
1573
|
+
"test",
|
|
1574
|
+
"labels",
|
|
1575
|
+
"5001",
|
|
1576
|
+
"--folder-id",
|
|
1577
|
+
"42",
|
|
1578
|
+
"--labels",
|
|
1579
|
+
"[]",
|
|
1580
|
+
]);
|
|
1581
|
+
|
|
1582
|
+
expect(sdk.tasks.saveTags).toHaveBeenCalledWith(5001, [], {
|
|
1583
|
+
folderId: 42,
|
|
1584
|
+
});
|
|
1585
|
+
});
|
|
1586
|
+
|
|
1587
|
+
it("should reject invalid JSON", async () => {
|
|
1588
|
+
const sdk = mockSdk();
|
|
1589
|
+
|
|
1590
|
+
const program = buildProgram();
|
|
1591
|
+
await program.parseAsync([
|
|
1592
|
+
"node",
|
|
1593
|
+
"test",
|
|
1594
|
+
"labels",
|
|
1595
|
+
"5001",
|
|
1596
|
+
"--folder-id",
|
|
1597
|
+
"42",
|
|
1598
|
+
"--labels",
|
|
1599
|
+
"not-json",
|
|
1600
|
+
]);
|
|
1601
|
+
|
|
1602
|
+
expect(sdk.tasks.saveTags).not.toHaveBeenCalled();
|
|
1603
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1604
|
+
expect.objectContaining({ Message: "Invalid JSON in --labels" }),
|
|
1605
|
+
);
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
it("should reject non-array tags", async () => {
|
|
1609
|
+
const sdk = mockSdk();
|
|
1610
|
+
|
|
1611
|
+
const program = buildProgram();
|
|
1612
|
+
await program.parseAsync([
|
|
1613
|
+
"node",
|
|
1614
|
+
"test",
|
|
1615
|
+
"labels",
|
|
1616
|
+
"5001",
|
|
1617
|
+
"--folder-id",
|
|
1618
|
+
"42",
|
|
1619
|
+
"--labels",
|
|
1620
|
+
'{"name":"urgent"}',
|
|
1621
|
+
]);
|
|
1622
|
+
|
|
1623
|
+
expect(sdk.tasks.saveTags).not.toHaveBeenCalled();
|
|
1624
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1625
|
+
expect.objectContaining({
|
|
1626
|
+
Message: "--labels must be a JSON array",
|
|
1627
|
+
}),
|
|
1628
|
+
);
|
|
1629
|
+
});
|
|
1630
|
+
|
|
1631
|
+
it("should propagate API errors", async () => {
|
|
1632
|
+
mockSdk({ saveTags: vi.fn().mockRejectedValue(new Error("nope")) });
|
|
1633
|
+
|
|
1634
|
+
const program = buildProgram();
|
|
1635
|
+
await program.parseAsync([
|
|
1636
|
+
"node",
|
|
1637
|
+
"test",
|
|
1638
|
+
"labels",
|
|
1639
|
+
"5001",
|
|
1640
|
+
"--folder-id",
|
|
1641
|
+
"42",
|
|
1642
|
+
"--labels",
|
|
1643
|
+
"[]",
|
|
1644
|
+
]);
|
|
1645
|
+
|
|
1646
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1647
|
+
expect.objectContaining({
|
|
1648
|
+
Result: "Failure",
|
|
1649
|
+
Message: "Error saving labels on task '5001'",
|
|
1650
|
+
}),
|
|
1651
|
+
);
|
|
1652
|
+
});
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
describe("tasks metadata", () => {
|
|
1656
|
+
beforeEach(() => {
|
|
1657
|
+
vi.resetAllMocks();
|
|
1658
|
+
process.exitCode = undefined;
|
|
1659
|
+
});
|
|
1660
|
+
|
|
1661
|
+
it("should edit metadata", async () => {
|
|
1662
|
+
const sdk = mockSdk();
|
|
1663
|
+
|
|
1664
|
+
const program = buildProgram();
|
|
1665
|
+
await program.parseAsync([
|
|
1666
|
+
"node",
|
|
1667
|
+
"test",
|
|
1668
|
+
"metadata",
|
|
1669
|
+
"5001",
|
|
1670
|
+
"--folder-id",
|
|
1671
|
+
"42",
|
|
1672
|
+
"--title",
|
|
1673
|
+
"Review invoice",
|
|
1674
|
+
"--priority",
|
|
1675
|
+
"High",
|
|
1676
|
+
]);
|
|
1677
|
+
|
|
1678
|
+
expect(sdk.tasks.editMetadata).toHaveBeenCalledWith(5001, {
|
|
1679
|
+
folderId: 42,
|
|
1680
|
+
title: "Review invoice",
|
|
1681
|
+
priority: "High",
|
|
1682
|
+
});
|
|
1683
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1684
|
+
expect.objectContaining({ Code: "TaskMetadataEdited" }),
|
|
1685
|
+
);
|
|
1686
|
+
});
|
|
1687
|
+
|
|
1688
|
+
it("should reject an invalid <task-id>", async () => {
|
|
1689
|
+
mockSdk();
|
|
1690
|
+
|
|
1691
|
+
const program = buildProgram();
|
|
1692
|
+
await program.parseAsync([
|
|
1693
|
+
"node",
|
|
1694
|
+
"test",
|
|
1695
|
+
"metadata",
|
|
1696
|
+
"abc",
|
|
1697
|
+
"--folder-id",
|
|
1698
|
+
"42",
|
|
1699
|
+
"--title",
|
|
1700
|
+
"x",
|
|
1701
|
+
]);
|
|
1702
|
+
|
|
1703
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1704
|
+
expect.objectContaining({ Message: "Invalid <task-id>: abc" }),
|
|
1705
|
+
);
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
it("should reject an invalid priority", async () => {
|
|
1709
|
+
const sdk = mockSdk();
|
|
1710
|
+
|
|
1711
|
+
const program = buildProgram();
|
|
1712
|
+
await program.parseAsync([
|
|
1713
|
+
"node",
|
|
1714
|
+
"test",
|
|
1715
|
+
"metadata",
|
|
1716
|
+
"5001",
|
|
1717
|
+
"--folder-id",
|
|
1718
|
+
"42",
|
|
1719
|
+
"--priority",
|
|
1720
|
+
"Urgent",
|
|
1721
|
+
]);
|
|
1722
|
+
|
|
1723
|
+
expect(sdk.tasks.editMetadata).not.toHaveBeenCalled();
|
|
1724
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1725
|
+
expect.objectContaining({ Message: "Invalid priority 'Urgent'" }),
|
|
1726
|
+
);
|
|
1727
|
+
});
|
|
1728
|
+
|
|
1729
|
+
it("should propagate API errors", async () => {
|
|
1730
|
+
mockSdk({ editMetadata: vi.fn().mockRejectedValue(new Error("nope")) });
|
|
1731
|
+
|
|
1732
|
+
const program = buildProgram();
|
|
1733
|
+
await program.parseAsync([
|
|
1734
|
+
"node",
|
|
1735
|
+
"test",
|
|
1736
|
+
"metadata",
|
|
1737
|
+
"5001",
|
|
1738
|
+
"--folder-id",
|
|
1739
|
+
"42",
|
|
1740
|
+
"--title",
|
|
1741
|
+
"x",
|
|
1742
|
+
]);
|
|
1743
|
+
|
|
1744
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1745
|
+
expect.objectContaining({
|
|
1746
|
+
Result: "Failure",
|
|
1747
|
+
Message: "Error editing metadata on task '5001'",
|
|
1748
|
+
}),
|
|
1749
|
+
);
|
|
1750
|
+
});
|
|
1751
|
+
|
|
1752
|
+
it("should pass --catalog-id", async () => {
|
|
1753
|
+
const sdk = mockSdk();
|
|
1754
|
+
|
|
1755
|
+
const program = buildProgram();
|
|
1756
|
+
await program.parseAsync([
|
|
1757
|
+
"node",
|
|
1758
|
+
"test",
|
|
1759
|
+
"metadata",
|
|
1760
|
+
"5001",
|
|
1761
|
+
"--folder-id",
|
|
1762
|
+
"42",
|
|
1763
|
+
"--catalog-id",
|
|
1764
|
+
"9",
|
|
1765
|
+
]);
|
|
1766
|
+
|
|
1767
|
+
expect(sdk.tasks.editMetadata).toHaveBeenCalledWith(5001, {
|
|
1768
|
+
folderId: 42,
|
|
1769
|
+
taskCatalogId: 9,
|
|
1770
|
+
});
|
|
1771
|
+
});
|
|
1772
|
+
|
|
1773
|
+
it("should pass --unset-catalog", async () => {
|
|
1774
|
+
const sdk = mockSdk();
|
|
1775
|
+
|
|
1776
|
+
const program = buildProgram();
|
|
1777
|
+
await program.parseAsync([
|
|
1778
|
+
"node",
|
|
1779
|
+
"test",
|
|
1780
|
+
"metadata",
|
|
1781
|
+
"5001",
|
|
1782
|
+
"--folder-id",
|
|
1783
|
+
"42",
|
|
1784
|
+
"--unset-catalog",
|
|
1785
|
+
]);
|
|
1786
|
+
|
|
1787
|
+
expect(sdk.tasks.editMetadata).toHaveBeenCalledWith(5001, {
|
|
1788
|
+
folderId: 42,
|
|
1789
|
+
unlinkTaskCatalog: true,
|
|
1790
|
+
});
|
|
1791
|
+
});
|
|
1792
|
+
|
|
1793
|
+
it("should pass --note", async () => {
|
|
1794
|
+
const sdk = mockSdk();
|
|
1795
|
+
|
|
1796
|
+
const program = buildProgram();
|
|
1797
|
+
await program.parseAsync([
|
|
1798
|
+
"node",
|
|
1799
|
+
"test",
|
|
1800
|
+
"metadata",
|
|
1801
|
+
"5001",
|
|
1802
|
+
"--folder-id",
|
|
1803
|
+
"42",
|
|
1804
|
+
"--note",
|
|
1805
|
+
"reclassified",
|
|
1806
|
+
]);
|
|
1807
|
+
|
|
1808
|
+
expect(sdk.tasks.editMetadata).toHaveBeenCalledWith(5001, {
|
|
1809
|
+
folderId: 42,
|
|
1810
|
+
noteText: "reclassified",
|
|
1811
|
+
});
|
|
1812
|
+
});
|
|
1813
|
+
|
|
1814
|
+
it("should reject --catalog-id together with --unset-catalog", async () => {
|
|
1815
|
+
const sdk = mockSdk();
|
|
1816
|
+
|
|
1817
|
+
const program = buildProgram();
|
|
1818
|
+
await program.parseAsync([
|
|
1819
|
+
"node",
|
|
1820
|
+
"test",
|
|
1821
|
+
"metadata",
|
|
1822
|
+
"5001",
|
|
1823
|
+
"--folder-id",
|
|
1824
|
+
"42",
|
|
1825
|
+
"--catalog-id",
|
|
1826
|
+
"9",
|
|
1827
|
+
"--unset-catalog",
|
|
1828
|
+
]);
|
|
1829
|
+
|
|
1830
|
+
expect(sdk.tasks.editMetadata).not.toHaveBeenCalled();
|
|
1831
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1832
|
+
expect.objectContaining({
|
|
1833
|
+
Result: "Failure",
|
|
1834
|
+
Message:
|
|
1835
|
+
"--catalog-id and --unset-catalog cannot be used together",
|
|
1836
|
+
}),
|
|
1837
|
+
);
|
|
1838
|
+
});
|
|
1839
|
+
|
|
1840
|
+
it("should reject an edit with no metadata flags", async () => {
|
|
1841
|
+
const sdk = mockSdk();
|
|
1842
|
+
|
|
1843
|
+
const program = buildProgram();
|
|
1844
|
+
await program.parseAsync([
|
|
1845
|
+
"node",
|
|
1846
|
+
"test",
|
|
1847
|
+
"metadata",
|
|
1848
|
+
"5001",
|
|
1849
|
+
"--folder-id",
|
|
1850
|
+
"42",
|
|
1851
|
+
]);
|
|
1852
|
+
|
|
1853
|
+
expect(sdk.tasks.editMetadata).not.toHaveBeenCalled();
|
|
1854
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1855
|
+
expect.objectContaining({
|
|
1856
|
+
Result: "Failure",
|
|
1857
|
+
Message: "No metadata changes provided",
|
|
1858
|
+
}),
|
|
1859
|
+
);
|
|
1860
|
+
});
|
|
1861
|
+
|
|
1862
|
+
it("should reject an empty --priority instead of a no-op edit", async () => {
|
|
1863
|
+
const sdk = mockSdk();
|
|
1864
|
+
|
|
1865
|
+
const program = buildProgram();
|
|
1866
|
+
await program.parseAsync([
|
|
1867
|
+
"node",
|
|
1868
|
+
"test",
|
|
1869
|
+
"metadata",
|
|
1870
|
+
"5001",
|
|
1871
|
+
"--folder-id",
|
|
1872
|
+
"42",
|
|
1873
|
+
"--priority",
|
|
1874
|
+
"",
|
|
1875
|
+
]);
|
|
1876
|
+
|
|
1877
|
+
expect(sdk.tasks.editMetadata).not.toHaveBeenCalled();
|
|
1878
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1879
|
+
expect.objectContaining({ Message: "Invalid priority ''" }),
|
|
1880
|
+
);
|
|
1881
|
+
});
|
|
1882
|
+
|
|
1883
|
+
it("should reject an empty --catalog-id instead of a no-op edit", async () => {
|
|
1884
|
+
const sdk = mockSdk();
|
|
1885
|
+
|
|
1886
|
+
const program = buildProgram();
|
|
1887
|
+
await program.parseAsync([
|
|
1888
|
+
"node",
|
|
1889
|
+
"test",
|
|
1890
|
+
"metadata",
|
|
1891
|
+
"5001",
|
|
1892
|
+
"--folder-id",
|
|
1893
|
+
"42",
|
|
1894
|
+
"--catalog-id",
|
|
1895
|
+
"",
|
|
1896
|
+
]);
|
|
1897
|
+
|
|
1898
|
+
expect(sdk.tasks.editMetadata).not.toHaveBeenCalled();
|
|
1899
|
+
expect(OutputFormatter.error).toHaveBeenCalled();
|
|
1900
|
+
});
|
|
1901
|
+
});
|
|
1902
|
+
|
|
1903
|
+
describe("tasks data", () => {
|
|
1904
|
+
beforeEach(() => {
|
|
1905
|
+
vi.resetAllMocks();
|
|
1906
|
+
process.exitCode = undefined;
|
|
1907
|
+
});
|
|
1908
|
+
|
|
1909
|
+
it("should get task data", async () => {
|
|
1910
|
+
const taskData = { id: 5001, data: { amount: 1200 } };
|
|
1911
|
+
const sdk = mockSdk({
|
|
1912
|
+
getDataById: vi.fn().mockResolvedValue(taskData),
|
|
1913
|
+
});
|
|
1914
|
+
|
|
1915
|
+
const program = buildProgram();
|
|
1916
|
+
await program.parseAsync([
|
|
1917
|
+
"node",
|
|
1918
|
+
"test",
|
|
1919
|
+
"data",
|
|
1920
|
+
"get",
|
|
1921
|
+
"5001",
|
|
1922
|
+
"--folder-id",
|
|
1923
|
+
"42",
|
|
1924
|
+
]);
|
|
1925
|
+
|
|
1926
|
+
expect(sdk.tasks.getDataById).toHaveBeenCalledWith(5001, {
|
|
1927
|
+
folderId: 42,
|
|
1928
|
+
});
|
|
1929
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1930
|
+
expect.objectContaining({ Code: "TaskData", Data: taskData }),
|
|
1931
|
+
);
|
|
1932
|
+
});
|
|
1933
|
+
|
|
1934
|
+
it("should reject an invalid <task-id>", async () => {
|
|
1935
|
+
mockSdk();
|
|
1936
|
+
|
|
1937
|
+
const program = buildProgram();
|
|
1938
|
+
await program.parseAsync([
|
|
1939
|
+
"node",
|
|
1940
|
+
"test",
|
|
1941
|
+
"data",
|
|
1942
|
+
"get",
|
|
1943
|
+
"abc",
|
|
1944
|
+
"--folder-id",
|
|
1945
|
+
"42",
|
|
1946
|
+
]);
|
|
1947
|
+
|
|
1948
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1949
|
+
expect.objectContaining({ Message: "Invalid <task-id>: abc" }),
|
|
1950
|
+
);
|
|
1951
|
+
});
|
|
1952
|
+
|
|
1953
|
+
it("should propagate API errors", async () => {
|
|
1954
|
+
mockSdk({ getDataById: vi.fn().mockRejectedValue(new Error("nope")) });
|
|
1955
|
+
|
|
1956
|
+
const program = buildProgram();
|
|
1957
|
+
await program.parseAsync([
|
|
1958
|
+
"node",
|
|
1959
|
+
"test",
|
|
1960
|
+
"data",
|
|
1961
|
+
"get",
|
|
1962
|
+
"5001",
|
|
1963
|
+
"--folder-id",
|
|
1964
|
+
"42",
|
|
1965
|
+
]);
|
|
1966
|
+
|
|
1967
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1968
|
+
expect.objectContaining({
|
|
1969
|
+
Result: "Failure",
|
|
1970
|
+
Message: "Error getting data for task '5001'",
|
|
1971
|
+
}),
|
|
1972
|
+
);
|
|
1973
|
+
});
|
|
1974
|
+
});
|
|
1975
|
+
|
|
1976
|
+
describe("tasks save-data", () => {
|
|
1977
|
+
beforeEach(() => {
|
|
1978
|
+
vi.resetAllMocks();
|
|
1979
|
+
process.exitCode = undefined;
|
|
1980
|
+
});
|
|
1981
|
+
|
|
1982
|
+
it("should save task data", async () => {
|
|
1983
|
+
const sdk = mockSdk();
|
|
1984
|
+
|
|
1985
|
+
const program = buildProgram();
|
|
1986
|
+
await program.parseAsync([
|
|
1987
|
+
"node",
|
|
1988
|
+
"test",
|
|
1989
|
+
"data",
|
|
1990
|
+
"save",
|
|
1991
|
+
"5001",
|
|
1992
|
+
"--folder-id",
|
|
1993
|
+
"42",
|
|
1994
|
+
"--data",
|
|
1995
|
+
'{"amount":1200}',
|
|
1996
|
+
]);
|
|
1997
|
+
|
|
1998
|
+
expect(sdk.tasks.saveData).toHaveBeenCalledWith(
|
|
1999
|
+
5001,
|
|
2000
|
+
{ amount: 1200 },
|
|
2001
|
+
{ folderId: 42 },
|
|
2002
|
+
);
|
|
2003
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
2004
|
+
expect.objectContaining({ Code: "TaskDataSaved" }),
|
|
2005
|
+
);
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
it("should reject invalid JSON", async () => {
|
|
2009
|
+
const sdk = mockSdk();
|
|
2010
|
+
|
|
2011
|
+
const program = buildProgram();
|
|
2012
|
+
await program.parseAsync([
|
|
2013
|
+
"node",
|
|
2014
|
+
"test",
|
|
2015
|
+
"data",
|
|
2016
|
+
"save",
|
|
2017
|
+
"5001",
|
|
2018
|
+
"--folder-id",
|
|
2019
|
+
"42",
|
|
2020
|
+
"--data",
|
|
2021
|
+
"not-json",
|
|
2022
|
+
]);
|
|
2023
|
+
|
|
2024
|
+
expect(sdk.tasks.saveData).not.toHaveBeenCalled();
|
|
2025
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2026
|
+
expect.objectContaining({ Message: "Invalid JSON in --data" }),
|
|
2027
|
+
);
|
|
2028
|
+
});
|
|
2029
|
+
|
|
2030
|
+
it("should reject non-object data", async () => {
|
|
2031
|
+
const sdk = mockSdk();
|
|
2032
|
+
|
|
2033
|
+
const program = buildProgram();
|
|
2034
|
+
await program.parseAsync([
|
|
2035
|
+
"node",
|
|
2036
|
+
"test",
|
|
2037
|
+
"data",
|
|
2038
|
+
"save",
|
|
2039
|
+
"5001",
|
|
2040
|
+
"--folder-id",
|
|
2041
|
+
"42",
|
|
2042
|
+
"--data",
|
|
2043
|
+
"[1,2,3]",
|
|
2044
|
+
]);
|
|
2045
|
+
|
|
2046
|
+
expect(sdk.tasks.saveData).not.toHaveBeenCalled();
|
|
2047
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2048
|
+
expect.objectContaining({
|
|
2049
|
+
Message: "--data must be a JSON object",
|
|
2050
|
+
}),
|
|
2051
|
+
);
|
|
2052
|
+
});
|
|
2053
|
+
|
|
2054
|
+
it("should propagate API errors", async () => {
|
|
2055
|
+
mockSdk({ saveData: vi.fn().mockRejectedValue(new Error("nope")) });
|
|
2056
|
+
|
|
2057
|
+
const program = buildProgram();
|
|
2058
|
+
await program.parseAsync([
|
|
2059
|
+
"node",
|
|
2060
|
+
"test",
|
|
2061
|
+
"data",
|
|
2062
|
+
"save",
|
|
2063
|
+
"5001",
|
|
2064
|
+
"--folder-id",
|
|
2065
|
+
"42",
|
|
2066
|
+
"--data",
|
|
2067
|
+
"{}",
|
|
2068
|
+
]);
|
|
2069
|
+
|
|
2070
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2071
|
+
expect.objectContaining({
|
|
2072
|
+
Result: "Failure",
|
|
2073
|
+
Message: "Error saving data for task '5001'",
|
|
2074
|
+
}),
|
|
2075
|
+
);
|
|
2076
|
+
});
|
|
2077
|
+
});
|
|
2078
|
+
|
|
2079
|
+
describe("folder scope options", () => {
|
|
2080
|
+
const FOLDER_KEY = "12345678-1234-1234-1234-123456789abc";
|
|
2081
|
+
const originalTTY = process.stdout.isTTY;
|
|
2082
|
+
|
|
2083
|
+
beforeEach(() => {
|
|
2084
|
+
vi.resetAllMocks();
|
|
2085
|
+
process.exitCode = undefined;
|
|
2086
|
+
});
|
|
2087
|
+
|
|
2088
|
+
afterEach(() => {
|
|
2089
|
+
process.stdout.isTTY = originalTTY;
|
|
2090
|
+
});
|
|
2091
|
+
|
|
2092
|
+
it("should register --folder-path and --folder-key on every folder-scoped command", () => {
|
|
2093
|
+
const program = buildProgram();
|
|
2094
|
+
const folderScoped: Array<[string, string | undefined]> = [
|
|
2095
|
+
["list", undefined],
|
|
2096
|
+
["get", undefined],
|
|
2097
|
+
["complete", undefined],
|
|
2098
|
+
["users", undefined],
|
|
2099
|
+
["labels", undefined],
|
|
2100
|
+
["metadata", undefined],
|
|
2101
|
+
["catalogs", "list"],
|
|
2102
|
+
["catalogs", "get"],
|
|
2103
|
+
["catalogs", "create"],
|
|
2104
|
+
["catalogs", "update"],
|
|
2105
|
+
["comments", "list"],
|
|
2106
|
+
["comments", "add"],
|
|
2107
|
+
["data", "get"],
|
|
2108
|
+
["data", "save"],
|
|
2109
|
+
];
|
|
2110
|
+
|
|
2111
|
+
for (const [name, subName] of folderScoped) {
|
|
2112
|
+
let command = program.commands.find((c) => c.name() === name);
|
|
2113
|
+
if (subName) {
|
|
2114
|
+
command = command?.commands.find((c) => c.name() === subName);
|
|
2115
|
+
}
|
|
2116
|
+
const flags = command?.options.map((o) => o.long) ?? [];
|
|
2117
|
+
expect(flags, `${name} ${subName ?? ""}`).toContain(
|
|
2118
|
+
"--folder-path",
|
|
2119
|
+
);
|
|
2120
|
+
expect(flags, `${name} ${subName ?? ""}`).toContain("--folder-key");
|
|
2121
|
+
}
|
|
2122
|
+
});
|
|
2123
|
+
|
|
2124
|
+
it("should reject --folder-path combined with --folder-id at parse time", async () => {
|
|
2125
|
+
const program = buildProgram();
|
|
2126
|
+
await expect(
|
|
2127
|
+
program.parseAsync([
|
|
2128
|
+
"node",
|
|
2129
|
+
"test",
|
|
2130
|
+
"list",
|
|
2131
|
+
"--folder-id",
|
|
2132
|
+
"42",
|
|
2133
|
+
"--folder-path",
|
|
2134
|
+
"Shared",
|
|
2135
|
+
]),
|
|
2136
|
+
).rejects.toThrow(/cannot be used with/);
|
|
2137
|
+
expect(createTasksClient).not.toHaveBeenCalled();
|
|
2138
|
+
});
|
|
2139
|
+
|
|
2140
|
+
it("should reject --folder-path combined with --folder-key at parse time", async () => {
|
|
2141
|
+
const program = buildProgram();
|
|
2142
|
+
await expect(
|
|
2143
|
+
program.parseAsync([
|
|
2144
|
+
"node",
|
|
2145
|
+
"test",
|
|
2146
|
+
"list",
|
|
2147
|
+
"--folder-path",
|
|
2148
|
+
"Shared",
|
|
2149
|
+
"--folder-key",
|
|
2150
|
+
FOLDER_KEY,
|
|
2151
|
+
]),
|
|
2152
|
+
).rejects.toThrow(/cannot be used with/);
|
|
2153
|
+
expect(createTasksClient).not.toHaveBeenCalled();
|
|
2154
|
+
});
|
|
2155
|
+
|
|
2156
|
+
it("should reject --folder-key combined with --folder-id at parse time", async () => {
|
|
2157
|
+
const program = buildProgram();
|
|
2158
|
+
await expect(
|
|
2159
|
+
program.parseAsync([
|
|
2160
|
+
"node",
|
|
2161
|
+
"test",
|
|
2162
|
+
"list",
|
|
2163
|
+
"--folder-id",
|
|
2164
|
+
"42",
|
|
2165
|
+
"--folder-key",
|
|
2166
|
+
FOLDER_KEY,
|
|
2167
|
+
]),
|
|
2168
|
+
).rejects.toThrow(/cannot be used with/);
|
|
2169
|
+
expect(createTasksClient).not.toHaveBeenCalled();
|
|
2170
|
+
});
|
|
2171
|
+
|
|
2172
|
+
it("should resolve --folder-key for list and pass the numeric id", async () => {
|
|
2173
|
+
const sdk = mockSdk();
|
|
2174
|
+
vi.mocked(resolveFolderIdByKey).mockResolvedValue(55);
|
|
2175
|
+
|
|
2176
|
+
const program = buildProgram();
|
|
2177
|
+
await program.parseAsync([
|
|
2178
|
+
"node",
|
|
2179
|
+
"test",
|
|
2180
|
+
"list",
|
|
2181
|
+
"--folder-key",
|
|
2182
|
+
FOLDER_KEY,
|
|
2183
|
+
]);
|
|
2184
|
+
|
|
2185
|
+
expect(resolveFolderIdByKey).toHaveBeenCalledWith(
|
|
2186
|
+
FOLDER_KEY,
|
|
2187
|
+
undefined,
|
|
2188
|
+
);
|
|
2189
|
+
expect(sdk.tasks.getAll).toHaveBeenCalledWith(
|
|
2190
|
+
expect.objectContaining({ folderId: 55 }),
|
|
2191
|
+
);
|
|
2192
|
+
});
|
|
2193
|
+
|
|
2194
|
+
it("should resolve --folder-path for list and pass the numeric id", async () => {
|
|
2195
|
+
const sdk = mockSdk();
|
|
2196
|
+
vi.mocked(resolveFolderIdByPath).mockResolvedValue(77);
|
|
2197
|
+
|
|
2198
|
+
const program = buildProgram();
|
|
2199
|
+
await program.parseAsync([
|
|
2200
|
+
"node",
|
|
2201
|
+
"test",
|
|
2202
|
+
"list",
|
|
2203
|
+
"--folder-path",
|
|
2204
|
+
"Shared/Finance",
|
|
2205
|
+
]);
|
|
2206
|
+
|
|
2207
|
+
expect(resolveFolderIdByPath).toHaveBeenCalledWith(
|
|
2208
|
+
"Shared/Finance",
|
|
2209
|
+
undefined,
|
|
2210
|
+
);
|
|
2211
|
+
expect(sdk.tasks.getAll).toHaveBeenCalledWith(
|
|
2212
|
+
expect.objectContaining({ folderId: 77 }),
|
|
2213
|
+
);
|
|
2214
|
+
});
|
|
2215
|
+
|
|
2216
|
+
it("should stop list without calling the API when resolution fails", async () => {
|
|
2217
|
+
const sdk = mockSdk();
|
|
2218
|
+
vi.mocked(resolveFolderIdByKey).mockResolvedValue(null);
|
|
2219
|
+
|
|
2220
|
+
const program = buildProgram();
|
|
2221
|
+
await program.parseAsync([
|
|
2222
|
+
"node",
|
|
2223
|
+
"test",
|
|
2224
|
+
"list",
|
|
2225
|
+
"--folder-key",
|
|
2226
|
+
FOLDER_KEY,
|
|
2227
|
+
]);
|
|
2228
|
+
|
|
2229
|
+
expect(sdk.tasks.getAll).not.toHaveBeenCalled();
|
|
2230
|
+
});
|
|
2231
|
+
|
|
2232
|
+
it("should accept --task-type with --folder-path on get", async () => {
|
|
2233
|
+
const sdk = mockSdk();
|
|
2234
|
+
vi.mocked(resolveFolderIdByPath).mockResolvedValue(77);
|
|
2235
|
+
|
|
2236
|
+
const program = buildProgram();
|
|
2237
|
+
await program.parseAsync([
|
|
2238
|
+
"node",
|
|
2239
|
+
"test",
|
|
2240
|
+
"get",
|
|
2241
|
+
"123",
|
|
2242
|
+
"--task-type",
|
|
2243
|
+
"FormTask",
|
|
2244
|
+
"--folder-path",
|
|
2245
|
+
"Shared",
|
|
2246
|
+
]);
|
|
2247
|
+
|
|
2248
|
+
expect(sdk.tasks.getById).toHaveBeenCalledWith(
|
|
2249
|
+
123,
|
|
2250
|
+
expect.objectContaining({ taskType: "FormTask" }),
|
|
2251
|
+
77,
|
|
2252
|
+
);
|
|
2253
|
+
});
|
|
2254
|
+
|
|
2255
|
+
it("should reject --task-type on get without any folder option", async () => {
|
|
2256
|
+
mockSdk();
|
|
2257
|
+
|
|
2258
|
+
const program = buildProgram();
|
|
2259
|
+
await program.parseAsync([
|
|
2260
|
+
"node",
|
|
2261
|
+
"test",
|
|
2262
|
+
"get",
|
|
2263
|
+
"123",
|
|
2264
|
+
"--task-type",
|
|
2265
|
+
"FormTask",
|
|
2266
|
+
]);
|
|
2267
|
+
|
|
2268
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2269
|
+
expect.objectContaining({
|
|
2270
|
+
Message: "A folder is required when --task-type is specified",
|
|
2271
|
+
}),
|
|
2272
|
+
);
|
|
2273
|
+
expect(process.exitCode).toBe(1);
|
|
2274
|
+
});
|
|
2275
|
+
|
|
2276
|
+
it("should resolve --folder-path for complete", async () => {
|
|
2277
|
+
const sdk = mockSdk();
|
|
2278
|
+
vi.mocked(resolveFolderIdByPath).mockResolvedValue(88);
|
|
2279
|
+
|
|
2280
|
+
const program = buildProgram();
|
|
2281
|
+
await program.parseAsync([
|
|
2282
|
+
"node",
|
|
2283
|
+
"test",
|
|
2284
|
+
"complete",
|
|
2285
|
+
"5",
|
|
2286
|
+
"--type",
|
|
2287
|
+
"FormTask",
|
|
2288
|
+
"--folder-path",
|
|
2289
|
+
"Shared",
|
|
2290
|
+
]);
|
|
2291
|
+
|
|
2292
|
+
expect(sdk.tasks.complete).toHaveBeenCalledWith(
|
|
2293
|
+
expect.objectContaining({ taskId: 5, type: "FormTask" }),
|
|
2294
|
+
88,
|
|
2295
|
+
);
|
|
2296
|
+
});
|
|
2297
|
+
|
|
2298
|
+
it("should fail loud on complete without a folder when not interactive", async () => {
|
|
2299
|
+
process.stdout.isTTY = false;
|
|
2300
|
+
const sdk = mockSdk();
|
|
2301
|
+
|
|
2302
|
+
const program = buildProgram();
|
|
2303
|
+
await program.parseAsync([
|
|
2304
|
+
"node",
|
|
2305
|
+
"test",
|
|
2306
|
+
"complete",
|
|
2307
|
+
"5",
|
|
2308
|
+
"--type",
|
|
2309
|
+
"FormTask",
|
|
2310
|
+
]);
|
|
2311
|
+
|
|
2312
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2313
|
+
expect.objectContaining({ Message: "A folder is required" }),
|
|
2314
|
+
);
|
|
2315
|
+
expect(process.exitCode).toBe(1);
|
|
2316
|
+
expect(sdk.tasks.complete).not.toHaveBeenCalled();
|
|
2317
|
+
});
|
|
2318
|
+
|
|
2319
|
+
it("should resolve --folder-path for users", async () => {
|
|
2320
|
+
const sdk = mockSdk();
|
|
2321
|
+
vi.mocked(resolveFolderIdByPath).mockResolvedValue(99);
|
|
2322
|
+
|
|
2323
|
+
const program = buildProgram();
|
|
2324
|
+
await program.parseAsync([
|
|
2325
|
+
"node",
|
|
2326
|
+
"test",
|
|
2327
|
+
"users",
|
|
2328
|
+
"--folder-path",
|
|
2329
|
+
"Shared/Finance",
|
|
2330
|
+
]);
|
|
2331
|
+
|
|
2332
|
+
expect(sdk.tasks.getUsers).toHaveBeenCalledWith(
|
|
2333
|
+
99,
|
|
2334
|
+
expect.objectContaining({ pageSize: 100 }),
|
|
2335
|
+
);
|
|
2336
|
+
});
|
|
2337
|
+
|
|
2338
|
+
it("should reject the users folder-id argument combined with --folder-key", async () => {
|
|
2339
|
+
const sdk = mockSdk();
|
|
2340
|
+
|
|
2341
|
+
const program = buildProgram();
|
|
2342
|
+
await program.parseAsync([
|
|
2343
|
+
"node",
|
|
2344
|
+
"test",
|
|
2345
|
+
"users",
|
|
2346
|
+
"42",
|
|
2347
|
+
"--folder-key",
|
|
2348
|
+
FOLDER_KEY,
|
|
2349
|
+
]);
|
|
2350
|
+
|
|
2351
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2352
|
+
expect.objectContaining({
|
|
2353
|
+
Message:
|
|
2354
|
+
"The folder-id argument cannot be used with --folder-path or --folder-key",
|
|
2355
|
+
}),
|
|
2356
|
+
);
|
|
2357
|
+
expect(process.exitCode).toBe(1);
|
|
2358
|
+
expect(sdk.tasks.getUsers).not.toHaveBeenCalled();
|
|
2359
|
+
});
|
|
2360
|
+
|
|
2361
|
+
it("should reject --folder-path combined with --folder-key on users at parse time", async () => {
|
|
2362
|
+
const program = buildProgram();
|
|
2363
|
+
await expect(
|
|
2364
|
+
program.parseAsync([
|
|
2365
|
+
"node",
|
|
2366
|
+
"test",
|
|
2367
|
+
"users",
|
|
2368
|
+
"--folder-path",
|
|
2369
|
+
"Shared",
|
|
2370
|
+
"--folder-key",
|
|
2371
|
+
FOLDER_KEY,
|
|
2372
|
+
]),
|
|
2373
|
+
).rejects.toThrow(/cannot be used with/);
|
|
2374
|
+
expect(createTasksClient).not.toHaveBeenCalled();
|
|
2375
|
+
});
|
|
2376
|
+
|
|
2377
|
+
it("should fail loud on users without a folder when not interactive", async () => {
|
|
2378
|
+
process.stdout.isTTY = false;
|
|
2379
|
+
const sdk = mockSdk();
|
|
2380
|
+
|
|
2381
|
+
const program = buildProgram();
|
|
2382
|
+
await program.parseAsync(["node", "test", "users"]);
|
|
2383
|
+
|
|
2384
|
+
// The guidance must name the positional argument, not --folder-id,
|
|
2385
|
+
// which this command does not register.
|
|
2386
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
2387
|
+
expect.objectContaining({
|
|
2388
|
+
Message: "A folder is required",
|
|
2389
|
+
Instructions: expect.stringContaining("folder-id argument"),
|
|
2390
|
+
}),
|
|
2391
|
+
);
|
|
2392
|
+
expect(process.exitCode).toBe(1);
|
|
2393
|
+
expect(sdk.tasks.getUsers).not.toHaveBeenCalled();
|
|
2394
|
+
});
|
|
2395
|
+
|
|
2396
|
+
it("should resolve --folder-key for catalogs list", async () => {
|
|
2397
|
+
mockSdk();
|
|
2398
|
+
const catalogs = mockCatalogs();
|
|
2399
|
+
vi.mocked(resolveFolderIdByKey).mockResolvedValue(66);
|
|
2400
|
+
|
|
2401
|
+
const program = buildProgram();
|
|
2402
|
+
await program.parseAsync([
|
|
2403
|
+
"node",
|
|
2404
|
+
"test",
|
|
2405
|
+
"catalogs",
|
|
2406
|
+
"list",
|
|
2407
|
+
"--folder-key",
|
|
2408
|
+
FOLDER_KEY,
|
|
2409
|
+
]);
|
|
2410
|
+
|
|
2411
|
+
expect(resolveFolderIdByKey).toHaveBeenCalledWith(
|
|
2412
|
+
FOLDER_KEY,
|
|
2413
|
+
undefined,
|
|
2414
|
+
);
|
|
2415
|
+
expect(catalogs.getAll).toHaveBeenCalledWith(
|
|
2416
|
+
expect.objectContaining({ folderId: 66 }),
|
|
2417
|
+
);
|
|
2418
|
+
});
|
|
2419
|
+
});
|