import { describe, it, expect } from '@jest/globals';
import { execFileSync } from 'child_process';
import fs from 'fs';
import os from 'os';
import path from 'path';
import { handleReviewAuto } from '../../src/mcp/tools/reviewAuto.js';
function sh(bin: string, args: string[], cwd: string): string {
return execFileSync(bin, args, { cwd, stdio: ['ignore', 'pipe', 'pipe'] }).toString('utf-8');
}
function writeFile(filePath: string, contents: string): void {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, contents, 'utf-8');
}
function normalizeReviewAuto(result: any): any {
const normalized = JSON.parse(JSON.stringify(result));
if (normalized.output?.metadata?.reviewed_at) normalized.output.metadata.reviewed_at = '<fixed>';
if (normalized.output?.stats?.duration_ms) normalized.output.stats.duration_ms = 0;
if (normalized.output?.stats?.timings_ms) normalized.output.stats.timings_ms = {};
if (normalized.output?.run_id) normalized.output.run_id = '<fixed>';
if (normalized.output?.review?.metadata?.reviewed_at) normalized.output.review.metadata.reviewed_at = '<fixed>';
if (normalized.output?.review?.metadata?.review_duration_ms) normalized.output.review.metadata.review_duration_ms = 0;
return normalized;
}
describe('review_auto tool', () => {
it('selects review_diff when diff is provided', async () => {
const diff = `diff --git a/src/a.ts b/src/a.ts
index 1234567..abcdefg 100644
--- a/src/a.ts
+++ b/src/a.ts
@@ -1,1 +1,1 @@
-export const a = 1;
+export const a = 2;
`;
const mockServiceClient = {
getWorkspacePath: () => process.cwd(),
getFile: async () => '',
searchAndAsk: async () => '',
} as any;
const resultStr = await handleReviewAuto(
{
diff,
review_diff_options: { enable_llm: false, enable_static_analysis: false, include_sarif: false, include_markdown: false },
},
mockServiceClient
);
const parsed = JSON.parse(resultStr);
expect(normalizeReviewAuto(parsed)).toMatchSnapshot();
});
it('selects review_git_diff when no diff is provided', async () => {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ce-review-auto-'));
sh('git', ['init'], tmp);
sh('git', ['config', 'user.email', 'ci@example.com'], tmp);
sh('git', ['config', 'user.name', 'CI'], tmp);
writeFile(path.join(tmp, 'src/a.ts'), ['export const a = 1;', ''].join('\n'));
sh('git', ['add', '.'], tmp);
sh('git', ['commit', '-m', 'base'], tmp);
writeFile(path.join(tmp, 'src/a.ts'), ['export const a = 1;', 'export const b = 2;', ''].join('\n'));
sh('git', ['add', '.'], tmp);
const mockServiceClient = {
getWorkspacePath: () => tmp,
searchAndAsk: async () =>
JSON.stringify({
findings: [
{
id: 'finding_1',
title: 'Add input validation',
body: 'Mocked output.',
confidence_score: 0.95,
priority: 1,
category: 'correctness',
code_location: { file_path: 'src/a.ts', line_range: { start: 2, end: 2 } },
suggestion: { description: 'Add a guard', can_auto_fix: false },
is_on_changed_line: true,
},
],
overall_correctness: 'needs attention',
overall_explanation: 'Mocked output.',
overall_confidence_score: 0.8,
}),
} as any;
const resultStr = await handleReviewAuto(
{
target: 'staged',
review_git_diff_options: { confidence_threshold: 0.7, changed_lines_only: true },
},
mockServiceClient
);
const parsed = JSON.parse(resultStr);
expect(normalizeReviewAuto(parsed)).toMatchSnapshot();
});
});