23import * as
fs from
'fs/promises';
24import * as
path from
'path';
61suite(
'LazyFileLoader Test Suite',
function () {
74 tempDir = await
fs.mkdtemp(
path.join(__dirname,
'temp-lazyloader-'));
75 testFilePath =
path.join(tempDir,
'test-config.json');
82 teardown(async () => {
85 await
fs.rm(tempDir, { recursive:
true, force:
true });
95 suite(
'Basic File Loading', () => {
100 test(
'should load and parse JSON file correctly', async () => {
101 const testData:
TestConfig = { name:
'test', value: 42, enabled:
true };
102 await
fs.writeFile(testFilePath, JSON.stringify(testData));
104 await loader.updateFilePath(testFilePath);
105 const result = await loader.get();
107 assert.strictEqual(result?.name,
'test');
108 assert.strictEqual(result?.value, 42);
109 assert.strictEqual(result?.enabled,
true);
116 test(
'should load plain text file as string', async () => {
118 const textFile =
path.join(tempDir,
'test.txt');
119 const testContent =
'Hello, World!\nThis is a test file.';
121 await
fs.writeFile(textFile, testContent);
122 await loader.updateFilePath(textFile);
124 const result = await loader.get();
125 assert.strictEqual(result, testContent);
132 test(
'should handle empty JSON file', async () => {
133 await
fs.writeFile(testFilePath,
'{}');
134 await loader.updateFilePath(testFilePath);
136 const result = await loader.get();
137 assert.strictEqual(typeof result,
'object');
138 assert.strictEqual(Object.keys(result || {}).length, 0);
146 suite(
'Caching Behavior', () => {
151 test(
'should cache loaded content and not reload on subsequent calls', async () => {
152 const testData:
TestConfig = { name:
'cached', value: 100, enabled:
false };
153 await
fs.writeFile(testFilePath, JSON.stringify(testData));
154 await loader.updateFilePath(testFilePath);
157 const result1 = await loader.get();
160 const modifiedData:
TestConfig = { name:
'modified', value: 200, enabled:
true };
161 await
fs.writeFile(testFilePath, JSON.stringify(modifiedData));
164 const result2 = await loader.get();
166 assert.strictEqual(result1?.name, result2?.name);
167 assert.strictEqual(result1?.value, result2?.value);
168 assert.strictEqual(result2?.name,
'cached');
175 test(
'should reload content after calling reload()', async () => {
176 const initialData:
TestConfig = { name:
'initial', value: 1, enabled:
true };
177 await
fs.writeFile(testFilePath, JSON.stringify(initialData));
178 await loader.updateFilePath(testFilePath);
181 const result1 = await loader.get();
182 assert.strictEqual(result1?.name,
'initial');
185 const updatedData:
TestConfig = { name:
'updated', value: 2, enabled:
false };
186 await
fs.writeFile(testFilePath, JSON.stringify(updatedData));
188 const result2 = await loader.reload();
189 assert.strictEqual(result2?.name,
'updated');
190 assert.strictEqual(result2?.value, 2);
197 test(
'should clear cache with unload()', async () => {
198 const testData:
TestConfig = { name:
'test', value: 42, enabled:
true };
199 await
fs.writeFile(testFilePath, JSON.stringify(testData));
200 await loader.updateFilePath(testFilePath);
203 const result1 = await loader.get();
210 const newData:
TestConfig = { name:
'new', value: 99, enabled:
false };
211 await
fs.writeFile(testFilePath, JSON.stringify(newData));
214 const result2 = await loader.get();
215 assert.strictEqual(result2?.name,
'new');
216 assert.strictEqual(result2?.value, 99);
224 suite(
'Path Resolution', () => {
229 test(
'should handle absolute paths correctly', async () => {
230 const testData:
TestConfig = { name:
'absolute', value: 123, enabled:
true };
231 await
fs.writeFile(testFilePath, JSON.stringify(testData));
233 await loader.updateFilePath(testFilePath);
234 const result = await loader.get();
236 assert.strictEqual(result?.name,
'absolute');
243 test(
'should resolve relative paths with working directory', async () => {
244 const testData:
TestConfig = { name:
'relative', value: 456, enabled:
false };
245 await
fs.writeFile(testFilePath, JSON.stringify(testData));
247 await loader.updateCurrentWorkingDirectory(tempDir);
248 await loader.updateFilePath(
'test-config.json');
250 const result = await loader.get();
251 assert.strictEqual(result?.name,
'relative');
258 test(
'should validate working directory exists', async () => {
259 const nonExistentPath =
path.join(tempDir,
'nonexistent');
260 const success = await loader.updateCurrentWorkingDirectory(nonExistentPath);
261 assert.strictEqual(success,
false);
269 suite(
'Error Handling', () => {
274 test(
'should return undefined for non-existent file', async () => {
275 const nonExistentPath =
path.join(tempDir,
'nonexistent.json');
276 await loader.updateFilePath(nonExistentPath);
279 const result = await loader.get();
280 assert.strictEqual(result, undefined,
'Should return undefined for non-existent file');
287 test(
'should handle malformed JSON gracefully', async () => {
288 const malformedJson =
'{ name: "test", value: 42, invalid }';
289 await
fs.writeFile(testFilePath, malformedJson);
290 await loader.updateFilePath(testFilePath);
292 const result = await loader.get();
294 assert.strictEqual(result, malformedJson);
301 test(
'should return undefined when no file path is set', async () => {
302 const result = await loader.get();
303 assert.strictEqual(result, undefined);
311 suite(
'File Path Management', () => {
316 test(
'should update file path successfully', async () => {
317 const testData:
TestConfig = { name:
'test', value: 42, enabled:
true };
318 await
fs.writeFile(testFilePath, JSON.stringify(testData));
320 const success = await loader.updateFilePath(testFilePath);
321 assert.strictEqual(success,
true);
323 const retrievedPath = loader.getFilePath();
324 assert.strictEqual(retrievedPath, testFilePath);
331 test(
'should reload content when updating file path with cached data', async () => {
333 const firstData:
TestConfig = { name:
'first', value: 1, enabled:
true };
334 await
fs.writeFile(testFilePath, JSON.stringify(firstData));
335 await loader.updateFilePath(testFilePath);
339 const secondFilePath =
path.join(tempDir,
'second.json');
340 const secondData:
TestConfig = { name:
'second', value: 2, enabled:
false };
341 await
fs.writeFile(secondFilePath, JSON.stringify(secondData));
344 const success = await loader.updateFilePath(secondFilePath);
345 assert.strictEqual(success,
true);
347 const result = await loader.get();
348 assert.strictEqual(result?.name,
'second');
355 test(
'should return false when updating to invalid file with cached data', async () => {
357 const testData:
TestConfig = { name:
'test', value: 42, enabled:
true };
358 await
fs.writeFile(testFilePath, JSON.stringify(testData));
359 await loader.updateFilePath(testFilePath);
363 const nonExistentPath =
path.join(tempDir,
'nonexistent.json');
365 const success = await loader.updateFilePath(nonExistentPath);
366 assert.fail(
'Expected error when updating to invalid file');
368 assert.ok((error as any).code ===
'ENOENT',
'Should throw ENOENT error for invalid file path');
377 suite(
'Type Safety and Complex Data', () => {
382 test(
'should handle complex nested objects with type safety', async () => {
384 const complexFile =
path.join(tempDir,
'complex.json');
389 author:
'Test Author'
391 items: [
'item1',
'item2',
'item3']
394 await
fs.writeFile(complexFile, JSON.stringify(testData));
395 await complexLoader.updateFilePath(complexFile);
397 const result = await complexLoader.get();
401 assert.strictEqual(result.metadata.version,
'1.0.0');
402 assert.strictEqual(result.metadata.author,
'Test Author');
403 assert.ok(Array.isArray(result.items));
404 assert.strictEqual(result.items.length, 3);
412 test(
'should support JSONC files with comments', async () => {
413 const jsoncFile =
path.join(tempDir,
'config.jsonc');
414 const jsoncContent = `{
416 "name":
"jsonc-test",
422 await
fs.writeFile(jsoncFile, jsoncContent);
423 await loader.updateFilePath(jsoncFile);
425 const result = await loader.get();
428 assert.ok(result !== undefined,
'JSONC file should be parsed successfully');
429 assert.strictEqual((result as any).name,
'jsonc-test');
430 assert.strictEqual((result as any).value, 789);
431 assert.strictEqual((result as any).enabled,
true);
439 suite(
'Path Utilities', () => {
444 test(
'should check path existence correctly', async () => {
446 const exists = await loader.pathExists(testFilePath);
447 assert.strictEqual(exists,
false);
450 await
fs.writeFile(testFilePath,
'{}');
451 const existsAfterCreation = await loader.pathExists(testFilePath);
452 assert.strictEqual(existsAfterCreation,
true);
459 test(
'should handle directory paths in pathExists', async () => {
460 const exists = await loader.pathExists(tempDir);
461 assert.strictEqual(exists,
true);
469 suite(
'Edge Cases and Performance', () => {
474 test(
'should handle large JSON files efficiently', async () => {
475 const largeArray = Array.from({ length: 1000 }, (_, i) => ({
481 const largeFile =
path.join(tempDir,
'large.json');
482 await
fs.writeFile(largeFile, JSON.stringify(largeArray));
485 await largeLoader.updateFilePath(largeFile);
487 const startTime = Date.now();
488 const result = await largeLoader.get();
489 const loadTime = Date.now() - startTime;
492 assert.strictEqual(result.length, 1000);
493 assert.ok(loadTime < 1000,
'Loading should complete within 1 second');
496 const cachedStartTime = Date.now();
497 const cachedResult = await largeLoader.get();
498 const cachedLoadTime = Date.now() - cachedStartTime;
502 assert.deepStrictEqual(cachedResult, result,
'Cached result should be identical to original');
503 assert.ok(cachedLoadTime <= loadTime + 50,
'Cached access should be at least as fast as initial load');
510 test(
'should handle empty file paths gracefully', async () => {
511 const success = await loader.updateFilePath(
'');
512 assert.strictEqual(success,
true);
516 const result = await loader.get();
517 assert.fail(
'Expected error for empty file path');
519 assert.ok((error as any).code ===
'EISDIR',
'Should throw EISDIR error for directory path');
527 test(
'should handle concurrent file loading requests', async () => {
528 const testFile =
path.join(tempDir,
'concurrent.json');
530 name:
'concurrent-test',
535 await
fs.writeFile(testFile, JSON.stringify(testData));
536 await loader.updateFilePath(testFile);
539 const promises = Array.from({ length: 5 }, () => loader.get());
540 const results = await Promise.all(
promises);
543 results.forEach((result, index) => {
544 assert.ok(result, `Result ${index} should not be
null or undefined`);
545 assert.strictEqual(result?.name,
'concurrent-test', `Result ${index} should have correct name`);
546 assert.strictEqual(result?.value, 42, `Result ${index} should have correct value`);
547 assert.strictEqual(result?.enabled,
true, `Result ${index} should have correct enabled status`);
551 assert.deepStrictEqual(result, results[0], `Result ${index} should have identical content to result 0`);
556 const cachedResult = await loader.get();
557 assert.deepStrictEqual(cachedResult, results[0],
'Cached result after concurrent loading should match');
564 test(
'should maintain independence between loader instances', async () => {
568 const file1 =
path.join(tempDir,
'config1.json');
569 const file2 =
path.join(tempDir,
'config2.json');
571 const data1:
TestConfig = { name:
'loader1', value: 1, enabled:
true };
572 const data2:
TestConfig = { name:
'loader2', value: 2, enabled:
false };
574 await
fs.writeFile(file1, JSON.stringify(data1));
575 await
fs.writeFile(file2, JSON.stringify(data2));
577 await loader1.updateFilePath(file1);
578 await loader2.updateFilePath(file2);
580 const result1 = await loader1.get();
581 const result2 = await loader2.get();
583 assert.strictEqual(result1?.name,
'loader1');
584 assert.strictEqual(result2?.name,
'loader2');
585 assert.notStrictEqual(result1, result2);
Generic lazy file loader with caching and type safety @template T The expected type of the loaded fil...
Complex nested data structure interface for advanced type safety testing.
Basic test configuration interface for LazyFileLoader testing.
import *as path from path
import *as assert from assert
import *as fs from fs promises
export const Record< string,(...args:any[])=> string