forge-mutation-tester
Version:
Mutation testing tool for Solidity smart contracts using Gambit
172 lines (166 loc) • 7.49 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;
};
})();
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