import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { DataSource, Repository } from 'typeorm'; import { PrintTaskService } from '../../src/services/print-task.service'; import { FeiePrintTaskMt } from '../../src/entities/feie-print-task.mt.entity'; import { FeiePrinterMt } from '../../src/entities/feie-printer.mt.entity'; import type { FeieApiConfig, CreatePrintTaskDto } from '../../src/types/feie.types'; // Mock dependencies vi.mock('../../src/services/feie-api.service', () => { return { FeieApiService: vi.fn().mockImplementation(() => ({ printReceipt: vi.fn().mockResolvedValue({ ret: 0, msg: 'success', data: '123456' }), queryOrderStatus: vi.fn().mockResolvedValue({ ret: 0, data: '已打印' }) })) }; }); vi.mock('../../src/services/printer.service', () => { return { PrinterService: vi.fn().mockImplementation(() => ({})) }; }); vi.mock('@d8d/shared-crud', () => { return { GenericCrudService: vi.fn().mockImplementation(() => ({ create: vi.fn(), findOne: vi.fn(), findMany: vi.fn(), update: vi.fn(), delete: vi.fn(), repository: { findAndCount: vi.fn() } })) }; }); describe('PrintTaskService', () => { let printTaskService: PrintTaskService; let mockDataSource: DataSource; let mockPrinterRepository: Repository; const mockFeieConfig: FeieApiConfig = { baseUrl: 'http://api.feieyun.cn/Api/Open/', user: 'test_user', ukey: 'test_ukey' }; beforeEach(() => { vi.clearAllMocks(); vi.useFakeTimers(); mockPrinterRepository = { findOne: vi.fn() } as unknown as Repository; mockDataSource = { getRepository: vi.fn().mockReturnValue(mockPrinterRepository) } as unknown as DataSource; printTaskService = new PrintTaskService(mockDataSource, mockFeieConfig); }); afterEach(() => { vi.useRealTimers(); }); describe('createPrintTask', () => { it('应该成功创建立即打印任务', async () => { const tenantId = 1; const taskDto: CreatePrintTaskDto = { printerSn: 'TEST123456', content: '测试打印内容', printType: 'RECEIPT', delaySeconds: 0 }; const mockPrinter: Partial = { id: 1, tenantId, printerSn: taskDto.printerSn, printerStatus: 'ACTIVE' }; const mockTask: Partial = { id: 1, tenantId, taskId: 'FEIE_123456789_1234', printerSn: taskDto.printerSn, content: taskDto.content, printType: taskDto.printType, printStatus: 'PENDING' }; // Mock dependencies vi.mocked(mockPrinterRepository.findOne).mockResolvedValue(mockPrinter as FeiePrinterMt); (printTaskService as any).create = vi.fn().mockResolvedValue(mockTask); (printTaskService as any).executePrintTask = vi.fn().mockResolvedValue(mockTask); const result = await printTaskService.createPrintTask(tenantId, taskDto); expect(result).toEqual(mockTask); expect(mockPrinterRepository.findOne).toHaveBeenCalledWith({ where: { tenantId, printerSn: taskDto.printerSn } }); expect((printTaskService as any).executePrintTask).toHaveBeenCalledWith(tenantId, mockTask.taskId); }); it('应该成功创建延迟打印任务', async () => { const tenantId = 1; const taskDto: CreatePrintTaskDto = { printerSn: 'TEST123456', content: '测试打印内容', printType: 'RECEIPT', delaySeconds: 120 // 2分钟延迟 }; const mockPrinter: Partial = { id: 1, tenantId, printerSn: taskDto.printerSn, printerStatus: 'ACTIVE' }; const mockTask: Partial = { id: 1, tenantId, taskId: 'FEIE_123456789_1234', printerSn: taskDto.printerSn, content: taskDto.content, printType: taskDto.printType, printStatus: 'DELAYED', scheduledAt: new Date(Date.now() + 120000) }; // Mock dependencies vi.mocked(mockPrinterRepository.findOne).mockResolvedValue(mockPrinter as FeiePrinterMt); (printTaskService as any).create = vi.fn().mockResolvedValue(mockTask); const result = await printTaskService.createPrintTask(tenantId, taskDto); expect(result).toEqual(mockTask); expect(result.printStatus).toBe('DELAYED'); expect(result.scheduledAt).toBeInstanceOf(Date); expect((printTaskService as any).executePrintTask).not.toHaveBeenCalled(); }); it('应该在打印机不存在时抛出错误', async () => { const tenantId = 1; const taskDto: CreatePrintTaskDto = { printerSn: 'NONEXISTENT', content: '测试打印内容', printType: 'RECEIPT' }; // Mock printer not found vi.mocked(mockPrinterRepository.findOne).mockResolvedValue(null); await expect(printTaskService.createPrintTask(tenantId, taskDto)) .rejects .toThrow('打印机不存在'); }); }); describe('executePrintTask', () => { it('应该成功执行打印任务', async () => { const tenantId = 1; const taskId = 'FEIE_123456789_1234'; const mockTask: Partial = { id: 1, tenantId, taskId, printerSn: 'TEST123456', content: '测试内容', printType: 'RECEIPT', printStatus: 'PENDING', retryCount: 0, maxRetries: 3 }; const updatedTask: Partial = { ...mockTask, printStatus: 'SUCCESS', printedAt: new Date() }; // Mock dependencies (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask); (printTaskService as any).update = vi.fn().mockResolvedValue(updatedTask); const result = await printTaskService.executePrintTask(tenantId, taskId); expect(result).toEqual(updatedTask); expect((printTaskService as any).update).toHaveBeenCalledWith(mockTask.id, { printStatus: 'PRINTING', errorMessage: null }); }); it('应该在任务已取消时抛出错误', async () => { const tenantId = 1; const taskId = 'FEIE_123456789_1234'; const mockTask: Partial = { id: 1, tenantId, taskId, printStatus: 'CANCELLED' }; // Mock dependencies (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask); await expect(printTaskService.executePrintTask(tenantId, taskId)) .rejects .toThrow('打印任务已取消'); }); it('应该在任务已完成时抛出错误', async () => { const tenantId = 1; const taskId = 'FEIE_123456789_1234'; const mockTask: Partial = { id: 1, tenantId, taskId, printStatus: 'SUCCESS' }; // Mock dependencies (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask); await expect(printTaskService.executePrintTask(tenantId, taskId)) .rejects .toThrow('打印任务已完成'); }); it('应该在打印失败时重试', async () => { const tenantId = 1; const taskId = 'FEIE_123456789_1234'; const mockTask: Partial = { id: 1, tenantId, taskId, printerSn: 'TEST123456', content: '测试内容', printType: 'RECEIPT', printStatus: 'PENDING', retryCount: 0, maxRetries: 3 }; // Mock dependencies (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask); (printTaskService as any).update = vi.fn().mockImplementation(async (id, data) => ({ ...mockTask, ...data })); // Mock printReceipt to throw error const mockFeieApiService = { printReceipt: vi.fn().mockRejectedValue(new Error('打印失败')) }; (printTaskService as any).feieApiService = mockFeieApiService; await expect(printTaskService.executePrintTask(tenantId, taskId)) .rejects .toThrow('打印失败'); // Check that retry was scheduled expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 5000); }); }); describe('cancelPrintTask', () => { it('应该成功取消打印任务', async () => { const tenantId = 1; const taskId = 'FEIE_123456789_1234'; const mockTask: Partial = { id: 1, tenantId, taskId, printStatus: 'PENDING' }; const updatedTask: Partial = { ...mockTask, printStatus: 'CANCELLED', cancelledAt: new Date(), cancelReason: 'MANUAL' }; // Mock dependencies (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask); (printTaskService as any).update = vi.fn().mockResolvedValue(updatedTask); const result = await printTaskService.cancelPrintTask(tenantId, taskId); expect(result).toEqual(updatedTask); }); it('应该在任务已完成时抛出错误', async () => { const tenantId = 1; const taskId = 'FEIE_123456789_1234'; const mockTask: Partial = { id: 1, tenantId, taskId, printStatus: 'SUCCESS' }; // Mock dependencies (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask); await expect(printTaskService.cancelPrintTask(tenantId, taskId)) .rejects .toThrow('打印任务状态为SUCCESS,无法取消'); }); }); describe('retryPrintTask', () => { it('应该成功重试失败的打印任务', async () => { const tenantId = 1; const taskId = 'FEIE_123456789_1234'; const mockTask: Partial = { id: 1, tenantId, taskId, printStatus: 'FAILED', errorMessage: '之前的错误' }; const updatedTask: Partial = { ...mockTask, printStatus: 'PENDING', errorMessage: null, retryCount: 0 }; // Mock dependencies (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask); (printTaskService as any).update = vi.fn().mockResolvedValue(updatedTask); (printTaskService as any).executePrintTask = vi.fn().mockResolvedValue(updatedTask); const result = await printTaskService.retryPrintTask(tenantId, taskId); expect(result).toEqual(updatedTask); expect((printTaskService as any).executePrintTask).toHaveBeenCalledWith(tenantId, taskId); }); it('应该在任务不是失败状态时抛出错误', async () => { const tenantId = 1; const taskId = 'FEIE_123456789_1234'; const mockTask: Partial = { id: 1, tenantId, taskId, printStatus: 'PENDING' }; // Mock dependencies (printTaskService as any).findOne = vi.fn().mockResolvedValue(mockTask); await expect(printTaskService.retryPrintTask(tenantId, taskId)) .rejects .toThrow('只有失败的打印任务可以重试'); }); }); });