/**
* Unit tests for Reactive Review MCP Tools
*
* Tests the 7 new reactive review tools:
* - reactive_review_pr
* - get_review_status
* - pause_review
* - resume_review
* - get_review_telemetry
* - scrub_secrets
* - validate_content
*/
import { describe, it, expect, beforeEach, jest } from '@jest/globals';
import {
handleScrubSecrets,
handleValidateContent,
reactiveReviewPRTool,
getReviewStatusTool,
pauseReviewTool,
resumeReviewTool,
getReviewTelemetryTool,
scrubSecretsTool,
validateContentTool,
reactiveReviewTools,
} from '../../src/mcp/tools/reactiveReview.js';
describe('Reactive Review Tools', () => {
beforeEach(() => {
jest.clearAllMocks();
});
// ============================================================================
// Tool Schema Tests
// ============================================================================
describe('Tool Schemas', () => {
it('should expose all 7 reactive review tools', () => {
expect(reactiveReviewTools).toHaveLength(7);
});
it('should have correct tool names', () => {
const names = reactiveReviewTools.map(t => t.name);
expect(names).toContain('reactive_review_pr');
expect(names).toContain('get_review_status');
expect(names).toContain('pause_review');
expect(names).toContain('resume_review');
expect(names).toContain('get_review_telemetry');
expect(names).toContain('scrub_secrets');
expect(names).toContain('validate_content');
});
it('reactive_review_pr should require commit_hash, base_ref, changed_files', () => {
expect(reactiveReviewPRTool.inputSchema.required).toEqual([
'commit_hash',
'base_ref',
'changed_files',
]);
});
it('get_review_status should require session_id', () => {
expect(getReviewStatusTool.inputSchema.required).toEqual(['session_id']);
});
it('pause_review should require session_id', () => {
expect(pauseReviewTool.inputSchema.required).toEqual(['session_id']);
});
it('resume_review should require session_id', () => {
expect(resumeReviewTool.inputSchema.required).toEqual(['session_id']);
});
it('get_review_telemetry should require session_id', () => {
expect(getReviewTelemetryTool.inputSchema.required).toEqual(['session_id']);
});
it('scrub_secrets should require content', () => {
expect(scrubSecretsTool.inputSchema.required).toEqual(['content']);
});
it('validate_content should require content', () => {
expect(validateContentTool.inputSchema.required).toEqual(['content']);
});
});
// ============================================================================
// scrub_secrets Tool Tests
// ============================================================================
describe('handleScrubSecrets', () => {
it('should scrub AWS access keys', async () => {
const result = await handleScrubSecrets({
content: 'AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE',
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
expect(parsed.secrets_found).toBe(true);
expect(parsed.secrets_count).toBeGreaterThan(0);
expect(parsed.scrubbed_content).not.toContain('AKIAIOSFODNN7EXAMPLE');
});
it('should scrub OpenAI API keys', async () => {
const result = await handleScrubSecrets({
content: 'const apiKey = "sk-proj-abc123def456ghi789jklmno";',
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
expect(parsed.secrets_found).toBe(true);
});
it('should scrub GitHub tokens', async () => {
const result = await handleScrubSecrets({
content: 'GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
expect(parsed.secrets_found).toBe(true);
expect(parsed.scrubbed_content).not.toContain('ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
});
it('should handle content with no secrets', async () => {
const result = await handleScrubSecrets({
content: 'const message = "Hello, World!";',
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
expect(parsed.secrets_found).toBe(false);
expect(parsed.secrets_count).toBe(0);
});
it('should scrub multiple secret types', async () => {
const result = await handleScrubSecrets({
content: `
AWS_KEY=AKIAIOSFODNN7EXAMPLE
STRIPE_KEY=sk_test_abc123xyz789
API_KEY=my-secret-api-key-12345
`,
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
expect(parsed.secrets_found).toBe(true);
expect(parsed.secrets_count).toBeGreaterThanOrEqual(2);
});
it('should throw error for missing content', async () => {
await expect(
handleScrubSecrets({ content: '' })
).rejects.toThrow('Missing content argument');
});
it('should include processing time', async () => {
const result = await handleScrubSecrets({
content: 'test content',
});
const parsed = JSON.parse(result);
expect(parsed.processing_time_ms).toBeDefined();
expect(typeof parsed.processing_time_ms).toBe('number');
});
});
// ============================================================================
// validate_content Tool Tests
// ============================================================================
describe('handleValidateContent', () => {
it('should validate balanced brackets', async () => {
const result = await handleValidateContent({
content: 'function test() { return { a: 1 }; }',
content_type: 'generated_code',
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
expect(parsed.passed).toBe(true);
});
it('should detect unbalanced brackets', async () => {
const result = await handleValidateContent({
content: 'function test() { return { a: 1 }',
content_type: 'generated_code',
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
expect(parsed.passed).toBe(false);
expect(parsed.findings_count).toBeGreaterThan(0);
});
it('should detect TODO comments in code', async () => {
const result = await handleValidateContent({
content: '// TODO: fix this later\nconst x = 1;',
content_type: 'generated_code',
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
// TODO detection is Tier 2 (warning, not error)
expect(parsed.by_severity.warnings).toBeGreaterThanOrEqual(0);
});
it('should validate JSON structure', async () => {
const result = await handleValidateContent({
content: '{"valid": "json", "number": 42}',
content_type: 'raw_text',
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
});
it('should detect secrets in content', async () => {
const result = await handleValidateContent({
content: 'const key = "sk-proj-abcdefghijklmnopqrstuvwxyz12345";',
content_type: 'generated_code',
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
// Secret scrubbing may or may not detect depending on pattern
// Just verify the validation ran successfully
expect(parsed.tiers_run).toBeDefined();
});
it('should throw error for missing content', async () => {
await expect(
handleValidateContent({ content: '', content_type: 'raw_text' })
).rejects.toThrow('Missing content argument');
});
it('should include tiers run', async () => {
const result = await handleValidateContent({
content: 'test content',
content_type: 'raw_text',
});
const parsed = JSON.parse(result);
expect(parsed.tiers_run).toBeDefined();
expect(Array.isArray(parsed.tiers_run)).toBe(true);
});
it('should include processing time', async () => {
const result = await handleValidateContent({
content: 'test content',
content_type: 'raw_text',
});
const parsed = JSON.parse(result);
expect(parsed.processing_time_ms).toBeDefined();
expect(typeof parsed.processing_time_ms).toBe('number');
});
it('should handle all content types', async () => {
const contentTypes: Array<'review_finding' | 'plan_output' | 'generated_code' | 'raw_text'> = [
'review_finding',
'plan_output',
'generated_code',
'raw_text',
];
for (const contentType of contentTypes) {
const result = await handleValidateContent({
content: 'sample content',
content_type: contentType,
});
const parsed = JSON.parse(result);
expect(parsed.success).toBe(true);
}
});
});
// ============================================================================
// Tool Description Tests
// ============================================================================
describe('Tool Descriptions', () => {
it('reactive_review_pr should mention environment variables', () => {
expect(reactiveReviewPRTool.description).toContain('REACTIVE_ENABLED');
expect(reactiveReviewPRTool.description).toContain('REACTIVE_PARALLEL_EXEC');
});
it('scrub_secrets should list secret types', () => {
expect(scrubSecretsTool.description).toContain('AWS');
expect(scrubSecretsTool.description).toContain('OpenAI');
expect(scrubSecretsTool.description).toContain('GitHub');
});
it('validate_content should describe tiers', () => {
expect(validateContentTool.description).toContain('Tier 1');
expect(validateContentTool.description).toContain('Tier 2');
expect(validateContentTool.description).toContain('brackets');
});
});
});