@enactprotocol/shared 1.2.13 → 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.
- package/README.md +44 -0
- package/package.json +16 -58
- package/src/config.ts +476 -0
- package/src/constants.ts +36 -0
- package/src/execution/command.ts +314 -0
- package/src/execution/index.ts +73 -0
- package/src/execution/runtime.ts +308 -0
- package/src/execution/types.ts +379 -0
- package/src/execution/validation.ts +508 -0
- package/src/index.ts +237 -30
- package/src/manifest/index.ts +36 -0
- package/src/manifest/loader.ts +187 -0
- package/src/manifest/parser.ts +173 -0
- package/src/manifest/validator.ts +309 -0
- package/src/paths.ts +108 -0
- package/src/registry.ts +219 -0
- package/src/resolver.ts +345 -0
- package/src/types/index.ts +30 -0
- package/src/types/manifest.ts +255 -0
- package/src/types.ts +5 -188
- package/src/utils/fs.ts +281 -0
- package/src/utils/logger.ts +270 -59
- package/src/utils/version.ts +304 -36
- package/tests/config.test.ts +515 -0
- package/tests/execution/command.test.ts +317 -0
- package/tests/execution/validation.test.ts +384 -0
- package/tests/fixtures/invalid-tool.yaml +4 -0
- package/tests/fixtures/valid-tool.md +62 -0
- package/tests/fixtures/valid-tool.yaml +40 -0
- package/tests/index.test.ts +8 -0
- package/tests/manifest/loader.test.ts +291 -0
- package/tests/manifest/parser.test.ts +345 -0
- package/tests/manifest/validator.test.ts +394 -0
- package/tests/manifest-types.test.ts +358 -0
- package/tests/paths.test.ts +153 -0
- package/tests/registry.test.ts +231 -0
- package/tests/resolver.test.ts +272 -0
- package/tests/utils/fs.test.ts +388 -0
- package/tests/utils/logger.test.ts +480 -0
- package/tests/utils/version.test.ts +390 -0
- package/tsconfig.json +12 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/dist/LocalToolResolver.d.ts +0 -84
- package/dist/LocalToolResolver.js +0 -353
- package/dist/api/enact-api.d.ts +0 -130
- package/dist/api/enact-api.js +0 -428
- package/dist/api/index.d.ts +0 -2
- package/dist/api/index.js +0 -2
- package/dist/api/types.d.ts +0 -103
- package/dist/api/types.js +0 -1
- package/dist/constants.d.ts +0 -7
- package/dist/constants.js +0 -10
- package/dist/core/DaggerExecutionProvider.d.ts +0 -169
- package/dist/core/DaggerExecutionProvider.js +0 -1029
- package/dist/core/DirectExecutionProvider.d.ts +0 -23
- package/dist/core/DirectExecutionProvider.js +0 -406
- package/dist/core/EnactCore.d.ts +0 -162
- package/dist/core/EnactCore.js +0 -597
- package/dist/core/NativeExecutionProvider.d.ts +0 -9
- package/dist/core/NativeExecutionProvider.js +0 -16
- package/dist/core/index.d.ts +0 -3
- package/dist/core/index.js +0 -3
- package/dist/exec/index.d.ts +0 -3
- package/dist/exec/index.js +0 -3
- package/dist/exec/logger.d.ts +0 -11
- package/dist/exec/logger.js +0 -57
- package/dist/exec/validate.d.ts +0 -5
- package/dist/exec/validate.js +0 -167
- package/dist/index.d.ts +0 -21
- package/dist/index.js +0 -25
- package/dist/lib/enact-direct.d.ts +0 -150
- package/dist/lib/enact-direct.js +0 -159
- package/dist/lib/index.d.ts +0 -1
- package/dist/lib/index.js +0 -1
- package/dist/security/index.d.ts +0 -3
- package/dist/security/index.js +0 -3
- package/dist/security/security.d.ts +0 -23
- package/dist/security/security.js +0 -137
- package/dist/security/sign.d.ts +0 -103
- package/dist/security/sign.js +0 -666
- package/dist/security/verification-enforcer.d.ts +0 -53
- package/dist/security/verification-enforcer.js +0 -204
- package/dist/services/McpCoreService.d.ts +0 -98
- package/dist/services/McpCoreService.js +0 -124
- package/dist/services/index.d.ts +0 -1
- package/dist/services/index.js +0 -1
- package/dist/types.d.ts +0 -132
- package/dist/types.js +0 -3
- package/dist/utils/config.d.ts +0 -111
- package/dist/utils/config.js +0 -342
- package/dist/utils/env-loader.d.ts +0 -54
- package/dist/utils/env-loader.js +0 -270
- package/dist/utils/help.d.ts +0 -36
- package/dist/utils/help.js +0 -248
- package/dist/utils/index.d.ts +0 -7
- package/dist/utils/index.js +0 -7
- package/dist/utils/logger.d.ts +0 -35
- package/dist/utils/logger.js +0 -75
- package/dist/utils/silent-monitor.d.ts +0 -67
- package/dist/utils/silent-monitor.js +0 -242
- package/dist/utils/timeout.d.ts +0 -5
- package/dist/utils/timeout.js +0 -23
- package/dist/utils/version.d.ts +0 -4
- package/dist/utils/version.js +0 -35
- package/dist/web/env-manager-server.d.ts +0 -29
- package/dist/web/env-manager-server.js +0 -367
- package/dist/web/index.d.ts +0 -1
- package/dist/web/index.js +0 -1
- package/src/LocalToolResolver.ts +0 -424
- package/src/api/enact-api.ts +0 -604
- package/src/api/index.ts +0 -2
- package/src/api/types.ts +0 -114
- package/src/core/DaggerExecutionProvider.ts +0 -1357
- package/src/core/DirectExecutionProvider.ts +0 -484
- package/src/core/EnactCore.ts +0 -847
- package/src/core/index.ts +0 -3
- package/src/exec/index.ts +0 -3
- package/src/exec/logger.ts +0 -63
- package/src/exec/validate.ts +0 -238
- package/src/lib/enact-direct.ts +0 -254
- package/src/lib/index.ts +0 -1
- package/src/services/McpCoreService.ts +0 -201
- package/src/services/index.ts +0 -1
- package/src/utils/config.ts +0 -438
- package/src/utils/env-loader.ts +0 -370
- package/src/utils/help.ts +0 -257
- package/src/utils/index.ts +0 -7
- package/src/utils/silent-monitor.ts +0 -328
- package/src/utils/timeout.ts +0 -26
- package/src/web/env-manager-server.ts +0 -465
- package/src/web/index.ts +0 -1
- package/src/web/static/app.js +0 -663
- package/src/web/static/index.html +0 -117
- package/src/web/static/style.css +0 -291
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import type {
|
|
3
|
+
Author,
|
|
4
|
+
EnvVariable,
|
|
5
|
+
PackageManifest,
|
|
6
|
+
ParsedManifest,
|
|
7
|
+
ResourceRequirements,
|
|
8
|
+
ToolAnnotations,
|
|
9
|
+
ToolExample,
|
|
10
|
+
ToolManifest,
|
|
11
|
+
ToolResolution,
|
|
12
|
+
ValidationError,
|
|
13
|
+
ValidationResult,
|
|
14
|
+
ValidationWarning,
|
|
15
|
+
} from "../src/types/manifest";
|
|
16
|
+
import { MANIFEST_FILES, PACKAGE_MANIFEST_FILE } from "../src/types/manifest";
|
|
17
|
+
|
|
18
|
+
describe("manifest types", () => {
|
|
19
|
+
describe("ToolManifest", () => {
|
|
20
|
+
test("accepts minimal valid manifest", () => {
|
|
21
|
+
const manifest: ToolManifest = {
|
|
22
|
+
name: "org/tool",
|
|
23
|
+
description: "A test tool",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
expect(manifest.name).toBe("org/tool");
|
|
27
|
+
expect(manifest.description).toBe("A test tool");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("accepts full manifest with all fields", () => {
|
|
31
|
+
const manifest: ToolManifest = {
|
|
32
|
+
name: "acme/utils/greeter",
|
|
33
|
+
description: "Greets users by name",
|
|
34
|
+
enact: "2.0.0",
|
|
35
|
+
version: "1.0.0",
|
|
36
|
+
from: "node:18-alpine",
|
|
37
|
+
command: "echo 'Hello ${name}'",
|
|
38
|
+
timeout: "30s",
|
|
39
|
+
license: "MIT",
|
|
40
|
+
tags: ["greeting", "utility"],
|
|
41
|
+
inputSchema: {
|
|
42
|
+
type: "object",
|
|
43
|
+
properties: {
|
|
44
|
+
name: { type: "string" },
|
|
45
|
+
},
|
|
46
|
+
required: ["name"],
|
|
47
|
+
},
|
|
48
|
+
outputSchema: {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: {
|
|
51
|
+
message: { type: "string" },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
env: {
|
|
55
|
+
API_KEY: {
|
|
56
|
+
description: "API key for service",
|
|
57
|
+
secret: true,
|
|
58
|
+
},
|
|
59
|
+
LOG_LEVEL: {
|
|
60
|
+
description: "Logging level",
|
|
61
|
+
default: "info",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
annotations: {
|
|
65
|
+
title: "Greeter Tool",
|
|
66
|
+
readOnlyHint: true,
|
|
67
|
+
destructiveHint: false,
|
|
68
|
+
idempotentHint: true,
|
|
69
|
+
openWorldHint: false,
|
|
70
|
+
},
|
|
71
|
+
resources: {
|
|
72
|
+
memory: "512Mi",
|
|
73
|
+
disk: "1Gi",
|
|
74
|
+
},
|
|
75
|
+
doc: "Extended documentation here",
|
|
76
|
+
authors: [
|
|
77
|
+
{
|
|
78
|
+
name: "Alice",
|
|
79
|
+
email: "alice@example.com",
|
|
80
|
+
url: "https://example.com",
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
examples: [
|
|
84
|
+
{
|
|
85
|
+
input: { name: "World" },
|
|
86
|
+
output: { message: "Hello World" },
|
|
87
|
+
description: "Basic greeting",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
"x-custom-field": "custom value",
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
expect(manifest.name).toBe("acme/utils/greeter");
|
|
94
|
+
expect(manifest.enact).toBe("2.0.0");
|
|
95
|
+
expect(manifest.env?.API_KEY?.secret).toBe(true);
|
|
96
|
+
expect(manifest.annotations?.readOnlyHint).toBe(true);
|
|
97
|
+
expect(manifest["x-custom-field"]).toBe("custom value");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("supports custom x- prefixed fields", () => {
|
|
101
|
+
const manifest: ToolManifest = {
|
|
102
|
+
name: "org/tool",
|
|
103
|
+
description: "Test",
|
|
104
|
+
"x-internal-id": "12345",
|
|
105
|
+
"x-team-owner": "platform",
|
|
106
|
+
"x-nested": { foo: "bar" },
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
expect(manifest["x-internal-id"]).toBe("12345");
|
|
110
|
+
expect(manifest["x-team-owner"]).toBe("platform");
|
|
111
|
+
expect(manifest["x-nested"]).toEqual({ foo: "bar" });
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("EnvVariable", () => {
|
|
116
|
+
test("supports secret variables", () => {
|
|
117
|
+
const envVar: EnvVariable = {
|
|
118
|
+
description: "API authentication token",
|
|
119
|
+
secret: true,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
expect(envVar.secret).toBe(true);
|
|
123
|
+
expect(envVar.default).toBeUndefined();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("supports non-secret variables with defaults", () => {
|
|
127
|
+
const envVar: EnvVariable = {
|
|
128
|
+
description: "Logging level",
|
|
129
|
+
secret: false,
|
|
130
|
+
default: "info",
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
expect(envVar.secret).toBe(false);
|
|
134
|
+
expect(envVar.default).toBe("info");
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("Author", () => {
|
|
139
|
+
test("requires name field", () => {
|
|
140
|
+
const author: Author = {
|
|
141
|
+
name: "Alice Developer",
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
expect(author.name).toBe("Alice Developer");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("supports optional email and url", () => {
|
|
148
|
+
const author: Author = {
|
|
149
|
+
name: "Bob",
|
|
150
|
+
email: "bob@example.com",
|
|
151
|
+
url: "https://bob.dev",
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
expect(author.email).toBe("bob@example.com");
|
|
155
|
+
expect(author.url).toBe("https://bob.dev");
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe("ToolAnnotations", () => {
|
|
160
|
+
test("all fields are optional", () => {
|
|
161
|
+
const annotations: ToolAnnotations = {};
|
|
162
|
+
expect(annotations.title).toBeUndefined();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("supports all behavior hints", () => {
|
|
166
|
+
const annotations: ToolAnnotations = {
|
|
167
|
+
title: "My Tool",
|
|
168
|
+
readOnlyHint: true,
|
|
169
|
+
destructiveHint: true,
|
|
170
|
+
idempotentHint: false,
|
|
171
|
+
openWorldHint: true,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
expect(annotations.title).toBe("My Tool");
|
|
175
|
+
expect(annotations.readOnlyHint).toBe(true);
|
|
176
|
+
expect(annotations.destructiveHint).toBe(true);
|
|
177
|
+
expect(annotations.idempotentHint).toBe(false);
|
|
178
|
+
expect(annotations.openWorldHint).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe("ResourceRequirements", () => {
|
|
183
|
+
test("supports memory, gpu, and disk", () => {
|
|
184
|
+
const resources: ResourceRequirements = {
|
|
185
|
+
memory: "2Gi",
|
|
186
|
+
gpu: "24Gi",
|
|
187
|
+
disk: "100Gi",
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
expect(resources.memory).toBe("2Gi");
|
|
191
|
+
expect(resources.gpu).toBe("24Gi");
|
|
192
|
+
expect(resources.disk).toBe("100Gi");
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
describe("ToolExample", () => {
|
|
197
|
+
test("supports input and output", () => {
|
|
198
|
+
const example: ToolExample = {
|
|
199
|
+
input: { file: "data.csv", operation: "validate" },
|
|
200
|
+
output: { status: "success", valid: true },
|
|
201
|
+
description: "Validate CSV file",
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
expect(example.input).toEqual({ file: "data.csv", operation: "validate" });
|
|
205
|
+
expect(example.output).toEqual({ status: "success", valid: true });
|
|
206
|
+
expect(example.description).toBe("Validate CSV file");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("supports examples without input (no-arg tools)", () => {
|
|
210
|
+
const example: ToolExample = {
|
|
211
|
+
output: { timestamp: "2025-01-29T00:00:00Z" },
|
|
212
|
+
description: "Returns current timestamp",
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
expect(example.input).toBeUndefined();
|
|
216
|
+
expect(example.output).toBeDefined();
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe("PackageManifest", () => {
|
|
221
|
+
test("supports shared configuration", () => {
|
|
222
|
+
const pkg: PackageManifest = {
|
|
223
|
+
enact: "2.0.0",
|
|
224
|
+
env: {
|
|
225
|
+
API_TOKEN: {
|
|
226
|
+
description: "Shared API token",
|
|
227
|
+
secret: true,
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
authors: [{ name: "Team" }],
|
|
231
|
+
license: "MIT",
|
|
232
|
+
"x-org-id": "acme",
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
expect(pkg.enact).toBe("2.0.0");
|
|
236
|
+
expect(pkg.env?.API_TOKEN?.secret).toBe(true);
|
|
237
|
+
expect(pkg.authors?.[0]?.name).toBe("Team");
|
|
238
|
+
expect(pkg["x-org-id"]).toBe("acme");
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe("ParsedManifest", () => {
|
|
243
|
+
test("contains manifest and format", () => {
|
|
244
|
+
const parsed: ParsedManifest = {
|
|
245
|
+
manifest: {
|
|
246
|
+
name: "org/tool",
|
|
247
|
+
description: "Test",
|
|
248
|
+
},
|
|
249
|
+
format: "yaml",
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
expect(parsed.manifest.name).toBe("org/tool");
|
|
253
|
+
expect(parsed.format).toBe("yaml");
|
|
254
|
+
expect(parsed.body).toBeUndefined();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("contains body for markdown format", () => {
|
|
258
|
+
const parsed: ParsedManifest = {
|
|
259
|
+
manifest: {
|
|
260
|
+
name: "org/tool",
|
|
261
|
+
description: "Test",
|
|
262
|
+
},
|
|
263
|
+
body: "# Tool Name\n\nDocumentation here.",
|
|
264
|
+
format: "md",
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
expect(parsed.format).toBe("md");
|
|
268
|
+
expect(parsed.body).toContain("# Tool Name");
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe("ValidationResult", () => {
|
|
273
|
+
test("represents valid result", () => {
|
|
274
|
+
const result: ValidationResult = {
|
|
275
|
+
valid: true,
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
expect(result.valid).toBe(true);
|
|
279
|
+
expect(result.errors).toBeUndefined();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("represents invalid result with errors", () => {
|
|
283
|
+
const error: ValidationError = {
|
|
284
|
+
path: "name",
|
|
285
|
+
message: "Name is required",
|
|
286
|
+
code: "REQUIRED",
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const warning: ValidationWarning = {
|
|
290
|
+
path: "version",
|
|
291
|
+
message: "Version is recommended",
|
|
292
|
+
code: "MISSING_RECOMMENDED",
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const result: ValidationResult = {
|
|
296
|
+
valid: false,
|
|
297
|
+
errors: [error],
|
|
298
|
+
warnings: [warning],
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
expect(result.valid).toBe(false);
|
|
302
|
+
expect(result.errors?.length).toBe(1);
|
|
303
|
+
expect(result.errors?.[0]?.code).toBe("REQUIRED");
|
|
304
|
+
expect(result.warnings?.length).toBe(1);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
describe("ToolResolution", () => {
|
|
309
|
+
test("contains all resolution info", () => {
|
|
310
|
+
const resolution: ToolResolution = {
|
|
311
|
+
manifest: {
|
|
312
|
+
name: "org/tool",
|
|
313
|
+
description: "Test",
|
|
314
|
+
},
|
|
315
|
+
sourceDir: "/home/user/.enact/tools/org/tool",
|
|
316
|
+
location: "user",
|
|
317
|
+
manifestPath: "/home/user/.enact/tools/org/tool/enact.yaml",
|
|
318
|
+
version: "1.0.0",
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
expect(resolution.location).toBe("user");
|
|
322
|
+
expect(resolution.version).toBe("1.0.0");
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("supports all location types", () => {
|
|
326
|
+
const locations: ToolResolution["location"][] = [
|
|
327
|
+
"file",
|
|
328
|
+
"project",
|
|
329
|
+
"user",
|
|
330
|
+
"cache",
|
|
331
|
+
"registry",
|
|
332
|
+
];
|
|
333
|
+
|
|
334
|
+
for (const loc of locations) {
|
|
335
|
+
const resolution: ToolResolution = {
|
|
336
|
+
manifest: { name: "test", description: "test" },
|
|
337
|
+
sourceDir: "/test",
|
|
338
|
+
location: loc,
|
|
339
|
+
manifestPath: "/test/enact.yaml",
|
|
340
|
+
};
|
|
341
|
+
expect(resolution.location).toBe(loc);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
describe("constants", () => {
|
|
347
|
+
test("MANIFEST_FILES contains expected files", () => {
|
|
348
|
+
expect(MANIFEST_FILES).toContain("enact.md");
|
|
349
|
+
expect(MANIFEST_FILES).toContain("enact.yaml");
|
|
350
|
+
expect(MANIFEST_FILES).toContain("enact.yml");
|
|
351
|
+
expect(MANIFEST_FILES.length).toBe(3);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test("PACKAGE_MANIFEST_FILE is correct", () => {
|
|
355
|
+
expect(PACKAGE_MANIFEST_FILE).toBe("enact-package.yaml");
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
getCacheDir,
|
|
7
|
+
getConfigPath,
|
|
8
|
+
getEnactHome,
|
|
9
|
+
getGlobalEnvPath,
|
|
10
|
+
getProjectEnactDir,
|
|
11
|
+
getProjectEnvPath,
|
|
12
|
+
getToolsDir,
|
|
13
|
+
} from "../src/paths";
|
|
14
|
+
|
|
15
|
+
const TEST_DIR = join(import.meta.dir, "fixtures", "path-test");
|
|
16
|
+
const NESTED_DIR = join(TEST_DIR, "nested", "deep", "directory");
|
|
17
|
+
|
|
18
|
+
describe("path utilities", () => {
|
|
19
|
+
beforeAll(() => {
|
|
20
|
+
// Create test directory structure
|
|
21
|
+
mkdirSync(NESTED_DIR, { recursive: true });
|
|
22
|
+
mkdirSync(join(TEST_DIR, ".enact"), { recursive: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterAll(() => {
|
|
26
|
+
// Clean up test directories
|
|
27
|
+
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("getEnactHome", () => {
|
|
31
|
+
test("returns ~/.enact/ path", () => {
|
|
32
|
+
const home = getEnactHome();
|
|
33
|
+
const expected = join(homedir(), ".enact");
|
|
34
|
+
expect(home).toBe(expected);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("returns absolute path", () => {
|
|
38
|
+
const home = getEnactHome();
|
|
39
|
+
expect(home.startsWith("/") || home.match(/^[A-Z]:\\/)).toBe(true);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("getProjectEnactDir", () => {
|
|
44
|
+
test("finds .enact in current directory", () => {
|
|
45
|
+
const result = getProjectEnactDir(TEST_DIR);
|
|
46
|
+
expect(result).toBe(join(TEST_DIR, ".enact"));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("finds .enact in parent directory", () => {
|
|
50
|
+
const result = getProjectEnactDir(NESTED_DIR);
|
|
51
|
+
expect(result).toBe(join(TEST_DIR, ".enact"));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("returns null when .enact not found (stops at root)", () => {
|
|
55
|
+
// Note: This test may find ~/.enact/ if it exists
|
|
56
|
+
// That's actually correct behavior - it walks up to find .enact
|
|
57
|
+
// To truly test "not found", we'd need to mock the filesystem
|
|
58
|
+
// For now, we just verify it returns a valid path or null
|
|
59
|
+
const result = getProjectEnactDir("/tmp/nonexistent-unlikely-path-12345");
|
|
60
|
+
// Result will be null or a valid .enact directory path
|
|
61
|
+
if (result !== null) {
|
|
62
|
+
expect(result.endsWith(".enact")).toBe(true);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("uses current working directory by default", () => {
|
|
67
|
+
// Save original cwd
|
|
68
|
+
const originalCwd = process.cwd();
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// Change to test directory
|
|
72
|
+
process.chdir(NESTED_DIR);
|
|
73
|
+
const result = getProjectEnactDir();
|
|
74
|
+
expect(result).toBe(join(TEST_DIR, ".enact"));
|
|
75
|
+
} finally {
|
|
76
|
+
// Restore original cwd
|
|
77
|
+
process.chdir(originalCwd);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("getToolsDir", () => {
|
|
83
|
+
test("returns ~/.enact/tools/ for user scope", () => {
|
|
84
|
+
const result = getToolsDir("user");
|
|
85
|
+
const expected = join(homedir(), ".enact", "tools");
|
|
86
|
+
expect(result).toBe(expected);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("returns .enact/tools/ for project scope", () => {
|
|
90
|
+
const result = getToolsDir("project", TEST_DIR);
|
|
91
|
+
expect(result).toBe(join(TEST_DIR, ".enact", "tools"));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("finds project tools in parent directory", () => {
|
|
95
|
+
const result = getToolsDir("project", NESTED_DIR);
|
|
96
|
+
expect(result).toBe(join(TEST_DIR, ".enact", "tools"));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("returns null for project scope when .enact not found", () => {
|
|
100
|
+
// Similar to above - may find ~/.enact/ if it exists
|
|
101
|
+
const result = getToolsDir("project", "/tmp/no-enact-unlikely-path");
|
|
102
|
+
// Result will be null or a valid tools directory
|
|
103
|
+
if (result !== null) {
|
|
104
|
+
expect(result.endsWith("tools")).toBe(true);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe("getCacheDir", () => {
|
|
110
|
+
test("returns ~/.enact/cache/ path", () => {
|
|
111
|
+
const result = getCacheDir();
|
|
112
|
+
const expected = join(homedir(), ".enact", "cache");
|
|
113
|
+
expect(result).toBe(expected);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("getConfigPath", () => {
|
|
118
|
+
test("returns ~/.enact/config.yaml path", () => {
|
|
119
|
+
const result = getConfigPath();
|
|
120
|
+
const expected = join(homedir(), ".enact", "config.yaml");
|
|
121
|
+
expect(result).toBe(expected);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("getGlobalEnvPath", () => {
|
|
126
|
+
test("returns ~/.enact/.env path", () => {
|
|
127
|
+
const result = getGlobalEnvPath();
|
|
128
|
+
const expected = join(homedir(), ".enact", ".env");
|
|
129
|
+
expect(result).toBe(expected);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("getProjectEnvPath", () => {
|
|
134
|
+
test("returns .enact/.env path for project", () => {
|
|
135
|
+
const result = getProjectEnvPath(TEST_DIR);
|
|
136
|
+
expect(result).toBe(join(TEST_DIR, ".enact", ".env"));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("finds project .env in parent directory", () => {
|
|
140
|
+
const result = getProjectEnvPath(NESTED_DIR);
|
|
141
|
+
expect(result).toBe(join(TEST_DIR, ".enact", ".env"));
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("returns null when .enact not found", () => {
|
|
145
|
+
// Similar to above - may find ~/.enact/ if it exists
|
|
146
|
+
const result = getProjectEnvPath("/tmp/no-project-unlikely-path");
|
|
147
|
+
// Result will be null or a valid .env path
|
|
148
|
+
if (result !== null) {
|
|
149
|
+
expect(result.endsWith(".env")).toBe(true);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|