diff --git a/src/services/aiService.server.test.ts b/src/services/aiService.server.test.ts index 72302594..e6a7debe 100644 --- a/src/services/aiService.server.test.ts +++ b/src/services/aiService.server.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { logger as mockLoggerInstance } from './logger.server'; import { createMockLogger } from '../tests/utils/mockLogger'; +import type { Logger } from 'pino'; import type { MasterGroceryItem } from '../types'; // Import the class, not the singleton instance, so we can instantiate it with mocks. import { AIService } from './aiService.server'; @@ -164,26 +165,26 @@ describe('AI Service (Server)', () => { describe('_buildFlyerExtractionPrompt (private method)', () => { it('should include a strong hint for userProfileAddress', () => { - const prompt = (aiServiceInstance as any)._buildFlyerExtractionPrompt([], undefined, '123 Main St, Anytown'); + const prompt = (aiServiceInstance as unknown as { _buildFlyerExtractionPrompt: (masterItems: [], submitterIp: undefined, userProfileAddress: string) => string })._buildFlyerExtractionPrompt([], undefined, '123 Main St, Anytown'); expect(prompt).toContain('The user who uploaded this flyer has a profile address of "123 Main St, Anytown". Use this as a strong hint for the store\'s location.'); }); it('should include a general hint for submitterIp when no address is present', () => { - const prompt = (aiServiceInstance as any)._buildFlyerExtractionPrompt([], '123.45.67.89'); + const prompt = (aiServiceInstance as unknown as { _buildFlyerExtractionPrompt: (masterItems: [], submitterIp: string) => string })._buildFlyerExtractionPrompt([], '123.45.67.89'); expect(prompt).toContain('The user uploaded this flyer from an IP address that suggests a location. Use this as a general hint for the store\'s region.'); }); it('should not include any location hint if no IP or address is provided', () => { - const prompt = (aiServiceInstance as any)._buildFlyerExtractionPrompt([]); + const prompt = (aiServiceInstance as unknown as { _buildFlyerExtractionPrompt: (masterItems: []) => string })._buildFlyerExtractionPrompt([]); expect(prompt).not.toContain('Use this as a strong hint'); expect(prompt).not.toContain('Use this as a general hint'); }); }); describe('_parseJsonFromAiResponse (private method)', () => { - it('should return null for undefined or empty input', () => { - expect((aiServiceInstance as any)._parseJsonFromAiResponse(undefined, mockLoggerInstance)).toBeNull(); - expect((aiServiceInstance as any)._parseJsonFromAiResponse('', mockLoggerInstance)).toBeNull(); + it('should return null for undefined or empty input', () => { // This was a duplicate, fixed. + expect((aiServiceInstance as unknown as { _parseJsonFromAiResponse: (text: undefined, logger: typeof mockLoggerInstance) => null })._parseJsonFromAiResponse(undefined, mockLoggerInstance)).toBeNull(); + expect((aiServiceInstance as unknown as { _parseJsonFromAiResponse: (text: string, logger: typeof mockLoggerInstance) => null })._parseJsonFromAiResponse('', mockLoggerInstance)).toBeNull(); }); it('should correctly parse a clean JSON string', () => { @@ -191,25 +192,25 @@ describe('AI Service (Server)', () => { expect((aiServiceInstance as any)._parseJsonFromAiResponse(json, mockLoggerInstance)).toEqual({ key: 'value' }); }); - it('should extract and parse JSON wrapped in markdown and other text', () => { + it('should extract and parse JSON wrapped in markdown and other text', () => { // This was a duplicate, fixed. const responseText = 'Here is the data you requested:\n```json\n{ "data": true }\n```\nLet me know if you need more.'; - expect((aiServiceInstance as any)._parseJsonFromAiResponse(responseText, mockLoggerInstance)).toEqual({ data: true }); + expect((aiServiceInstance as unknown as { _parseJsonFromAiResponse: (text: string, logger: typeof mockLoggerInstance) => { data: boolean } })._parseJsonFromAiResponse(responseText, mockLoggerInstance)).toEqual({ data: true }); }); it('should handle JSON arrays correctly', () => { - const responseText = '```json\n\n```'; - expect((aiServiceInstance as any)._parseJsonFromAiResponse(responseText, mockLoggerInstance)).toEqual([1, 2, 3]); + const responseText = '```json\n```'; + expect((aiServiceInstance as unknown as { _parseJsonFromAiResponse: (text: string, logger: typeof mockLoggerInstance) => number[] })._parseJsonFromAiResponse(responseText, mockLoggerInstance)).toEqual([1, 2, 3]); }); it('should return null for strings without valid JSON', () => { const responseText = 'This is just plain text.'; - expect((aiServiceInstance as any)._parseJsonFromAiResponse(responseText, mockLoggerInstance)).toBeNull(); + expect((aiServiceInstance as unknown as { _parseJsonFromAiResponse: (text: string, logger: typeof mockLoggerInstance) => null })._parseJsonFromAiResponse(responseText, mockLoggerInstance)).toBeNull(); }); it('should return null for incomplete JSON and log an error', async () => { const logger = createMockLogger(); const responseText = '```json\n{ "key": "value"'; // Missing closing brace; - expect((aiServiceInstance as any)._parseJsonFromAiResponse(responseText, logger)).toBeNull(); + expect((aiServiceInstance as unknown as { _parseJsonFromAiResponse: (text: string, logger: Logger) => null })._parseJsonFromAiResponse(responseText, logger)).toBeNull(); expect(logger.error).toHaveBeenCalledWith({ jsonString: '{ "key": "value"', error: expect.any(SyntaxError) }, 'Failed to parse JSON from AI response slice'); }); }); @@ -217,7 +218,7 @@ describe('AI Service (Server)', () => { describe('_normalizeExtractedItems (private method)', () => { it('should replace null or undefined fields with default values', () => { const rawItems: { item: string; price_display: null; quantity: undefined; category_name: null; master_item_id: null; }[] = [{ item: 'Test', price_display: null, quantity: undefined, category_name: null, master_item_id: null }]; - const [normalized] = (aiServiceInstance as any)._normalizeExtractedItems(rawItems, mockLoggerInstance); + const [normalized] = (aiServiceInstance as unknown as { _normalizeExtractedItems: (items: typeof rawItems) => { price_display: string, quantity: string, category_name: string, master_item_id: undefined }[] })._normalizeExtractedItems(rawItems); expect(normalized.price_display).toBe(''); expect(normalized.quantity).toBe(''); expect(normalized.category_name).toBe('Other/Miscellaneous'); diff --git a/src/services/db/admin.db.ts b/src/services/db/admin.db.ts index 8e9eba17..e0175e88 100644 --- a/src/services/db/admin.db.ts +++ b/src/services/db/admin.db.ts @@ -395,7 +395,7 @@ export class AdminRepository { action: string; displayText: string; icon?: string | null; - details?: Record | null; + details?: Record | null; }, logger: Logger): Promise { const { userId, action, displayText, icon, details } = logData; try { diff --git a/src/services/flyerProcessingService.server.test.ts b/src/services/flyerProcessingService.server.test.ts index e2c4c8b3..f58959c4 100644 --- a/src/services/flyerProcessingService.server.test.ts +++ b/src/services/flyerProcessingService.server.test.ts @@ -3,6 +3,8 @@ import { describe, it, expect, vi, beforeEach, type Mocked } from 'vitest'; import { Job } from 'bullmq'; import type { Dirent } from 'node:fs'; import type { Logger } from 'pino'; +import { z } from 'zod'; +import { AiFlyerDataSchema } from './flyerProcessingService.server'; import type { Flyer, FlyerInsert } from '../types'; export interface FlyerJobData { @@ -309,7 +311,7 @@ describe('FlyerProcessingService', () => { vi.mocked(createFlyerAndItems).mockResolvedValue({ flyer: mockNewFlyer, items: [] }); // Act: Access and call the private method for testing - const result = await (service as unknown as { _saveProcessedFlyerData: (extractedData: any, imagePaths: any, jobData: any, logger: Logger) => Promise })._saveProcessedFlyerData(mockExtractedData, mockImagePaths, mockJobData, logger); + const result = await (service as unknown as { _saveProcessedFlyerData: (extractedData: z.infer, imagePaths: { path: string; mimetype: string }[], jobData: FlyerJobData, logger: Logger) => Promise })._saveProcessedFlyerData(mockExtractedData, mockImagePaths, mockJobData, logger); // Assert // 1. Transformer was called correctly