/** * OCR Service Unit Tests * TDD: Test-Driven Development */ import { describe, it, expect } from '@jest/globals'; import { OCRService } from '../../../src/services/ocr.service'; describe('OCRService', () => { describe('shouldCreateDocument', () => { const defaultThreshold = 0.3; it('should create document when confidence > threshold', () => { // @ralph 这个测试是否清晰描述了决策逻辑? const result = OCRService.shouldCreateDocument(0.8, defaultThreshold); expect(result).toBe(true); }); it('should not create document when confidence < threshold', () => { // @ralph 边界条件是否正确处理? const result = OCRService.shouldCreateDocument(0.2, defaultThreshold); expect(result).toBe(false); }); it('should handle threshold boundary (>=)', () => { // @ralph 边界值处理是否明确? expect(OCRService.shouldCreateDocument(0.3, 0.3)).toBe(true); expect(OCRService.shouldCreateDocument(0.31, 0.3)).toBe(true); expect(OCRService.shouldCreateDocument(0.29, 0.3)).toBe(false); }); it('should handle perfect confidence', () => { // @ralph 最佳情况是否考虑? const result = OCRService.shouldCreateDocument(1.0, defaultThreshold); expect(result).toBe(true); }); it('should handle zero confidence', () => { // @ralph 最坏情况是否考虑? const result = OCRService.shouldCreateDocument(0.0, defaultThreshold); expect(result).toBe(false); }); it('should handle negative confidence', () => { // @ralph 异常值是否处理? const result = OCRService.shouldCreateDocument(-0.1, defaultThreshold); expect(result).toBe(false); }); it('should handle confidence > 1 (invalid)', () => { // @ralph 非法值是否处理? const result = OCRService.shouldCreateDocument(1.5, defaultThreshold); expect(result).toBe(false); // Should return false for invalid input }); }); describe('processingStatus', () => { it('should return pending for new upload', () => { // @ralph 初始状态是否正确? const status = OCRService.getInitialStatus(); expect(status).toBe('pending'); }); it('should process image with provider', async () => { // @ralph 处理流程是否正确? const mockOCR = jest.fn().mockResolvedValue({ text: 'test', confidence: 0.9 }); const result = await OCRService.process('image-id', mockOCR); expect(result.text).toBe('test'); expect(result.confidence).toBe(0.9); expect(result.shouldCreateDocument).toBe(true); }); }); describe('error handling', () => { it('should handle OCR provider failure', async () => { // @ralph 失败处理是否完善? const mockOCR = jest.fn().mockRejectedValue(new Error('OCR failed')); await expect(OCRService.process('image-id', mockOCR)).rejects.toThrow('OCR failed'); }); it('should handle timeout', async () => { // @ralph 超时是否处理? const mockOCR = jest.fn().mockImplementation(() => new Promise((resolve) => setTimeout(resolve, 10000)) ); await expect( OCRService.process('image-id', mockOCR, { timeout: 100 }) ).rejects.toThrow('timeout'); }); it('should handle empty result', async () => { // @ralph 空结果是否处理? const mockOCR = jest.fn().mockResolvedValue({ text: '', confidence: 0 }); const result = await OCRService.process('image-id', mockOCR); expect(result.text).toBe(''); expect(result.shouldCreateDocument).toBe(false); }); }); describe('confidence validation', () => { it('should validate confidence range', () => { // @ralph 范围检查是否完整? expect(OCRService.isValidConfidence(0.5)).toBe(true); expect(OCRService.isValidConfidence(0)).toBe(true); expect(OCRService.isValidConfidence(1)).toBe(true); expect(OCRService.isValidConfidence(-0.1)).toBe(false); expect(OCRService.isValidConfidence(1.1)).toBe(false); expect(OCRService.isValidConfidence(NaN)).toBe(false); }); }); describe('retry logic', () => { it('should retry on transient failure', async () => { // @ralph 重试逻辑是否合理? const mockOCR = jest.fn() .mockRejectedValueOnce(new Error('network error')) .mockResolvedValueOnce({ text: 'retry success', confidence: 0.8 }); const result = await OCRService.process('image-id', mockOCR, { retries: 1 }); expect(mockOCR).toHaveBeenCalledTimes(2); expect(result.text).toBe('retry success'); }); it('should not retry on permanent failure', async () => { // @ralph 错误类型是否区分? const mockOCR = jest.fn() .mockRejectedValue(new Error('invalid image format')); await expect( OCRService.process('image-id', mockOCR, { retries: 2 }) ).rejects.toThrow('invalid image format'); expect(mockOCR).toHaveBeenCalledTimes(1); // No retry }); it('should respect max retry limit', async () => { // @ralph 重试次数是否限制? const mockOCR = jest.fn() .mockRejectedValue(new Error('network error')); await expect( OCRService.process('image-id', mockOCR, { retries: 2 }) ).rejects.toThrow('network error'); expect(mockOCR).toHaveBeenCalledTimes(3); // initial + 2 retries }); }); });