UNPKG

forge-mutation-tester

Version:

Mutation testing tool for Solidity smart contracts using Gambit

172 lines (166 loc) 7.49 kB
#!/usr/bin/env node "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; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const commander_1 = require("commander"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); const toml = __importStar(require("toml")); const chalk_1 = __importDefault(require("chalk")); const run_1 = require("./commands/run"); const program = new commander_1.Command(); // Read version from package.json const packageJsonPath = path.join(__dirname, '..', 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); program .name('forge-mutation-tester') .description('AI-powered mutation testing tool for Solidity smart contracts') .version(packageJson.version); program .argument('<config>', 'Path to TOML configuration file') .option('-v, --verbose', 'Enable verbose output') .action(async (configPath, options) => { try { // Check if config file exists if (!fs.existsSync(configPath)) { console.error(chalk_1.default.red(`Error: Configuration file not found: ${configPath}`)); process.exit(1); } // Read and parse TOML config const configContent = fs.readFileSync(configPath, 'utf-8'); let config; try { config = toml.parse(configContent); } catch (parseError) { console.error(chalk_1.default.red(`Error parsing TOML configuration: ${parseError.message}`)); process.exit(1); } // Validate configuration if (!config.openai?.api_key) { console.log(chalk_1.default.yellow('⚠️ Warning: No OpenAI API key provided')); console.log(chalk_1.default.dim(' Mutation testing will run, but no tests will be generated.')); console.log(chalk_1.default.dim(' To enable AI test generation, add your API key to the config:')); console.log(chalk_1.default.dim(' [openai]')); console.log(chalk_1.default.dim(' api_key = "sk-..."')); console.log(''); } if (!config.repository?.url && !config.repository?.local_path) { console.error(chalk_1.default.red('Error: Either repository.url or repository.local_path must be provided')); process.exit(1); } if (config.repository?.url && config.repository?.local_path) { console.error(chalk_1.default.red('Error: Cannot use both repository.url and repository.local_path. Choose one.')); process.exit(1); } // Convert config to RunOptions format const runOptions = { repo: config.repository?.url, localPath: config.repository?.local_path, token: config.repository?.token, branch: config.repository?.branch || 'main', output: config.output?.directory || 'mutation-results', openaiKey: config.openai?.api_key, model: config.openai?.model || 'gpt-4-turbo-preview', cleanup: config.output?.cleanup !== false, // Default true iterative: config.testing?.iterative !== false, // Default true numMutants: config.testing?.num_mutants || 25, // Default 25 includePatterns: config.files?.include, // File patterns to include excludePatterns: config.files?.exclude, // File patterns to exclude solcRemappings: config.solidity?.remappings // Custom Solidity remappings }; if (options.verbose) { console.log(chalk_1.default.dim('Using configuration:')); console.log(chalk_1.default.dim(JSON.stringify(runOptions, null, 2))); } // Run mutation testing await (0, run_1.runMutationTest)(runOptions); } catch (error) { console.error(chalk_1.default.red('Error:', error.message)); if (options.verbose && error.stack) { console.error(chalk_1.default.dim(error.stack)); } process.exit(1); } }); // Add example command program .command('init') .description('Create an example configuration file') .action(() => { const exampleConfig = `# Forge Mutation Tester Configuration [repository] # Use EITHER url OR local_path, not both # url = "https://github.com/owner/repo" local_path = "./path/to/your/project" # branch = "main" # Optional, defaults to 'main' # token = "ghp_..." # Optional, for private repos [openai] # api_key = "sk-..." # Optional - your OpenAI API key for test generation # model = "gpt-4-turbo-preview" # Optional, defaults to 'gpt-4-turbo-preview' [output] # directory = "mutation-results" # Optional, defaults to 'mutation-results' # cleanup = true # Optional, defaults to true (only applies to cloned repos) [testing] # iterative = true # Optional, defaults to true (set to false to disable) # num_mutants = 25 # Optional, defaults to 25 mutants per file [files] # Optional: File filtering for large repositories # include = ["src/core/**/*.sol", "src/utils/**/*.sol"] # Only test these patterns # exclude = ["**/*Test.sol", "**/mocks/**"] # Skip these patterns [solidity] # Optional: Custom Solidity remappings (if auto-detection is insufficient) # remappings = ["@openzeppelin/=lib/openzeppelin/", "solmate/=dependencies/solmate/src/"] `; const configPath = 'mutation-config.toml'; if (fs.existsSync(configPath)) { console.error(chalk_1.default.yellow(`Warning: ${configPath} already exists. Not overwriting.`)); process.exit(1); } fs.writeFileSync(configPath, exampleConfig); console.log(chalk_1.default.green(`✓ Created example configuration file: ${configPath}`)); console.log(chalk_1.default.dim('Edit this file with your settings and run:')); console.log(chalk_1.default.cyan(` forge-mutation-tester ${configPath}`)); }); program.parse(process.argv); // Show help if no arguments if (!process.argv.slice(2).length) { program.outputHelp(); } //# sourceMappingURL=cli.js.map