// src/services/flyerDataTransformer.test.ts import { describe, it, expect, vi, beforeEach } from 'vitest'; import { FlyerDataTransformer } from './flyerDataTransformer'; import { logger as mockLogger } from './logger.server'; import { generateFlyerIcon } from '../utils/imageProcessor'; import type { z } from 'zod'; import type { AiFlyerDataSchema } from './flyerProcessingService.server'; // Mock the dependencies vi.mock('../utils/imageProcessor', () => ({ generateFlyerIcon: vi.fn(), })); vi.mock('./logger.server', () => ({ logger: { info: vi.fn(), error: vi.fn(), warn: vi.fn(), debug: vi.fn() } })); describe('FlyerDataTransformer', () => { let transformer: FlyerDataTransformer; beforeEach(() => { vi.clearAllMocks(); transformer = new FlyerDataTransformer(); // Provide a default mock implementation for generateFlyerIcon vi.mocked(generateFlyerIcon).mockResolvedValue('icon-flyer-page-1.webp'); }); it('should transform AI data into database-ready format with a user ID', async () => { // Arrange const extractedData: z.infer = { store_name: 'Test Store', valid_from: '2024-01-01', valid_to: '2024-01-07', store_address: '123 Test St', items: [ { item: 'Milk', price_display: '$3.99', price_in_cents: 399, quantity: '1L', category_name: 'Dairy', master_item_id: 10 }, { item: 'Bread', price_display: '$2.49', price_in_cents: 249, quantity: '1 loaf', category_name: 'Bakery', master_item_id: null }, ], }; const imagePaths = [{ path: '/uploads/flyer-page-1.jpg', mimetype: 'image/jpeg' }]; const originalFileName = 'my-flyer.pdf'; const checksum = 'checksum-abc-123'; const userId = 'user-xyz-456'; // Act const { flyerData, itemsForDb } = await transformer.transform(extractedData, imagePaths, originalFileName, checksum, userId, mockLogger); // Assert // 0. Check logging expect(mockLogger.info).toHaveBeenCalledWith('Starting data transformation from AI output to database format.'); expect(mockLogger.info).toHaveBeenCalledWith({ itemCount: 2, storeName: 'Test Store' }, 'Data transformation complete.'); // 1. Check flyer data expect(flyerData).toEqual({ file_name: originalFileName, image_url: '/flyer-images/flyer-page-1.jpg', icon_url: '/flyer-images/icons/icon-flyer-page-1.webp', checksum, store_name: 'Test Store', valid_from: '2024-01-01', valid_to: '2024-01-07', store_address: '123 Test St', item_count: 2, uploaded_by: userId, }); // 2. Check item data expect(itemsForDb).toHaveLength(2); expect(itemsForDb[0]).toEqual(expect.objectContaining({ item: 'Milk', master_item_id: 10, // Should be passed through view_count: 0, click_count: 0, })); expect(itemsForDb[1]).toEqual(expect.objectContaining({ item: 'Bread', master_item_id: undefined, // null should be converted to undefined view_count: 0, click_count: 0, })); expect((itemsForDb[0] as any).updated_at).toBeTypeOf('string'); // 3. Check that generateFlyerIcon was called correctly expect(generateFlyerIcon).toHaveBeenCalledWith('/uploads/flyer-page-1.jpg', '/uploads/icons', mockLogger); }); it('should handle missing optional data gracefully', async () => { // Arrange const extractedData: z.infer = { store_name: '', // Empty store name valid_from: null, valid_to: null, store_address: null, items: [], // No items }; const imagePaths = [{ path: '/uploads/another.png', mimetype: 'image/png' }]; const originalFileName = 'another.png'; const checksum = 'checksum-def-456'; // No userId provided vi.mocked(generateFlyerIcon).mockResolvedValue('icon-another.webp'); // Act const { flyerData, itemsForDb } = await transformer.transform(extractedData, imagePaths, originalFileName, checksum, undefined, mockLogger); // Assert // 0. Check logging expect(mockLogger.info).toHaveBeenCalledWith('Starting data transformation from AI output to database format.'); expect(mockLogger.info).toHaveBeenCalledWith({ itemCount: 0, storeName: 'Unknown Store (auto)' }, 'Data transformation complete.'); expect(itemsForDb).toHaveLength(0); expect(flyerData).toEqual({ file_name: originalFileName, image_url: '/flyer-images/another.png', icon_url: '/flyer-images/icons/icon-another.webp', checksum, store_name: 'Unknown Store (auto)', // Should use fallback valid_from: null, valid_to: null, store_address: null, item_count: 0, uploaded_by: undefined, // Should be undefined }); }); });