kiwivm-cli 0.1.0 → 0.2.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/README.md +62 -36
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +370 -50
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/commands/admin.test.ts +135 -22
- package/src/commands/admin.ts +57 -19
- package/src/commands/backup.test.ts +26 -23
- package/src/commands/backup.ts +13 -15
- package/src/commands/help.test.ts +27 -7
- package/src/commands/help.ts +61 -26
- package/src/commands/info.test.ts +47 -43
- package/src/commands/info.ts +11 -13
- package/src/commands/iso.test.ts +58 -0
- package/src/commands/iso.ts +21 -0
- package/src/commands/migrate.test.ts +105 -0
- package/src/commands/migrate.ts +38 -0
- package/src/commands/network.test.ts +107 -30
- package/src/commands/network.ts +56 -18
- package/src/commands/power.test.ts +58 -40
- package/src/commands/power.ts +27 -16
- package/src/commands/shell.test.ts +66 -0
- package/src/commands/shell.ts +25 -0
- package/src/commands/snapshot.test.ts +141 -71
- package/src/commands/snapshot.ts +85 -33
- package/src/commands/stats.test.ts +81 -0
- package/src/commands/stats.ts +25 -0
- package/src/commands/system.test.ts +109 -40
- package/src/commands/system.ts +55 -23
- package/src/index.test.ts +435 -148
- package/src/index.ts +129 -57
- package/src/types.ts +57 -1
- package/dist/admin-fOud1ZmX.mjs +0 -15
- package/dist/admin-fOud1ZmX.mjs.map +0 -1
- package/dist/backup-D1UJ4aap.mjs +0 -12
- package/dist/backup-D1UJ4aap.mjs.map +0 -1
- package/dist/help-Dk-WApoi.mjs +0 -40
- package/dist/help-Dk-WApoi.mjs.map +0 -1
- package/dist/info-DKExtFYH.mjs +0 -13
- package/dist/info-DKExtFYH.mjs.map +0 -1
- package/dist/monitoring-BSuv8fj9.mjs +0 -13
- package/dist/monitoring-BSuv8fj9.mjs.map +0 -1
- package/dist/network-1ycEIJqT.mjs +0 -15
- package/dist/network-1ycEIJqT.mjs.map +0 -1
- package/dist/power-CDg0Mx1A.mjs +0 -14
- package/dist/power-CDg0Mx1A.mjs.map +0 -1
- package/dist/snapshot-LO_ufoj5.mjs +0 -23
- package/dist/snapshot-LO_ufoj5.mjs.map +0 -1
- package/dist/system-Bl-dsqX9.mjs +0 -21
- package/dist/system-Bl-dsqX9.mjs.map +0 -1
- package/src/commands/monitoring.test.ts +0 -82
- package/src/commands/monitoring.ts +0 -20
package/src/index.test.ts
CHANGED
|
@@ -3,35 +3,167 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
3
3
|
// All mock variables must be created inside vi.hoisted() so they are
|
|
4
4
|
// initialized before the hoisted vi.mock() factories reference them.
|
|
5
5
|
const {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
mockPowerStart,
|
|
7
|
+
mockPowerStop,
|
|
8
|
+
mockPowerRestart,
|
|
9
|
+
mockPowerKill,
|
|
10
|
+
mockInfoInfo,
|
|
11
|
+
mockInfoStatus,
|
|
12
|
+
mockSnapshotList,
|
|
13
|
+
mockSnapshotCreate,
|
|
14
|
+
mockSnapshotDelete,
|
|
15
|
+
mockSnapshotRestore,
|
|
16
|
+
mockSnapshotSticky,
|
|
17
|
+
mockSnapshotExport,
|
|
18
|
+
mockSnapshotImport,
|
|
19
|
+
mockBackupList,
|
|
20
|
+
mockBackupCopy,
|
|
21
|
+
mockSystemHostname,
|
|
22
|
+
mockSystemPassword,
|
|
23
|
+
mockSystemOsList,
|
|
24
|
+
mockSystemOsReinstall,
|
|
25
|
+
mockSystemSshKeyShow,
|
|
26
|
+
mockSystemSshKeySet,
|
|
27
|
+
mockNetworkRdnsSet,
|
|
28
|
+
mockNetworkIpv6Add,
|
|
29
|
+
mockNetworkIpv6Delete,
|
|
30
|
+
mockNetworkPrivateIpList,
|
|
31
|
+
mockNetworkPrivateIpAssign,
|
|
32
|
+
mockNetworkPrivateIpDelete,
|
|
33
|
+
mockIsoMount,
|
|
34
|
+
mockIsoUnmount,
|
|
35
|
+
mockShellExec,
|
|
36
|
+
mockShellScript,
|
|
37
|
+
mockMigrateLocations,
|
|
38
|
+
mockMigrateStart,
|
|
39
|
+
mockMigrateClone,
|
|
40
|
+
mockStatsUsage,
|
|
41
|
+
mockStatsAudit,
|
|
42
|
+
mockStatsRateLimit,
|
|
43
|
+
mockAdminSuspensions,
|
|
44
|
+
mockAdminUnsuspend,
|
|
45
|
+
mockAdminViolationsList,
|
|
46
|
+
mockAdminViolationsResolve,
|
|
47
|
+
mockAdminNotificationsGet,
|
|
48
|
+
mockAdminNotificationsSet,
|
|
49
|
+
mockHelpRun,
|
|
14
50
|
mockClientConstructor,
|
|
15
51
|
} = vi.hoisted(() => ({
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
52
|
+
mockPowerStart: vi.fn(),
|
|
53
|
+
mockPowerStop: vi.fn(),
|
|
54
|
+
mockPowerRestart: vi.fn(),
|
|
55
|
+
mockPowerKill: vi.fn(),
|
|
56
|
+
mockInfoInfo: vi.fn(),
|
|
57
|
+
mockInfoStatus: vi.fn(),
|
|
58
|
+
mockSnapshotList: vi.fn(),
|
|
59
|
+
mockSnapshotCreate: vi.fn(),
|
|
60
|
+
mockSnapshotDelete: vi.fn(),
|
|
61
|
+
mockSnapshotRestore: vi.fn(),
|
|
62
|
+
mockSnapshotSticky: vi.fn(),
|
|
63
|
+
mockSnapshotExport: vi.fn(),
|
|
64
|
+
mockSnapshotImport: vi.fn(),
|
|
65
|
+
mockBackupList: vi.fn(),
|
|
66
|
+
mockBackupCopy: vi.fn(),
|
|
67
|
+
mockSystemHostname: vi.fn(),
|
|
68
|
+
mockSystemPassword: vi.fn(),
|
|
69
|
+
mockSystemOsList: vi.fn(),
|
|
70
|
+
mockSystemOsReinstall: vi.fn(),
|
|
71
|
+
mockSystemSshKeyShow: vi.fn(),
|
|
72
|
+
mockSystemSshKeySet: vi.fn(),
|
|
73
|
+
mockNetworkRdnsSet: vi.fn(),
|
|
74
|
+
mockNetworkIpv6Add: vi.fn(),
|
|
75
|
+
mockNetworkIpv6Delete: vi.fn(),
|
|
76
|
+
mockNetworkPrivateIpList: vi.fn(),
|
|
77
|
+
mockNetworkPrivateIpAssign: vi.fn(),
|
|
78
|
+
mockNetworkPrivateIpDelete: vi.fn(),
|
|
79
|
+
mockIsoMount: vi.fn(),
|
|
80
|
+
mockIsoUnmount: vi.fn(),
|
|
81
|
+
mockShellExec: vi.fn(),
|
|
82
|
+
mockShellScript: vi.fn(),
|
|
83
|
+
mockMigrateLocations: vi.fn(),
|
|
84
|
+
mockMigrateStart: vi.fn(),
|
|
85
|
+
mockMigrateClone: vi.fn(),
|
|
86
|
+
mockStatsUsage: vi.fn(),
|
|
87
|
+
mockStatsAudit: vi.fn(),
|
|
88
|
+
mockStatsRateLimit: vi.fn(),
|
|
89
|
+
mockAdminSuspensions: vi.fn(),
|
|
90
|
+
mockAdminUnsuspend: vi.fn(),
|
|
91
|
+
mockAdminViolationsList: vi.fn(),
|
|
92
|
+
mockAdminViolationsResolve: vi.fn(),
|
|
93
|
+
mockAdminNotificationsGet: vi.fn(),
|
|
94
|
+
mockAdminNotificationsSet: vi.fn(),
|
|
95
|
+
mockHelpRun: vi.fn(),
|
|
24
96
|
mockClientConstructor: vi.fn(),
|
|
25
97
|
}));
|
|
26
98
|
|
|
27
|
-
vi.mock("./commands/power.ts", () => ({
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
vi.mock("./commands/
|
|
34
|
-
|
|
99
|
+
vi.mock("./commands/power.ts", () => ({
|
|
100
|
+
start: mockPowerStart,
|
|
101
|
+
stop: mockPowerStop,
|
|
102
|
+
restart: mockPowerRestart,
|
|
103
|
+
kill: mockPowerKill,
|
|
104
|
+
}));
|
|
105
|
+
vi.mock("./commands/info.ts", () => ({
|
|
106
|
+
info: mockInfoInfo,
|
|
107
|
+
status: mockInfoStatus,
|
|
108
|
+
}));
|
|
109
|
+
vi.mock("./commands/snapshot.ts", () => ({
|
|
110
|
+
list: mockSnapshotList,
|
|
111
|
+
create: mockSnapshotCreate,
|
|
112
|
+
deleteSnapshot: mockSnapshotDelete,
|
|
113
|
+
restore: mockSnapshotRestore,
|
|
114
|
+
sticky: mockSnapshotSticky,
|
|
115
|
+
exportSnapshot: mockSnapshotExport,
|
|
116
|
+
importSnapshot: mockSnapshotImport,
|
|
117
|
+
}));
|
|
118
|
+
vi.mock("./commands/backup.ts", () => ({
|
|
119
|
+
list: mockBackupList,
|
|
120
|
+
copy: mockBackupCopy,
|
|
121
|
+
}));
|
|
122
|
+
vi.mock("./commands/system.ts", () => ({
|
|
123
|
+
hostname: mockSystemHostname,
|
|
124
|
+
password: mockSystemPassword,
|
|
125
|
+
osList: mockSystemOsList,
|
|
126
|
+
osReinstall: mockSystemOsReinstall,
|
|
127
|
+
sshKeyShow: mockSystemSshKeyShow,
|
|
128
|
+
sshKeySet: mockSystemSshKeySet,
|
|
129
|
+
}));
|
|
130
|
+
vi.mock("./commands/network.ts", () => ({
|
|
131
|
+
rdnsSet: mockNetworkRdnsSet,
|
|
132
|
+
ipv6Add: mockNetworkIpv6Add,
|
|
133
|
+
ipv6Delete: mockNetworkIpv6Delete,
|
|
134
|
+
privateIpList: mockNetworkPrivateIpList,
|
|
135
|
+
privateIpAssign: mockNetworkPrivateIpAssign,
|
|
136
|
+
privateIpDelete: mockNetworkPrivateIpDelete,
|
|
137
|
+
}));
|
|
138
|
+
vi.mock("./commands/iso.ts", () => ({
|
|
139
|
+
mount: mockIsoMount,
|
|
140
|
+
unmount: mockIsoUnmount,
|
|
141
|
+
}));
|
|
142
|
+
vi.mock("./commands/shell.ts", () => ({
|
|
143
|
+
exec: mockShellExec,
|
|
144
|
+
script: mockShellScript,
|
|
145
|
+
}));
|
|
146
|
+
vi.mock("./commands/migrate.ts", () => ({
|
|
147
|
+
locations: mockMigrateLocations,
|
|
148
|
+
migrateStart: mockMigrateStart,
|
|
149
|
+
clone: mockMigrateClone,
|
|
150
|
+
}));
|
|
151
|
+
vi.mock("./commands/stats.ts", () => ({
|
|
152
|
+
usage: mockStatsUsage,
|
|
153
|
+
audit: mockStatsAudit,
|
|
154
|
+
rateLimit: mockStatsRateLimit,
|
|
155
|
+
}));
|
|
156
|
+
vi.mock("./commands/admin.ts", () => ({
|
|
157
|
+
suspensions: mockAdminSuspensions,
|
|
158
|
+
unsuspend: mockAdminUnsuspend,
|
|
159
|
+
violationsList: mockAdminViolationsList,
|
|
160
|
+
violationsResolve: mockAdminViolationsResolve,
|
|
161
|
+
notificationsGet: mockAdminNotificationsGet,
|
|
162
|
+
notificationsSet: mockAdminNotificationsSet,
|
|
163
|
+
}));
|
|
164
|
+
vi.mock("./commands/help.ts", () => ({
|
|
165
|
+
run: mockHelpRun,
|
|
166
|
+
}));
|
|
35
167
|
vi.mock("./client.ts", () => ({
|
|
36
168
|
KiwiVMClient: mockClientConstructor,
|
|
37
169
|
}));
|
|
@@ -53,206 +185,354 @@ describe("CLI dispatcher", () => {
|
|
|
53
185
|
vi.unstubAllGlobals();
|
|
54
186
|
});
|
|
55
187
|
|
|
56
|
-
// ----
|
|
188
|
+
// ---- Flat command dispatch ----
|
|
57
189
|
|
|
58
|
-
it("dispatches '
|
|
59
|
-
setArgv("
|
|
60
|
-
|
|
190
|
+
it("dispatches 'start' to power.start", async () => {
|
|
191
|
+
setArgv("start");
|
|
192
|
+
mockPowerStart.mockResolvedValueOnce({ error: 0 });
|
|
61
193
|
|
|
62
194
|
await main();
|
|
63
195
|
|
|
64
|
-
expect(
|
|
65
|
-
|
|
196
|
+
expect(mockPowerStart).toHaveBeenCalledExactlyOnceWith(
|
|
197
|
+
[],
|
|
66
198
|
{},
|
|
67
199
|
expect.anything(),
|
|
68
200
|
);
|
|
69
201
|
});
|
|
70
202
|
|
|
71
|
-
it("dispatches '
|
|
72
|
-
setArgv("
|
|
73
|
-
|
|
203
|
+
it("dispatches 'stop' to power.stop", async () => {
|
|
204
|
+
setArgv("stop");
|
|
205
|
+
mockPowerStop.mockResolvedValueOnce({ error: 0 });
|
|
74
206
|
|
|
75
207
|
await main();
|
|
76
208
|
|
|
77
|
-
expect(
|
|
78
|
-
|
|
209
|
+
expect(mockPowerStop).toHaveBeenCalledExactlyOnceWith(
|
|
210
|
+
[],
|
|
79
211
|
{},
|
|
80
212
|
expect.anything(),
|
|
81
213
|
);
|
|
82
214
|
});
|
|
83
215
|
|
|
84
|
-
it("dispatches '
|
|
85
|
-
setArgv("
|
|
86
|
-
|
|
216
|
+
it("dispatches 'restart' to power.restart", async () => {
|
|
217
|
+
setArgv("restart");
|
|
218
|
+
mockPowerRestart.mockResolvedValueOnce({ error: 0 });
|
|
87
219
|
|
|
88
220
|
await main();
|
|
89
221
|
|
|
90
|
-
expect(
|
|
91
|
-
|
|
222
|
+
expect(mockPowerRestart).toHaveBeenCalledExactlyOnceWith(
|
|
223
|
+
[],
|
|
92
224
|
{},
|
|
93
225
|
expect.anything(),
|
|
94
226
|
);
|
|
95
227
|
});
|
|
96
228
|
|
|
97
|
-
it("dispatches '
|
|
98
|
-
setArgv("
|
|
99
|
-
|
|
229
|
+
it("dispatches 'kill' to power.kill", async () => {
|
|
230
|
+
setArgv("kill");
|
|
231
|
+
mockPowerKill.mockResolvedValueOnce({ error: 0 });
|
|
100
232
|
|
|
101
233
|
await main();
|
|
102
234
|
|
|
103
|
-
expect(
|
|
104
|
-
|
|
235
|
+
expect(mockPowerKill).toHaveBeenCalledExactlyOnceWith(
|
|
236
|
+
[],
|
|
105
237
|
{},
|
|
106
238
|
expect.anything(),
|
|
107
239
|
);
|
|
108
240
|
});
|
|
109
241
|
|
|
110
|
-
it("dispatches '
|
|
111
|
-
setArgv("
|
|
112
|
-
|
|
242
|
+
it("dispatches 'info' to info.info", async () => {
|
|
243
|
+
setArgv("info");
|
|
244
|
+
mockInfoInfo.mockResolvedValueOnce({ error: 0 });
|
|
113
245
|
|
|
114
246
|
await main();
|
|
115
247
|
|
|
116
|
-
expect(
|
|
117
|
-
|
|
248
|
+
expect(mockInfoInfo).toHaveBeenCalledExactlyOnceWith(
|
|
249
|
+
[],
|
|
118
250
|
{},
|
|
119
251
|
expect.anything(),
|
|
120
252
|
);
|
|
121
253
|
});
|
|
122
254
|
|
|
123
|
-
it("dispatches '
|
|
124
|
-
setArgv("
|
|
125
|
-
|
|
255
|
+
it("dispatches 'status' to info.status", async () => {
|
|
256
|
+
setArgv("status");
|
|
257
|
+
mockInfoStatus.mockResolvedValueOnce({ error: 0 });
|
|
126
258
|
|
|
127
259
|
await main();
|
|
128
260
|
|
|
129
|
-
expect(
|
|
130
|
-
|
|
261
|
+
expect(mockInfoStatus).toHaveBeenCalledExactlyOnceWith(
|
|
262
|
+
[],
|
|
131
263
|
{},
|
|
132
264
|
expect.anything(),
|
|
133
265
|
);
|
|
134
266
|
});
|
|
135
267
|
|
|
136
|
-
it("dispatches '
|
|
137
|
-
setArgv("
|
|
138
|
-
|
|
268
|
+
it("dispatches 'hostname' with args to system.hostname", async () => {
|
|
269
|
+
setArgv("hostname", "my-vps");
|
|
270
|
+
mockSystemHostname.mockResolvedValueOnce({ error: 0 });
|
|
139
271
|
|
|
140
272
|
await main();
|
|
141
273
|
|
|
142
|
-
expect(
|
|
143
|
-
"
|
|
274
|
+
expect(mockSystemHostname).toHaveBeenCalledExactlyOnceWith(
|
|
275
|
+
["my-vps"],
|
|
144
276
|
{},
|
|
145
277
|
expect.anything(),
|
|
146
278
|
);
|
|
147
279
|
});
|
|
148
280
|
|
|
149
|
-
it("dispatches '
|
|
150
|
-
setArgv("
|
|
151
|
-
|
|
281
|
+
it("dispatches 'password' to system.password", async () => {
|
|
282
|
+
setArgv("password");
|
|
283
|
+
mockSystemPassword.mockResolvedValueOnce({ error: 0 });
|
|
152
284
|
|
|
153
285
|
await main();
|
|
154
286
|
|
|
155
|
-
expect(
|
|
156
|
-
|
|
287
|
+
expect(mockSystemPassword).toHaveBeenCalledExactlyOnceWith(
|
|
288
|
+
[],
|
|
157
289
|
{},
|
|
158
290
|
expect.anything(),
|
|
159
291
|
);
|
|
160
292
|
});
|
|
161
293
|
|
|
162
|
-
|
|
294
|
+
it("dispatches 'suspensions' to admin.suspensions", async () => {
|
|
295
|
+
setArgv("suspensions");
|
|
296
|
+
mockAdminSuspensions.mockResolvedValueOnce({ error: 0 });
|
|
297
|
+
|
|
298
|
+
await main();
|
|
299
|
+
|
|
300
|
+
expect(mockAdminSuspensions).toHaveBeenCalledExactlyOnceWith(
|
|
301
|
+
[],
|
|
302
|
+
{},
|
|
303
|
+
expect.anything(),
|
|
304
|
+
);
|
|
305
|
+
});
|
|
163
306
|
|
|
164
|
-
it("
|
|
165
|
-
setArgv("
|
|
166
|
-
|
|
307
|
+
it("dispatches 'unsuspend' with args to admin.unsuspend", async () => {
|
|
308
|
+
setArgv("unsuspend", "42");
|
|
309
|
+
mockAdminUnsuspend.mockResolvedValueOnce({ error: 0 });
|
|
167
310
|
|
|
168
311
|
await main();
|
|
169
312
|
|
|
170
|
-
expect(
|
|
171
|
-
"
|
|
172
|
-
{
|
|
313
|
+
expect(mockAdminUnsuspend).toHaveBeenCalledExactlyOnceWith(
|
|
314
|
+
["42"],
|
|
315
|
+
{},
|
|
173
316
|
expect.anything(),
|
|
174
317
|
);
|
|
175
318
|
});
|
|
176
319
|
|
|
177
|
-
it("
|
|
178
|
-
setArgv("
|
|
179
|
-
|
|
320
|
+
it("dispatches 'clone' with args to migrate.clone", async () => {
|
|
321
|
+
setArgv("clone", "1.2.3.4", "pass");
|
|
322
|
+
mockMigrateClone.mockResolvedValueOnce({ error: 0 });
|
|
180
323
|
|
|
181
324
|
await main();
|
|
182
325
|
|
|
183
|
-
expect(
|
|
184
|
-
"
|
|
185
|
-
{
|
|
326
|
+
expect(mockMigrateClone).toHaveBeenCalledExactlyOnceWith(
|
|
327
|
+
["1.2.3.4", "pass"],
|
|
328
|
+
{},
|
|
186
329
|
expect.anything(),
|
|
187
330
|
);
|
|
188
331
|
});
|
|
189
332
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
333
|
+
// ---- Subcommand dispatch ----
|
|
334
|
+
|
|
335
|
+
it("dispatches 'snapshot list' to snapshot.list", async () => {
|
|
336
|
+
setArgv("snapshot", "list");
|
|
337
|
+
mockSnapshotList.mockResolvedValueOnce({ error: 0 });
|
|
193
338
|
|
|
194
339
|
await main();
|
|
195
340
|
|
|
196
|
-
expect(
|
|
197
|
-
|
|
341
|
+
expect(mockSnapshotList).toHaveBeenCalledExactlyOnceWith(
|
|
342
|
+
[],
|
|
198
343
|
{},
|
|
199
344
|
expect.anything(),
|
|
200
345
|
);
|
|
201
346
|
});
|
|
202
347
|
|
|
203
|
-
it("
|
|
204
|
-
setArgv("
|
|
205
|
-
|
|
348
|
+
it("dispatches 'snapshot delete' with args to snapshot.deleteSnapshot", async () => {
|
|
349
|
+
setArgv("snapshot", "delete", "vsb123");
|
|
350
|
+
mockSnapshotDelete.mockResolvedValueOnce({ error: 0 });
|
|
206
351
|
|
|
207
352
|
await main();
|
|
208
353
|
|
|
209
|
-
expect(
|
|
210
|
-
"
|
|
354
|
+
expect(mockSnapshotDelete).toHaveBeenCalledExactlyOnceWith(
|
|
355
|
+
["vsb123"],
|
|
211
356
|
{},
|
|
212
357
|
expect.anything(),
|
|
213
358
|
);
|
|
214
359
|
});
|
|
215
360
|
|
|
216
|
-
|
|
361
|
+
it("dispatches 'snapshot create' with flags to snapshot.create", async () => {
|
|
362
|
+
setArgv("snapshot", "create", "--desc=test");
|
|
363
|
+
mockSnapshotCreate.mockResolvedValueOnce({ error: 0 });
|
|
217
364
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
365
|
+
await main();
|
|
366
|
+
|
|
367
|
+
expect(mockSnapshotCreate).toHaveBeenCalledExactlyOnceWith(
|
|
368
|
+
[],
|
|
369
|
+
{ desc: "test" },
|
|
370
|
+
expect.anything(),
|
|
371
|
+
);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it("dispatches 'snapshot sticky' with --on flag", async () => {
|
|
375
|
+
setArgv("snapshot", "sticky", "snap1", "--on");
|
|
376
|
+
mockSnapshotSticky.mockResolvedValueOnce({ error: 0 });
|
|
221
377
|
|
|
222
378
|
await main();
|
|
223
379
|
|
|
224
|
-
expect(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
380
|
+
expect(mockSnapshotSticky).toHaveBeenCalledExactlyOnceWith(
|
|
381
|
+
["snap1"],
|
|
382
|
+
{ on: "1" },
|
|
383
|
+
expect.anything(),
|
|
384
|
+
);
|
|
228
385
|
});
|
|
229
386
|
|
|
230
|
-
it("
|
|
231
|
-
setArgv(
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
387
|
+
it("dispatches 'backup list' to backup.list", async () => {
|
|
388
|
+
setArgv("backup", "list");
|
|
389
|
+
mockBackupList.mockResolvedValueOnce({ error: 0 });
|
|
390
|
+
|
|
391
|
+
await main();
|
|
392
|
+
|
|
393
|
+
expect(mockBackupList).toHaveBeenCalledExactlyOnceWith(
|
|
394
|
+
[],
|
|
395
|
+
{},
|
|
396
|
+
expect.anything(),
|
|
237
397
|
);
|
|
238
|
-
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it("dispatches 'backup copy' with args to backup.copy", async () => {
|
|
401
|
+
setArgv("backup", "copy", "tok123");
|
|
402
|
+
mockBackupCopy.mockResolvedValueOnce({ error: 0 });
|
|
239
403
|
|
|
240
404
|
await main();
|
|
241
405
|
|
|
242
|
-
expect(
|
|
243
|
-
"
|
|
244
|
-
{
|
|
406
|
+
expect(mockBackupCopy).toHaveBeenCalledExactlyOnceWith(
|
|
407
|
+
["tok123"],
|
|
408
|
+
{},
|
|
409
|
+
expect.anything(),
|
|
410
|
+
);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// ---- Default subcommand (no action specified) ----
|
|
414
|
+
|
|
415
|
+
it("dispatches 'ssh-key' (no action) as default to system.sshKeyShow", async () => {
|
|
416
|
+
setArgv("ssh-key");
|
|
417
|
+
mockSystemSshKeyShow.mockResolvedValueOnce({ error: 0 });
|
|
418
|
+
|
|
419
|
+
await main();
|
|
420
|
+
|
|
421
|
+
expect(mockSystemSshKeyShow).toHaveBeenCalledExactlyOnceWith(
|
|
422
|
+
[],
|
|
423
|
+
{},
|
|
424
|
+
expect.anything(),
|
|
425
|
+
);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it("dispatches 'ssh-key set' with args to system.sshKeySet", async () => {
|
|
429
|
+
setArgv("ssh-key", "set", "ssh-ed25519 AAAAC3...");
|
|
430
|
+
mockSystemSshKeySet.mockResolvedValueOnce({ error: 0 });
|
|
431
|
+
|
|
432
|
+
await main();
|
|
433
|
+
|
|
434
|
+
expect(mockSystemSshKeySet).toHaveBeenCalledExactlyOnceWith(
|
|
435
|
+
["ssh-ed25519 AAAAC3..."],
|
|
436
|
+
{},
|
|
437
|
+
expect.anything(),
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it("dispatches 'violations' (no action) as default to admin.violationsList", async () => {
|
|
442
|
+
setArgv("violations");
|
|
443
|
+
mockAdminViolationsList.mockResolvedValueOnce({ error: 0 });
|
|
444
|
+
|
|
445
|
+
await main();
|
|
446
|
+
|
|
447
|
+
expect(mockAdminViolationsList).toHaveBeenCalledExactlyOnceWith(
|
|
448
|
+
[],
|
|
449
|
+
{},
|
|
450
|
+
expect.anything(),
|
|
451
|
+
);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("dispatches 'violations resolve' with args to admin.violationsResolve", async () => {
|
|
455
|
+
setArgv("violations", "resolve", "14");
|
|
456
|
+
mockAdminViolationsResolve.mockResolvedValueOnce({ error: 0 });
|
|
457
|
+
|
|
458
|
+
await main();
|
|
459
|
+
|
|
460
|
+
expect(mockAdminViolationsResolve).toHaveBeenCalledExactlyOnceWith(
|
|
461
|
+
["14"],
|
|
462
|
+
{},
|
|
245
463
|
expect.anything(),
|
|
246
464
|
);
|
|
247
465
|
});
|
|
248
466
|
|
|
249
|
-
|
|
467
|
+
it("dispatches 'notifications' (no action) as default to admin.notificationsGet", async () => {
|
|
468
|
+
setArgv("notifications");
|
|
469
|
+
mockAdminNotificationsGet.mockResolvedValueOnce({ error: 0 });
|
|
470
|
+
|
|
471
|
+
await main();
|
|
472
|
+
|
|
473
|
+
expect(mockAdminNotificationsGet).toHaveBeenCalledExactlyOnceWith(
|
|
474
|
+
[],
|
|
475
|
+
{},
|
|
476
|
+
expect.anything(),
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it("dispatches 'notifications set' to admin.notificationsSet", async () => {
|
|
481
|
+
setArgv("notifications", "set", '{"1":1,"2":0}');
|
|
482
|
+
mockAdminNotificationsSet.mockResolvedValueOnce({ error: 0 });
|
|
483
|
+
|
|
484
|
+
await main();
|
|
485
|
+
|
|
486
|
+
expect(mockAdminNotificationsSet).toHaveBeenCalledExactlyOnceWith(
|
|
487
|
+
['{"1":1,"2":0}'],
|
|
488
|
+
{},
|
|
489
|
+
expect.anything(),
|
|
490
|
+
);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// ---- Unknown command / subcommand ----
|
|
494
|
+
|
|
495
|
+
it("exits with error code 1 on unknown command", async () => {
|
|
496
|
+
setArgv("unknown-command");
|
|
497
|
+
const stderrSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
498
|
+
|
|
499
|
+
await main();
|
|
500
|
+
|
|
501
|
+
expect(stderrSpy).toHaveBeenCalled();
|
|
502
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
503
|
+
stderrSpy.mockRestore();
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it("exits with error code 1 on unknown subcommand", async () => {
|
|
507
|
+
setArgv("snapshot", "unknown-action");
|
|
508
|
+
const stderrSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
509
|
+
|
|
510
|
+
await main();
|
|
511
|
+
|
|
512
|
+
expect(stderrSpy).toHaveBeenCalled();
|
|
513
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
514
|
+
stderrSpy.mockRestore();
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// ---- Auth: --veid and --api-key flags ----
|
|
518
|
+
|
|
519
|
+
it("creates KiwiVMClient with --veid and --api-key flag values", async () => {
|
|
520
|
+
setArgv("start", "--veid=12345", "--api-key=secret");
|
|
521
|
+
mockPowerStart.mockResolvedValueOnce({ error: 0 });
|
|
522
|
+
|
|
523
|
+
await main();
|
|
524
|
+
|
|
525
|
+
expect(mockClientConstructor).toHaveBeenCalledExactlyOnceWith({
|
|
526
|
+
veid: "12345",
|
|
527
|
+
apiKey: "secret",
|
|
528
|
+
});
|
|
529
|
+
});
|
|
250
530
|
|
|
251
531
|
it("creates KiwiVMClient from KIWIVM_VEID and KIWIVM_API_KEY env vars", async () => {
|
|
252
|
-
setArgv("
|
|
532
|
+
setArgv("start");
|
|
253
533
|
vi.stubEnv("KIWIVM_VEID", "env-veid");
|
|
254
534
|
vi.stubEnv("KIWIVM_API_KEY", "env-key");
|
|
255
|
-
|
|
535
|
+
mockPowerStart.mockResolvedValueOnce({ error: 0 });
|
|
256
536
|
|
|
257
537
|
await main();
|
|
258
538
|
|
|
@@ -263,10 +543,10 @@ describe("CLI dispatcher", () => {
|
|
|
263
543
|
});
|
|
264
544
|
|
|
265
545
|
it("prefers --veid and --api-key flags over env vars", async () => {
|
|
266
|
-
setArgv("
|
|
546
|
+
setArgv("start", "--veid=flag-veid", "--api-key=flag-key");
|
|
267
547
|
vi.stubEnv("KIWIVM_VEID", "env-veid");
|
|
268
548
|
vi.stubEnv("KIWIVM_API_KEY", "env-key");
|
|
269
|
-
|
|
549
|
+
mockPowerStart.mockResolvedValueOnce({ error: 0 });
|
|
270
550
|
|
|
271
551
|
await main();
|
|
272
552
|
|
|
@@ -276,44 +556,57 @@ describe("CLI dispatcher", () => {
|
|
|
276
556
|
});
|
|
277
557
|
});
|
|
278
558
|
|
|
279
|
-
// ----
|
|
559
|
+
// ---- Flag stripping ----
|
|
280
560
|
|
|
281
|
-
it("
|
|
282
|
-
setArgv("
|
|
283
|
-
|
|
284
|
-
mockPowerRun.mockResolvedValueOnce({ error: 0, message: "Started" });
|
|
561
|
+
it("strips --veid and --api-key from flags passed to handler", async () => {
|
|
562
|
+
setArgv("start", "--veid=12345", "--api-key=secret", "--other=value");
|
|
563
|
+
mockPowerStart.mockResolvedValueOnce({ error: 0 });
|
|
285
564
|
|
|
286
565
|
await main();
|
|
287
566
|
|
|
288
|
-
expect(
|
|
289
|
-
|
|
567
|
+
expect(mockPowerStart).toHaveBeenCalledExactlyOnceWith(
|
|
568
|
+
[],
|
|
569
|
+
{ other: "value" },
|
|
570
|
+
expect.anything(),
|
|
290
571
|
);
|
|
291
|
-
consoleSpy.mockRestore();
|
|
292
572
|
});
|
|
293
573
|
|
|
294
|
-
|
|
295
|
-
|
|
574
|
+
// ---- Standalone boolean flag ----
|
|
575
|
+
|
|
576
|
+
it("treats standalone --on flag as flags.on = '1'", async () => {
|
|
577
|
+
setArgv("snapshot", "sticky", "snap1", "--on");
|
|
578
|
+
mockSnapshotSticky.mockResolvedValueOnce({ error: 0 });
|
|
579
|
+
|
|
580
|
+
await main();
|
|
581
|
+
|
|
582
|
+
expect(mockSnapshotSticky).toHaveBeenCalledExactlyOnceWith(
|
|
583
|
+
["snap1"],
|
|
584
|
+
{ on: "1" },
|
|
585
|
+
expect.anything(),
|
|
586
|
+
);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
// ---- JSON output ----
|
|
590
|
+
|
|
591
|
+
it("writes JSON result to stdout", async () => {
|
|
592
|
+
setArgv("start");
|
|
296
593
|
const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
297
|
-
|
|
298
|
-
error: 0,
|
|
299
|
-
data: { key: "value", nested: { deep: true } },
|
|
300
|
-
};
|
|
301
|
-
mockPowerRun.mockResolvedValueOnce(result);
|
|
594
|
+
mockPowerStart.mockResolvedValueOnce({ error: 0, message: "Started" });
|
|
302
595
|
|
|
303
596
|
await main();
|
|
304
597
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
598
|
+
expect(consoleSpy).toHaveBeenCalledExactlyOnceWith(
|
|
599
|
+
JSON.stringify({ error: 0, message: "Started" }),
|
|
600
|
+
);
|
|
308
601
|
consoleSpy.mockRestore();
|
|
309
602
|
});
|
|
310
603
|
|
|
311
604
|
// ---- Error handling: stderr + exit code 1 ----
|
|
312
605
|
|
|
313
606
|
it("writes errors to stderr and exits with code 1", async () => {
|
|
314
|
-
setArgv("
|
|
607
|
+
setArgv("start");
|
|
315
608
|
const stderrSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
316
|
-
|
|
609
|
+
mockPowerStart.mockRejectedValueOnce(new Error("API failure"));
|
|
317
610
|
|
|
318
611
|
await main();
|
|
319
612
|
|
|
@@ -324,19 +617,8 @@ describe("CLI dispatcher", () => {
|
|
|
324
617
|
stderrSpy.mockRestore();
|
|
325
618
|
});
|
|
326
619
|
|
|
327
|
-
it("exits with code 1 on unknown category", async () => {
|
|
328
|
-
setArgv("unknown", "action");
|
|
329
|
-
const stderrSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
330
|
-
|
|
331
|
-
await main();
|
|
332
|
-
|
|
333
|
-
expect(stderrSpy).toHaveBeenCalled();
|
|
334
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
335
|
-
stderrSpy.mockRestore();
|
|
336
|
-
});
|
|
337
|
-
|
|
338
620
|
it("exits with code 1 when neither flags nor env vars provide credentials", async () => {
|
|
339
|
-
setArgv("
|
|
621
|
+
setArgv("start");
|
|
340
622
|
vi.stubEnv("KIWIVM_VEID", undefined);
|
|
341
623
|
vi.stubEnv("KIWIVM_API_KEY", undefined);
|
|
342
624
|
const stderrSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
@@ -350,25 +632,30 @@ describe("CLI dispatcher", () => {
|
|
|
350
632
|
|
|
351
633
|
// ---- Help command ----
|
|
352
634
|
|
|
353
|
-
it("prints help text when
|
|
635
|
+
it("prints help text when command is 'help'", async () => {
|
|
354
636
|
setArgv("help");
|
|
355
637
|
const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
638
|
+
mockHelpRun.mockReturnValue("Usage: kiwivm-cli ...");
|
|
356
639
|
|
|
357
640
|
await main();
|
|
358
641
|
|
|
359
642
|
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
360
643
|
expect.stringContaining("kiwivm-cli"),
|
|
361
644
|
);
|
|
645
|
+
expect(process.exit).not.toHaveBeenCalledWith(1);
|
|
362
646
|
consoleLogSpy.mockRestore();
|
|
363
647
|
});
|
|
364
648
|
|
|
365
|
-
it("prints help
|
|
366
|
-
setArgv(
|
|
649
|
+
it("prints help text when no command provided (empty args)", async () => {
|
|
650
|
+
setArgv();
|
|
367
651
|
const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
652
|
+
mockHelpRun.mockReturnValue("Usage: kiwivm-cli ...");
|
|
368
653
|
|
|
369
654
|
await main();
|
|
370
655
|
|
|
371
|
-
|
|
656
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
657
|
+
expect.stringContaining("kiwivm-cli"),
|
|
658
|
+
);
|
|
372
659
|
expect(process.exit).not.toHaveBeenCalledWith(1);
|
|
373
660
|
consoleLogSpy.mockRestore();
|
|
374
661
|
});
|