@mdfriday/foundry
Version:
The core engine of MDFriday. Convert Markdown and shortcodes into fully themed static sites – Hugo-style, powered by TypeScript.
363 lines • 16.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const osfs_1 = require("../vo/osfs");
const fs = __importStar(require("fs/promises"));
// Mock fs/promises
jest.mock('fs/promises');
jest.mock('@pkg/log');
const mockFs = fs;
describe('OS Filesystem Value Objects', () => {
afterEach(() => {
jest.clearAllMocks();
});
describe('OsFileInfo', () => {
let mockStats;
let osFileInfo;
beforeEach(() => {
mockStats = {
size: 1024,
mode: 0o644,
mtime: new Date('2023-01-01'),
isDirectory: jest.fn().mockReturnValue(false),
};
osFileInfo = new osfs_1.OsFileInfo(mockStats, 'test.txt');
});
it('should return correct name', () => {
expect(osFileInfo.name()).toBe('test.txt');
});
it('should return correct size', () => {
expect(osFileInfo.size()).toBe(1024);
});
it('should return correct mode', () => {
expect(osFileInfo.mode()).toBe(0o644);
});
it('should return correct modification time', () => {
expect(osFileInfo.modTime()).toEqual(new Date('2023-01-01'));
});
it('should return correct isDir status', () => {
expect(osFileInfo.isDir()).toBe(false);
expect(mockStats.isDirectory).toHaveBeenCalledTimes(1);
});
it('should return stats as sys info', () => {
expect(osFileInfo.sys()).toBe(mockStats);
});
it('should handle directory stats', () => {
mockStats.isDirectory.mockReturnValue(true);
expect(osFileInfo.isDir()).toBe(true);
});
});
describe('OsFile', () => {
let osFile;
let mockHandle;
beforeEach(() => {
mockHandle = {
close: jest.fn(),
read: jest.fn(),
write: jest.fn(),
stat: jest.fn(),
sync: jest.fn(),
truncate: jest.fn(),
};
mockFs.open.mockResolvedValue(mockHandle);
osFile = new osfs_1.OsFile('/test/file.txt', 'r');
});
describe('constructor', () => {
it('should initialize with file path and flags', () => {
expect(osFile.name()).toBe('/test/file.txt');
});
});
describe('ensureOpen', () => {
it('should open file handle when not already open', async () => {
await osFile.ensureOpen();
expect(mockFs.open).toHaveBeenCalledWith('/test/file.txt', 'r');
});
it('should not open file handle when already open', async () => {
await osFile.ensureOpen();
await osFile.ensureOpen();
expect(mockFs.open).toHaveBeenCalledTimes(1);
});
it('should not open file handle when closed', async () => {
await osFile.close();
await osFile.ensureOpen();
expect(mockFs.open).not.toHaveBeenCalled();
});
});
describe('close', () => {
it('should close file handle when open', async () => {
await osFile.ensureOpen();
await osFile.close();
expect(mockHandle.close).toHaveBeenCalledTimes(1);
});
it('should handle close when handle is null', async () => {
await osFile.close();
expect(mockHandle.close).not.toHaveBeenCalled();
});
it('should set closed flag', async () => {
await osFile.close();
await expect(osFile.read(new Uint8Array(10))).rejects.toThrow('File is closed');
});
});
describe('read', () => {
it('should read data from file', async () => {
const buffer = new Uint8Array(10);
mockHandle.read.mockResolvedValue({ bytesRead: 5 });
const result = await osFile.read(buffer);
expect(mockHandle.read).toHaveBeenCalledWith(buffer, 0, 10, 0);
expect(result.bytesRead).toBe(5);
expect(result.buffer).toBe(buffer);
});
it('should update position after read', async () => {
const buffer = new Uint8Array(10);
mockHandle.read.mockResolvedValue({ bytesRead: 5 });
await osFile.read(buffer);
await osFile.read(buffer);
expect(mockHandle.read).toHaveBeenNthCalledWith(1, buffer, 0, 10, 0);
expect(mockHandle.read).toHaveBeenNthCalledWith(2, buffer, 0, 10, 5);
});
it('should throw error when file is closed', async () => {
await osFile.close();
await expect(osFile.read(new Uint8Array(10))).rejects.toThrow('File is closed');
});
});
describe('readAt', () => {
it('should read data at specific offset', async () => {
const buffer = new Uint8Array(10);
mockHandle.read.mockResolvedValue({ bytesRead: 5 });
const result = await osFile.readAt(buffer, 100);
expect(mockHandle.read).toHaveBeenCalledWith(buffer, 0, 10, 100);
expect(result.bytesRead).toBe(5);
});
});
describe('seek', () => {
it('should seek from beginning (SEEK_SET)', async () => {
const position = await osFile.seek(100, 0);
expect(position).toBe(100);
});
it('should seek from current position (SEEK_CUR)', async () => {
await osFile.seek(50, 0); // Set to 50
const position = await osFile.seek(25, 1); // Add 25
expect(position).toBe(75);
});
it('should seek from end (SEEK_END)', async () => {
mockHandle.stat.mockResolvedValue({ size: 1000 });
const position = await osFile.seek(-10, 2);
expect(position).toBe(990);
expect(mockHandle.stat).toHaveBeenCalledTimes(1);
});
});
describe('write', () => {
it('should write data to file', async () => {
const buffer = new Uint8Array([1, 2, 3]);
mockHandle.write.mockResolvedValue({ bytesWritten: 3 });
const result = await osFile.write(buffer);
expect(mockHandle.write).toHaveBeenCalledWith(buffer, 0, 3, 0);
expect(result.bytesWritten).toBe(3);
expect(result.buffer).toBe(buffer);
});
it('should update position after write', async () => {
const buffer = new Uint8Array([1, 2, 3]);
mockHandle.write.mockResolvedValue({ bytesWritten: 3 });
await osFile.write(buffer);
await osFile.write(buffer);
expect(mockHandle.write).toHaveBeenNthCalledWith(1, buffer, 0, 3, 0);
expect(mockHandle.write).toHaveBeenNthCalledWith(2, buffer, 0, 3, 3);
});
});
describe('writeString', () => {
it('should write string to file', async () => {
const testString = 'Hello World';
mockHandle.write.mockResolvedValue({ bytesWritten: 11 });
const result = await osFile.writeString(testString);
expect(result.bytesWritten).toBe(11);
expect(mockHandle.write).toHaveBeenCalledWith(Buffer.from(testString, 'utf8'), 0, 11, 0);
});
});
describe('readdir', () => {
it('should read directory entries', async () => {
const mockDirents = [
{ name: 'file1.txt' },
{ name: 'file2.txt' },
];
const mockStats = { size: 100, mode: 0o644, mtime: new Date() };
mockFs.readdir.mockResolvedValue(mockDirents);
mockFs.stat.mockResolvedValue(mockStats);
const result = await osFile.readdir(0);
expect(mockFs.readdir).toHaveBeenCalledWith('/test/file.txt', { withFileTypes: true });
expect(result).toHaveLength(2);
expect(result[0].name()).toBe('file1.txt');
expect(result[1].name()).toBe('file2.txt');
});
it('should limit results when count is specified', async () => {
const mockDirents = [
{ name: 'file1.txt' },
{ name: 'file2.txt' },
{ name: 'file3.txt' },
];
const mockStats = { size: 100, mode: 0o644, mtime: new Date() };
mockFs.readdir.mockResolvedValue(mockDirents);
mockFs.stat.mockResolvedValue(mockStats);
const result = await osFile.readdir(2);
expect(result).toHaveLength(2);
});
});
describe('truncate', () => {
it('should truncate file to specified size', async () => {
await osFile.truncate(100);
expect(mockHandle.truncate).toHaveBeenCalledWith(100);
});
it('should reset position when truncated below current position', async () => {
await osFile.seek(200, 0);
await osFile.truncate(100);
const position = await osFile.seek(0, 1); // Get current position
expect(position).toBe(100);
});
});
});
describe('OsFs', () => {
let osFs;
beforeEach(() => {
osFs = new osfs_1.OsFs();
});
describe('create', () => {
it('should create new file', async () => {
const result = await osFs.create('/test/newfile.txt');
expect(mockFs.writeFile).toHaveBeenCalledWith('/test/newfile.txt', '');
expect(result).toBeInstanceOf(osfs_1.OsFile);
expect(result.name()).toBe('/test/newfile.txt');
});
});
describe('mkdir', () => {
it('should create directory', async () => {
await osFs.mkdir('/test/newdir', 0o755);
expect(mockFs.mkdir).toHaveBeenCalledWith('/test/newdir', { mode: 0o755 });
});
});
describe('mkdirAll', () => {
it('should create directory recursively', async () => {
await osFs.mkdirAll('/test/deep/dir', 0o755);
expect(mockFs.mkdir).toHaveBeenCalledWith('/test/deep/dir', {
mode: 0o755,
recursive: true
});
});
});
describe('open', () => {
it('should open existing file', async () => {
const mockStats = { isDirectory: () => false };
mockFs.stat.mockResolvedValue(mockStats);
mockFs.access.mockResolvedValue();
const result = await osFs.open('/test/file.txt');
expect(mockFs.access).toHaveBeenCalledWith('/test/file.txt');
expect(mockFs.stat).toHaveBeenCalledWith('/test/file.txt');
expect(result).toBeInstanceOf(osfs_1.OsFile);
});
it('should open directory', async () => {
const mockStats = { isDirectory: () => true };
mockFs.stat.mockResolvedValue(mockStats);
mockFs.access.mockResolvedValue();
const result = await osFs.open('/test/dir');
expect(result).toBeInstanceOf(osfs_1.OsFile);
});
});
describe('remove', () => {
it('should remove file', async () => {
const mockStats = { isDirectory: () => false };
mockFs.stat.mockResolvedValue(mockStats);
await osFs.remove('/test/file.txt');
expect(mockFs.unlink).toHaveBeenCalledWith('/test/file.txt');
});
it('should remove directory', async () => {
const mockStats = { isDirectory: () => true };
mockFs.stat.mockResolvedValue(mockStats);
await osFs.remove('/test/dir');
expect(mockFs.rmdir).toHaveBeenCalledWith('/test/dir');
});
});
describe('removeAll', () => {
it('should remove directory recursively', async () => {
await osFs.removeAll('/test/dir');
expect(mockFs.rm).toHaveBeenCalledWith('/test/dir', {
recursive: true,
force: true
});
});
});
describe('rename', () => {
it('should rename file', async () => {
await osFs.rename('/test/old.txt', '/test/new.txt');
expect(mockFs.rename).toHaveBeenCalledWith('/test/old.txt', '/test/new.txt');
});
});
describe('stat', () => {
it('should return file stats', async () => {
const mockStats = { size: 100, mode: 0o644, mtime: new Date() };
mockFs.stat.mockResolvedValue(mockStats);
const result = await osFs.stat('/test/file.txt');
expect(mockFs.stat).toHaveBeenCalledWith('/test/file.txt');
expect(result).toBeInstanceOf(osfs_1.OsFileInfo);
expect(result.name()).toBe('file.txt');
});
});
describe('name', () => {
it('should return filesystem name', () => {
expect(osFs.name()).toBe('OsFs');
});
});
describe('permission methods', () => {
it('should change file mode', async () => {
await osFs.chmod('/test/file.txt', 0o644);
expect(mockFs.chmod).toHaveBeenCalledWith('/test/file.txt', 0o644);
});
it('should change file ownership', async () => {
await osFs.chown('/test/file.txt', 1000, 1000);
expect(mockFs.chown).toHaveBeenCalledWith('/test/file.txt', 1000, 1000);
});
it('should change file times', async () => {
const atime = new Date('2023-01-01');
const mtime = new Date('2023-01-02');
await osFs.chtimes('/test/file.txt', atime, mtime);
expect(mockFs.utimes).toHaveBeenCalledWith('/test/file.txt', atime, mtime);
});
});
});
describe('newOsFs factory function', () => {
it('should create new OsFs instance', () => {
const result = (0, osfs_1.newOsFs)();
expect(result).toBeInstanceOf(osfs_1.OsFs);
});
});
});
//# sourceMappingURL=vo-osfs.test.js.map