UNPKG

ai-code-review-assistant

Version:

AI code review tools that rely on the DeepSeek model to support git submissions

165 lines (136 loc) 4.51 kB
const CodeReviewTool = require('../src/index'); const { execSync } = require('child_process'); const axios = require('axios'); const fs = require('fs'); // Mock 外部依赖 jest.mock('child_process'); jest.mock('axios'); jest.mock('fs'); jest.mock('ora', () => { const spinner = { start: jest.fn().mockReturnThis(), succeed: jest.fn().mockReturnThis(), fail: jest.fn().mockReturnThis(), stop: jest.fn().mockReturnThis(), warn: jest.fn().mockReturnThis(), text: '' }; return jest.fn(() => spinner); }); // Mock readline const mockReadline = { question: jest.fn(), close: jest.fn() }; jest.mock('readline', () => ({ createInterface: jest.fn().mockReturnValue(mockReadline) })); describe('CodeReviewTool', () => { let reviewer; const mockApiKey = 'test-api-key'; beforeEach(() => { // 重置所有mock jest.clearAllMocks(); // 初始化测试实例 reviewer = new CodeReviewTool({ apiKey: mockApiKey }); }); describe('构造函数', () => { test('使用默认配置正确初始化', () => { expect(reviewer.config.apiKey).toBe(mockApiKey); expect(reviewer.config.apiEndpoint).toBe('https://api.deepseek.com/chat/completions'); expect(reviewer.config.maxDiffLines).toBe(300); expect(reviewer.config.model).toBe('deepseek-chat'); expect(reviewer.config.ignoreFiles).toEqual(['.lock', '.json', '.md', '.gitignore']); }); }); describe('getGitDiff', () => { test('成功获取Git差异', () => { const mockDiff = 'mock diff content'; execSync.mockReturnValue(mockDiff); const diff = reviewer.getGitDiff(); expect(diff).toBe(mockDiff); expect(execSync).toHaveBeenCalledWith('git diff --cached'); }); test('获取Git差异失败时返回null', () => { execSync.mockImplementation(() => { throw new Error('git command failed'); }); const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(() => {}); const result = reviewer.getGitDiff(); expect(result).toBeNull(); expect(mockConsoleError).toHaveBeenCalledWith('❌ 获取Git Diff失败: git command failed'); mockConsoleError.mockRestore(); }); }); describe('parseDiff', () => { test('正确解析Git差异', () => { const mockDiff = ` diff --git a/test.js b/test.js +new line -old line diff --git a/ignore.json b/ignore.json +ignored content `; const files = reviewer.parseDiff(mockDiff); expect(files).toHaveLength(1); expect(files[0].filename).toBe('test.js'); expect(files[0].changes).toEqual(['+new line', '-old line']); }); test('忽略指定类型的文件', () => { const mockDiff = ` diff --git a/test.json b/test.json +ignored `; const files = reviewer.parseDiff(mockDiff); expect(files).toHaveLength(0); }); }); describe('reviewCode', () => { const mockFiles = [{ filename: 'test.js', changes: ['+new line', '-old line'] }]; test('成功进行代码审查', async () => { const mockResponse = { data: { choices: [{ message: { content: 'AI review result' } }] } }; axios.post.mockResolvedValue(mockResponse); mockReadline.question.mockImplementation((_, callback) => callback('y')); const result = await reviewer.reviewCode(mockFiles); expect(result).toBe(true); expect(axios.post).toHaveBeenCalled(); }); test('超过最大行数限制时跳过审查', async () => { const largeFiles = [{ filename: 'test.js', changes: Array(301).fill('+line') }]; const result = await reviewer.reviewCode(largeFiles); expect(result).toBe(true); expect(axios.post).not.toHaveBeenCalled(); }); test('API调用失败时处理错误', async () => { axios.post.mockRejectedValue(new Error('API error')); const result = await reviewer.reviewCode(mockFiles); expect(result).toBe(true); }); }); describe('run', () => { beforeEach(() => { execSync.mockReturnValue('mock diff content'); }); test('没有代码变更时直接退出', async () => { execSync.mockReturnValue(''); const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); await reviewer.run(); expect(consoleSpy).toHaveBeenCalledWith("✅ 没有检测到代码变更"); consoleSpy.mockRestore(); }); }); });