@forwardimpact/libutil 0.1.72 → 0.1.73

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.
@@ -1,358 +0,0 @@
1
- import { test, describe, beforeEach, afterEach, mock } from "node:test";
2
- import assert from "node:assert";
3
- import fs from "fs";
4
- import fsPromises from "fs/promises";
5
- import path from "path";
6
- import { fileURLToPath } from "url";
7
-
8
- import { createMockLogger } from "@forwardimpact/libharness";
9
-
10
- // Module under test
11
- import { Finder } from "../finder.js";
12
-
13
- describe("Finder", () => {
14
- let mockLogger;
15
- let mockProcess;
16
- let finder;
17
- let tempDir;
18
-
19
- beforeEach(() => {
20
- mockLogger = createMockLogger();
21
-
22
- mockProcess = {
23
- cwd: () => "/test/project",
24
- };
25
-
26
- finder = new Finder(fsPromises, mockLogger, mockProcess);
27
-
28
- // Create a temporary directory for testing
29
- const __filename = fileURLToPath(import.meta.url);
30
- const __dirname = path.dirname(__filename);
31
- tempDir = path.join(__dirname, ".tmp-linker-test");
32
-
33
- // Clean up any existing temp directory
34
- if (fs.existsSync(tempDir)) {
35
- fs.rmSync(tempDir, { recursive: true, force: true });
36
- }
37
- fs.mkdirSync(tempDir, { recursive: true });
38
- });
39
-
40
- afterEach(() => {
41
- // Clean up temp directory
42
- if (fs.existsSync(tempDir)) {
43
- fs.rmSync(tempDir, { recursive: true, force: true });
44
- }
45
- });
46
-
47
- describe("constructor", () => {
48
- test("creates Finder with fs, logger and process", () => {
49
- const finder = new Finder(fsPromises, mockLogger, mockProcess);
50
-
51
- assert.ok(finder instanceof Finder);
52
- });
53
-
54
- test("validates fs parameter", () => {
55
- assert.throws(() => new Finder(), {
56
- message: /fs is required/,
57
- });
58
- assert.throws(() => new Finder(null), {
59
- message: /fs is required/,
60
- });
61
- });
62
-
63
- test("validates logger parameter", () => {
64
- assert.throws(() => new Finder(fsPromises), {
65
- message: /logger is required/,
66
- });
67
- assert.throws(() => new Finder(fsPromises, null), {
68
- message: /logger is required/,
69
- });
70
- });
71
-
72
- test("validates process parameter", () => {
73
- assert.throws(() => new Finder(fsPromises, mockLogger, null), {
74
- message: /process is required/,
75
- });
76
- });
77
-
78
- test("uses global process when not provided", () => {
79
- const finder = new Finder(fsPromises, mockLogger);
80
- assert.ok(finder instanceof Finder);
81
- });
82
- });
83
-
84
- describe("findUpward", () => {
85
- test("finds file in current directory", () => {
86
- // Create test structure
87
- const testFile = path.join(tempDir, "target.txt");
88
- fs.writeFileSync(testFile, "test");
89
-
90
- const result = finder.findUpward(tempDir, "target.txt");
91
-
92
- assert.strictEqual(result, testFile);
93
- });
94
-
95
- test("finds file in parent directory", () => {
96
- // Create test structure
97
- const subDir = path.join(tempDir, "subdir");
98
- fs.mkdirSync(subDir);
99
- const testFile = path.join(tempDir, "target.txt");
100
- fs.writeFileSync(testFile, "test");
101
-
102
- const result = finder.findUpward(subDir, "target.txt");
103
-
104
- assert.strictEqual(result, testFile);
105
- });
106
-
107
- test("returns null when file not found", () => {
108
- const result = finder.findUpward(tempDir, "nonexistent.txt");
109
-
110
- assert.strictEqual(result, null);
111
- });
112
-
113
- test("respects maxDepth parameter", () => {
114
- // Create nested structure
115
- const deepDir = path.join(tempDir, "a", "b", "c");
116
- fs.mkdirSync(deepDir, { recursive: true });
117
- const testFile = path.join(tempDir, "target.txt");
118
- fs.writeFileSync(testFile, "test");
119
-
120
- // Should not find with maxDepth of 2
121
- const result = finder.findUpward(deepDir, "target.txt", 2);
122
-
123
- assert.strictEqual(result, null);
124
- });
125
- });
126
-
127
- describe("findProjectRoot", () => {
128
- test("finds project root with package.json", () => {
129
- // Create test project structure
130
- const projectRoot = path.join(tempDir, "project");
131
- const packagesDir = path.join(projectRoot, "packages", "somepackage");
132
- fs.mkdirSync(packagesDir, { recursive: true });
133
- fs.writeFileSync(path.join(projectRoot, "package.json"), "{}");
134
-
135
- // Test from the package directory (3 levels deep from project root)
136
- const result = finder.findProjectRoot(packagesDir);
137
-
138
- assert.strictEqual(result, projectRoot);
139
- });
140
-
141
- test("throws error when project root not found", () => {
142
- // Create a directory structure without package.json at any level
143
- const deepDir = path.join(tempDir, "no-project", "deep", "dir");
144
- fs.mkdirSync(deepDir, { recursive: true });
145
-
146
- assert.throws(() => finder.findProjectRoot(deepDir), {
147
- message: /Could not find project root/,
148
- });
149
- });
150
- });
151
-
152
- describe("createSymlink", () => {
153
- test("creates symlink between directories", async () => {
154
- const sourceDir = path.join(tempDir, "source");
155
- const targetPath = path.join(tempDir, "target");
156
-
157
- await finder.createSymlink(sourceDir, targetPath);
158
-
159
- assert.ok(fs.existsSync(targetPath));
160
- assert.ok(fs.lstatSync(targetPath).isSymbolicLink());
161
- assert.strictEqual(mockLogger.debug.mock.calls.length, 1);
162
- assert.ok(
163
- mockLogger.debug.mock.calls[0].arguments[1].includes("Created symlink"),
164
- );
165
- });
166
-
167
- test("removes existing target before creating symlink", async () => {
168
- const sourceDir = path.join(tempDir, "source");
169
- const targetPath = path.join(tempDir, "target");
170
-
171
- // Create existing target directory
172
- fs.mkdirSync(targetPath);
173
- fs.writeFileSync(path.join(targetPath, "existing.txt"), "test");
174
-
175
- await finder.createSymlink(sourceDir, targetPath);
176
-
177
- assert.ok(fs.existsSync(targetPath));
178
- assert.ok(fs.lstatSync(targetPath).isSymbolicLink());
179
- // Original file should be gone
180
- assert.ok(!fs.existsSync(path.join(targetPath, "existing.txt")));
181
- });
182
-
183
- test("removes existing symlink before creating new one", async () => {
184
- const sourceDir = path.join(tempDir, "source");
185
- const oldSourceDir = path.join(tempDir, "old-source");
186
- const targetPath = path.join(tempDir, "target");
187
-
188
- // Create old symlink
189
- fs.mkdirSync(oldSourceDir);
190
- fs.symlinkSync(oldSourceDir, targetPath, "dir");
191
-
192
- await finder.createSymlink(sourceDir, targetPath);
193
-
194
- assert.ok(fs.existsSync(targetPath));
195
- assert.ok(fs.lstatSync(targetPath).isSymbolicLink());
196
- // Should point to new source
197
- assert.strictEqual(fs.readlinkSync(targetPath), sourceDir);
198
- });
199
- });
200
-
201
- describe("createPackageSymlinks", () => {
202
- test("creates symlinks when project root is found", async () => {
203
- // Mock findProjectRoot to return a valid path
204
- const originalFindProjectRoot = finder.findProjectRoot;
205
- finder.findProjectRoot = mock.fn(() => {
206
- const projectRoot = path.join(tempDir, "project");
207
- const packagesDir = path.join(projectRoot, "packages");
208
- fs.mkdirSync(path.join(packagesDir, "libtype"), { recursive: true });
209
- fs.mkdirSync(path.join(packagesDir, "librpc"), { recursive: true });
210
- return projectRoot;
211
- });
212
-
213
- const generatedPath = path.join(tempDir, "generated");
214
-
215
- await finder.createPackageSymlinks(generatedPath);
216
-
217
- // Should have called findProjectRoot
218
- assert.strictEqual(finder.findProjectRoot.mock.calls.length, 1);
219
-
220
- // Restore original method
221
- finder.findProjectRoot = originalFindProjectRoot;
222
- });
223
-
224
- test("creates symlinks for standard packages", async () => {
225
- // Create mock project structure
226
- const projectRoot = path.join(tempDir, "project");
227
- const packagesDir = path.join(projectRoot, "packages");
228
- fs.mkdirSync(path.join(packagesDir, "libtype"), { recursive: true });
229
- fs.mkdirSync(path.join(packagesDir, "librpc"), { recursive: true });
230
-
231
- // Mock findProjectRoot
232
- const originalFindProjectRoot = finder.findProjectRoot;
233
- finder.findProjectRoot = mock.fn(() => projectRoot);
234
-
235
- const generatedPath = path.join(tempDir, "generated");
236
- fs.mkdirSync(generatedPath, { recursive: true });
237
-
238
- await finder.createPackageSymlinks(generatedPath);
239
-
240
- // Check that symlinks were created
241
- const libtypeTarget = path.join(packagesDir, "libtype", "generated");
242
- const librpcTarget = path.join(packagesDir, "librpc", "generated");
243
-
244
- assert.ok(fs.existsSync(libtypeTarget));
245
- assert.ok(fs.lstatSync(libtypeTarget).isSymbolicLink());
246
- assert.ok(fs.existsSync(librpcTarget));
247
- assert.ok(fs.lstatSync(librpcTarget).isSymbolicLink());
248
-
249
- assert.ok(
250
- mockLogger.debug.mock.calls.some((call) =>
251
- call.arguments[1].includes("Created symlink"),
252
- ),
253
- );
254
-
255
- // Restore original method
256
- finder.findProjectRoot = originalFindProjectRoot;
257
- });
258
- });
259
-
260
- describe("findData", () => {
261
- test("finds data/ in CWD via findUpward", () => {
262
- const dataDir = path.join(tempDir, "data");
263
- fs.mkdirSync(dataDir);
264
-
265
- const cwdFinder = new Finder(fsPromises, mockLogger, {
266
- cwd: () => tempDir,
267
- });
268
- const result = cwdFinder.findData("data", "/nonexistent-home");
269
-
270
- assert.strictEqual(result, dataDir);
271
- });
272
-
273
- test("finds data/ in a parent directory via findUpward", () => {
274
- const dataDir = path.join(tempDir, "data");
275
- fs.mkdirSync(dataDir);
276
- const subDir = path.join(tempDir, "products", "pathway");
277
- fs.mkdirSync(subDir, { recursive: true });
278
-
279
- const cwdFinder = new Finder(fsPromises, mockLogger, {
280
- cwd: () => subDir,
281
- });
282
- const result = cwdFinder.findData("data", "/nonexistent-home");
283
-
284
- assert.strictEqual(result, dataDir);
285
- });
286
-
287
- test("falls back to ~/.fit/data/ when CWD traversal fails", () => {
288
- const fakeHome = path.join(tempDir, "fakehome");
289
- const homeFitData = path.join(fakeHome, ".fit", "data");
290
- fs.mkdirSync(homeFitData, { recursive: true });
291
-
292
- const isolatedDir = path.join(tempDir, "isolated");
293
- fs.mkdirSync(isolatedDir);
294
-
295
- const cwdFinder = new Finder(fsPromises, mockLogger, {
296
- cwd: () => isolatedDir,
297
- });
298
- const result = cwdFinder.findData("data", fakeHome);
299
-
300
- assert.strictEqual(result, homeFitData);
301
- });
302
-
303
- test("throws when neither CWD traversal nor HOME fallback finds directory", () => {
304
- const isolatedDir = path.join(tempDir, "isolated");
305
- fs.mkdirSync(isolatedDir);
306
-
307
- const cwdFinder = new Finder(fsPromises, mockLogger, {
308
- cwd: () => isolatedDir,
309
- });
310
-
311
- assert.throws(() => cwdFinder.findData("data", "/nonexistent-home"), {
312
- message: /No data directory found/,
313
- });
314
- });
315
-
316
- test("CWD takes priority over HOME when both exist", () => {
317
- const cwdData = path.join(tempDir, "data");
318
- fs.mkdirSync(cwdData);
319
-
320
- const fakeHome = path.join(tempDir, "fakehome");
321
- const homeFitData = path.join(fakeHome, ".fit", "data");
322
- fs.mkdirSync(homeFitData, { recursive: true });
323
-
324
- const cwdFinder = new Finder(fsPromises, mockLogger, {
325
- cwd: () => tempDir,
326
- });
327
- const result = cwdFinder.findData("data", fakeHome);
328
-
329
- assert.strictEqual(result, cwdData);
330
- });
331
- });
332
-
333
- describe("findPackagePath", () => {
334
- test("finds package in local monorepo structure", () => {
335
- // Create mock monorepo structure
336
- const projectRoot = path.join(tempDir, "project");
337
- const packagePath = path.join(projectRoot, "packages", "libtype");
338
- fs.mkdirSync(packagePath, { recursive: true });
339
-
340
- const result = finder.findPackagePath(projectRoot, "libtype");
341
-
342
- assert.strictEqual(result, packagePath);
343
- });
344
- });
345
-
346
- describe("findGeneratedPath", () => {
347
- test("returns generated directory path for package", () => {
348
- // Create mock structure
349
- const projectRoot = path.join(tempDir, "project");
350
- const packagePath = path.join(projectRoot, "packages", "libtype");
351
- fs.mkdirSync(packagePath, { recursive: true });
352
-
353
- const result = finder.findGeneratedPath(projectRoot, "libtype");
354
-
355
- assert.strictEqual(result, path.join(packagePath, "generated"));
356
- });
357
- });
358
- });
Binary file
Binary file
package/test/http.test.js DELETED
@@ -1,93 +0,0 @@
1
- import { test, describe } from "node:test";
2
- import assert from "node:assert";
3
- import { parseJsonBody } from "../http.js";
4
-
5
- /**
6
- * Creates a mock HTTP request object with event emitter behavior
7
- * @param {object} options - Request options
8
- * @returns {object} Mock request object
9
- */
10
- function createMockRequest(options = {}) {
11
- const { body = {} } = options;
12
- const bodyStr = JSON.stringify(body);
13
- let dataCallback;
14
- let endCallback;
15
- let errorCallback;
16
-
17
- return {
18
- on: (event, callback) => {
19
- if (event === "data") dataCallback = callback;
20
- if (event === "end") endCallback = callback;
21
- if (event === "error") errorCallback = callback;
22
- },
23
- simulateBody: () => {
24
- if (dataCallback) dataCallback(bodyStr);
25
- if (endCallback) endCallback();
26
- },
27
- simulateError: (err) => {
28
- if (errorCallback) errorCallback(err);
29
- },
30
- };
31
- }
32
-
33
- describe("parseJsonBody", () => {
34
- test("parses valid JSON body", async () => {
35
- const req = createMockRequest({
36
- body: { message: "Hello", correlationId: "123" },
37
- });
38
-
39
- const parsePromise = parseJsonBody(req);
40
- req.simulateBody();
41
- const result = await parsePromise;
42
-
43
- assert.deepStrictEqual(result, { message: "Hello", correlationId: "123" });
44
- });
45
-
46
- test("returns empty object for invalid JSON", async () => {
47
- const req = createMockRequest({ body: {} });
48
- let dataCallback;
49
- let endCallback;
50
-
51
- req.on = (event, callback) => {
52
- if (event === "data") dataCallback = callback;
53
- if (event === "end") endCallback = callback;
54
- };
55
-
56
- const parsePromise = parseJsonBody(req);
57
- dataCallback("not valid json {{{");
58
- endCallback();
59
- const result = await parsePromise;
60
-
61
- assert.deepStrictEqual(result, {});
62
- });
63
-
64
- test("rejects on request error", async () => {
65
- const req = createMockRequest({ body: {} });
66
- const testError = new Error("Connection reset");
67
-
68
- const parsePromise = parseJsonBody(req);
69
- req.simulateError(testError);
70
-
71
- await assert.rejects(() => parsePromise, {
72
- message: "Connection reset",
73
- });
74
- });
75
-
76
- test("handles empty body", async () => {
77
- const req = createMockRequest({ body: {} });
78
- let dataCallback;
79
- let endCallback;
80
-
81
- req.on = (event, callback) => {
82
- if (event === "data") dataCallback = callback;
83
- if (event === "end") endCallback = callback;
84
- };
85
-
86
- const parsePromise = parseJsonBody(req);
87
- dataCallback("");
88
- endCallback();
89
- const result = await parsePromise;
90
-
91
- assert.deepStrictEqual(result, {});
92
- });
93
- });
@@ -1,35 +0,0 @@
1
- import { describe, test, mock } from "node:test";
2
- import assert from "node:assert";
3
-
4
- // Module under test
5
- import { createBundleDownloader } from "../index.js";
6
-
7
- const noop = () => {};
8
- const mockLogger = { info: noop, debug: noop, warn: noop, error: noop };
9
-
10
- describe("libutil", () => {
11
- describe("createBundleDownloader", () => {
12
- test("creates BundleDownloader instance with correct dependencies", () => {
13
- const mockStorageFactory = mock.fn();
14
-
15
- const downloader = createBundleDownloader(mockStorageFactory, mockLogger);
16
-
17
- assert.ok(downloader);
18
- assert.ok(typeof downloader.initialize === "function");
19
- assert.ok(typeof downloader.download === "function");
20
- });
21
-
22
- test("validates storageFactory parameter", () => {
23
- assert.throws(() => createBundleDownloader(null, mockLogger), {
24
- message: /createStorage is required/,
25
- });
26
- });
27
-
28
- test("validates logger parameter", () => {
29
- const mockStorageFactory = mock.fn();
30
- assert.throws(() => createBundleDownloader(mockStorageFactory, null), {
31
- message: /logger is required/,
32
- });
33
- });
34
- });
35
- });