test.jsโข16.8 kB
/**
* Comprehensive Test Suite for Python Code Review MCP
* Tests all analyzer functionality, security detection, and report generation
*/
import { PythonAnalyzer } from './python-analyzer.js';
import { ReportFormatter } from './report-formatter.js';
class TestRunner {
analyzer;
formatter;
testsPassed = 0;
testsTotal = 0;
constructor() {
this.analyzer = new PythonAnalyzer();
this.formatter = new ReportFormatter();
}
assert(condition, message) {
this.testsTotal++;
if (condition) {
this.testsPassed++;
console.log(`โ
${message}`);
}
else {
console.log(`โ ${message}`);
}
}
assertContains(haystack, needle, message) {
this.assert(haystack.includes(needle), message);
}
assertGreaterThan(actual, expected, message) {
this.assert(actual > expected, `${message} (${actual} > ${expected})`);
}
assertLessThan(actual, expected, message) {
this.assert(actual < expected, `${message} (${actual} < ${expected})`);
}
async runAllTests() {
console.log('๐งช Starting Python Code Review MCP Test Suite...\n');
await this.testSecurityVulnerabilities();
await this.testCodeQualityAnalysis();
await this.testReportGeneration();
await this.testEdgeCases();
await this.testRealWorldExamples();
this.printResults();
}
async testSecurityVulnerabilities() {
console.log('๐ Testing Security Vulnerability Detection...');
// Test SQL Injection Detection
const sqlInjectionCode = `
import sqlite3
def get_user(user_id):
conn = sqlite3.connect('db.sqlite')
cursor = conn.cursor()
# Vulnerable to SQL injection
query = "SELECT * FROM users WHERE id = '%s'" % user_id
cursor.execute(query)
return cursor.fetchone()
def get_user_f_string(user_id):
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
def get_user_concat(user_id):
cursor.execute("SELECT * FROM users WHERE id = " + str(user_id))
`;
const sqlResult = this.analyzer.analyzePythonCode(sqlInjectionCode, 'sql_test.py');
const sqlIssues = sqlResult.issues.filter(i => i.rule.includes('sql-injection'));
this.assertGreaterThan(sqlIssues.length, 2, 'Should detect multiple SQL injection vulnerabilities');
this.assert(sqlIssues.some(i => i.severity === 'critical'), 'Should flag string formatting SQL injection as critical');
this.assert(sqlIssues.some(i => i.rule === 'sql-injection-f-string'), 'Should detect f-string SQL injection');
// Test Command Injection Detection
const cmdInjectionCode = `
import os
import subprocess
def dangerous_system_call(user_input):
os.system("ls " + user_input) # Vulnerable
def dangerous_subprocess(command):
subprocess.run(command, shell=True) # Vulnerable
def safe_subprocess(command):
subprocess.run([command], shell=False) # Safe
`;
const cmdResult = this.analyzer.analyzePythonCode(cmdInjectionCode, 'cmd_test.py');
const cmdIssues = cmdResult.issues.filter(i => i.rule.includes('command-injection') || i.rule.includes('os-system'));
this.assertGreaterThan(cmdIssues.length, 1, 'Should detect command injection vulnerabilities');
this.assert(cmdIssues.some(i => i.severity === 'critical'), 'Should flag os.system as critical');
// Test Hardcoded Secrets Detection
const secretsCode = `
# Bad practices
DATABASE_PASSWORD = "secret123"
API_KEY = "sk-1234567890abcdef"
JWT_SECRET = "my-super-secret-key"
ACCESS_TOKEN = "bearer-token-here"
# Good practice
DATABASE_PASSWORD = os.getenv("DB_PASSWORD")
`;
const secretsResult = this.analyzer.analyzePythonCode(secretsCode, 'secrets_test.py');
const secretIssues = secretsResult.issues.filter(i => i.rule.includes('hardcoded'));
this.assertGreaterThan(secretIssues.length, 3, 'Should detect hardcoded secrets');
this.assert(secretIssues.every(i => i.severity === 'high'), 'All hardcoded secrets should be high severity');
// Test Code Injection Detection
const codeInjectionCode = `
def dangerous_eval(user_input):
result = eval(user_input) # Very dangerous
return result
def dangerous_exec(code):
exec(code) # Also dangerous
`;
const codeResult = this.analyzer.analyzePythonCode(codeInjectionCode, 'code_injection_test.py');
const codeIssues = codeResult.issues.filter(i => i.rule.includes('code-injection'));
this.assertGreaterThan(codeIssues.length, 1, 'Should detect eval and exec usage');
this.assert(codeIssues.every(i => i.severity === 'critical'), 'Code injection should be critical severity');
console.log();
}
async testCodeQualityAnalysis() {
console.log('๐ Testing Code Quality Analysis...');
// Test Naming Convention Detection
const namingCode = `
class badClassName: # Should be PascalCase
pass
def BadFunctionName(): # Should be snake_case
pass
BAD_CONSTANT = "test" # Actually OK for constants
good_variable = "test" # OK
`;
const namingResult = this.analyzer.analyzePythonCode(namingCode, 'naming_test.py');
const namingIssues = namingResult.issues.filter(i => i.rule.includes('naming-convention'));
this.assertGreaterThan(namingIssues.length, 1, 'Should detect naming convention violations');
// Test Exception Handling Issues
const exceptionCode = `
def bad_exception_handling():
try:
risky_operation()
except: # Bare except - bad
pass
try:
another_operation()
except Exception: # Too broad - medium issue
pass
def good_exception_handling():
try:
risky_operation()
except ValueError as e: # Specific exception - good
logger.error(f"Value error: {e}")
`;
const exceptionResult = this.analyzer.analyzePythonCode(exceptionCode, 'exception_test.py');
const exceptionIssues = exceptionResult.issues.filter(i => i.rule.includes('except'));
this.assertGreaterThan(exceptionIssues.length, 1, 'Should detect exception handling issues');
this.assert(exceptionIssues.some(i => i.rule === 'bare-except'), 'Should detect bare except clause');
// Test Import Issues
const importCode = `
from module import * # Wildcard import - bad
import os, sys, json # Multiple imports on one line
`;
const importResult = this.analyzer.analyzePythonCode(importCode, 'import_test.py');
const importIssues = importResult.issues.filter(i => i.rule.includes('import'));
this.assertGreaterThan(importIssues.length, 1, 'Should detect import issues');
// Test Performance Issues
const performanceCode = `
def inefficient_loop(items):
for i in range(len(items)): # Should use enumerate
print(f"Item {i}: {items[i]}")
def list_concatenation():
result = []
for item in items:
result += [item] # Inefficient, should use extend
`;
const perfResult = this.analyzer.analyzePythonCode(performanceCode, 'performance_test.py');
const perfIssues = perfResult.issues.filter(i => i.rule.includes('range-len') || i.rule.includes('concatenation'));
this.assertGreaterThan(perfIssues.length, 0, 'Should detect performance issues');
console.log();
}
async testReportGeneration() {
console.log('๐ Testing Report Generation...');
const testCode = `
import sqlite3
import os
# Security issue
def get_user(user_id):
query = "SELECT * FROM users WHERE id = '%s'" % user_id
cursor.execute(query)
# Quality issue
class badClass: # Wrong naming convention
def __init__(self):
password = "hardcoded123" # Another security issue
# Performance issue
def process_items(items):
for i in range(len(items)):
print(items[i])
`;
const result = this.analyzer.analyzePythonCode(testCode, 'comprehensive_test.py');
// Test detailed report generation
const detailedReport = this.formatter.generateDetailedReport(result);
this.assertContains(detailedReport, 'PYTHON CODE REVIEW REPORT', 'Should contain report header');
this.assertContains(detailedReport, 'EXECUTIVE SUMMARY', 'Should contain executive summary');
this.assertContains(detailedReport, 'QUALITY SCORECARD', 'Should contain quality scorecard');
this.assertContains(detailedReport, 'DETAILED ISSUES', 'Should contain detailed issues');
this.assertContains(detailedReport, 'RECOMMENDATIONS', 'Should contain recommendations');
// Test summary report generation
const summaryReport = this.formatter.generateSummaryReport(result);
this.assertContains(summaryReport, 'PYTHON CODE REVIEW REPORT', 'Summary should contain header');
this.assertContains(summaryReport, 'QUALITY SCORECARD', 'Summary should contain scorecard');
// Test security report generation
const securityReport = this.formatter.generateSecurityReport(result);
this.assertContains(securityReport, 'SECURITY ANALYSIS REPORT', 'Security report should have security header');
this.assertContains(securityReport, 'Security Score', 'Security report should show security score');
// Test scoring system
this.assert(result.codeQualityScore >= 0 && result.codeQualityScore <= 100, 'Code quality score should be 0-100');
this.assert(result.securityScore >= 0 && result.securityScore <= 100, 'Security score should be 0-100');
// With security issues, security score should be lower
this.assertLessThan(result.securityScore, 90, 'Security score should reflect detected vulnerabilities');
console.log();
}
async testEdgeCases() {
console.log('๐งช Testing Edge Cases...');
// Test empty code
const emptyResult = this.analyzer.analyzePythonCode('', 'empty.py');
this.assert(emptyResult.totalLines === 1, 'Empty code should have 1 line'); // Empty string splits to one line
this.assert(emptyResult.totalIssues === 0, 'Empty code should have no issues');
// Test single line code
const singleLineResult = this.analyzer.analyzePythonCode('print("hello")', 'single.py');
this.assert(singleLineResult.totalLines === 1, 'Single line code should have 1 line');
// Test code with only comments
const commentOnlyCode = `
# This is a comment
# Another comment
"""
Multiline comment
"""
`;
const commentResult = this.analyzer.analyzePythonCode(commentOnlyCode, 'comments.py');
this.assert(commentResult.totalIssues === 0, 'Comment-only code should have no issues');
// Test very long function (should trigger complexity warning)
const longFunctionCode = `
def very_long_function():
""" This function is intentionally very long """
${Array(60).fill(' print("line")').join('\n')}
`;
const longResult = this.analyzer.analyzePythonCode(longFunctionCode, 'long_function.py');
// Note: Our current pattern matching might not catch this perfectly, but let's check
this.assertGreaterThan(longResult.totalLines, 50, 'Long function should have many lines');
// Test code with mixed indentation (Python would fail, but we should still analyze)
const mixedIndentCode = `
def function():
if True:
print("4 spaces")
print("tab") # This would cause Python syntax error
`;
const mixedResult = this.analyzer.analyzePythonCode(mixedIndentCode, 'mixed_indent.py');
this.assert(mixedResult.totalLines > 0, 'Should handle mixed indentation gracefully');
console.log();
}
async testRealWorldExamples() {
console.log('๐ Testing Real-World Examples...');
// Test Flask application with security issues
const flaskApp = `
from flask import Flask, request, render_template
import sqlite3
import os
app = Flask(__name__)
app.secret_key = "hardcoded-secret-key" # Security issue
@app.route('/user/<user_id>')
def get_user(user_id):
conn = sqlite3.connect('users.db')
cursor = conn.cursor()
# SQL Injection vulnerability
query = f"SELECT * FROM users WHERE id = {user_id}"
cursor.execute(query)
user = cursor.fetchone()
if user:
return render_template('user.html', user=user)
else:
return "User not found", 404
@app.route('/execute')
def execute_command():
cmd = request.args.get('cmd')
# Command injection vulnerability
os.system(cmd)
return "Command executed"
if __name__ == '__main__':
app.run(debug=True) # Debug mode in production - bad practice
`;
const flaskResult = this.analyzer.analyzePythonCode(flaskApp, 'flask_app.py');
this.assertGreaterThan(flaskResult.totalIssues, 3, 'Flask app should have multiple security issues');
this.assertGreaterThan(flaskResult.criticalIssues, 0, 'Should have critical security issues');
this.assertLessThan(flaskResult.securityScore, 50, 'Security score should be very low');
// Test Django model with good practices
const djangoModel = `
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
import uuid
class BlogPost(models.Model):
"""Model for blog posts with proper practices."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
verbose_name = 'Blog Post'
verbose_name_plural = 'Blog Posts'
def __str__(self):
return self.title
def get_absolute_url(self):
return f"/blog/{self.id}/"
`;
const djangoResult = this.analyzer.analyzePythonCode(djangoModel, 'django_model.py');
this.assertLessThan(djangoResult.totalIssues, 3, 'Well-written Django model should have few issues');
this.assertGreaterThan(djangoResult.codeQualityScore, 80, 'Good code should have high quality score');
this.assertGreaterThan(djangoResult.securityScore, 90, 'Secure code should have high security score');
// Test data processing script with performance issues
const dataProcessing = `
import pandas as pd
def process_data(filename):
# Multiple performance and quality issues
data = []
# Reading file line by line instead of using pandas directly
with open(filename, 'r') as f:
for line in f:
data += [line.strip()] # Inefficient list concatenation
# Converting to DataFrame inefficiently
df = pd.DataFrame()
for i in range(len(data)): # Should use enumerate
df = df.append({'value': data[i]}, ignore_index=True)
return df
def analyze_data(df):
# TODO: Implement analysis
pass # Empty function
`;
const dataResult = this.analyzer.analyzePythonCode(dataProcessing, 'data_processing.py');
this.assertGreaterThan(dataResult.totalIssues, 2, 'Data processing should have multiple issues');
const todoIssues = dataResult.issues.filter(i => i.rule === 'todo-comment');
this.assertGreaterThan(todoIssues.length, 0, 'Should detect TODO comments');
console.log();
}
printResults() {
console.log('=' + '='.repeat(50));
console.log(`๐งช TEST RESULTS: ${this.testsPassed}/${this.testsTotal} tests passed`);
if (this.testsPassed === this.testsTotal) {
console.log('๐ ALL TESTS PASSED! Python Code Review MCP is working perfectly.');
}
else {
console.log(`โ ${this.testsTotal - this.testsPassed} tests failed. Please review the implementation.`);
}
const successRate = (this.testsPassed / this.testsTotal * 100).toFixed(1);
console.log(`๐ Success Rate: ${successRate}%`);
console.log('\n๐ฏ Test Coverage Summary:');
console.log('โ
Security vulnerability detection');
console.log('โ
Code quality analysis');
console.log('โ
Report generation (detailed, summary, security)');
console.log('โ
Edge cases and error handling');
console.log('โ
Real-world application examples');
console.log();
}
}
// Run tests if this file is executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
const testRunner = new TestRunner();
testRunner.runAllTests().catch(console.error);
}
export { TestRunner };
//# sourceMappingURL=test.js.map