Asper Header  1.0.14
The header injector extension
Loading...
Searching...
No Matches
lazyFileLoad.test.ts
Go to the documentation of this file.
1
22import * as assert from 'assert';
23import * as fs from 'fs/promises';
24import * as path from 'path';
25import { LazyFileLoader } from '../modules/lazyFileLoad';
26
27// Test interfaces for type safety
35interface TestConfig {
36 name: string;
37 value: number;
38 enabled: boolean;
39}
40
49interface ComplexTestData {
50 metadata: {
51 version: string;
52 author: string;
53 };
54 items: string[];
55}
56
61suite('LazyFileLoader Test Suite', function () {
63 let tempDir: string;
64 let testFilePath: string;
65
70 setup(async () => {
71 loader = new LazyFileLoader<TestConfig>();
72
73 // Create temporary directory for test files
74 tempDir = await fs.mkdtemp(path.join(__dirname, 'temp-lazyloader-'));
75 testFilePath = path.join(tempDir, 'test-config.json');
76 });
77
82 teardown(async () => {
83 // Clean up temporary files and directory
84 try {
85 await fs.rm(tempDir, { recursive: true, force: true });
86 } catch (error) {
87 // Ignore cleanup errors in tests
88 }
89 });
90
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));
103
104 await loader.updateFilePath(testFilePath);
105 const result = await loader.get();
106
107 assert.strictEqual(result?.name, 'test');
108 assert.strictEqual(result?.value, 42);
109 assert.strictEqual(result?.enabled, true);
110 });
111
116 test('should load plain text file as string', async () => {
117 const loader = new LazyFileLoader<string>();
118 const textFile = path.join(tempDir, 'test.txt');
119 const testContent = 'Hello, World!\nThis is a test file.';
120
121 await fs.writeFile(textFile, testContent);
122 await loader.updateFilePath(textFile);
123
124 const result = await loader.get();
125 assert.strictEqual(result, testContent);
126 });
127
132 test('should handle empty JSON file', async () => {
133 await fs.writeFile(testFilePath, '{}');
134 await loader.updateFilePath(testFilePath);
135
136 const result = await loader.get();
137 assert.strictEqual(typeof result, 'object');
138 assert.strictEqual(Object.keys(result || {}).length, 0);
139 });
140 });
141
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);
155
156 // First load
157 const result1 = await loader.get();
158
159 // Modify file content after first load
160 const modifiedData: TestConfig = { name: 'modified', value: 200, enabled: true };
161 await fs.writeFile(testFilePath, JSON.stringify(modifiedData));
162
163 // Second load should return cached content
164 const result2 = await loader.get();
165
166 assert.strictEqual(result1?.name, result2?.name);
167 assert.strictEqual(result1?.value, result2?.value);
168 assert.strictEqual(result2?.name, 'cached'); // Should be original, not modified
169 });
170
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);
179
180 // First load
181 const result1 = await loader.get();
182 assert.strictEqual(result1?.name, 'initial');
183
184 // Modify file and reload
185 const updatedData: TestConfig = { name: 'updated', value: 2, enabled: false };
186 await fs.writeFile(testFilePath, JSON.stringify(updatedData));
187
188 const result2 = await loader.reload();
189 assert.strictEqual(result2?.name, 'updated');
190 assert.strictEqual(result2?.value, 2);
191 });
192
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);
201
202 // Load data
203 const result1 = await loader.get();
204 assert.ok(result1);
205
206 // Unload cache
207 loader.unload();
208
209 // Modify file
210 const newData: TestConfig = { name: 'new', value: 99, enabled: false };
211 await fs.writeFile(testFilePath, JSON.stringify(newData));
212
213 // Should reload from file
214 const result2 = await loader.get();
215 assert.strictEqual(result2?.name, 'new');
216 assert.strictEqual(result2?.value, 99);
217 });
218 });
219
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));
232
233 await loader.updateFilePath(testFilePath);
234 const result = await loader.get();
235
236 assert.strictEqual(result?.name, 'absolute');
237 });
238
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));
246
247 await loader.updateCurrentWorkingDirectory(tempDir);
248 await loader.updateFilePath('test-config.json');
249
250 const result = await loader.get();
251 assert.strictEqual(result?.name, 'relative');
252 });
253
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);
262 });
263 });
264
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);
277
278 // LazyFileLoader returns undefined for non-existent files when no paths work
279 const result = await loader.get();
280 assert.strictEqual(result, undefined, 'Should return undefined for non-existent file');
281 });
282
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);
291
292 const result = await loader.get();
293 // Should return raw content since file loaded successfully, even if JSON parsing failed
294 assert.strictEqual(result, malformedJson);
295 });
296
301 test('should return undefined when no file path is set', async () => {
302 const result = await loader.get();
303 assert.strictEqual(result, undefined);
304 });
305 });
306
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));
319
320 const success = await loader.updateFilePath(testFilePath);
321 assert.strictEqual(success, true);
322
323 const retrievedPath = loader.getFilePath();
324 assert.strictEqual(retrievedPath, testFilePath);
325 });
326
331 test('should reload content when updating file path with cached data', async () => {
332 // Create first file
333 const firstData: TestConfig = { name: 'first', value: 1, enabled: true };
334 await fs.writeFile(testFilePath, JSON.stringify(firstData));
335 await loader.updateFilePath(testFilePath);
336 await loader.get(); // Load and cache
337
338 // Create second file
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));
342
343 // Update to second file
344 const success = await loader.updateFilePath(secondFilePath);
345 assert.strictEqual(success, true);
346
347 const result = await loader.get();
348 assert.strictEqual(result?.name, 'second');
349 });
350
355 test('should return false when updating to invalid file with cached data', async () => {
356 // Load valid file first
357 const testData: TestConfig = { name: 'test', value: 42, enabled: true };
358 await fs.writeFile(testFilePath, JSON.stringify(testData));
359 await loader.updateFilePath(testFilePath);
360 await loader.get(); // Load and cache
361
362 // Try to update to non-existent file - this throws error in actual implementation
363 const nonExistentPath = path.join(tempDir, 'nonexistent.json');
364 try {
365 const success = await loader.updateFilePath(nonExistentPath);
366 assert.fail('Expected error when updating to invalid file');
367 } catch (error) {
368 assert.ok((error as any).code === 'ENOENT', 'Should throw ENOENT error for invalid file path');
369 }
370 });
371 });
372
377 suite('Type Safety and Complex Data', () => {
382 test('should handle complex nested objects with type safety', async () => {
383 const complexLoader = new LazyFileLoader<ComplexTestData>();
384 const complexFile = path.join(tempDir, 'complex.json');
385
386 const testData: ComplexTestData = {
387 metadata: {
388 version: '1.0.0',
389 author: 'Test Author'
390 },
391 items: ['item1', 'item2', 'item3']
392 };
393
394 await fs.writeFile(complexFile, JSON.stringify(testData));
395 await complexLoader.updateFilePath(complexFile);
396
397 const result = await complexLoader.get();
398
399 assert.ok(result);
400 if (result) { // Type guard to handle possible undefined
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);
405 }
406 });
407
412 test('should support JSONC files with comments', async () => {
413 const jsoncFile = path.join(tempDir, 'config.jsonc');
414 const jsoncContent = `{
415 // This is a comment
416 "name": "jsonc-test",
417 "value": 789,
418 /* Multi-line comment */
419 "enabled": true
420 }`;
421
422 await fs.writeFile(jsoncFile, jsoncContent);
423 await loader.updateFilePath(jsoncFile);
424
425 const result = await loader.get();
426
427 // Since we use a JSONC parser, this should successfully parse the file with comments
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);
432 });
433 });
434
439 suite('Path Utilities', () => {
444 test('should check path existence correctly', async () => {
445 // Test existing path
446 const exists = await loader.pathExists(testFilePath);
447 assert.strictEqual(exists, false); // File doesn't exist yet
448
449 // Create file and test again
450 await fs.writeFile(testFilePath, '{}');
451 const existsAfterCreation = await loader.pathExists(testFilePath);
452 assert.strictEqual(existsAfterCreation, true);
453 });
454
459 test('should handle directory paths in pathExists', async () => {
460 const exists = await loader.pathExists(tempDir);
461 assert.strictEqual(exists, true);
462 });
463 });
464
469 suite('Edge Cases and Performance', () => {
474 test('should handle large JSON files efficiently', async () => {
475 const largeArray = Array.from({ length: 1000 }, (_, i) => ({
476 id: i,
477 name: `item-${i}`,
478 value: Math.random()
479 }));
480
481 const largeFile = path.join(tempDir, 'large.json');
482 await fs.writeFile(largeFile, JSON.stringify(largeArray));
483
484 const largeLoader = new LazyFileLoader<typeof largeArray>();
485 await largeLoader.updateFilePath(largeFile);
486
487 const startTime = Date.now();
488 const result = await largeLoader.get();
489 const loadTime = Date.now() - startTime;
490
491 assert.ok(result);
492 assert.strictEqual(result.length, 1000);
493 assert.ok(loadTime < 1000, 'Loading should complete within 1 second');
494
495 // Second call should be faster due to caching
496 const cachedStartTime = Date.now();
497 const cachedResult = await largeLoader.get();
498 const cachedLoadTime = Date.now() - cachedStartTime;
499
500 // Cached access should be faster, but timing can be unpredictable in tests
501 // Just verify cache is working by checking the result is the same
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');
504 });
505
510 test('should handle empty file paths gracefully', async () => {
511 const success = await loader.updateFilePath('');
512 assert.strictEqual(success, true); // updateFilePath should accept any string
513
514 // Empty path resolves to current directory, causing EISDIR error
515 try {
516 const result = await loader.get();
517 assert.fail('Expected error for empty file path');
518 } catch (error) {
519 assert.ok((error as any).code === 'EISDIR', 'Should throw EISDIR error for directory path');
520 }
521 });
522
527 test('should handle concurrent file loading requests', async () => {
528 const testFile = path.join(tempDir, 'concurrent.json');
529 const testData: TestConfig = {
530 name: 'concurrent-test',
531 value: 42,
532 enabled: true
533 };
534
535 await fs.writeFile(testFile, JSON.stringify(testData));
536 await loader.updateFilePath(testFile);
537
538 // Make multiple concurrent calls to get()
539 const promises = Array.from({ length: 5 }, () => loader.get());
540 const results = await Promise.all(promises);
541
542 // All results should be identical and valid
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`);
548
549 // All results should have identical content (deep equality check)
550 if (index > 0) {
551 assert.deepStrictEqual(result, results[0], `Result ${index} should have identical content to result 0`);
552 }
553 });
554
555 // Verify that after concurrent loading, subsequent calls return the same cached result
556 const cachedResult = await loader.get();
557 assert.deepStrictEqual(cachedResult, results[0], 'Cached result after concurrent loading should match');
558 });
559
564 test('should maintain independence between loader instances', async () => {
565 const loader1 = new LazyFileLoader<TestConfig>();
566 const loader2 = new LazyFileLoader<TestConfig>();
567
568 const file1 = path.join(tempDir, 'config1.json');
569 const file2 = path.join(tempDir, 'config2.json');
570
571 const data1: TestConfig = { name: 'loader1', value: 1, enabled: true };
572 const data2: TestConfig = { name: 'loader2', value: 2, enabled: false };
573
574 await fs.writeFile(file1, JSON.stringify(data1));
575 await fs.writeFile(file2, JSON.stringify(data2));
576
577 await loader1.updateFilePath(file1);
578 await loader2.updateFilePath(file2);
579
580 const result1 = await loader1.get();
581 const result2 = await loader2.get();
582
583 assert.strictEqual(result1?.name, 'loader1');
584 assert.strictEqual(result2?.name, 'loader2');
585 assert.notStrictEqual(result1, result2);
586 });
587 });
588});
Generic lazy file loader with caching and type safety @template T The expected type of the loaded fil...
import fs from fs
Definition esbuild.js:9
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