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.
Files changed (52) hide show
  1. package/README.md +62 -36
  2. package/dist/index.d.mts.map +1 -1
  3. package/dist/index.mjs +370 -50
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +1 -1
  6. package/src/commands/admin.test.ts +135 -22
  7. package/src/commands/admin.ts +57 -19
  8. package/src/commands/backup.test.ts +26 -23
  9. package/src/commands/backup.ts +13 -15
  10. package/src/commands/help.test.ts +27 -7
  11. package/src/commands/help.ts +61 -26
  12. package/src/commands/info.test.ts +47 -43
  13. package/src/commands/info.ts +11 -13
  14. package/src/commands/iso.test.ts +58 -0
  15. package/src/commands/iso.ts +21 -0
  16. package/src/commands/migrate.test.ts +105 -0
  17. package/src/commands/migrate.ts +38 -0
  18. package/src/commands/network.test.ts +107 -30
  19. package/src/commands/network.ts +56 -18
  20. package/src/commands/power.test.ts +58 -40
  21. package/src/commands/power.ts +27 -16
  22. package/src/commands/shell.test.ts +66 -0
  23. package/src/commands/shell.ts +25 -0
  24. package/src/commands/snapshot.test.ts +141 -71
  25. package/src/commands/snapshot.ts +85 -33
  26. package/src/commands/stats.test.ts +81 -0
  27. package/src/commands/stats.ts +25 -0
  28. package/src/commands/system.test.ts +109 -40
  29. package/src/commands/system.ts +55 -23
  30. package/src/index.test.ts +435 -148
  31. package/src/index.ts +129 -57
  32. package/src/types.ts +57 -1
  33. package/dist/admin-fOud1ZmX.mjs +0 -15
  34. package/dist/admin-fOud1ZmX.mjs.map +0 -1
  35. package/dist/backup-D1UJ4aap.mjs +0 -12
  36. package/dist/backup-D1UJ4aap.mjs.map +0 -1
  37. package/dist/help-Dk-WApoi.mjs +0 -40
  38. package/dist/help-Dk-WApoi.mjs.map +0 -1
  39. package/dist/info-DKExtFYH.mjs +0 -13
  40. package/dist/info-DKExtFYH.mjs.map +0 -1
  41. package/dist/monitoring-BSuv8fj9.mjs +0 -13
  42. package/dist/monitoring-BSuv8fj9.mjs.map +0 -1
  43. package/dist/network-1ycEIJqT.mjs +0 -15
  44. package/dist/network-1ycEIJqT.mjs.map +0 -1
  45. package/dist/power-CDg0Mx1A.mjs +0 -14
  46. package/dist/power-CDg0Mx1A.mjs.map +0 -1
  47. package/dist/snapshot-LO_ufoj5.mjs +0 -23
  48. package/dist/snapshot-LO_ufoj5.mjs.map +0 -1
  49. package/dist/system-Bl-dsqX9.mjs +0 -21
  50. package/dist/system-Bl-dsqX9.mjs.map +0 -1
  51. package/src/commands/monitoring.test.ts +0 -82
  52. package/src/commands/monitoring.ts +0 -20
@@ -1,24 +1,62 @@
1
1
  import type { KiwiVMClient } from "../client.ts";
2
2
 
3
- export async function run(
4
- action: string,
5
- flags: Record<string, string>,
3
+ export async function rdnsSet(
4
+ args: string[],
5
+ _flags: Record<string, string>,
6
6
  client: KiwiVMClient,
7
7
  ): Promise<unknown> {
8
- switch (action) {
9
- case "ipv6-add":
10
- return client.call("ipv6/add");
11
- case "ipv6-delete":
12
- return client.call("ipv6/delete", { ip: flags["ip"] });
13
- case "private-list":
14
- return client.call("privateIp/getAvailableIps");
15
- case "private-assign":
16
- return client.call("privateIp/assign", { ip: flags["ip"] });
17
- case "private-delete":
18
- return client.call("privateIp/delete", { ip: flags["ip"] });
19
- default:
20
- throw new Error(
21
- `Unknown network action: ${action}. Valid: ipv6-add, ipv6-delete, private-list, private-assign, private-delete`,
22
- );
8
+ const ip = args[0];
9
+ const ptr = args[1];
10
+ if (!ip || !ptr) {
11
+ throw new Error("rdns set requires both <ip> and <ptr> arguments");
23
12
  }
13
+ return client.call("setPTR", { ip, ptr });
14
+ }
15
+
16
+ export async function ipv6Add(
17
+ _args: string[],
18
+ _flags: Record<string, string>,
19
+ client: KiwiVMClient,
20
+ ): Promise<unknown> {
21
+ return client.call("ipv6/add");
22
+ }
23
+
24
+ export async function ipv6Delete(
25
+ args: string[],
26
+ _flags: Record<string, string>,
27
+ client: KiwiVMClient,
28
+ ): Promise<unknown> {
29
+ const ip = args[0];
30
+ if (!ip) {
31
+ throw new Error("ipv6 delete requires a <subnet> argument");
32
+ }
33
+ return client.call("ipv6/delete", { ip });
34
+ }
35
+
36
+ export async function privateIpList(
37
+ _args: string[],
38
+ _flags: Record<string, string>,
39
+ client: KiwiVMClient,
40
+ ): Promise<unknown> {
41
+ return client.call("privateIp/getAvailableIps");
42
+ }
43
+
44
+ export async function privateIpAssign(
45
+ args: string[],
46
+ _flags: Record<string, string>,
47
+ client: KiwiVMClient,
48
+ ): Promise<unknown> {
49
+ return client.call("privateIp/assign", { ip: args[0] });
50
+ }
51
+
52
+ export async function privateIpDelete(
53
+ args: string[],
54
+ _flags: Record<string, string>,
55
+ client: KiwiVMClient,
56
+ ): Promise<unknown> {
57
+ const ip = args[0];
58
+ if (!ip) {
59
+ throw new Error("private-ip delete requires an <ip> argument");
60
+ }
61
+ return client.call("privateIp/delete", { ip });
24
62
  }
@@ -1,68 +1,86 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
  import type { KiwiVMClient } from "../client.ts";
3
- import { run } from "./power.ts";
3
+ import { kill, restart, start, stop } from "./power.ts";
4
4
 
5
5
  function mockClient() {
6
6
  const call = vi.fn();
7
7
  return { client: { call } as unknown as KiwiVMClient, call };
8
8
  }
9
9
 
10
- describe("power command", () => {
11
- it("start calls client.call('start')", async () => {
12
- const { client, call } = mockClient();
13
- call.mockResolvedValueOnce({ error: 0 });
10
+ describe("power handlers", () => {
11
+ describe("start", () => {
12
+ it("calls client.call('start')", async () => {
13
+ const { client, call } = mockClient();
14
+ call.mockResolvedValueOnce({ error: 0 });
14
15
 
15
- const result = await run("start", {}, client);
16
+ const result = await start([], {}, client);
16
17
 
17
- expect(client.call).toHaveBeenCalledExactlyOnceWith("start");
18
- expect(result).toEqual({ error: 0 });
19
- });
20
-
21
- it("stop calls client.call('stop')", async () => {
22
- const { client, call } = mockClient();
23
- call.mockResolvedValueOnce({ error: 0 });
18
+ expect(call).toHaveBeenCalledExactlyOnceWith("start");
19
+ expect(result).toEqual({ error: 0 });
20
+ });
24
21
 
25
- const result = await run("stop", {}, client);
22
+ it("propagates errors from the client", async () => {
23
+ const { client, call } = mockClient();
24
+ call.mockRejectedValueOnce(new Error("API failure"));
26
25
 
27
- expect(client.call).toHaveBeenCalledExactlyOnceWith("stop");
28
- expect(result).toEqual({ error: 0 });
26
+ await expect(start([], {}, client)).rejects.toThrow("API failure");
27
+ });
29
28
  });
30
29
 
31
- it("restart calls client.call('restart')", async () => {
32
- const { client, call } = mockClient();
33
- call.mockResolvedValueOnce({ error: 0 });
30
+ describe("stop", () => {
31
+ it("calls client.call('stop')", async () => {
32
+ const { client, call } = mockClient();
33
+ call.mockResolvedValueOnce({ error: 0 });
34
+
35
+ const result = await stop([], {}, client);
36
+
37
+ expect(call).toHaveBeenCalledExactlyOnceWith("stop");
38
+ expect(result).toEqual({ error: 0 });
39
+ });
34
40
 
35
- const result = await run("restart", {}, client);
41
+ it("propagates errors from the client", async () => {
42
+ const { client, call } = mockClient();
43
+ call.mockRejectedValueOnce(new Error("API failure"));
36
44
 
37
- expect(client.call).toHaveBeenCalledExactlyOnceWith("restart");
38
- expect(result).toEqual({ error: 0 });
45
+ await expect(stop([], {}, client)).rejects.toThrow("API failure");
46
+ });
39
47
  });
40
48
 
41
- it("kill calls client.call('kill')", async () => {
42
- const { client, call } = mockClient();
43
- call.mockResolvedValueOnce({ error: 0 });
49
+ describe("restart", () => {
50
+ it("calls client.call('restart')", async () => {
51
+ const { client, call } = mockClient();
52
+ call.mockResolvedValueOnce({ error: 0 });
44
53
 
45
- const result = await run("kill", {}, client);
54
+ const result = await restart([], {}, client);
46
55
 
47
- expect(client.call).toHaveBeenCalledExactlyOnceWith("kill");
48
- expect(result).toEqual({ error: 0 });
56
+ expect(call).toHaveBeenCalledExactlyOnceWith("restart");
57
+ expect(result).toEqual({ error: 0 });
58
+ });
59
+
60
+ it("propagates errors from the client", async () => {
61
+ const { client, call } = mockClient();
62
+ call.mockRejectedValueOnce(new Error("API failure"));
63
+
64
+ await expect(restart([], {}, client)).rejects.toThrow("API failure");
65
+ });
49
66
  });
50
67
 
51
- it("returns the raw API response", async () => {
52
- const { client, call } = mockClient();
53
- const apiResponse = { error: 0, message: "Virtual server is running." };
54
- call.mockResolvedValueOnce(apiResponse);
68
+ describe("kill", () => {
69
+ it("calls client.call('kill')", async () => {
70
+ const { client, call } = mockClient();
71
+ call.mockResolvedValueOnce({ error: 0 });
55
72
 
56
- const result = await run("start", {}, client);
73
+ const result = await kill([], {}, client);
57
74
 
58
- expect(result).toBe(apiResponse);
59
- });
75
+ expect(call).toHaveBeenCalledExactlyOnceWith("kill");
76
+ expect(result).toEqual({ error: 0 });
77
+ });
60
78
 
61
- it("propagates errors from the client", async () => {
62
- const { client, call } = mockClient();
63
- const apiError = new Error("API failure");
64
- call.mockRejectedValueOnce(apiError);
79
+ it("propagates errors from the client", async () => {
80
+ const { client, call } = mockClient();
81
+ call.mockRejectedValueOnce(new Error("API failure"));
65
82
 
66
- await expect(run("restart", {}, client)).rejects.toThrow("API failure");
83
+ await expect(kill([], {}, client)).rejects.toThrow("API failure");
84
+ });
67
85
  });
68
86
  });
@@ -1,22 +1,33 @@
1
1
  import type { KiwiVMClient } from "../client.ts";
2
2
 
3
- export async function run(
4
- action: string,
3
+ export async function start(
4
+ _args: string[],
5
5
  _flags: Record<string, string>,
6
6
  client: KiwiVMClient,
7
7
  ): Promise<unknown> {
8
- switch (action) {
9
- case "start":
10
- return client.call("start");
11
- case "stop":
12
- return client.call("stop");
13
- case "restart":
14
- return client.call("restart");
15
- case "kill":
16
- return client.call("kill");
17
- default:
18
- throw new Error(
19
- `Unknown power action: ${action}. Valid: start, stop, restart, kill`,
20
- );
21
- }
8
+ return client.call("start");
9
+ }
10
+
11
+ export async function stop(
12
+ _args: string[],
13
+ _flags: Record<string, string>,
14
+ client: KiwiVMClient,
15
+ ): Promise<unknown> {
16
+ return client.call("stop");
17
+ }
18
+
19
+ export async function restart(
20
+ _args: string[],
21
+ _flags: Record<string, string>,
22
+ client: KiwiVMClient,
23
+ ): Promise<unknown> {
24
+ return client.call("restart");
25
+ }
26
+
27
+ export async function kill(
28
+ _args: string[],
29
+ _flags: Record<string, string>,
30
+ client: KiwiVMClient,
31
+ ): Promise<unknown> {
32
+ return client.call("kill");
22
33
  }
@@ -0,0 +1,66 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import type { KiwiVMClient } from "../client.ts";
3
+ import { exec, script } from "./shell.ts";
4
+
5
+ function mockClient() {
6
+ const call = vi.fn();
7
+ return { client: { call } as unknown as KiwiVMClient, call };
8
+ }
9
+
10
+ describe("shell handlers", () => {
11
+ describe("exec", () => {
12
+ it("calls basicShell/exec with command from args", async () => {
13
+ const { client, call } = mockClient();
14
+ call.mockResolvedValueOnce({ error: 0 });
15
+
16
+ const result = await exec(["uptime"], {}, client);
17
+
18
+ expect(call).toHaveBeenCalledExactlyOnceWith("basicShell/exec", {
19
+ command: "uptime",
20
+ });
21
+ expect(result).toEqual({ error: 0 });
22
+ });
23
+
24
+ it("throws when no command provided", async () => {
25
+ const { client } = mockClient();
26
+
27
+ await expect(exec([], {}, client)).rejects.toThrow("command");
28
+ });
29
+
30
+ it("propagates errors from the client", async () => {
31
+ const { client, call } = mockClient();
32
+ call.mockRejectedValueOnce(new Error("Exec failed"));
33
+
34
+ await expect(exec(["uptime"], {}, client)).rejects.toThrow("Exec failed");
35
+ });
36
+ });
37
+
38
+ describe("script", () => {
39
+ it("calls shellScript/exec with script from args", async () => {
40
+ const { client, call } = mockClient();
41
+ call.mockResolvedValueOnce({ error: 0 });
42
+
43
+ const result = await script(["apt update"], {}, client);
44
+
45
+ expect(call).toHaveBeenCalledExactlyOnceWith("shellScript/exec", {
46
+ script: "apt update",
47
+ });
48
+ expect(result).toEqual({ error: 0 });
49
+ });
50
+
51
+ it("throws when no script provided", async () => {
52
+ const { client } = mockClient();
53
+
54
+ await expect(script([], {}, client)).rejects.toThrow("script");
55
+ });
56
+
57
+ it("propagates errors from the client", async () => {
58
+ const { client, call } = mockClient();
59
+ call.mockRejectedValueOnce(new Error("Script failed"));
60
+
61
+ await expect(script(["apt update"], {}, client)).rejects.toThrow(
62
+ "Script failed",
63
+ );
64
+ });
65
+ });
66
+ });
@@ -0,0 +1,25 @@
1
+ import type { KiwiVMClient } from "../client.ts";
2
+
3
+ export async function exec(
4
+ args: string[],
5
+ _flags: Record<string, string>,
6
+ client: KiwiVMClient,
7
+ ): Promise<unknown> {
8
+ const command = args[0];
9
+ if (!command) {
10
+ throw new Error("shell exec requires a <command> argument");
11
+ }
12
+ return client.call("basicShell/exec", { command });
13
+ }
14
+
15
+ export async function script(
16
+ args: string[],
17
+ _flags: Record<string, string>,
18
+ client: KiwiVMClient,
19
+ ): Promise<unknown> {
20
+ const scriptContent = args[0];
21
+ if (!scriptContent) {
22
+ throw new Error("shell script requires a <script> argument");
23
+ }
24
+ return client.call("shellScript/exec", { script: scriptContent });
25
+ }
@@ -1,44 +1,23 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
2
  import type { KiwiVMClient } from "../client.ts";
3
- import { run } from "./snapshot.ts";
3
+ import {
4
+ create,
5
+ deleteSnapshot,
6
+ exportSnapshot,
7
+ importSnapshot,
8
+ list,
9
+ restore,
10
+ sticky,
11
+ } from "./snapshot.ts";
4
12
 
5
13
  function mockClient() {
6
14
  const call = vi.fn();
7
15
  return { client: { call } as unknown as KiwiVMClient, call };
8
16
  }
9
17
 
10
- describe("snapshot command", () => {
11
- describe("create", () => {
12
- it("calls snapshot/create with description flag", async () => {
13
- const { client, call } = mockClient();
14
- call.mockResolvedValueOnce({ error: 0 });
15
-
16
- const result = await run(
17
- "create",
18
- { description: "pre-upgrade" },
19
- client,
20
- );
21
-
22
- expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/create", {
23
- description: "pre-upgrade",
24
- });
25
- expect(result).toEqual({ error: 0 });
26
- });
27
-
28
- it("calls snapshot/create without description when flag is omitted", async () => {
29
- const { client, call } = mockClient();
30
- call.mockResolvedValueOnce({ error: 0 });
31
-
32
- await run("create", {}, client);
33
-
34
- expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/create", {
35
- description: undefined,
36
- });
37
- });
38
- });
39
-
18
+ describe("snapshot handlers", () => {
40
19
  describe("list", () => {
41
- it("calls snapshot/list", async () => {
20
+ it("calls client.call('snapshot/list')", async () => {
42
21
  const { client, call } = mockClient();
43
22
  const snapshots = {
44
23
  error: 0,
@@ -58,102 +37,193 @@ describe("snapshot command", () => {
58
37
  };
59
38
  call.mockResolvedValueOnce(snapshots);
60
39
 
61
- const result = await run("list", {}, client);
40
+ const result = await list([], {}, client);
62
41
 
63
- expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/list");
42
+ expect(call).toHaveBeenCalledExactlyOnceWith("snapshot/list");
64
43
  expect(result).toEqual(snapshots);
65
44
  });
45
+
46
+ it("propagates errors from the client", async () => {
47
+ const { client, call } = mockClient();
48
+ call.mockRejectedValueOnce(new Error("API failure"));
49
+
50
+ await expect(list([], {}, client)).rejects.toThrow("API failure");
51
+ });
66
52
  });
67
53
 
68
- describe("delete", () => {
69
- it("calls snapshot/delete with snapshot flag", async () => {
54
+ describe("create", () => {
55
+ it("calls snapshot/create with description flag", async () => {
56
+ const { client, call } = mockClient();
57
+ call.mockResolvedValueOnce({ error: 0 });
58
+
59
+ const result = await create([], { desc: "pre-upgrade" }, client);
60
+
61
+ expect(call).toHaveBeenCalledExactlyOnceWith("snapshot/create", {
62
+ description: "pre-upgrade",
63
+ });
64
+ expect(result).toEqual({ error: 0 });
65
+ });
66
+
67
+ it("calls snapshot/create with description undefined when flag omitted", async () => {
70
68
  const { client, call } = mockClient();
71
69
  call.mockResolvedValueOnce({ error: 0 });
72
70
 
73
- await run("delete", { snapshot: "snap1" }, client);
71
+ await create([], {}, client);
74
72
 
75
- expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/delete", {
73
+ expect(call).toHaveBeenCalledExactlyOnceWith("snapshot/create", {
74
+ description: undefined,
75
+ });
76
+ });
77
+
78
+ it("propagates errors from the client", async () => {
79
+ const { client, call } = mockClient();
80
+ call.mockRejectedValueOnce(new Error("API failure"));
81
+
82
+ await expect(create([], {}, client)).rejects.toThrow("API failure");
83
+ });
84
+ });
85
+
86
+ describe("deleteSnapshot", () => {
87
+ it("calls snapshot/delete with snapshot token from args", async () => {
88
+ const { client, call } = mockClient();
89
+ call.mockResolvedValueOnce({ error: 0 });
90
+
91
+ await deleteSnapshot(["snap1"], {}, client);
92
+
93
+ expect(call).toHaveBeenCalledExactlyOnceWith("snapshot/delete", {
76
94
  snapshot: "snap1",
77
95
  });
78
96
  });
97
+
98
+ it("throws when no snapshot token provided", async () => {
99
+ const { client } = mockClient();
100
+
101
+ await expect(deleteSnapshot([], {}, client)).rejects.toThrow(/token/);
102
+ });
103
+
104
+ it("propagates errors from the client", async () => {
105
+ const { client, call } = mockClient();
106
+ call.mockRejectedValueOnce(new Error("Snapshot not found"));
107
+
108
+ await expect(deleteSnapshot(["nonexistent"], {}, client)).rejects.toThrow(
109
+ "Snapshot not found",
110
+ );
111
+ });
79
112
  });
80
113
 
81
114
  describe("restore", () => {
82
- it("calls snapshot/restore with snapshot flag", async () => {
115
+ it("calls snapshot/restore with snapshot token from args", async () => {
83
116
  const { client, call } = mockClient();
84
117
  call.mockResolvedValueOnce({ error: 0 });
85
118
 
86
- await run("restore", { snapshot: "snap1" }, client);
119
+ await restore(["snap1"], {}, client);
87
120
 
88
- expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/restore", {
121
+ expect(call).toHaveBeenCalledExactlyOnceWith("snapshot/restore", {
89
122
  snapshot: "snap1",
90
123
  });
91
124
  });
125
+
126
+ it("propagates errors from the client", async () => {
127
+ const { client, call } = mockClient();
128
+ call.mockRejectedValueOnce(new Error("Snapshot not found"));
129
+
130
+ await expect(restore(["nonexistent"], {}, client)).rejects.toThrow(
131
+ "Snapshot not found",
132
+ );
133
+ });
92
134
  });
93
135
 
94
136
  describe("sticky", () => {
95
- it("calls snapshot/toggleSticky with snapshot and sticky flags", async () => {
137
+ it("calls snapshot/toggleSticky with sticky=1 when --on flag set", async () => {
96
138
  const { client, call } = mockClient();
97
139
  call.mockResolvedValueOnce({ error: 0 });
98
140
 
99
- await run("sticky", { snapshot: "snap1", sticky: "1" }, client);
141
+ await sticky(["snap1"], { on: "1" }, client);
100
142
 
101
- expect(client.call).toHaveBeenCalledExactlyOnceWith(
102
- "snapshot/toggleSticky",
103
- { snapshot: "snap1", sticky: 1 },
104
- );
143
+ expect(call).toHaveBeenCalledExactlyOnceWith("snapshot/toggleSticky", {
144
+ snapshot: "snap1",
145
+ sticky: 1,
146
+ });
105
147
  });
106
148
 
107
- it("converts sticky flag string '0' to number 0", async () => {
149
+ it("calls snapshot/toggleSticky with sticky=0 when --off flag set", async () => {
108
150
  const { client, call } = mockClient();
109
151
  call.mockResolvedValueOnce({ error: 0 });
110
152
 
111
- await run("sticky", { snapshot: "snap1", sticky: "0" }, client);
153
+ await sticky(["snap1"], { off: "1" }, client);
112
154
 
113
- expect(client.call).toHaveBeenCalledExactlyOnceWith(
114
- "snapshot/toggleSticky",
115
- { snapshot: "snap1", sticky: 0 },
155
+ expect(call).toHaveBeenCalledExactlyOnceWith("snapshot/toggleSticky", {
156
+ snapshot: "snap1",
157
+ sticky: 0,
158
+ });
159
+ });
160
+
161
+ it("throws when neither --on nor --off flag provided", async () => {
162
+ const { client } = mockClient();
163
+
164
+ await expect(sticky(["snap1"], {}, client)).rejects.toThrow("--on");
165
+ });
166
+
167
+ it("propagates errors from the client", async () => {
168
+ const { client, call } = mockClient();
169
+ call.mockRejectedValueOnce(new Error("API failure"));
170
+
171
+ await expect(sticky(["snap1"], { on: "1" }, client)).rejects.toThrow(
172
+ "API failure",
116
173
  );
117
174
  });
118
175
  });
119
176
 
120
- describe("export", () => {
121
- it("calls snapshot/export with snapshot flag", async () => {
177
+ describe("exportSnapshot", () => {
178
+ it("calls snapshot/export with snapshot token from args", async () => {
122
179
  const { client, call } = mockClient();
123
180
  call.mockResolvedValueOnce({ error: 0 });
124
181
 
125
- await run("export", { snapshot: "snap1" }, client);
182
+ await exportSnapshot(["snap1"], {}, client);
126
183
 
127
- expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/export", {
184
+ expect(call).toHaveBeenCalledExactlyOnceWith("snapshot/export", {
128
185
  snapshot: "snap1",
129
186
  });
130
187
  });
188
+
189
+ it("propagates errors from the client", async () => {
190
+ const { client, call } = mockClient();
191
+ call.mockRejectedValueOnce(new Error("Export failed"));
192
+
193
+ await expect(exportSnapshot(["snap1"], {}, client)).rejects.toThrow(
194
+ "Export failed",
195
+ );
196
+ });
131
197
  });
132
198
 
133
- describe("import", () => {
134
- it("calls snapshot/import with sourceVeid and sourceToken", async () => {
199
+ describe("importSnapshot", () => {
200
+ it("calls snapshot/import with sourceVeid and sourceToken from args", async () => {
135
201
  const { client, call } = mockClient();
136
202
  call.mockResolvedValueOnce({ error: 0 });
137
203
 
138
- await run(
139
- "import",
140
- { sourceVeid: "67890", sourceToken: "abc123" },
141
- client,
142
- );
204
+ await importSnapshot(["67890", "abc123"], {}, client);
143
205
 
144
- expect(client.call).toHaveBeenCalledExactlyOnceWith("snapshot/import", {
206
+ expect(call).toHaveBeenCalledExactlyOnceWith("snapshot/import", {
145
207
  sourceVeid: "67890",
146
208
  sourceToken: "abc123",
147
209
  });
148
210
  });
149
- });
150
211
 
151
- it("propagates errors from the client", async () => {
152
- const { client, call } = mockClient();
153
- call.mockRejectedValueOnce(new Error("Snapshot not found"));
212
+ it("throws when only sourceVeid provided (missing token)", async () => {
213
+ const { client } = mockClient();
214
+
215
+ await expect(importSnapshot(["67890"], {}, client)).rejects.toThrow(
216
+ /token/i,
217
+ );
218
+ });
154
219
 
155
- await expect(
156
- run("delete", { snapshot: "nonexistent" }, client),
157
- ).rejects.toThrow("Snapshot not found");
220
+ it("propagates errors from the client", async () => {
221
+ const { client, call } = mockClient();
222
+ call.mockRejectedValueOnce(new Error("Import failed"));
223
+
224
+ await expect(
225
+ importSnapshot(["67890", "abc123"], {}, client),
226
+ ).rejects.toThrow("Import failed");
227
+ });
158
228
  });
159
229
  });