agentic-qe
Version:
Agentic Quality Engineering Fleet System - AI-driven quality management platform
163 lines • 5.98 kB
JavaScript
"use strict";
/**
* Test Mutate Command
* Perform mutation testing to assess test quality
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.mutate = void 0;
const Logger_1 = require("../../../utils/Logger");
async function mutate(options) {
const logger = Logger_1.Logger.getInstance();
try {
// Generate mutants from source code
const mutants = generateMutants(options.sourceCode);
// Simulate test execution against each mutant
const results = await testMutants(mutants, options.testSuite);
// Calculate mutation score
const killedMutants = results.filter(m => m.killed).length;
const mutationScore = mutants.length > 0
? (killedMutants / mutants.length) * 100
: 0;
// Identify surviving mutants
const survivingMutants = results.filter(m => !m.killed);
// Generate test suggestions
const testSuggestions = generateTestSuggestions(survivingMutants);
logger.info(`Mutation testing: ${killedMutants}/${mutants.length} mutants killed (${mutationScore.toFixed(1)}%)`);
return {
success: true,
mutants: results,
mutationScore: parseFloat(mutationScore.toFixed(2)),
survivingMutants,
testSuggestions
};
}
catch (error) {
logger.error('Failed to perform mutation testing:', error);
throw error;
}
}
exports.mutate = mutate;
function generateMutants(sourceCode) {
const mutants = [];
const lines = sourceCode.split('\n');
let mutantId = 0;
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
const line = lines[lineNum];
// Arithmetic operator mutations
const arithmeticMutations = [
{ from: '+', to: '-', type: 'arithmetic' },
{ from: '-', to: '+', type: 'arithmetic' },
{ from: '*', to: '/', type: 'arithmetic' },
{ from: '/', to: '*', type: 'arithmetic' }
];
for (const mutation of arithmeticMutations) {
if (line.includes(mutation.from)) {
mutants.push({
id: mutantId++,
type: mutation.type,
original: mutation.from,
mutated: mutation.to,
line: lineNum + 1,
killed: false
});
}
}
// Comparison operator mutations
const comparisonMutations = [
{ from: '>', to: '<', type: 'comparison' },
{ from: '<', to: '>', type: 'comparison' },
{ from: '>=', to: '<=', type: 'comparison' },
{ from: '<=', to: '>=', type: 'comparison' },
{ from: '==', to: '!=', type: 'comparison' },
{ from: '!=', to: '==', type: 'comparison' }
];
for (const mutation of comparisonMutations) {
if (line.includes(mutation.from)) {
mutants.push({
id: mutantId++,
type: mutation.type,
original: mutation.from,
mutated: mutation.to,
line: lineNum + 1,
killed: false
});
}
}
// Return value mutations
if (line.includes('return')) {
mutants.push({
id: mutantId++,
type: 'return',
original: 'return',
mutated: 'return null',
line: lineNum + 1,
killed: false
});
}
// Conditional mutations
if (line.includes('if')) {
mutants.push({
id: mutantId++,
type: 'conditional',
original: 'if',
mutated: 'if (true)',
line: lineNum + 1,
killed: false
});
}
}
return mutants;
}
async function testMutants(mutants, testSuite) {
// Simulate test execution
// In a real implementation, this would actually run tests against each mutant
return mutants.map(mutant => {
// Simulate 70% kill rate
const killed = Math.random() > 0.3;
return {
...mutant,
killed,
killedBy: killed ? testSuite : undefined
};
});
}
function generateTestSuggestions(survivingMutants) {
const suggestions = [];
const mutantsByType = {};
// Group surviving mutants by type
for (const mutant of survivingMutants) {
if (!mutantsByType[mutant.type]) {
mutantsByType[mutant.type] = [];
}
mutantsByType[mutant.type].push(mutant);
}
// Generate suggestions
for (const [type, mutants] of Object.entries(mutantsByType)) {
if (type === 'arithmetic') {
suggestions.push(`Add tests for edge cases in arithmetic operations (${mutants.length} surviving mutants)`);
}
else if (type === 'comparison') {
suggestions.push(`Test boundary conditions more thoroughly (${mutants.length} comparison mutants survived)`);
}
else if (type === 'return') {
suggestions.push(`Add tests for return value validation (${mutants.length} return mutants survived)`);
}
else if (type === 'conditional') {
suggestions.push(`Test both branches of conditionals (${mutants.length} conditional mutants survived)`);
}
}
// Add specific line suggestions
const linesByType = survivingMutants.reduce((acc, m) => {
if (!acc[m.type])
acc[m.type] = [];
acc[m.type].push(m.line);
return acc;
}, {});
for (const [type, lines] of Object.entries(linesByType)) {
if (lines.length > 0) {
suggestions.push(`Focus on lines ${lines.join(', ')} for ${type} coverage`);
}
}
return suggestions;
}
//# sourceMappingURL=mutate.js.map