spanwright
Version:
CLI tool to generate Cloud Spanner E2E testing framework projects with Go database tools and Playwright browser automation
667 lines • 41 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 vitest_1 = require("vitest");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const file_operations_1 = require("../file-operations");
const errors_1 = require("../errors");
const constants_1 = require("../constants");
// Mock the fs module
vitest_1.vi.mock('fs');
(0, vitest_1.describe)('File Operations Module', () => {
let mockFs;
(0, vitest_1.beforeEach)(() => {
mockFs = {
existsSync: vitest_1.vi.mocked(fs.existsSync),
mkdirSync: vitest_1.vi.mocked(fs.mkdirSync),
readdirSync: vitest_1.vi.mocked(fs.readdirSync),
statSync: vitest_1.vi.mocked(fs.statSync),
copyFileSync: vitest_1.vi.mocked(fs.copyFileSync),
unlinkSync: vitest_1.vi.mocked(fs.unlinkSync),
renameSync: vitest_1.vi.mocked(fs.renameSync),
readFileSync: vitest_1.vi.mocked(fs.readFileSync),
writeFileSync: vitest_1.vi.mocked(fs.writeFileSync),
};
});
(0, vitest_1.afterEach)(() => {
vitest_1.vi.restoreAllMocks();
});
(0, vitest_1.describe)('ensureDirectoryExists', () => {
(0, vitest_1.it)('should create directory if it does not exist', () => {
const dirPath = '/test/directory';
mockFs.mkdirSync.mockReturnValue(undefined);
(0, file_operations_1.ensureDirectoryExists)(dirPath);
(0, vitest_1.expect)(mockFs.mkdirSync).toHaveBeenCalledWith(dirPath, { recursive: true });
});
(0, vitest_1.it)('should create directory idempotently', () => {
const dirPath = '/test/directory';
mockFs.mkdirSync.mockReturnValue(undefined);
(0, file_operations_1.ensureDirectoryExists)(dirPath);
(0, vitest_1.expect)(mockFs.mkdirSync).toHaveBeenCalledWith(dirPath, { recursive: true });
});
(0, vitest_1.it)('should throw FileSystemError if directory creation fails', () => {
const dirPath = '/test/directory';
mockFs.existsSync.mockReturnValue(false);
mockFs.mkdirSync.mockImplementation(() => {
throw new Error('Permission denied');
});
(0, vitest_1.expect)(() => (0, file_operations_1.ensureDirectoryExists)(dirPath)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.ensureDirectoryExists)(dirPath)).toThrow('Failed to create directory: /test/directory');
});
(0, vitest_1.it)('should throw FileSystemError if mkdirSync throws error', () => {
const dirPath = '/test/directory';
mockFs.mkdirSync.mockImplementation(() => {
throw new Error('Permission denied');
});
(0, vitest_1.expect)(() => (0, file_operations_1.ensureDirectoryExists)(dirPath)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.ensureDirectoryExists)(dirPath)).toThrow('Failed to create directory: /test/directory');
});
(0, vitest_1.it)('should throw SecurityError for path traversal attempts', () => {
const dirPath = '../../../etc';
(0, vitest_1.expect)(() => (0, file_operations_1.ensureDirectoryExists)(dirPath)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.ensureDirectoryExists)(dirPath)).toThrow('Path traversal attempt detected');
});
(0, vitest_1.it)('should throw SecurityError for null byte injection', () => {
const dirPath = '/test/directory\0/evil';
(0, vitest_1.expect)(() => (0, file_operations_1.ensureDirectoryExists)(dirPath)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.ensureDirectoryExists)(dirPath)).toThrow('Null byte in path detected');
});
});
(0, vitest_1.describe)('copyDirectory', () => {
(0, vitest_1.it)('should copy directory with files recursively', () => {
const src = '/source';
const dest = '/destination';
mockFs.existsSync.mockReturnValue(false); // For ensureDirectoryExists
mockFs.mkdirSync.mockReturnValue(undefined);
mockFs.readdirSync.mockReturnValue(['file1.txt', 'subdir']);
mockFs.statSync
.mockReturnValueOnce({ isDirectory: () => false }) // file1.txt
.mockReturnValueOnce({ isDirectory: () => true }); // subdir
mockFs.copyFileSync.mockReturnValue(undefined);
// Mock recursive call
mockFs.readdirSync.mockReturnValueOnce(['file2.txt']);
mockFs.statSync.mockReturnValueOnce({ isDirectory: () => false });
(0, file_operations_1.copyDirectory)(src, dest);
(0, vitest_1.expect)(mockFs.readdirSync).toHaveBeenCalledWith(src);
(0, vitest_1.expect)(mockFs.copyFileSync).toHaveBeenCalledWith(path.join(src, 'file2.txt'), path.join(dest, 'file2.txt'));
(0, vitest_1.expect)(mockFs.mkdirSync).toHaveBeenCalledWith(dest, { recursive: true });
});
(0, vitest_1.it)('should handle empty directory', () => {
const src = '/source';
const dest = '/destination';
mockFs.existsSync.mockReturnValue(false);
mockFs.mkdirSync.mockReturnValue(undefined);
mockFs.readdirSync.mockReturnValue([]);
(0, file_operations_1.copyDirectory)(src, dest);
(0, vitest_1.expect)(mockFs.readdirSync).toHaveBeenCalledWith(src);
(0, vitest_1.expect)(mockFs.copyFileSync).not.toHaveBeenCalled();
});
(0, vitest_1.it)('should throw FileSystemError if source directory cannot be read', () => {
const src = '/source';
const dest = '/destination';
mockFs.existsSync.mockReturnValue(false);
mockFs.mkdirSync.mockReturnValue(undefined);
mockFs.readdirSync.mockImplementation(() => {
throw new Error('Directory not found');
});
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow('Failed to copy directory from /source to /destination');
});
(0, vitest_1.it)('should throw FileSystemError if file copy fails', () => {
const src = '/source';
const dest = '/destination';
mockFs.existsSync.mockReturnValue(false);
mockFs.mkdirSync.mockReturnValue(undefined);
mockFs.readdirSync.mockReturnValue(['file1.txt']);
mockFs.statSync.mockReturnValue({ isDirectory: () => false });
mockFs.copyFileSync.mockImplementation(() => {
throw new Error('Copy failed');
});
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow('Failed to copy directory from /source to /destination');
});
(0, vitest_1.it)('should throw FileSystemError if statSync fails', () => {
const src = '/source';
const dest = '/destination';
mockFs.existsSync.mockReturnValue(false);
mockFs.mkdirSync.mockReturnValue(undefined);
mockFs.readdirSync.mockReturnValue(['file1.txt']);
mockFs.statSync.mockImplementation(() => {
throw new Error('Stat failed');
});
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow('Failed to copy directory from /source to /destination');
});
(0, vitest_1.it)('should throw SecurityError for path traversal in source', () => {
const src = '../../../etc/passwd';
const dest = '/destination';
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow('Path traversal attempt detected');
});
(0, vitest_1.it)('should throw SecurityError for path traversal in destination', () => {
const src = '/source';
const dest = '../../../etc/evil';
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow('Path traversal attempt detected');
});
(0, vitest_1.it)('should throw SecurityError for path traversal in file paths during copy', () => {
const src = '/source';
const dest = '/destination';
mockFs.existsSync.mockReturnValue(false);
mockFs.mkdirSync.mockReturnValue(undefined);
// Return a file that would traverse outside when joined
mockFs.readdirSync.mockReturnValue(['../../../etc/passwd']);
(0, vitest_1.expect)(() => (0, file_operations_1.copyDirectory)(src, dest)).toThrow(errors_1.SecurityError);
});
});
(0, vitest_1.describe)('safeFileExists', () => {
(0, vitest_1.it)('should return true if file exists', () => {
const filePath = '/test/file.txt';
mockFs.existsSync.mockReturnValue(true);
const result = (0, file_operations_1.safeFileExists)(filePath);
(0, vitest_1.expect)(result).toBe(true);
(0, vitest_1.expect)(mockFs.existsSync).toHaveBeenCalledWith(filePath);
});
(0, vitest_1.it)('should return false if file does not exist', () => {
const filePath = '/test/file.txt';
mockFs.existsSync.mockReturnValue(false);
const result = (0, file_operations_1.safeFileExists)(filePath);
(0, vitest_1.expect)(result).toBe(false);
(0, vitest_1.expect)(mockFs.existsSync).toHaveBeenCalledWith(filePath);
});
(0, vitest_1.it)('should return false if existsSync throws error', () => {
const filePath = '/test/file.txt';
mockFs.existsSync.mockImplementation(() => {
throw new Error('Access denied');
});
const result = (0, file_operations_1.safeFileExists)(filePath);
(0, vitest_1.expect)(result).toBe(false);
(0, vitest_1.expect)(mockFs.existsSync).toHaveBeenCalledWith(filePath);
});
});
(0, vitest_1.describe)('safeFileDelete', () => {
(0, vitest_1.it)('should delete file if it exists', () => {
const filePath = '/test/file.txt';
mockFs.unlinkSync.mockReturnValue(undefined);
(0, file_operations_1.safeFileDelete)(filePath);
(0, vitest_1.expect)(mockFs.unlinkSync).toHaveBeenCalledWith(filePath);
});
(0, vitest_1.it)('should handle file not existing gracefully', () => {
const filePath = '/test/file.txt';
const error = new Error('File not found');
error.code = 'ENOENT';
mockFs.unlinkSync.mockImplementation(() => {
throw error;
});
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileDelete)(filePath)).not.toThrow();
(0, vitest_1.expect)(mockFs.unlinkSync).toHaveBeenCalledWith(filePath);
});
(0, vitest_1.it)('should throw FileSystemError if deletion fails', () => {
const filePath = '/test/file.txt';
mockFs.existsSync.mockReturnValue(true);
mockFs.unlinkSync.mockImplementation(() => {
throw new Error('Permission denied');
});
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileDelete)(filePath)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileDelete)(filePath)).toThrow('Failed to delete file: /test/file.txt');
});
(0, vitest_1.it)('should handle existsSync errors gracefully', () => {
const filePath = '/test/file.txt';
mockFs.existsSync.mockImplementation(() => {
throw new Error('Access denied');
});
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileDelete)(filePath)).not.toThrow();
});
(0, vitest_1.it)('should throw SecurityError for path traversal attempts', () => {
const filePath = '../../../etc/passwd';
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileDelete)(filePath)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileDelete)(filePath)).toThrow('Path traversal attempt detected');
});
});
(0, vitest_1.describe)('safeFileRename', () => {
(0, vitest_1.it)('should rename file if it exists', () => {
const oldPath = '/test/old.txt';
const newPath = '/test/new.txt';
mockFs.renameSync.mockReturnValue(undefined);
(0, file_operations_1.safeFileRename)(oldPath, newPath);
(0, vitest_1.expect)(mockFs.renameSync).toHaveBeenCalledWith(oldPath, newPath);
});
(0, vitest_1.it)('should handle file not existing for rename', () => {
const oldPath = '/test/old.txt';
const newPath = '/test/new.txt';
mockFs.renameSync.mockImplementation(() => {
throw new Error('File not found');
});
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileRename)(oldPath, newPath)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(mockFs.renameSync).toHaveBeenCalledWith(oldPath, newPath);
});
(0, vitest_1.it)('should throw FileSystemError if rename fails', () => {
const oldPath = '/test/old.txt';
const newPath = '/test/new.txt';
mockFs.existsSync.mockReturnValue(true);
mockFs.renameSync.mockImplementation(() => {
throw new Error('Permission denied');
});
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileRename)(oldPath, newPath)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileRename)(oldPath, newPath)).toThrow('Failed to rename file from /test/old.txt to /test/new.txt');
});
(0, vitest_1.it)('should handle existsSync errors gracefully', () => {
const oldPath = '/test/old.txt';
const newPath = '/test/new.txt';
mockFs.existsSync.mockImplementation(() => {
throw new Error('Access denied');
});
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileRename)(oldPath, newPath)).not.toThrow();
});
(0, vitest_1.it)('should throw SecurityError for path traversal in old path', () => {
const oldPath = '../../../etc/passwd';
const newPath = '/test/new.txt';
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileRename)(oldPath, newPath)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileRename)(oldPath, newPath)).toThrow('Path traversal attempt detected');
});
(0, vitest_1.it)('should throw SecurityError for path traversal in new path', () => {
const oldPath = '/test/old.txt';
const newPath = '../../../etc/passwd';
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileRename)(oldPath, newPath)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.safeFileRename)(oldPath, newPath)).toThrow('Path traversal attempt detected');
});
});
(0, vitest_1.describe)('readFileContent', () => {
(0, vitest_1.it)('should read file content successfully', () => {
const filePath = '/test/file.txt';
const content = 'Hello, World!';
mockFs.readFileSync.mockReturnValue(content);
const result = (0, file_operations_1.readFileContent)(filePath);
(0, vitest_1.expect)(result).toBe(content);
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(filePath, 'utf8');
});
(0, vitest_1.it)('should throw FileSystemError if file read fails', () => {
const filePath = '/test/file.txt';
mockFs.readFileSync.mockImplementation(() => {
throw new Error('File not found');
});
(0, vitest_1.expect)(() => (0, file_operations_1.readFileContent)(filePath)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.readFileContent)(filePath)).toThrow('Failed to read file: /test/file.txt');
});
(0, vitest_1.it)('should handle empty file content', () => {
const filePath = '/test/empty.txt';
mockFs.readFileSync.mockReturnValue('');
const result = (0, file_operations_1.readFileContent)(filePath);
(0, vitest_1.expect)(result).toBe('');
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(filePath, 'utf8');
});
(0, vitest_1.it)('should throw SecurityError for path traversal attempts', () => {
const filePath = '../../../etc/passwd';
(0, vitest_1.expect)(() => (0, file_operations_1.readFileContent)(filePath)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.readFileContent)(filePath)).toThrow('Path traversal attempt detected');
});
(0, vitest_1.it)('should throw SecurityError for absolute paths', () => {
const filePath = '/etc/passwd';
(0, vitest_1.expect)(() => (0, file_operations_1.readFileContent)(filePath)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.readFileContent)(filePath)).toThrow('Path traversal attempt detected');
});
});
(0, vitest_1.describe)('writeFileContent', () => {
(0, vitest_1.it)('should write file content successfully', () => {
const filePath = '/test/file.txt';
const content = 'Hello, World!';
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.writeFileContent)(filePath, content);
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(filePath, content, 'utf8');
});
(0, vitest_1.it)('should throw FileSystemError if file write fails', () => {
const filePath = '/test/file.txt';
const content = 'Hello, World!';
mockFs.writeFileSync.mockImplementation(() => {
throw new Error('Permission denied');
});
(0, vitest_1.expect)(() => (0, file_operations_1.writeFileContent)(filePath, content)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.writeFileContent)(filePath, content)).toThrow('Failed to write file: /test/file.txt');
});
(0, vitest_1.it)('should handle empty content', () => {
const filePath = '/test/empty.txt';
const content = '';
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.writeFileContent)(filePath, content);
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(filePath, content, 'utf8');
});
(0, vitest_1.it)('should throw SecurityError for path traversal attempts', () => {
const filePath = '../../../etc/passwd';
const content = 'malicious content';
(0, vitest_1.expect)(() => (0, file_operations_1.writeFileContent)(filePath, content)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.writeFileContent)(filePath, content)).toThrow('Path traversal attempt detected');
});
(0, vitest_1.it)('should throw SecurityError for null byte injection', () => {
const filePath = '/test/file.txt\0.sh';
const content = 'malicious content';
(0, vitest_1.expect)(() => (0, file_operations_1.writeFileContent)(filePath, content)).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, file_operations_1.writeFileContent)(filePath, content)).toThrow('Null byte in path detected');
});
});
(0, vitest_1.describe)('escapeRegExp', () => {
(0, vitest_1.it)('should escape regex special characters', () => {
const input = 'Hello.*+?^${}()|[]\\World';
const expected = 'Hello\\.\\*\\+\\?\\^\\$\\{\\}\\(\\)\\|\\[\\]\\\\World';
const result = (0, file_operations_1.escapeRegExp)(input);
(0, vitest_1.expect)(result).toBe(expected);
});
(0, vitest_1.it)('should return same string if no special characters', () => {
const input = 'HelloWorld123';
const expected = 'HelloWorld123';
const result = (0, file_operations_1.escapeRegExp)(input);
(0, vitest_1.expect)(result).toBe(expected);
});
(0, vitest_1.it)('should handle empty string', () => {
const input = '';
const expected = '';
const result = (0, file_operations_1.escapeRegExp)(input);
(0, vitest_1.expect)(result).toBe(expected);
});
(0, vitest_1.it)('should handle string with only special characters', () => {
const input = '.*+?^${}()|[]\\';
const expected = '\\.\\*\\+\\?\\^\\$\\{\\}\\(\\)\\|\\[\\]\\\\';
const result = (0, file_operations_1.escapeRegExp)(input);
(0, vitest_1.expect)(result).toBe(expected);
});
});
(0, vitest_1.describe)('replaceInFile', () => {
(0, vitest_1.it)('should replace single occurrence in file', () => {
const filePath = '/test/file.txt';
const originalContent = 'Hello PROJECT_NAME!';
const expectedContent = 'Hello MyProject!';
mockFs.readFileSync.mockReturnValue(originalContent);
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.replaceInFile)(filePath, { PROJECT_NAME: 'MyProject' });
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(filePath, 'utf8');
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(filePath, expectedContent, 'utf8');
});
(0, vitest_1.it)('should replace multiple occurrences in file', () => {
const filePath = '/test/file.txt';
const originalContent = 'Hello PROJECT_NAME! Welcome to PROJECT_NAME.';
const expectedContent = 'Hello MyProject! Welcome to MyProject.';
mockFs.readFileSync.mockReturnValue(originalContent);
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.replaceInFile)(filePath, { PROJECT_NAME: 'MyProject' });
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(filePath, 'utf8');
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(filePath, expectedContent, 'utf8');
});
(0, vitest_1.it)('should handle multiple replacements', () => {
const filePath = '/test/file.txt';
const originalContent = 'Hello PROJECT_NAME from USER_NAME!';
const expectedContent = 'Hello MyProject from John!';
mockFs.readFileSync.mockReturnValue(originalContent);
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.replaceInFile)(filePath, {
PROJECT_NAME: 'MyProject',
USER_NAME: 'John',
});
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(filePath, 'utf8');
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(filePath, expectedContent, 'utf8');
});
(0, vitest_1.it)('should handle replacement with regex special characters', () => {
const filePath = '/test/file.txt';
const originalContent = 'pattern.*+?^${}()|[]\\test';
const expectedContent = 'replacedtest';
mockFs.readFileSync.mockReturnValue(originalContent);
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.replaceInFile)(filePath, { 'pattern.*+?^${}()|[]\\': 'replaced' });
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(filePath, 'utf8');
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(filePath, expectedContent, 'utf8');
});
(0, vitest_1.it)('should handle file with no matches', () => {
const filePath = '/test/file.txt';
const originalContent = 'Hello World!';
const expectedContent = 'Hello World!';
mockFs.readFileSync.mockReturnValue(originalContent);
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.replaceInFile)(filePath, { PROJECT_NAME: 'MyProject' });
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(filePath, 'utf8');
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(filePath, expectedContent, 'utf8');
});
(0, vitest_1.it)('should throw FileSystemError if file read fails', () => {
const filePath = '/test/file.txt';
mockFs.readFileSync.mockImplementation(() => {
throw new Error('File not found');
});
(0, vitest_1.expect)(() => (0, file_operations_1.replaceInFile)(filePath, { PROJECT_NAME: 'MyProject' })).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.replaceInFile)(filePath, { PROJECT_NAME: 'MyProject' })).toThrow('Failed to read file: /test/file.txt');
});
(0, vitest_1.it)('should throw FileSystemError if file write fails', () => {
const filePath = '/test/file.txt';
mockFs.readFileSync.mockReturnValue('Hello PROJECT_NAME!');
mockFs.writeFileSync.mockImplementation(() => {
throw new Error('Permission denied');
});
(0, vitest_1.expect)(() => (0, file_operations_1.replaceInFile)(filePath, { PROJECT_NAME: 'MyProject' })).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.replaceInFile)(filePath, { PROJECT_NAME: 'MyProject' })).toThrow('Failed to write file: /test/file.txt');
});
});
(0, vitest_1.describe)('processTemplateFiles', () => {
(0, vitest_1.it)('should rename template files and process go.mod', () => {
const projectPath = '/test/project';
const projectName = 'my-project';
// Mock file existence checks
mockFs.existsSync
.mockReturnValueOnce(true) // _package.json exists
.mockReturnValueOnce(true) // _gitignore exists
.mockReturnValueOnce(true) // go.mod.template exists
.mockReturnValueOnce(true) // go.mod exists after rename
.mockReturnValueOnce(false) // expected-primary.yaml.template doesn't exist
.mockReturnValueOnce(false); // expected-secondary.yaml.template doesn't exist
mockFs.renameSync.mockReturnValue(undefined);
mockFs.readFileSync.mockReturnValue('module PROJECT_NAME');
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.processTemplateFiles)(projectPath, projectName);
// Check template file renames
(0, vitest_1.expect)(mockFs.renameSync).toHaveBeenCalledWith(path.join(projectPath, constants_1.FILE_PATTERNS.PACKAGE_JSON_TEMPLATE), path.join(projectPath, constants_1.FILE_PATTERNS.PACKAGE_JSON));
(0, vitest_1.expect)(mockFs.renameSync).toHaveBeenCalledWith(path.join(projectPath, constants_1.FILE_PATTERNS.GITIGNORE_TEMPLATE), path.join(projectPath, constants_1.FILE_PATTERNS.GITIGNORE));
(0, vitest_1.expect)(mockFs.renameSync).toHaveBeenCalledWith(path.join(projectPath, constants_1.FILE_PATTERNS.GO_MOD_TEMPLATE), path.join(projectPath, constants_1.FILE_PATTERNS.GO_MOD));
// Check go.mod processing
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(path.join(projectPath, constants_1.FILE_PATTERNS.GO_MOD), 'utf8');
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(path.join(projectPath, constants_1.FILE_PATTERNS.GO_MOD), 'module my-project', 'utf8');
});
(0, vitest_1.it)('should handle missing template files gracefully', () => {
const projectPath = '/test/project';
const projectName = 'my-project';
// Mock renameSync to throw for missing files
mockFs.renameSync.mockImplementation(() => {
throw new Error('File not found');
});
// Should not throw error for missing template files
(0, vitest_1.expect)(() => (0, file_operations_1.processTemplateFiles)(projectPath, projectName)).not.toThrow();
});
(0, vitest_1.it)('should skip go.mod processing if file does not exist after rename', () => {
const projectPath = '/test/project';
const projectName = 'my-project';
// Mock renameSync to succeed
mockFs.renameSync.mockReturnValue(undefined);
// Mock go.mod as non-existent after rename (this uses safeFileExists)
mockFs.existsSync.mockImplementation((path) => {
return !path.endsWith('go.mod');
});
(0, file_operations_1.processTemplateFiles)(projectPath, projectName);
(0, vitest_1.expect)(mockFs.renameSync).toHaveBeenCalledTimes(3);
(0, vitest_1.expect)(mockFs.readFileSync).not.toHaveBeenCalled();
(0, vitest_1.expect)(mockFs.writeFileSync).not.toHaveBeenCalled();
});
(0, vitest_1.it)('should handle validation template files', () => {
const projectPath = '/test/project';
const projectName = 'my-project';
// Mock template files and validation templates exist
mockFs.existsSync
.mockReturnValueOnce(true) // _package.json exists
.mockReturnValueOnce(true) // _gitignore exists
.mockReturnValueOnce(true) // go.mod.template exists
.mockReturnValueOnce(true) // go.mod exists after rename
.mockReturnValueOnce(true) // expected-primary.yaml.template exists
.mockReturnValueOnce(true); // expected-secondary.yaml.template exists
mockFs.renameSync.mockReturnValue(undefined);
mockFs.readFileSync.mockReturnValue('module PROJECT_NAME');
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.processTemplateFiles)(projectPath, projectName);
// Should still process normally, validation templates are preserved
(0, vitest_1.expect)(mockFs.renameSync).toHaveBeenCalledTimes(3);
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledTimes(1);
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledTimes(1);
});
});
(0, vitest_1.describe)('replaceProjectNameInGoFiles', () => {
(0, vitest_1.it)('should replace PROJECT_NAME in Go files recursively', () => {
const projectPath = '/test/project';
const projectName = 'my-project';
// Mock directory structure
mockFs.readdirSync
.mockReturnValueOnce(['main.go', 'subdir']) // root directory
.mockReturnValueOnce(['helper.go', 'test.txt']); // subdir
mockFs.statSync
.mockReturnValueOnce({ isDirectory: () => false }) // main.go
.mockReturnValueOnce({ isDirectory: () => true }) // subdir
.mockReturnValueOnce({ isDirectory: () => false }) // helper.go
.mockReturnValueOnce({ isDirectory: () => false }); // test.txt
mockFs.readFileSync
.mockReturnValueOnce('package PROJECT_NAME') // main.go
.mockReturnValueOnce('import "PROJECT_NAME/pkg"'); // helper.go
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.replaceProjectNameInGoFiles)(projectPath, projectName);
// Should process both Go files
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(path.join(projectPath, 'main.go'), 'utf8');
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(path.join(projectPath, 'subdir', 'helper.go'), 'utf8');
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(path.join(projectPath, 'main.go'), 'package my-project', 'utf8');
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(path.join(projectPath, 'subdir', 'helper.go'), 'import "my-project/pkg"', 'utf8');
// Should not process non-Go files
(0, vitest_1.expect)(mockFs.readFileSync).not.toHaveBeenCalledWith(path.join(projectPath, 'subdir', 'test.txt'), 'utf8');
});
(0, vitest_1.it)('should handle empty directories', () => {
const projectPath = '/test/project';
const projectName = 'my-project';
mockFs.readdirSync.mockReturnValue([]);
(0, file_operations_1.replaceProjectNameInGoFiles)(projectPath, projectName);
(0, vitest_1.expect)(mockFs.statSync).not.toHaveBeenCalled();
(0, vitest_1.expect)(mockFs.readFileSync).not.toHaveBeenCalled();
(0, vitest_1.expect)(mockFs.writeFileSync).not.toHaveBeenCalled();
});
(0, vitest_1.it)('should handle directories with no Go files', () => {
const projectPath = '/test/project';
const projectName = 'my-project';
mockFs.readdirSync.mockReturnValue(['readme.txt', 'config.json']);
mockFs.statSync
.mockReturnValueOnce({ isDirectory: () => false }) // readme.txt
.mockReturnValueOnce({ isDirectory: () => false }); // config.json
(0, file_operations_1.replaceProjectNameInGoFiles)(projectPath, projectName);
(0, vitest_1.expect)(mockFs.readFileSync).not.toHaveBeenCalled();
(0, vitest_1.expect)(mockFs.writeFileSync).not.toHaveBeenCalled();
});
(0, vitest_1.it)('should throw FileSystemError if directory read fails', () => {
const projectPath = '/test/project';
const projectName = 'my-project';
mockFs.readdirSync.mockImplementation(() => {
throw new Error('Permission denied');
});
(0, vitest_1.expect)(() => (0, file_operations_1.replaceProjectNameInGoFiles)(projectPath, projectName)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.replaceProjectNameInGoFiles)(projectPath, projectName)).toThrow('Failed to process directory: /test/project');
});
(0, vitest_1.it)('should throw FileSystemError if stat fails', () => {
const projectPath = '/test/project';
const projectName = 'my-project';
mockFs.readdirSync.mockReturnValue(['main.go']);
mockFs.statSync.mockImplementation(() => {
throw new Error('Stat failed');
});
(0, vitest_1.expect)(() => (0, file_operations_1.replaceProjectNameInGoFiles)(projectPath, projectName)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.replaceProjectNameInGoFiles)(projectPath, projectName)).toThrow('Failed to process directory: /test/project');
});
(0, vitest_1.it)('should handle nested directory structure', () => {
const projectPath = '/test/project';
const projectName = 'my-project';
// Mock deeply nested structure
mockFs.readdirSync
.mockReturnValueOnce(['pkg']) // root
.mockReturnValueOnce(['handlers']) // pkg
.mockReturnValueOnce(['auth.go']); // pkg/handlers
mockFs.statSync
.mockReturnValueOnce({ isDirectory: () => true }) // pkg
.mockReturnValueOnce({ isDirectory: () => true }) // handlers
.mockReturnValueOnce({ isDirectory: () => false }); // auth.go
mockFs.readFileSync.mockReturnValue('package PROJECT_NAME');
mockFs.writeFileSync.mockReturnValue(undefined);
(0, file_operations_1.replaceProjectNameInGoFiles)(projectPath, projectName);
(0, vitest_1.expect)(mockFs.readFileSync).toHaveBeenCalledWith(path.join(projectPath, 'pkg', 'handlers', 'auth.go'), 'utf8');
(0, vitest_1.expect)(mockFs.writeFileSync).toHaveBeenCalledWith(path.join(projectPath, 'pkg', 'handlers', 'auth.go'), 'package my-project', 'utf8');
});
});
(0, vitest_1.describe)('removeSecondaryDbFiles', () => {
(0, vitest_1.it)('should remove secondary database files', () => {
const projectPath = '/test/project';
mockFs.unlinkSync.mockReturnValue(undefined);
(0, file_operations_1.removeSecondaryDbFiles)(projectPath);
(0, vitest_1.expect)(mockFs.unlinkSync).toHaveBeenCalledTimes(3);
});
(0, vitest_1.it)('should handle missing files gracefully', () => {
const projectPath = '/test/project';
const error = new Error('File not found');
error.code = 'ENOENT';
mockFs.unlinkSync.mockImplementation(() => {
throw error;
});
(0, vitest_1.expect)(() => (0, file_operations_1.removeSecondaryDbFiles)(projectPath)).not.toThrow();
});
(0, vitest_1.it)('should handle partial file existence', () => {
const projectPath = '/test/project';
// Mock first file to succeed, others to fail with ENOENT
let callCount = 0;
mockFs.unlinkSync.mockImplementation(() => {
callCount++;
if (callCount > 1) {
const error = new Error('File not found');
error.code = 'ENOENT';
throw error;
}
});
(0, vitest_1.expect)(() => (0, file_operations_1.removeSecondaryDbFiles)(projectPath)).not.toThrow();
(0, vitest_1.expect)(mockFs.unlinkSync).toHaveBeenCalledTimes(3);
});
(0, vitest_1.it)('should throw FileSystemError if file deletion fails', () => {
const projectPath = '/test/project';
mockFs.existsSync.mockReturnValue(true);
mockFs.unlinkSync.mockImplementation(() => {
throw new Error('Permission denied');
});
(0, vitest_1.expect)(() => (0, file_operations_1.removeSecondaryDbFiles)(projectPath)).toThrow(errors_1.FileSystemError);
(0, vitest_1.expect)(() => (0, file_operations_1.removeSecondaryDbFiles)(projectPath)).toThrow('Failed to delete file:');
});
});
});
//# sourceMappingURL=file-operations.test.js.map