UNPKG

kira-crud

Version:

Intelligent CRUD Generator for Laravel and Angular

471 lines (398 loc) 14 kB
#!/usr/bin/env node /** * Transaction Management CLI * Command line tool for testing and demonstrating the transaction system */ const inquirer = require('inquirer'); const chalk = require('chalk'); const ora = require('ora'); const figlet = require('figlet'); const boxen = require('boxen'); const path = require('path'); const fs = require('fs').promises; const { program } = require('commander'); const gradient = require('gradient-string'); const TransactionManager = require('./utils/transaction-manager'); // Constants for styling const titleGradient = gradient(['#4285F4', '#34A853']); /** * Display the banner */ function displayBanner() { console.log( titleGradient.multiline( figlet.textSync('Transaction', { font: 'Small', horizontalLayout: 'default' }) ) ); console.log( boxen( `${chalk.bold('KIRA Transaction Manager')} ${chalk.dim('v1.0.0')}\n` + `Test and demonstrate the transaction rollback system`, { padding: 1, margin: { top: 1, bottom: 1 }, borderStyle: 'round', borderColor: 'blue' } ) ); } /** * Create a test transaction * @param {Object} options - Test options */ async function createTestTransaction(options) { const transaction = new TransactionManager({ verbose: options.verbose, createBackups: true }); await transaction.init(); // Create test directory const testDir = path.join(process.cwd(), 'transaction-test'); await fs.mkdir(testDir, { recursive: true }); // Add operations to the transaction await transaction.addMkdir(testDir); // Create a test file const testFile1 = path.join(testDir, 'test1.txt'); await transaction.addWrite(testFile1, 'This is test file 1'); // Create a second test file const testFile2 = path.join(testDir, 'test2.txt'); await transaction.addWrite(testFile2, 'This is test file 2'); // Create a subdirectory const subDir = path.join(testDir, 'subdir'); await transaction.addMkdir(subDir); // Create a file in the subdirectory const testFile3 = path.join(subDir, 'test3.txt'); await transaction.addWrite(testFile3, 'This is test file 3'); return { transaction, testDir, files: [testFile1, testFile2, testFile3] }; } /** * Run a successful transaction test * @param {Object} options - Test options */ async function runSuccessTest(options) { const spinner = ora('Creating test transaction...').start(); try { const { transaction, testDir, files } = await createTestTransaction(options); spinner.text = 'Executing transaction...'; const success = await transaction.execute(); if (success) { spinner.succeed('Transaction completed successfully'); // Verify files were created let allFilesExist = true; for (const file of files) { try { await fs.access(file); } catch (error) { allFilesExist = false; console.error(chalk.red(`File not found: ${file}`)); } } if (allFilesExist) { console.log(chalk.green('All files were created successfully')); } // Clean up test files if requested if (options.cleanup) { spinner.text = 'Cleaning up test files...'; spinner.start(); await fs.rm(testDir, { recursive: true, force: true }); await transaction.cleanup(); spinner.succeed('Test files cleaned up'); } else { console.log(chalk.yellow(`Test files remain in: ${testDir}`)); console.log(chalk.yellow(`Backup files in: ${transaction.backupPath}`)); } } else { spinner.fail('Transaction failed'); } } catch (error) { spinner.fail('Test failed'); console.error(chalk.red(`Error: ${error.message}`)); } } /** * Run a failed transaction test * @param {Object} options - Test options */ async function runFailureTest(options) { const spinner = ora('Creating test transaction...').start(); try { const { transaction, testDir, files } = await createTestTransaction(options); // Add a failing operation const impossibleDir = '/this/directory/does/not/exist/and/cannot/be/created'; const impossibleFile = path.join(impossibleDir, 'impossible.txt'); await transaction.addWrite(impossibleFile, 'This should fail'); spinner.text = 'Executing transaction (expected to fail)...'; const success = await transaction.execute(); if (success) { spinner.fail('Transaction succeeded unexpectedly'); } else { spinner.succeed('Transaction failed as expected'); // Check if rollback was successful let allFilesRemoved = true; for (const file of files) { try { await fs.access(file); allFilesRemoved = false; console.error(chalk.red(`File still exists: ${file}`)); } catch (error) { // File should not exist after rollback, so this is good } } if (allFilesRemoved) { console.log(chalk.green('All files were properly rolled back')); } else { console.log(chalk.red('Some files were not rolled back properly')); } // Clean up test directory if it still exists try { await fs.access(testDir); if (options.cleanup) { spinner.text = 'Cleaning up test directory...'; spinner.start(); await fs.rm(testDir, { recursive: true, force: true }); await transaction.cleanup(); spinner.succeed('Test directory cleaned up'); } else { console.log(chalk.yellow(`Test directory remains: ${testDir}`)); console.log(chalk.yellow(`Backup directory: ${transaction.backupPath}`)); } } catch (error) { // Directory doesn't exist, which is expected after rollback } } } catch (error) { spinner.fail('Test failed unexpectedly'); console.error(chalk.red(`Error: ${error.message}`)); } } /** * Run an interactive transaction test */ async function runInteractiveTest() { displayBanner(); const { testType } = await inquirer.prompt({ type: 'list', name: 'testType', message: 'Select a test to run:', choices: [ { name: 'Successful Transaction', value: 'success' }, { name: 'Failed Transaction with Rollback', value: 'failure' }, { name: 'Custom Transaction', value: 'custom' } ] }); const options = await inquirer.prompt([ { type: 'confirm', name: 'verbose', message: 'Enable verbose output?', default: true }, { type: 'confirm', name: 'cleanup', message: 'Clean up test files after completion?', default: true } ]); if (testType === 'success') { await runSuccessTest(options); } else if (testType === 'failure') { await runFailureTest(options); } else if (testType === 'custom') { await runCustomTest(options); } } /** * Run a custom transaction test * @param {Object} options - Test options */ async function runCustomTest(options) { console.log(chalk.blue('\nCustom Transaction Test')); console.log(chalk.blue('------------------------')); const transaction = new TransactionManager({ verbose: options.verbose, createBackups: true }); await transaction.init(); // Create test directory const testDir = path.join(process.cwd(), 'custom-transaction-test'); await fs.mkdir(testDir, { recursive: true }); await transaction.addMkdir(testDir); let keepAdding = true; const operations = []; while (keepAdding) { const { operationType } = await inquirer.prompt({ type: 'list', name: 'operationType', message: 'Add operation:', choices: [ { name: 'Write File', value: 'write' }, { name: 'Create Directory', value: 'mkdir' }, { name: 'Delete File', value: 'delete' }, { name: 'Force Failure', value: 'fail' }, { name: 'Execute Transaction', value: 'execute' } ] }); if (operationType === 'execute') { keepAdding = false; } else if (operationType === 'write') { const { fileName, content } = await inquirer.prompt([ { type: 'input', name: 'fileName', message: 'File name:', default: `file${operations.length + 1}.txt` }, { type: 'input', name: 'content', message: 'File content:', default: `This is test file ${operations.length + 1}` } ]); const filePath = path.join(testDir, fileName); await transaction.addWrite(filePath, content); operations.push({ type: 'write', path: filePath }); console.log(chalk.green(`Added write operation for: ${filePath}`)); } else if (operationType === 'mkdir') { const { dirName } = await inquirer.prompt({ type: 'input', name: 'dirName', message: 'Directory name:', default: `dir${operations.length + 1}` }); const dirPath = path.join(testDir, dirName); await transaction.addMkdir(dirPath); operations.push({ type: 'mkdir', path: dirPath }); console.log(chalk.green(`Added mkdir operation for: ${dirPath}`)); } else if (operationType === 'delete') { const { fileName } = await inquirer.prompt({ type: 'input', name: 'fileName', message: 'File to delete:', default: 'file-to-delete.txt' }); // Create the file first so we can delete it const filePath = path.join(testDir, fileName); await fs.writeFile(filePath, 'This file will be deleted'); await transaction.addDelete(filePath); operations.push({ type: 'delete', path: filePath }); console.log(chalk.green(`Added delete operation for: ${filePath}`)); } else if (operationType === 'fail') { const impossibleDir = '/this/directory/does/not/exist'; const impossibleFile = path.join(impossibleDir, 'impossible.txt'); await transaction.addWrite(impossibleFile, 'This should fail'); operations.push({ type: 'write', path: impossibleFile }); console.log(chalk.yellow(`Added failing operation for: ${impossibleFile}`)); } } const spinner = ora('Executing transaction...').start(); try { const success = await transaction.execute(); if (success) { spinner.succeed('Transaction completed successfully'); // Verify files and directories for (const operation of operations) { if (operation.type === 'write') { try { await fs.access(operation.path); console.log(chalk.green(`File created: ${operation.path}`)); } catch (error) { console.log(chalk.red(`File not found: ${operation.path}`)); } } else if (operation.type === 'mkdir') { try { await fs.access(operation.path); console.log(chalk.green(`Directory created: ${operation.path}`)); } catch (error) { console.log(chalk.red(`Directory not found: ${operation.path}`)); } } else if (operation.type === 'delete') { try { await fs.access(operation.path); console.log(chalk.red(`File still exists: ${operation.path}`)); } catch (error) { console.log(chalk.green(`File deleted: ${operation.path}`)); } } } } else { spinner.fail('Transaction failed'); // Check rollback status console.log(chalk.yellow('\nChecking rollback status:')); for (const operation of operations) { if (operation.type === 'write') { try { await fs.access(operation.path); console.log(chalk.red(`File still exists: ${operation.path}`)); } catch (error) { console.log(chalk.green(`File properly rolled back: ${operation.path}`)); } } } } // Clean up test directory if requested if (options.cleanup) { spinner.text = 'Cleaning up test files...'; spinner.start(); await fs.rm(testDir, { recursive: true, force: true }); await transaction.cleanup(); spinner.succeed('Test files cleaned up'); } else { console.log(chalk.yellow(`\nTest files remain in: ${testDir}`)); console.log(chalk.yellow(`Backup files in: ${transaction.backupPath}`)); } } catch (error) { spinner.fail('Test failed unexpectedly'); console.error(chalk.red(`Error: ${error.message}`)); } } /** * Command line interface setup */ program .version('1.0.0') .description('KIRA Transaction Manager Test Tool') .option('-s, --success', 'Run a successful transaction test') .option('-f, --failure', 'Run a transaction test with failure and rollback') .option('-v, --verbose', 'Enable verbose output') .option('-c, --cleanup', 'Clean up test files after completion') .option('-i, --interactive', 'Run interactive test'); program.parse(process.argv); /** * Main application entry point */ async function main() { const options = program.opts(); // Default to interactive mode if no specific options provided if (!options.success && !options.failure && !options.interactive) { options.interactive = true; } try { if (options.interactive) { await runInteractiveTest(); } else if (options.success) { displayBanner(); await runSuccessTest(options); } else if (options.failure) { displayBanner(); await runFailureTest(options); } } catch (error) { console.error(chalk.red(`\nAn error occurred: ${error.message}\n`)); process.exit(1); } } // Run the application if (require.main === module) { main(); } module.exports = { runSuccessTest, runFailureTest, runCustomTest };