@mdfriday/foundry
Version:
The core engine of MDFriday. Convert Markdown and shortcodes into fully themed static sites – Hugo-style, powered by TypeScript.
320 lines • 14.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const static_copier_1 = require("../vo/static-copier");
// Mock dependencies
jest.mock('@pkg/log', () => ({
getDomainLogger: () => ({
error: jest.fn(),
info: jest.fn(),
debug: jest.fn(),
warn: jest.fn(),
}),
}));
describe('StaticCopier Value Object', () => {
let mockSourceFs1;
let mockSourceFs2;
let mockTargetFs;
let mockFile;
let mockFileInfo;
let staticCopier;
beforeEach(() => {
mockFileInfo = {
name: jest.fn().mockReturnValue('test.txt'),
size: jest.fn().mockReturnValue(100),
mode: jest.fn().mockReturnValue(0o644),
modTime: jest.fn().mockReturnValue(new Date()),
isDir: jest.fn().mockReturnValue(false),
sys: jest.fn().mockReturnValue(null),
};
mockFile = {
close: jest.fn(),
read: jest.fn(),
readAt: jest.fn(),
seek: jest.fn(),
write: jest.fn(),
writeAt: jest.fn(),
name: jest.fn().mockReturnValue('/test.txt'),
readdir: jest.fn(),
readdirnames: jest.fn(),
stat: jest.fn().mockResolvedValue(mockFileInfo),
sync: jest.fn(),
truncate: jest.fn(),
writeString: jest.fn(),
};
mockSourceFs1 = {
name: jest.fn().mockReturnValue('SourceFs1'),
create: jest.fn(),
mkdir: jest.fn(),
mkdirAll: jest.fn(),
open: jest.fn().mockResolvedValue(mockFile),
openFile: jest.fn(),
remove: jest.fn(),
removeAll: jest.fn(),
rename: jest.fn(),
stat: jest.fn(),
chmod: jest.fn(),
chown: jest.fn(),
chtimes: jest.fn(),
};
mockSourceFs2 = {
name: jest.fn().mockReturnValue('SourceFs2'),
create: jest.fn(),
mkdir: jest.fn(),
mkdirAll: jest.fn(),
open: jest.fn().mockResolvedValue(mockFile),
openFile: jest.fn(),
remove: jest.fn(),
removeAll: jest.fn(),
rename: jest.fn(),
stat: jest.fn(),
chmod: jest.fn(),
chown: jest.fn(),
chtimes: jest.fn(),
};
mockTargetFs = {
name: jest.fn().mockReturnValue('TargetFs'),
create: jest.fn().mockResolvedValue(mockFile),
mkdir: jest.fn(),
mkdirAll: jest.fn(),
open: jest.fn().mockResolvedValue(mockFile),
openFile: jest.fn(),
remove: jest.fn(),
removeAll: jest.fn(),
rename: jest.fn(),
stat: jest.fn(),
chmod: jest.fn(),
chown: jest.fn(),
chtimes: jest.fn(),
};
});
afterEach(() => {
jest.clearAllMocks();
});
describe('constructor', () => {
it('should initialize with source and target filesystems', () => {
staticCopier = new static_copier_1.StaticCopier([mockSourceFs1, mockSourceFs2], mockTargetFs);
expect(staticCopier).toBeDefined();
expect(staticCopier.fromFss).toEqual([mockSourceFs1, mockSourceFs2]);
expect(staticCopier.toFs).toBe(mockTargetFs);
});
it('should handle empty source filesystems array', () => {
staticCopier = new static_copier_1.StaticCopier([], mockTargetFs);
expect(staticCopier.fromFss).toEqual([]);
expect(staticCopier.toFs).toBe(mockTargetFs);
});
});
describe('copy', () => {
beforeEach(() => {
staticCopier = new static_copier_1.StaticCopier([mockSourceFs1], mockTargetFs);
});
it('should return early when no source filesystems', async () => {
const emptyCopier = new static_copier_1.StaticCopier([], mockTargetFs);
await emptyCopier.copy();
// Should not attempt any filesystem operations
expect(mockSourceFs1.open).not.toHaveBeenCalled();
expect(mockTargetFs.open).not.toHaveBeenCalled();
});
it('should return early when no target filesystem', async () => {
const noTargetCopier = new static_copier_1.StaticCopier([mockSourceFs1], null);
await noTargetCopier.copy();
// Should not attempt any filesystem operations
expect(mockSourceFs1.open).not.toHaveBeenCalled();
});
it('should copy files from source to target filesystem', async () => {
// Mock directory structure
const mockDirInfo = {
...mockFileInfo,
name: jest.fn().mockReturnValue(''),
isDir: jest.fn().mockReturnValue(true),
};
const mockDirFile = {
...mockFile,
stat: jest.fn().mockResolvedValue(mockDirInfo),
readdir: jest.fn().mockResolvedValue([mockFileInfo]),
};
const mockSourceFile = {
...mockFile,
read: jest.fn().mockResolvedValue({ bytesRead: 100, buffer: Buffer.from('test content') }),
};
const mockTargetFile = {
...mockFile,
write: jest.fn().mockResolvedValue({ bytesWritten: 100, buffer: Buffer.from('test content') }),
};
mockSourceFs1.open
.mockResolvedValueOnce(mockDirFile) // Root directory
.mockResolvedValueOnce(mockSourceFile); // Source file
mockTargetFs.open.mockResolvedValue(mockTargetFile);
await staticCopier.copy();
expect(mockSourceFs1.open).toHaveBeenCalledWith('/');
expect(mockTargetFs.create).toHaveBeenCalled();
});
it('should handle filesystem errors gracefully', async () => {
mockSourceFs1.open.mockRejectedValue(new Error('Filesystem error'));
// Should not throw, but should log error and continue
await expect(staticCopier.copy()).resolves.not.toThrow();
});
it('should copy from multiple source filesystems', async () => {
const multiSourceCopier = new static_copier_1.StaticCopier([mockSourceFs1, mockSourceFs2], mockTargetFs);
// Mock simple file structure for both sources
const mockDirInfo = {
...mockFileInfo,
isDir: jest.fn().mockReturnValue(true),
};
const mockDirFile = {
...mockFile,
stat: jest.fn().mockResolvedValue(mockDirInfo),
readdir: jest.fn().mockResolvedValue([]),
};
mockSourceFs1.open.mockResolvedValue(mockDirFile);
mockSourceFs2.open.mockResolvedValue(mockDirFile);
await multiSourceCopier.copy();
expect(mockSourceFs1.open).toHaveBeenCalledWith('/');
expect(mockSourceFs2.open).toHaveBeenCalledWith('/');
});
});
describe('copyFile', () => {
beforeEach(() => {
staticCopier = new static_copier_1.StaticCopier([mockSourceFs1], mockTargetFs);
});
it('should copy file content from source to target', async () => {
const sourceContent = Buffer.from('Hello, World!');
const mockSourceFile = {
...mockFile,
read: jest.fn()
.mockResolvedValueOnce({ bytesRead: sourceContent.length, buffer: sourceContent })
.mockResolvedValueOnce({ bytesRead: 0, buffer: Buffer.alloc(0) }), // EOF
};
const mockTargetFile = {
...mockFile,
write: jest.fn().mockResolvedValue({ bytesWritten: sourceContent.length, buffer: sourceContent }),
};
mockSourceFs1.open.mockResolvedValue(mockSourceFile);
mockTargetFs.create.mockResolvedValue(mockTargetFile);
await staticCopier.copyFile(mockSourceFs1, '/test.txt', mockTargetFs, '/target');
expect(mockSourceFs1.open).toHaveBeenCalledWith('/test.txt');
expect(mockTargetFs.create).toHaveBeenCalledWith('/target/test.txt');
expect(mockSourceFile.read).toHaveBeenCalled();
// Check that write was called - implementation may pass array or buffer
expect(mockTargetFile.write).toHaveBeenCalled();
const writeCall = mockTargetFile.write.mock.calls[0][0];
expect(writeCall).toBeDefined();
// NOTE: Current implementation passes Uint8Array instead of Buffer
});
it('should handle file read errors gracefully', async () => {
mockSourceFs1.open.mockRejectedValue(new Error('Cannot read file'));
// Should not throw, but should log error and continue
await expect(staticCopier.copyFile(mockSourceFs1, '/test.txt', mockTargetFs, '/target')).resolves.not.toThrow();
});
it('should handle file write errors gracefully', async () => {
const mockSourceFile = {
...mockFile,
read: jest.fn().mockResolvedValue({ bytesRead: 10, buffer: Buffer.from('test') }),
};
mockSourceFs1.open.mockResolvedValue(mockSourceFile);
mockTargetFs.create.mockRejectedValue(new Error('Cannot write file'));
// Should not throw, but should log error and continue
await expect(staticCopier.copyFile(mockSourceFs1, '/test.txt', mockTargetFs, '/target')).resolves.not.toThrow();
});
});
describe('walkFileSystem', () => {
beforeEach(() => {
staticCopier = new static_copier_1.StaticCopier([mockSourceFs1], mockTargetFs);
});
it('should handle single file', async () => {
const callback = jest.fn();
mockFile.stat.mockResolvedValue(mockFileInfo);
await staticCopier.walkFileSystem(mockSourceFs1, '/test.txt', callback);
expect(callback).toHaveBeenCalledWith('/test.txt', false);
expect(mockFile.close).toHaveBeenCalled();
});
it('should handle directory with files', async () => {
const callback = jest.fn();
const mockDirInfo = {
...mockFileInfo,
isDir: jest.fn().mockReturnValue(true),
};
const mockChildFile = {
name: jest.fn().mockReturnValue('child.txt'),
isDir: jest.fn().mockReturnValue(false),
};
const mockDirFile = {
...mockFile,
stat: jest.fn().mockResolvedValue(mockDirInfo),
readdir: jest.fn().mockResolvedValue([mockChildFile]),
};
const mockChildFileHandle = {
...mockFile,
stat: jest.fn().mockResolvedValue(mockChildFile),
};
mockSourceFs1.open
.mockResolvedValueOnce(mockDirFile) // Directory
.mockResolvedValueOnce(mockChildFileHandle); // Child file
await staticCopier.walkFileSystem(mockSourceFs1, '/testdir', callback);
expect(callback).toHaveBeenCalledWith('/testdir/child.txt', false);
});
it('should handle nested directories', async () => {
const callback = jest.fn();
const mockDirInfo = {
...mockFileInfo,
isDir: jest.fn().mockReturnValue(true),
};
const mockSubDirInfo = {
name: jest.fn().mockReturnValue('subdir'),
isDir: jest.fn().mockReturnValue(true),
};
const mockDirFile = {
...mockFile,
stat: jest.fn().mockResolvedValue(mockDirInfo),
readdir: jest.fn().mockResolvedValue([mockSubDirInfo]),
};
const mockSubDirFile = {
...mockFile,
stat: jest.fn().mockResolvedValue(mockSubDirInfo),
readdir: jest.fn().mockResolvedValue([]),
};
mockSourceFs1.open
.mockResolvedValueOnce(mockDirFile) // Root directory
.mockResolvedValueOnce(mockSubDirFile); // Subdirectory
await staticCopier.walkFileSystem(mockSourceFs1, '/testdir', callback);
expect(mockSourceFs1.open).toHaveBeenCalledWith('/testdir');
expect(mockSourceFs1.open).toHaveBeenCalledWith('/testdir/subdir');
});
it('should handle filesystem errors during walk gracefully', async () => {
const callback = jest.fn();
mockSourceFs1.open.mockRejectedValue(new Error('Cannot access path'));
// Should not throw, but should log error and continue
await expect(staticCopier.walkFileSystem(mockSourceFs1, '/nonexistent', callback)).resolves.not.toThrow();
});
});
describe('edge cases', () => {
it('should handle null source filesystems', async () => {
const nullSourceCopier = new static_copier_1.StaticCopier(null, mockTargetFs);
await nullSourceCopier.copy();
// Should not crash and not attempt operations
expect(mockTargetFs.open).not.toHaveBeenCalled();
});
it('should handle undefined target filesystem', async () => {
const undefinedTargetCopier = new static_copier_1.StaticCopier([mockSourceFs1], undefined);
await undefinedTargetCopier.copy();
// Should not crash and not attempt operations
expect(mockSourceFs1.open).not.toHaveBeenCalled();
});
it('should handle empty directory', async () => {
const callback = jest.fn();
const mockDirInfo = {
...mockFileInfo,
isDir: jest.fn().mockReturnValue(true),
};
const mockDirFile = {
...mockFile,
stat: jest.fn().mockResolvedValue(mockDirInfo),
readdir: jest.fn().mockResolvedValue([]),
};
mockSourceFs1.open.mockResolvedValue(mockDirFile);
await staticCopier.walkFileSystem(mockSourceFs1, '/empty', callback);
expect(callback).not.toHaveBeenCalled(); // No files to process
expect(mockDirFile.close).toHaveBeenCalled();
});
});
});
//# sourceMappingURL=vo-static-copier.test.js.map