UNPKG

spanwright

Version:

CLI tool to generate Cloud Spanner E2E testing framework projects with Go database tools and Playwright browser automation

667 lines 41 kB
"use strict"; 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