Files
flyer-crawler.projectium.com/src/services/flyerDataTransformer.test.ts
Torben Sorensen 424cbaf0d4
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Has been cancelled
feat: Implement deals repository and routes for fetching best watched item prices
- Added a new DealsRepository class to interact with the database for fetching the best sale prices of watched items.
- Created a new route `/api/users/deals/best-watched-prices` to handle requests for the best prices of items the authenticated user is watching.
- Enhanced logging in the FlyerDataTransformer and FlyerProcessingService for better traceability.
- Updated tests to ensure proper logging and functionality in the FlyerProcessingService.
- Refactored logger client to support structured logging for better consistency across the application.
2025-12-13 20:02:18 -08:00

126 lines
4.7 KiB
TypeScript

// 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<typeof AiFlyerDataSchema> = {
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<typeof AiFlyerDataSchema> = {
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
});
});
});