@enactprotocol/cli 1.2.8 → 2.0.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 (73) hide show
  1. package/README.md +88 -0
  2. package/package.json +34 -38
  3. package/src/commands/auth/index.ts +940 -0
  4. package/src/commands/cache/index.ts +361 -0
  5. package/src/commands/config/README.md +239 -0
  6. package/src/commands/config/index.ts +164 -0
  7. package/src/commands/env/README.md +197 -0
  8. package/src/commands/env/index.ts +392 -0
  9. package/src/commands/exec/README.md +110 -0
  10. package/src/commands/exec/index.ts +195 -0
  11. package/src/commands/get/index.ts +198 -0
  12. package/src/commands/index.ts +30 -0
  13. package/src/commands/inspect/index.ts +264 -0
  14. package/src/commands/install/README.md +146 -0
  15. package/src/commands/install/index.ts +682 -0
  16. package/src/commands/list/README.md +115 -0
  17. package/src/commands/list/index.ts +138 -0
  18. package/src/commands/publish/index.ts +350 -0
  19. package/src/commands/report/index.ts +366 -0
  20. package/src/commands/run/README.md +124 -0
  21. package/src/commands/run/index.ts +686 -0
  22. package/src/commands/search/index.ts +368 -0
  23. package/src/commands/setup/index.ts +274 -0
  24. package/src/commands/sign/index.ts +652 -0
  25. package/src/commands/trust/README.md +214 -0
  26. package/src/commands/trust/index.ts +453 -0
  27. package/src/commands/unyank/index.ts +107 -0
  28. package/src/commands/yank/index.ts +143 -0
  29. package/src/index.ts +96 -0
  30. package/src/types.ts +81 -0
  31. package/src/utils/errors.ts +409 -0
  32. package/src/utils/exit-codes.ts +159 -0
  33. package/src/utils/ignore.ts +147 -0
  34. package/src/utils/index.ts +107 -0
  35. package/src/utils/output.ts +242 -0
  36. package/src/utils/spinner.ts +214 -0
  37. package/tests/commands/auth.test.ts +217 -0
  38. package/tests/commands/cache.test.ts +286 -0
  39. package/tests/commands/config.test.ts +277 -0
  40. package/tests/commands/env.test.ts +293 -0
  41. package/tests/commands/exec.test.ts +112 -0
  42. package/tests/commands/get.test.ts +179 -0
  43. package/tests/commands/inspect.test.ts +201 -0
  44. package/tests/commands/install-integration.test.ts +343 -0
  45. package/tests/commands/install.test.ts +288 -0
  46. package/tests/commands/list.test.ts +160 -0
  47. package/tests/commands/publish.test.ts +186 -0
  48. package/tests/commands/report.test.ts +194 -0
  49. package/tests/commands/run.test.ts +231 -0
  50. package/tests/commands/search.test.ts +131 -0
  51. package/tests/commands/sign.test.ts +164 -0
  52. package/tests/commands/trust.test.ts +236 -0
  53. package/tests/commands/unyank.test.ts +114 -0
  54. package/tests/commands/yank.test.ts +154 -0
  55. package/tests/e2e.test.ts +554 -0
  56. package/tests/fixtures/calculator/enact.yaml +34 -0
  57. package/tests/fixtures/echo-tool/enact.md +31 -0
  58. package/tests/fixtures/env-tool/enact.yaml +19 -0
  59. package/tests/fixtures/greeter/enact.yaml +18 -0
  60. package/tests/fixtures/invalid-tool/enact.yaml +4 -0
  61. package/tests/index.test.ts +8 -0
  62. package/tests/types.test.ts +84 -0
  63. package/tests/utils/errors.test.ts +303 -0
  64. package/tests/utils/exit-codes.test.ts +189 -0
  65. package/tests/utils/ignore.test.ts +461 -0
  66. package/tests/utils/output.test.ts +126 -0
  67. package/tsconfig.json +17 -0
  68. package/tsconfig.tsbuildinfo +1 -0
  69. package/dist/index.js +0 -231410
  70. package/dist/index.js.bak +0 -231409
  71. package/dist/web/static/app.js +0 -663
  72. package/dist/web/static/index.html +0 -117
  73. package/dist/web/static/style.css +0 -291
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Tests for CLI types
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import { type CommandContext, ExitCode, type GlobalOptions } from "../src/types";
7
+
8
+ describe("CLI Types", () => {
9
+ describe("ExitCode", () => {
10
+ test("Success is 0", () => {
11
+ expect(ExitCode.Success).toBe(0);
12
+ });
13
+
14
+ test("Error is 1", () => {
15
+ expect(ExitCode.Error).toBe(1);
16
+ });
17
+
18
+ test("InvalidArgs is 2", () => {
19
+ expect(ExitCode.InvalidArgs).toBe(2);
20
+ });
21
+
22
+ test("NotFound is 3", () => {
23
+ expect(ExitCode.NotFound).toBe(3);
24
+ });
25
+
26
+ test("PermissionDenied is 4", () => {
27
+ expect(ExitCode.PermissionDenied).toBe(4);
28
+ });
29
+
30
+ test("Cancelled is 130 (standard for Ctrl+C)", () => {
31
+ expect(ExitCode.Cancelled).toBe(130);
32
+ });
33
+ });
34
+
35
+ describe("GlobalOptions interface", () => {
36
+ test("accepts valid options", () => {
37
+ const options: GlobalOptions = {
38
+ verbose: true,
39
+ json: false,
40
+ quiet: false,
41
+ dryRun: true,
42
+ };
43
+
44
+ expect(options.verbose).toBe(true);
45
+ expect(options.json).toBe(false);
46
+ expect(options.quiet).toBe(false);
47
+ expect(options.dryRun).toBe(true);
48
+ });
49
+
50
+ test("all properties are optional", () => {
51
+ const options: GlobalOptions = {};
52
+ expect(options.verbose).toBeUndefined();
53
+ expect(options.json).toBeUndefined();
54
+ });
55
+ });
56
+
57
+ describe("CommandContext interface", () => {
58
+ test("accepts valid context", () => {
59
+ const ctx: CommandContext = {
60
+ cwd: "/test/path",
61
+ options: { verbose: true },
62
+ isCI: false,
63
+ isInteractive: true,
64
+ };
65
+
66
+ expect(ctx.cwd).toBe("/test/path");
67
+ expect(ctx.options.verbose).toBe(true);
68
+ expect(ctx.isCI).toBe(false);
69
+ expect(ctx.isInteractive).toBe(true);
70
+ });
71
+
72
+ test("CI environment detection", () => {
73
+ const ciCtx: CommandContext = {
74
+ cwd: "/test",
75
+ options: {},
76
+ isCI: true,
77
+ isInteractive: false,
78
+ };
79
+
80
+ expect(ciCtx.isCI).toBe(true);
81
+ expect(ciCtx.isInteractive).toBe(false);
82
+ });
83
+ });
84
+ });
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Tests for error handling utilities
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import {
7
+ AuthError,
8
+ CliError,
9
+ ConfigError,
10
+ ContainerError,
11
+ EXIT_AUTH_ERROR,
12
+ EXIT_CONFIG,
13
+ EXIT_CONTAINER_ERROR,
14
+ EXIT_EXECUTION_ERROR,
15
+ EXIT_FAILURE,
16
+ EXIT_MANIFEST_ERROR,
17
+ EXIT_NETWORK_ERROR,
18
+ EXIT_NOINPUT,
19
+ EXIT_NOPERM,
20
+ EXIT_REGISTRY_ERROR,
21
+ EXIT_TIMEOUT,
22
+ EXIT_TOOL_NOT_FOUND,
23
+ EXIT_TRUST_ERROR,
24
+ EXIT_USAGE,
25
+ EXIT_VALIDATION_ERROR,
26
+ ErrorMessages,
27
+ ExecutionError,
28
+ FileNotFoundError,
29
+ ManifestError,
30
+ NetworkError,
31
+ PermissionError,
32
+ RegistryError,
33
+ TimeoutError,
34
+ ToolNotFoundError,
35
+ TrustError,
36
+ UsageError,
37
+ ValidationError,
38
+ categorizeError,
39
+ } from "../../src/utils";
40
+
41
+ describe("error classes", () => {
42
+ describe("CliError", () => {
43
+ test("creates error with default exit code", () => {
44
+ const err = new CliError("test message");
45
+ expect(err.message).toBe("test message");
46
+ expect(err.exitCode).toBe(EXIT_FAILURE);
47
+ expect(err.suggestion).toBeUndefined();
48
+ });
49
+
50
+ test("creates error with custom exit code", () => {
51
+ const err = new CliError("test", 42);
52
+ expect(err.exitCode).toBe(42);
53
+ });
54
+
55
+ test("creates error with suggestion", () => {
56
+ const err = new CliError("test", 1, "Try this instead");
57
+ expect(err.suggestion).toBe("Try this instead");
58
+ });
59
+ });
60
+
61
+ describe("ToolNotFoundError", () => {
62
+ test("creates error with correct exit code", () => {
63
+ const err = new ToolNotFoundError("my-tool");
64
+ expect(err.message).toBe("Tool not found: my-tool");
65
+ expect(err.exitCode).toBe(EXIT_TOOL_NOT_FOUND);
66
+ expect(err.suggestion).toContain("owner/namespace/tool");
67
+ });
68
+ });
69
+
70
+ describe("ManifestError", () => {
71
+ test("creates error without path", () => {
72
+ const err = new ManifestError("Invalid manifest");
73
+ expect(err.message).toBe("Invalid manifest");
74
+ expect(err.exitCode).toBe(EXIT_MANIFEST_ERROR);
75
+ });
76
+
77
+ test("creates error with path", () => {
78
+ const err = new ManifestError("Invalid manifest", "/path/to/dir");
79
+ expect(err.message).toBe("Invalid manifest in /path/to/dir");
80
+ });
81
+ });
82
+
83
+ describe("ValidationError", () => {
84
+ test("creates error without field", () => {
85
+ const err = new ValidationError("must be a string");
86
+ expect(err.message).toBe("must be a string");
87
+ expect(err.exitCode).toBe(EXIT_VALIDATION_ERROR);
88
+ });
89
+
90
+ test("creates error with field", () => {
91
+ const err = new ValidationError("must be a string", "name");
92
+ expect(err.message).toBe("Invalid name: must be a string");
93
+ });
94
+ });
95
+
96
+ describe("AuthError", () => {
97
+ test("creates error with auth suggestion", () => {
98
+ const err = new AuthError("Not authenticated");
99
+ expect(err.exitCode).toBe(EXIT_AUTH_ERROR);
100
+ expect(err.suggestion).toContain("enact auth login");
101
+ });
102
+ });
103
+
104
+ describe("NetworkError", () => {
105
+ test("creates error with network suggestion", () => {
106
+ const err = new NetworkError("Connection refused");
107
+ expect(err.exitCode).toBe(EXIT_NETWORK_ERROR);
108
+ expect(err.suggestion).toContain("network connection");
109
+ });
110
+ });
111
+
112
+ describe("RegistryError", () => {
113
+ test("creates error with registry suggestion", () => {
114
+ const err = new RegistryError("Registry unavailable");
115
+ expect(err.exitCode).toBe(EXIT_REGISTRY_ERROR);
116
+ expect(err.suggestion).toContain("temporarily unavailable");
117
+ });
118
+ });
119
+
120
+ describe("TrustError", () => {
121
+ test("creates error with trust suggestion", () => {
122
+ const err = new TrustError("Verification failed");
123
+ expect(err.exitCode).toBe(EXIT_TRUST_ERROR);
124
+ expect(err.suggestion).toContain("enact trust check");
125
+ });
126
+ });
127
+
128
+ describe("TimeoutError", () => {
129
+ test("creates error with timeout details", () => {
130
+ const err = new TimeoutError("Download", 30000);
131
+ expect(err.message).toBe("Download timed out after 30s");
132
+ expect(err.exitCode).toBe(EXIT_TIMEOUT);
133
+ expect(err.suggestion).toContain("--timeout");
134
+ });
135
+ });
136
+
137
+ describe("ExecutionError", () => {
138
+ test("creates error with stderr", () => {
139
+ const err = new ExecutionError("Tool failed", "Error: something went wrong");
140
+ expect(err.exitCode).toBe(EXIT_EXECUTION_ERROR);
141
+ expect(err.stderr).toBe("Error: something went wrong");
142
+ });
143
+ });
144
+
145
+ describe("ContainerError", () => {
146
+ test("creates error with container suggestion", () => {
147
+ const err = new ContainerError("Docker not found");
148
+ expect(err.exitCode).toBe(EXIT_CONTAINER_ERROR);
149
+ expect(err.suggestion).toContain("Docker");
150
+ });
151
+ });
152
+
153
+ describe("FileNotFoundError", () => {
154
+ test("creates error with file path", () => {
155
+ const err = new FileNotFoundError("/path/to/file");
156
+ expect(err.message).toBe("File not found: /path/to/file");
157
+ expect(err.exitCode).toBe(EXIT_NOINPUT);
158
+ });
159
+ });
160
+
161
+ describe("PermissionError", () => {
162
+ test("creates error with path", () => {
163
+ const err = new PermissionError("/etc/passwd");
164
+ expect(err.message).toBe("Permission denied: /etc/passwd");
165
+ expect(err.exitCode).toBe(EXIT_NOPERM);
166
+ });
167
+ });
168
+
169
+ describe("ConfigError", () => {
170
+ test("creates error with config suggestion", () => {
171
+ const err = new ConfigError("Invalid config");
172
+ expect(err.exitCode).toBe(EXIT_CONFIG);
173
+ expect(err.suggestion).toContain("enact config list");
174
+ });
175
+ });
176
+
177
+ describe("UsageError", () => {
178
+ test("creates error with usage exit code", () => {
179
+ const err = new UsageError("Missing argument");
180
+ expect(err.exitCode).toBe(EXIT_USAGE);
181
+ });
182
+ });
183
+ });
184
+
185
+ describe("categorizeError", () => {
186
+ test("returns CliError as-is", () => {
187
+ const original = new ToolNotFoundError("test");
188
+ const result = categorizeError(original);
189
+ expect(result).toBe(original);
190
+ });
191
+
192
+ test("categorizes network errors", () => {
193
+ const err = new Error("ECONNREFUSED");
194
+ const result = categorizeError(err);
195
+ expect(result).toBeInstanceOf(NetworkError);
196
+ });
197
+
198
+ test("categorizes permission errors", () => {
199
+ const err = new Error("EACCES: permission denied");
200
+ const result = categorizeError(err);
201
+ expect(result).toBeInstanceOf(PermissionError);
202
+ });
203
+
204
+ test("categorizes file not found errors", () => {
205
+ const err = new Error("ENOENT: no such file or directory");
206
+ const result = categorizeError(err);
207
+ expect(result).toBeInstanceOf(FileNotFoundError);
208
+ });
209
+
210
+ test("categorizes timeout errors", () => {
211
+ const err = new Error("Request timed out");
212
+ const result = categorizeError(err);
213
+ expect(result).toBeInstanceOf(TimeoutError);
214
+ });
215
+
216
+ test("categorizes auth errors", () => {
217
+ const err = new Error("401 Unauthorized");
218
+ const result = categorizeError(err);
219
+ expect(result).toBeInstanceOf(AuthError);
220
+ });
221
+
222
+ test("returns generic CliError for unknown errors", () => {
223
+ const err = new Error("Unknown error");
224
+ const result = categorizeError(err);
225
+ expect(result).toBeInstanceOf(CliError);
226
+ expect(result.message).toBe("Unknown error");
227
+ });
228
+
229
+ test("handles non-Error objects", () => {
230
+ const result = categorizeError("string error");
231
+ expect(result).toBeInstanceOf(CliError);
232
+ expect(result.message).toBe("string error");
233
+ });
234
+ });
235
+
236
+ describe("ErrorMessages", () => {
237
+ describe("toolNotFound", () => {
238
+ test("returns structured error info", () => {
239
+ const info = ErrorMessages.toolNotFound("my-tool");
240
+ expect(info.message).toContain("my-tool");
241
+ expect(info.suggestions).toHaveLength(3);
242
+ expect(info.suggestions.some((s) => s.includes("search"))).toBe(true);
243
+ });
244
+ });
245
+
246
+ describe("notAuthenticated", () => {
247
+ test("returns structured error info", () => {
248
+ const info = ErrorMessages.notAuthenticated();
249
+ expect(info.message).toContain("not authenticated");
250
+ expect(info.suggestions.some((s) => s.includes("login"))).toBe(true);
251
+ });
252
+ });
253
+
254
+ describe("manifestNotFound", () => {
255
+ test("returns structured error info", () => {
256
+ const info = ErrorMessages.manifestNotFound("/my/dir");
257
+ expect(info.message).toContain("/my/dir");
258
+ expect(info.suggestions.some((s) => s.includes("init"))).toBe(true);
259
+ });
260
+ });
261
+
262
+ describe("invalidManifest", () => {
263
+ test("returns structured error info with errors", () => {
264
+ const errors = ["name is required", "version must be valid semver"];
265
+ const info = ErrorMessages.invalidManifest(errors);
266
+ expect(info.message).toBe("Invalid manifest");
267
+ expect(info.suggestions.some((s) => s.includes("name is required"))).toBe(true);
268
+ });
269
+ });
270
+
271
+ describe("registryUnavailable", () => {
272
+ test("returns structured error info", () => {
273
+ const info = ErrorMessages.registryUnavailable();
274
+ expect(info.message).toContain("unavailable");
275
+ expect(info.suggestions.some((s) => s.includes("internet"))).toBe(true);
276
+ });
277
+ });
278
+
279
+ describe("containerRuntimeNotFound", () => {
280
+ test("returns structured error info", () => {
281
+ const info = ErrorMessages.containerRuntimeNotFound();
282
+ expect(info.message).toContain("container runtime");
283
+ expect(info.suggestions.some((s) => s.includes("Docker"))).toBe(true);
284
+ });
285
+ });
286
+
287
+ describe("trustVerificationFailed", () => {
288
+ test("returns structured error info", () => {
289
+ const info = ErrorMessages.trustVerificationFailed("my-tool");
290
+ expect(info.message).toContain("my-tool");
291
+ expect(info.suggestions.some((s) => s.includes("enact trust"))).toBe(true);
292
+ });
293
+ });
294
+
295
+ describe("executionFailed", () => {
296
+ test("returns structured error info", () => {
297
+ const info = ErrorMessages.executionFailed("my-tool", 1);
298
+ expect(info.message).toContain("my-tool");
299
+ expect(info.message).toContain("exit code 1");
300
+ expect(info.suggestions.some((s) => s.includes("--verbose"))).toBe(true);
301
+ });
302
+ });
303
+ });
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Tests for exit codes
3
+ */
4
+
5
+ import { describe, expect, test } from "bun:test";
6
+ import {
7
+ EXIT_AUTH_ERROR,
8
+ EXIT_CANTCREAT,
9
+ EXIT_CONFIG,
10
+ EXIT_CONTAINER_ERROR,
11
+ EXIT_DATAERR,
12
+ EXIT_EXECUTION_ERROR,
13
+ EXIT_FAILURE,
14
+ EXIT_IOERR,
15
+ EXIT_MANIFEST_ERROR,
16
+ EXIT_NETWORK_ERROR,
17
+ EXIT_NOHOST,
18
+ EXIT_NOINPUT,
19
+ EXIT_NOPERM,
20
+ EXIT_NOUSER,
21
+ EXIT_OSERR,
22
+ EXIT_OSFILE,
23
+ EXIT_PROTOCOL,
24
+ EXIT_REGISTRY_ERROR,
25
+ EXIT_SOFTWARE,
26
+ EXIT_SUCCESS,
27
+ EXIT_TEMPFAIL,
28
+ EXIT_TIMEOUT,
29
+ EXIT_TOOL_NOT_FOUND,
30
+ EXIT_TRUST_ERROR,
31
+ EXIT_UNAVAILABLE,
32
+ EXIT_USAGE,
33
+ EXIT_VALIDATION_ERROR,
34
+ getExitCodeDescription,
35
+ } from "../../src/utils";
36
+
37
+ describe("exit codes", () => {
38
+ describe("standard exit codes", () => {
39
+ test("EXIT_SUCCESS is 0", () => {
40
+ expect(EXIT_SUCCESS).toBe(0);
41
+ });
42
+
43
+ test("EXIT_FAILURE is 1", () => {
44
+ expect(EXIT_FAILURE).toBe(1);
45
+ });
46
+
47
+ test("EXIT_USAGE is 2", () => {
48
+ expect(EXIT_USAGE).toBe(2);
49
+ });
50
+ });
51
+
52
+ describe("sysexits.h compatible codes", () => {
53
+ test("EXIT_DATAERR is 65", () => {
54
+ expect(EXIT_DATAERR).toBe(65);
55
+ });
56
+
57
+ test("EXIT_NOINPUT is 66", () => {
58
+ expect(EXIT_NOINPUT).toBe(66);
59
+ });
60
+
61
+ test("EXIT_NOUSER is 67", () => {
62
+ expect(EXIT_NOUSER).toBe(67);
63
+ });
64
+
65
+ test("EXIT_NOHOST is 68", () => {
66
+ expect(EXIT_NOHOST).toBe(68);
67
+ });
68
+
69
+ test("EXIT_UNAVAILABLE is 69", () => {
70
+ expect(EXIT_UNAVAILABLE).toBe(69);
71
+ });
72
+
73
+ test("EXIT_SOFTWARE is 70", () => {
74
+ expect(EXIT_SOFTWARE).toBe(70);
75
+ });
76
+
77
+ test("EXIT_OSERR is 71", () => {
78
+ expect(EXIT_OSERR).toBe(71);
79
+ });
80
+
81
+ test("EXIT_OSFILE is 72", () => {
82
+ expect(EXIT_OSFILE).toBe(72);
83
+ });
84
+
85
+ test("EXIT_CANTCREAT is 73", () => {
86
+ expect(EXIT_CANTCREAT).toBe(73);
87
+ });
88
+
89
+ test("EXIT_IOERR is 74", () => {
90
+ expect(EXIT_IOERR).toBe(74);
91
+ });
92
+
93
+ test("EXIT_TEMPFAIL is 75", () => {
94
+ expect(EXIT_TEMPFAIL).toBe(75);
95
+ });
96
+
97
+ test("EXIT_PROTOCOL is 76", () => {
98
+ expect(EXIT_PROTOCOL).toBe(76);
99
+ });
100
+
101
+ test("EXIT_NOPERM is 77", () => {
102
+ expect(EXIT_NOPERM).toBe(77);
103
+ });
104
+
105
+ test("EXIT_CONFIG is 78", () => {
106
+ expect(EXIT_CONFIG).toBe(78);
107
+ });
108
+ });
109
+
110
+ describe("enact-specific codes", () => {
111
+ test("EXIT_TOOL_NOT_FOUND is 100", () => {
112
+ expect(EXIT_TOOL_NOT_FOUND).toBe(100);
113
+ });
114
+
115
+ test("EXIT_MANIFEST_ERROR is 101", () => {
116
+ expect(EXIT_MANIFEST_ERROR).toBe(101);
117
+ });
118
+
119
+ test("EXIT_EXECUTION_ERROR is 102", () => {
120
+ expect(EXIT_EXECUTION_ERROR).toBe(102);
121
+ });
122
+
123
+ test("EXIT_TIMEOUT is 103", () => {
124
+ expect(EXIT_TIMEOUT).toBe(103);
125
+ });
126
+
127
+ test("EXIT_TRUST_ERROR is 104", () => {
128
+ expect(EXIT_TRUST_ERROR).toBe(104);
129
+ });
130
+
131
+ test("EXIT_REGISTRY_ERROR is 105", () => {
132
+ expect(EXIT_REGISTRY_ERROR).toBe(105);
133
+ });
134
+
135
+ test("EXIT_AUTH_ERROR is 106", () => {
136
+ expect(EXIT_AUTH_ERROR).toBe(106);
137
+ });
138
+
139
+ test("EXIT_VALIDATION_ERROR is 107", () => {
140
+ expect(EXIT_VALIDATION_ERROR).toBe(107);
141
+ });
142
+
143
+ test("EXIT_NETWORK_ERROR is 108", () => {
144
+ expect(EXIT_NETWORK_ERROR).toBe(108);
145
+ });
146
+
147
+ test("EXIT_CONTAINER_ERROR is 109", () => {
148
+ expect(EXIT_CONTAINER_ERROR).toBe(109);
149
+ });
150
+ });
151
+
152
+ describe("getExitCodeDescription", () => {
153
+ test("describes EXIT_SUCCESS", () => {
154
+ expect(getExitCodeDescription(EXIT_SUCCESS)).toBe("Success");
155
+ });
156
+
157
+ test("describes EXIT_FAILURE", () => {
158
+ expect(getExitCodeDescription(EXIT_FAILURE)).toBe("General error");
159
+ });
160
+
161
+ test("describes EXIT_USAGE", () => {
162
+ expect(getExitCodeDescription(EXIT_USAGE)).toBe("Invalid command line arguments");
163
+ });
164
+
165
+ test("describes EXIT_TOOL_NOT_FOUND", () => {
166
+ expect(getExitCodeDescription(EXIT_TOOL_NOT_FOUND)).toBe("Tool not found");
167
+ });
168
+
169
+ test("describes EXIT_MANIFEST_ERROR", () => {
170
+ expect(getExitCodeDescription(EXIT_MANIFEST_ERROR)).toBe("Manifest error");
171
+ });
172
+
173
+ test("describes EXIT_TRUST_ERROR", () => {
174
+ expect(getExitCodeDescription(EXIT_TRUST_ERROR)).toBe("Trust verification failed");
175
+ });
176
+
177
+ test("describes EXIT_AUTH_ERROR", () => {
178
+ expect(getExitCodeDescription(EXIT_AUTH_ERROR)).toBe("Authentication error");
179
+ });
180
+
181
+ test("describes EXIT_REGISTRY_ERROR", () => {
182
+ expect(getExitCodeDescription(EXIT_REGISTRY_ERROR)).toBe("Registry error");
183
+ });
184
+
185
+ test("returns unknown for unrecognized codes", () => {
186
+ expect(getExitCodeDescription(999)).toBe("Unknown error (code 999)");
187
+ });
188
+ });
189
+ });