UNPKG

@mdfriday/foundry

Version:

The core engine of MDFriday. Convert Markdown and shortcodes into fully themed static sites – Hugo-style, powered by TypeScript.

293 lines 13.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const overlaydir_1 = require("../entity/overlaydir"); const type_1 = require("../type"); // Mock dependencies jest.mock('@internal/domain/fs/vo/overlayoptions'); jest.mock('@pkg/log'); describe('OverlayDir Entity', () => { let mockFs1; let mockFs2; let mockDirOpener1; let mockDirOpener2; let mockInfoFunc; let mockDirsMerger; let overlayDir; beforeEach(() => { mockFs1 = { name: jest.fn().mockReturnValue('MockFs1'), open: jest.fn(), stat: jest.fn(), }; mockFs2 = { name: jest.fn().mockReturnValue('MockFs2'), open: jest.fn(), stat: jest.fn(), }; mockDirOpener1 = jest.fn(); mockDirOpener2 = jest.fn(); mockInfoFunc = jest.fn(); mockDirsMerger = jest.fn().mockImplementation((a, b) => [...a, ...b]); const options = { name: '/test/dir', fss: [mockFs1, mockFs2], dirOpeners: [mockDirOpener1, mockDirOpener2], info: mockInfoFunc, merge: mockDirsMerger, }; overlayDir = new overlaydir_1.OverlayDir(options); }); afterEach(() => { jest.clearAllMocks(); }); describe('constructor', () => { it('should initialize with default options', () => { const defaultDir = new overlaydir_1.OverlayDir(); expect(defaultDir.name()).toBe(''); expect(defaultDir.fss).toEqual([]); expect(defaultDir.dirOpeners).toEqual([]); expect(defaultDir.offset).toBe(0); expect(defaultDir.closed).toBe(false); }); it('should initialize with provided options', () => { expect(overlayDir.name()).toBe('/test/dir'); expect(overlayDir.fss).toEqual([mockFs1, mockFs2]); expect(overlayDir.dirOpeners).toEqual([mockDirOpener1, mockDirOpener2]); expect(overlayDir.info).toBe(mockInfoFunc); expect(overlayDir.merge).toBe(mockDirsMerger); }); it('should create copies of arrays to avoid mutation', () => { const originalFss = [mockFs1, mockFs2]; const originalOpeners = [mockDirOpener1, mockDirOpener2]; const options = { fss: originalFss, dirOpeners: originalOpeners, }; const dir = new overlaydir_1.OverlayDir(options); expect(dir.fss).not.toBe(originalFss); expect(dir.dirOpeners).not.toBe(originalOpeners); expect(dir.fss).toEqual(originalFss); expect(dir.dirOpeners).toEqual(originalOpeners); }); }); describe('name', () => { it('should return directory name', () => { expect(overlayDir.name()).toBe('/test/dir'); }); }); describe('isClosed', () => { it('should return false when directory is open', () => { const result = overlayDir.isClosed(); expect(result).toBe(false); }); it('should return true when closed flag is set', () => { overlayDir.closed = true; const result = overlayDir.isClosed(); expect(result).toBe(true); }); it('should return true when no filesystems or openers', () => { const emptyDir = new overlaydir_1.OverlayDir({ fss: [], dirOpeners: [] }); const result = emptyDir.isClosed(); expect(result).toBe(true); }); }); describe('readdir', () => { beforeEach(() => { // Mock file objects that will be returned by filesystem open const mockFile1 = { readdir: jest.fn().mockResolvedValue([ { name: () => 'file1.txt', isDir: () => false }, { name: () => 'dir1', isDir: () => true }, ]), close: jest.fn(), }; const mockFile2 = { readdir: jest.fn().mockResolvedValue([ { name: () => 'file2.txt', isDir: () => false }, { name: () => 'dir2', isDir: () => true }, ]), close: jest.fn(), }; mockFs1.open.mockResolvedValue(mockFile1); mockFs2.open.mockResolvedValue(mockFile2); // Ensure mockDirsMerger handles arrays correctly (like defaultDirMerger) mockDirsMerger.mockImplementation((lofi, bofi) => { const result = [...(lofi || [])]; for (const boFile of (bofi || [])) { let found = false; for (const loFile of (lofi || [])) { if (boFile.name() === loFile.name()) { found = true; break; } } if (!found) { result.push(boFile); } } return result; }); }); it('should throw error when directory is closed', async () => { overlayDir.closed = true; await expect(overlayDir.readdir(10)).rejects.toThrow(type_1.ErrFileClosed); }); it('should throw existing error if present', async () => { const testError = new Error('Test error'); overlayDir.err = testError; await expect(overlayDir.readdir(10)).rejects.toThrow(testError); }); it('should load and return directory entries', async () => { const result = await overlayDir.readdir(10); expect(mockFs1.open).toHaveBeenCalledWith('/test/dir'); expect(mockFs2.open).toHaveBeenCalledWith('/test/dir'); expect(result).toHaveLength(4); // 2 from each filesystem expect(mockDirsMerger).toHaveBeenCalled(); }); it('should handle root directory path conversion', async () => { const mockFile1 = { readdir: jest.fn().mockResolvedValue([ { name: () => 'root-file.txt', isDir: () => false }, ]), close: jest.fn(), }; mockFs1.open.mockResolvedValue(mockFile1); const rootDir = new overlaydir_1.OverlayDir({ name: '/', fss: [mockFs1], merge: mockDirsMerger // Use the same merger to ensure consistency }); const result = await rootDir.readdir(10); expect(mockFs1.open).toHaveBeenCalledWith(''); // "/" converted to "" expect(result).toHaveLength(1); }); it('should return all entries when count is 0 or negative', async () => { const result = await overlayDir.readdir(0); expect(result).toHaveLength(4); expect(overlayDir.err).toEqual(expect.any(Error)); expect(overlayDir.err.message).toBe('EOF'); }); it('should return limited entries when count is positive', async () => { const result = await overlayDir.readdir(2); expect(result).toHaveLength(2); expect(overlayDir.offset).toBe(2); }); it('should handle subsequent reads with offset', async () => { await overlayDir.readdir(2); // First read const result = await overlayDir.readdir(2); // Second read expect(result).toHaveLength(2); expect(overlayDir.offset).toBe(4); }); it('should throw EOF error when no more entries', async () => { await overlayDir.readdir(4); // Read all entries await expect(overlayDir.readdir(1)).rejects.toThrow('EOF'); }); it('should handle filesystem errors gracefully', async () => { mockFs1.open.mockRejectedValue(new Error('Access denied')); // mockFs2 should still work const result = await overlayDir.readdir(10); expect(result).toHaveLength(2); // Only from mockFs2 }); it('should read from directory openers when filesystems fail', async () => { const mockFile = { readdir: jest.fn().mockResolvedValue([ { name: () => 'virtual.txt', isDir: () => false }, ]), close: jest.fn(), }; mockDirOpener1.mockResolvedValue(mockFile); mockFs1.open.mockRejectedValue(new Error('Not found')); mockFs2.open.mockRejectedValue(new Error('Not found')); const result = await overlayDir.readdir(10); expect(mockDirOpener1).toHaveBeenCalled(); expect(result).toHaveLength(1); }); }); describe('readdirnames', () => { beforeEach(() => { const mockFile = { readdir: jest.fn().mockResolvedValue([ { name: () => 'file1.txt', isDir: () => false }, { name: () => 'file2.txt', isDir: () => false }, ]), close: jest.fn(), }; mockFs1.open.mockResolvedValue(mockFile); mockFs2.open.mockResolvedValue(mockFile); }); it('should throw error when directory is closed', async () => { overlayDir.closed = true; await expect(overlayDir.readdirnames(10)).rejects.toThrow(type_1.ErrFileClosed); }); it('should return directory entry names', async () => { const result = await overlayDir.readdirnames(10); expect(result).toEqual(['file1.txt', 'file2.txt', 'file1.txt', 'file2.txt']); }); }); describe('File interface methods', () => { it('should implement close method', async () => { await expect(overlayDir.close()).resolves.toBeUndefined(); expect(overlayDir.closed).toBe(true); }); it('should implement read method', async () => { const buffer = new Uint8Array(10); await expect(overlayDir.read(buffer)).rejects.toThrow('operation read not supported on directory'); }); it('should implement readAt method', async () => { const buffer = new Uint8Array(10); await expect(overlayDir.readAt(buffer, 0)).rejects.toThrow('operation readAt not supported on directory'); }); it('should implement seek method', async () => { await expect(overlayDir.seek(0, 0)).rejects.toThrow('operation seek not supported on directory'); }); it('should implement write method', async () => { const buffer = new Uint8Array([1, 2, 3]); await expect(overlayDir.write(buffer)).rejects.toThrow('operation write not supported on directory'); }); it('should implement writeAt method', async () => { const buffer = new Uint8Array([1, 2, 3]); await expect(overlayDir.writeAt(buffer, 0)).rejects.toThrow('operation writeAt not supported on directory'); }); it('should implement stat method', async () => { const mockFileInfo = { name: () => '/test/dir', isDir: () => true }; mockInfoFunc.mockResolvedValue(mockFileInfo); const result = await overlayDir.stat(); expect(mockInfoFunc).toHaveBeenCalled(); expect(result).toBe(mockFileInfo); }); it('should implement sync method', async () => { await expect(overlayDir.sync()).rejects.toThrow('operation sync not supported on directory'); }); it('should implement truncate method', async () => { await expect(overlayDir.truncate(100)).rejects.toThrow('operation truncate not supported on directory'); }); it('should implement writeString method', async () => { await expect(overlayDir.writeString('test')).rejects.toThrow('operation writeString not supported on directory'); }); }); describe('factory functions', () => { it('should create OverlayDir with newOverlayDir', () => { const options = { name: 'test' }; const result = (0, overlaydir_1.newOverlayDir)(options); expect(result).toBeInstanceOf(overlaydir_1.OverlayDir); expect(result.name()).toBe('test'); }); it('should create OverlayDir with openDir', async () => { const result = await (0, overlaydir_1.openDir)('/test', mockDirsMerger, mockInfoFunc, [mockFs1]); expect(result).toBeInstanceOf(overlaydir_1.OverlayDir); expect(result.name()).toBe('/test'); }); it('should manage directory cache with getDir and releaseDir', async () => { const dir1 = (0, overlaydir_1.getDir)(); const dir2 = (0, overlaydir_1.getDir)(); // Should return different instances from pool expect(dir1).toBeInstanceOf(overlaydir_1.OverlayDir); expect(dir2).toBeInstanceOf(overlaydir_1.OverlayDir); (0, overlaydir_1.releaseDir)(dir1); // After release, can get instance from pool const dir3 = (0, overlaydir_1.getDir)(); expect(dir3).toBeInstanceOf(overlaydir_1.OverlayDir); }); }); }); //# sourceMappingURL=entity-overlaydir.test.js.map