codebridge-ai
Version:
Complete fullstack AI coding platform with AST-based code integration and local LLM support. Now with comprehensive web technology support (HTML/CSS/JS) plus JavaScript, Python, Rust, and C++.
600 lines (531 loc) • 17.7 kB
JavaScript
/**
* 포괄적 다중 언어 테스트 실행기
* 다양한 복잡도와 시나리오로 모든 언어 테스트
*/
const fs = require('fs').promises;
const path = require('path');
const OllamaCodeBridge = require('../integrations/ollama-integration');
// 개선된 다중 언어 전처리기
function multiLanguagePreprocessor(response, language = 'javascript') {
// 언어별 코드 블록 패턴
const languagePatterns = {
javascript: [
/```(?:javascript|js|jsx|typescript|ts)?\n?([\s\S]*?)```/g,
/```\n?([\s\S]*?)```/g
],
python: [
/```(?:python|py)?\n?([\s\S]*?)```/g,
/```\n?([\s\S]*?)```/g
],
rust: [
/```(?:rust|rs)?\n?([\s\S]*?)```/g,
/```\n?([\s\S]*?)```/g
],
cpp: [
/```(?:cpp|c\+\+|c|cxx)?\n?([\s\S]*?)```/g,
/```\n?([\s\S]*?)```/g
],
html: [
/```(?:html|htm)?\n?([\s\S]*?)```/g,
/```\n?([\s\S]*?)```/g
],
css: [
/```(?:css|scss|sass)?\n?([\s\S]*?)```/g,
/```\n?([\s\S]*?)```/g
]
};
const patterns = languagePatterns[language] || languagePatterns.javascript;
// 코드 블록 추출
for (const pattern of patterns) {
const matches = [...response.matchAll(pattern)];
if (matches.length > 0) {
const longestMatch = matches.reduce((prev, current) =>
current[1].length > prev[1].length ? current : prev
);
return longestMatch[1].trim();
}
}
// 언어별 직접 추출
return extractCodeDirectly(response, language);
}
function extractCodeDirectly(response, language) {
const lines = response.split('\n');
const codeLines = [];
let inCode = false;
const languageMarkers = {
python: {
start: /^(def |class |from |import |@|if __name__|async def )/,
keywords: /\b(def|class|import|from|if|for|while|return|raise|try|except|with|as)\b/
},
javascript: {
start: /^(function |class |const |let |var |async function |export |import )/,
keywords: /\b(function|class|const|let|var|if|for|while|return|throw|try|catch)\b/
},
rust: {
start: /^(fn |struct |impl |use |pub |mod |trait |enum )/,
keywords: /\b(fn|struct|impl|use|if|for|while|return|match|pub|mod|let|mut)\b/
},
cpp: {
start: /^(class |struct |void |int |template |#include |namespace )/,
keywords: /\b(class|struct|int|void|if|for|while|return|throw|try|catch|template)\b/
}
};
const markers = languageMarkers[language] || languageMarkers.javascript;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trim();
if (!inCode && markers.start.test(trimmed)) {
inCode = true;
codeLines.push(line);
} else if (inCode) {
codeLines.push(line);
// 연속 빈 줄로 끝 감지
if (trimmed === '' && i > 0 && lines[i-1].trim() === '') {
break;
}
}
}
return codeLines.join('\n').trim();
}
class ComprehensiveMultiLanguageTester {
constructor() {
this.models = ['deepseek-coder:6.7b', 'starcoder2:3b'];
// 포괄적 테스트 케이스 - 다양한 복잡도
this.testScenarios = {
javascript: [
{
name: 'basic-validation',
code: `function divide(a, b) {
return a / b;
}`,
task: 'Add zero division check and type validation'
},
{
name: 'async-error-handling',
code: `async function fetchUserData(userId) {
const response = await fetch('/api/users/' + userId);
const data = await response.json();
return data;
}`,
task: 'Add comprehensive error handling and retry logic'
},
{
name: 'class-optimization',
code: `class EventEmitter {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
}
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(cb => cb(data));
}
}
}`,
task: 'Add off method, once method, and error handling'
}
],
python: [
{
name: 'basic-function',
code: `def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)`,
task: 'Add memoization, type hints, and docstring'
},
{
name: 'data-processing',
code: `def process_csv(filename):
data = []
with open(filename, 'r') as f:
for line in f:
data.append(line.strip().split(','))
return data`,
task: 'Add error handling, CSV library usage, and type hints'
},
{
name: 'class-improvement',
code: `class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()`,
task: 'Add empty check, peek method, size property, and type hints'
}
],
rust: [
{
name: 'basic-safety',
code: `fn divide(a: f64, b: f64) -> f64 {
a / b
}`,
task: 'Add Result type for safe division and handle zero'
},
{
name: 'ownership-optimization',
code: `fn process_string(s: String) -> String {
let result = s.to_uppercase();
result
}`,
task: 'Optimize to use string slice reference instead of ownership'
},
{
name: 'error-handling',
code: `fn read_number(s: &str) -> i32 {
s.parse().unwrap()
}`,
task: 'Replace unwrap with proper error handling using Result'
}
],
cpp: [
{
name: 'memory-safety',
code: `class Buffer {
char* data;
int size;
public:
Buffer(int s) {
data = new char[s];
size = s;
}
};`,
task: 'Add destructor, copy constructor, and move semantics'
},
{
name: 'modern-cpp',
code: `int* createArray(int size) {
int* arr = new int[size];
for(int i = 0; i < size; i++) {
arr[i] = i;
}
return arr;
}`,
task: 'Convert to use smart pointers and modern C++ features'
}
],
web: [
{
name: 'html-accessibility',
code: `<form>
<input type="text" placeholder="Email">
<input type="password" placeholder="Password">
<button>Login</button>
</form>`,
task: 'Add proper labels, ARIA attributes, and semantic HTML'
},
{
name: 'css-responsive',
code: `.container {
width: 1200px;
margin: 0 auto;
}
.card {
width: 300px;
float: left;
margin: 10px;
}`,
task: 'Make responsive with flexbox/grid and mobile-first approach'
}
]
};
}
async runComprehensiveTests() {
console.log('🚀 포괄적 다중 언어 테스트 시작\\n');
console.log('테스트 구성:');
console.log(`- 모델: ${this.models.join(', ')}`);
console.log(`- 언어: ${Object.keys(this.testScenarios).join(', ')}`);
console.log(`- 총 시나리오: ${Object.values(this.testScenarios).reduce((sum, scenarios) => sum + scenarios.length, 0)}개\\n`);
const allResults = [];
const startTime = Date.now();
for (const model of this.models) {
console.log(`\\n${'='.repeat(70)}`);
console.log(`🤖 모델: ${model}`);
console.log(`${'='.repeat(70)}`);
for (const [language, scenarios] of Object.entries(this.testScenarios)) {
console.log(`\\n📁 언어: ${language.toUpperCase()}`);
console.log(`${'-'.repeat(50)}`);
for (const scenario of scenarios) {
const result = await this.runSingleTest(model, language, scenario);
allResults.push(result);
// 간단한 결과 출력
if (result.success) {
console.log(` ✅ ${scenario.name}: 성공 (${result.duration}ms)`);
} else {
console.log(` ❌ ${scenario.name}: 실패 - ${result.error}`);
}
}
}
}
const totalDuration = Date.now() - startTime;
// 종합 분석
await this.generateComprehensiveReport(allResults, totalDuration);
}
async runSingleTest(model, language, scenario) {
const ollamaCodeBridge = new OllamaCodeBridge({
model,
temperature: 0.3
});
const startTime = Date.now();
try {
// 언어별 프롬프트 조정
const languageHints = {
python: 'Use proper Python syntax with type hints from typing module.',
rust: 'Use idiomatic Rust with proper error handling and ownership.',
cpp: 'Use modern C++ features (C++17 or later).',
javascript: 'Use modern ES6+ JavaScript syntax.',
web: 'Use semantic HTML5 and modern CSS.'
};
// 새로운 통합 방식: improveCode 메서드 사용 (웹 전처리기 포함)
const improveResult = await ollamaCodeBridge.improveCode(
scenario.code,
scenario.task,
{ fileType: language, debug: false }
);
const rawResponse = improveResult.rawResponse;
const improvedCode = improveResult.improvedSnippet;
const duration = Date.now() - startTime;
// 언어별 품질 검증
const qualityMetrics = this.analyzeCodeQuality(improvedCode, language, scenario.task);
const result = {
timestamp: new Date().toISOString(),
model,
language,
scenario: scenario.name,
task: scenario.task,
originalCode: scenario.code,
rawResponse,
improvedCode,
duration,
success: improvedCode.length > 0 && qualityMetrics.score >= 0.5,
qualityMetrics,
error: null
};
// 개별 결과 저장
await this.saveTestResult(result);
return result;
} catch (error) {
return {
timestamp: new Date().toISOString(),
model,
language,
scenario: scenario.name,
task: scenario.task,
originalCode: scenario.code,
duration: Date.now() - startTime,
success: false,
error: error.message
};
}
}
analyzeCodeQuality(code, language, task) {
const metrics = {};
let score = 0;
let maxScore = 0;
// 공통 검사
if (code && code.length > 0) {
metrics.hasCode = true;
score += 0.2;
}
maxScore += 0.2;
// 언어별 품질 검사
switch (language) {
case 'javascript':
if (code.includes('throw') || code.includes('try')) {
metrics.hasErrorHandling = true;
score += 0.3;
}
if (code.includes('const ') || code.includes('let ')) {
metrics.hasModernSyntax = true;
score += 0.2;
}
if (code.includes('async') || code.includes('await')) {
metrics.hasAsync = true;
score += 0.1;
}
maxScore += 0.6;
break;
case 'python':
if (code.includes('->') || code.includes(': ')) {
metrics.hasTypeHints = true;
score += 0.3;
}
if (code.includes('"""') || code.includes("'''")) {
metrics.hasDocstring = true;
score += 0.2;
}
if (code.includes('raise') || code.includes('try:')) {
metrics.hasErrorHandling = true;
score += 0.3;
}
maxScore += 0.8;
break;
case 'rust':
if (code.includes('Result<')) {
metrics.hasResultType = true;
score += 0.4;
}
if (code.includes('Ok(') || code.includes('Err(')) {
metrics.hasProperErrorHandling = true;
score += 0.3;
}
if (code.includes('&') && !code.includes('&mut')) {
metrics.hasBorrowing = true;
score += 0.2;
}
maxScore += 0.9;
break;
case 'cpp':
if (code.includes('std::unique_ptr') || code.includes('std::shared_ptr')) {
metrics.hasSmartPointers = true;
score += 0.4;
}
if (code.includes('~') && code.includes('()')) {
metrics.hasDestructor = true;
score += 0.3;
}
if (code.includes('std::')) {
metrics.hasStdLibrary = true;
score += 0.2;
}
maxScore += 0.9;
break;
case 'web':
if (code.includes('aria-') || code.includes('role=')) {
metrics.hasAccessibility = true;
score += 0.4;
}
if (code.includes('<label') || code.includes('for=')) {
metrics.hasLabels = true;
score += 0.3;
}
if (code.includes('@media') || code.includes('flex') || code.includes('grid')) {
metrics.hasResponsive = true;
score += 0.3;
}
maxScore += 1.0;
break;
}
// 작업 특정 검사
const taskLower = task.toLowerCase();
if (taskLower.includes('error') && (code.includes('try') || code.includes('except') || code.includes('Result'))) {
metrics.taskCompleted = true;
score += 0.2;
}
maxScore += 0.2;
return {
...metrics,
score: maxScore > 0 ? score / maxScore : 0,
rawScore: score,
maxScore
};
}
async saveTestResult(result) {
const modelDir = result.model.replace(':', '-');
const dir = path.join(__dirname, modelDir, result.language);
await fs.mkdir(dir, { recursive: true });
const filename = `${result.scenario}-${Date.now()}.json`;
const filepath = path.join(dir, filename);
await fs.writeFile(filepath, JSON.stringify(result, null, 2));
}
async generateComprehensiveReport(results, totalDuration) {
console.log(`\\n\\n${'='.repeat(70)}`);
console.log('📊 포괄적 테스트 결과 분석');
console.log(`${'='.repeat(70)}`);
// 모델별 통계
const modelStats = {};
for (const result of results) {
if (!modelStats[result.model]) {
modelStats[result.model] = {
total: 0,
success: 0,
languages: {},
totalDuration: 0,
qualityScores: []
};
}
const stats = modelStats[result.model];
stats.total++;
if (result.success) {
stats.success++;
if (result.qualityMetrics) {
stats.qualityScores.push(result.qualityMetrics.score);
}
}
stats.totalDuration += result.duration || 0;
// 언어별 통계
if (!stats.languages[result.language]) {
stats.languages[result.language] = { total: 0, success: 0 };
}
stats.languages[result.language].total++;
if (result.success) {
stats.languages[result.language].success++;
}
}
// 결과 출력
console.log('\\n### 모델별 종합 성과');
for (const [model, stats] of Object.entries(modelStats)) {
const successRate = (stats.success / stats.total * 100).toFixed(1);
const avgDuration = (stats.totalDuration / stats.total).toFixed(0);
const avgQuality = stats.qualityScores.length > 0
? (stats.qualityScores.reduce((a, b) => a + b, 0) / stats.qualityScores.length * 100).toFixed(1)
: 0;
console.log(`\\n**${model}**`);
console.log(`- 전체 성공률: ${stats.success}/${stats.total} (${successRate}%)`);
console.log(`- 평균 응답 시간: ${avgDuration}ms`);
console.log(`- 평균 품질 점수: ${avgQuality}%`);
console.log('\\n언어별 성공률:');
for (const [lang, langStats] of Object.entries(stats.languages)) {
const langSuccessRate = (langStats.success / langStats.total * 100).toFixed(1);
console.log(` - ${lang}: ${langStats.success}/${langStats.total} (${langSuccessRate}%)`);
}
}
// 언어별 종합
console.log('\\n### 언어별 종합 분석');
const languageStats = {};
for (const result of results) {
if (!languageStats[result.language]) {
languageStats[result.language] = { total: 0, success: 0, models: {} };
}
languageStats[result.language].total++;
if (result.success) {
languageStats[result.language].success++;
}
}
for (const [lang, stats] of Object.entries(languageStats)) {
const rate = (stats.success / stats.total * 100).toFixed(1);
console.log(`\\n**${lang.toUpperCase()}**: ${stats.success}/${stats.total} (${rate}%)`);
}
console.log(`\\n### 실행 시간`);
console.log(`총 테스트 시간: ${(totalDuration / 1000).toFixed(1)}초`);
console.log(`평균 테스트당 시간: ${(totalDuration / results.length).toFixed(0)}ms`);
// 최종 보고서 저장
const report = {
executionTime: new Date().toISOString(),
totalDuration,
totalTests: results.length,
models: this.models,
languages: Object.keys(this.testScenarios),
modelStats,
languageStats,
detailedResults: results
};
const reportPath = path.join(__dirname, 'comparison-reports', `comprehensive-test-${Date.now()}.json`);
await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
console.log(`\\n💾 상세 보고서 저장: ${reportPath}`);
console.log('\\n✅ 포괄적 테스트 완료!');
}
}
// 실행
if (require.main === module) {
const tester = new ComprehensiveMultiLanguageTester();
tester.runComprehensiveTests().catch(console.error);
}
module.exports = ComprehensiveMultiLanguageTester;