// src/tests/utils/mockFactories.ts import { UserProfile, User, Flyer, Store, SuggestedCorrection, Brand, Category, FlyerItem, MasterGroceryItem, ShoppingList, ShoppingListItem, Achievement, UserAchievement, Budget, SpendingByCategory, Recipe, RecipeIngredient, RecipeComment, ActivityLogItem, DietaryRestriction, UserDietaryRestriction, Appliance, UserAppliance, Notification, UnmatchedFlyerItem, AdminUserView, WatchedItemDeal, LeaderboardUser, UserWithPasswordHash, Profile, Address, StoreLocation, StoreLocationWithAddress, StoreWithLocations, MenuPlan, PlannedMeal, PantryItem, Product, ShoppingTrip, ShoppingTripItem, Receipt, ReceiptItem, SearchQuery, ProcessingStage, UserAlert, UserSubmittedPrice, RecipeRating, Tag, PantryLocation, DealItem, ItemPriceHistory, HistoricalPriceDataPoint, ReceiptDeal, RecipeCollection, SharedShoppingList, MostFrequentSaleItem, PantryRecipe, RecommendedRecipe, UnitPrice, Source, } from '../../types'; import type { AppStats } from '../../services/apiClient'; // --- ID Generator for Deterministic Mocking --- let idCounter = 0; /** * Generates a sequential number for deterministic IDs in tests. */ const getNextId = () => ++idCounter; /** * Resets the ID counter. Call this in `beforeEach` in your test setup * to ensure tests are isolated. * * @example * beforeEach(() => { * resetMockIds(); * }); */ export const resetMockIds = () => { idCounter = 0; }; // --- End ID Generator --- /** * Creates a mock User object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe User object. */ export const createMockUser = (overrides: Partial = {}): User => { // Generate a deterministic, valid UUID-like string for mock user IDs. // This prevents database errors in integration tests where a UUID is expected. const userId = overrides.user_id ?? `00000000-0000-0000-0000-${String(getNextId()).padStart(12, '0')}`; const defaultUser: User = { user_id: userId, email: `${userId}@example.com`, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultUser, ...overrides }; }; /** * Creates a mock UserProfile object for use in tests, ensuring type safety. * * @param overrides - An object containing properties to override the default mock values. * This allows for easy customization of the mock user for specific test cases. * For example: `createMockUserProfile({ role: 'admin', points: 500 })` * @returns A complete and type-safe UserProfile object. */ export const createMockUserProfile = ( overrides: Partial> & { user?: Partial } = {}, ): UserProfile => { // The user object is the source of truth for user_id and email. const user = createMockUser(overrides.user); const defaultProfile: UserProfile = { role: 'user', points: 0, full_name: 'Test User', avatar_url: null, preferences: {}, address_id: null, created_by: null, address: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), user, }; // Exclude 'user' from overrides to prevent overwriting the complete user object with a partial one const { user: _, ...profileOverrides } = overrides; // Combine defaults, overrides, and the fully constructed user object. return { ...defaultProfile, ...profileOverrides, user }; }; /** * Creates a mock Store object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Store object. */ export const createMockStore = (overrides: Partial = {}): Store => { const storeId = overrides.store_id ?? getNextId(); const defaultStore: Store = { store_id: storeId, name: 'Mock Store', logo_url: null, created_by: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultStore, ...overrides }; }; /** * Creates a mock Flyer object for use in tests, ensuring type safety. * * @param overrides - An object containing properties to override the default mock values, * including nested properties for the `store`. * e.g., `createMockFlyer({ item_count: 50, store: { name: 'Walmart' } })` * @returns A complete and type-safe Flyer object. */ export const createMockFlyer = ( overrides: Omit, 'store'> & { store?: Partial } = {}, ): Flyer => { const flyerId = overrides.flyer_id ?? getNextId(); // Ensure the store_id is consistent between the flyer and the nested store object const store = createMockStore({ ...overrides.store, // Prioritize the top-level store_id if provided store_id: overrides.store_id ?? overrides.store?.store_id, }); const baseUrl = 'https://example.com'; // A reasonable default for tests // Determine the final file_name to generate dependent properties from. const fileName = overrides.file_name ?? `flyer-${flyerId}.jpg`; // A simple hash function for mock checksum generation. const generateMockChecksum = (str: string) => { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = (hash << 5) - hash + char; hash |= 0; // Convert to 32bit integer } return `mock-checksum-${Math.abs(hash).toString(16)}`; }; const defaultFlyer: Flyer = { flyer_id: flyerId, file_name: fileName, image_url: `${baseUrl}/flyer-images/${fileName}`, icon_url: `${baseUrl}/flyer-images/icons/icon-${fileName.replace(/\.[^/.]+$/, '.webp')}`, checksum: generateMockChecksum(fileName), store_id: store.store_id, valid_from: new Date().toISOString().split('T')[0], valid_to: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], // 7 days from now store_address: '123 Main St, Anytown, USA', status: 'processed', item_count: 50, uploaded_by: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), store, }; const { store: _, ...flyerOverrides } = overrides; // Apply overrides. If checksum, file_name, etc., are in overrides, they will correctly replace the generated defaults. return { ...defaultFlyer, ...flyerOverrides }; }; /** * Creates a mock SuggestedCorrection object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe SuggestedCorrection object. */ export const createMockSuggestedCorrection = ( overrides: Partial = {}, ): SuggestedCorrection => { const defaultCorrection: SuggestedCorrection = { suggested_correction_id: getNextId(), flyer_item_id: getNextId(), user_id: `user-${getNextId()}`, correction_type: 'price', suggested_value: '$9.99', status: 'pending', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultCorrection, ...overrides }; }; /** * Creates a mock Brand object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Brand object. */ export const createMockBrand = (overrides: Partial = {}): Brand => { const brandId = overrides.brand_id ?? getNextId(); const defaultBrand: Brand = { brand_id: brandId, name: `Brand ${brandId}`, logo_url: null, store_id: null, store_name: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultBrand, ...overrides }; }; /** * Creates a mock Category object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Category object. */ export const createMockCategory = (overrides: Partial = {}): Category => { const categoryId = overrides.category_id ?? getNextId(); const defaultCategory: Category = { category_id: categoryId, name: `Category ${categoryId}`, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultCategory, ...overrides }; }; /** * Creates a mock Product object for use in tests. * @param overrides - An object containing properties to override the default mock values. * Can optionally include `master_item` and `brand` to link IDs. * @returns A complete and type-safe Product object. */ export const createMockProduct = ( overrides: Partial & { master_item?: Partial; brand?: Partial; } = {}, ): Product => { const productId = overrides.product_id ?? getNextId(); const masterItemId = overrides.master_item_id ?? overrides.master_item?.master_grocery_item_id ?? getNextId(); const brandId = overrides.brand_id ?? overrides.brand?.brand_id; // brand is optional const defaultProduct: Product = { product_id: productId, master_item_id: masterItemId, brand_id: brandId, name: `Mock Product ${productId}`, description: 'A mock product description.', size: '100g', upc_code: `0000${productId}`.slice(-12), created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; const { master_item: _, brand: __, ...itemOverrides } = overrides; return { ...defaultProduct, ...itemOverrides }; }; /** * Creates a mock FlyerItem object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe FlyerItem object. */ export const createMockFlyerItem = ( overrides: Partial & { flyer?: Partial } = {}, ): FlyerItem => { const flyerItemId = overrides.flyer_item_id ?? getNextId(); const flyerId = overrides.flyer_id ?? overrides.flyer?.flyer_id ?? getNextId(); const defaultItem: FlyerItem = { flyer_item_id: flyerItemId, flyer_id: flyerId, item: 'Mock Item', price_display: '$1.99', price_in_cents: 199, unit_price: null, quantity: 'each', view_count: 0, click_count: 0, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; const { flyer: _, ...itemOverrides } = overrides; return { ...defaultItem, ...itemOverrides }; }; /** * Creates a mock Recipe object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Recipe object. */ export const createMockRecipe = ( overrides: Omit, 'comments' | 'ingredients'> & { comments?: Partial[]; ingredients?: Partial[]; } = {}, ): Recipe => { const recipeId = overrides.recipe_id ?? getNextId(); const defaultRecipe: Recipe = { recipe_id: recipeId, user_id: `user-${getNextId()}`, name: `Mock Recipe ${recipeId}`, description: 'A delicious mock recipe.', instructions: '1. Mock the ingredients. 2. Mock the cooking. 3. Enjoy!', avg_rating: 4.5, rating_count: 50, fork_count: 10, status: 'public', prep_time_minutes: 15, cook_time_minutes: 30, servings: 4, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; const { comments: commentsOverrides, ingredients: ingredientsOverrides, ...recipeOverrides } = overrides; const recipe = { ...defaultRecipe, ...recipeOverrides }; if (commentsOverrides) { recipe.comments = commentsOverrides.map((comment) => createMockRecipeComment({ recipe_id: recipeId, ...comment, }), ); } if (ingredientsOverrides) { recipe.ingredients = ingredientsOverrides.map((ingredient) => createMockRecipeIngredient({ recipe_id: recipeId, ...ingredient, }), ); } return recipe; }; /** * Creates a mock RecipeIngredient object for use in tests. * @param overrides - An object containing properties to override the default mock values. * Can optionally include a `master_item` to link IDs. * @returns A complete and type-safe RecipeIngredient object. */ export const createMockRecipeIngredient = ( overrides: Partial & { master_item?: Partial } = {}, ): RecipeIngredient => { const recipeIngredientId = overrides.recipe_ingredient_id ?? getNextId(); const masterItemId = overrides.master_item_id ?? overrides.master_item?.master_grocery_item_id ?? getNextId(); const defaultIngredient: RecipeIngredient = { recipe_ingredient_id: recipeIngredientId, recipe_id: getNextId(), master_item_id: masterItemId, quantity: 1, unit: 'cup', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; const { master_item: _, ...itemOverrides } = overrides; return { ...defaultIngredient, ...itemOverrides }; }; /** * Creates a mock RecipeComment object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe RecipeComment object. */ export const createMockRecipeComment = (overrides: Partial = {}): RecipeComment => { const defaultComment: RecipeComment = { recipe_comment_id: getNextId(), recipe_id: getNextId(), user_id: `user-${getNextId()}`, content: 'This is a mock comment.', status: 'visible', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), user_full_name: 'Mock User', // This was correct user_avatar_url: undefined, }; return { ...defaultComment, ...overrides }; }; /** * Creates a mock PlannedMeal object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe PlannedMeal object. */ export const createMockPlannedMeal = (overrides: Partial = {}): PlannedMeal => { const defaultMeal: PlannedMeal = { planned_meal_id: getNextId(), menu_plan_id: getNextId(), recipe_id: getNextId(), plan_date: new Date().toISOString().split('T')[0], meal_type: 'dinner', servings_to_cook: 4, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultMeal, ...overrides }; }; /** * Creates a mock MenuPlan object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe MenuPlan object. */ export const createMockMenuPlan = ( overrides: Omit, 'planned_meals'> & { planned_meals?: Partial[]; } = {}, ): MenuPlan => { const menuPlanId = overrides.menu_plan_id ?? getNextId(); const defaultPlan: MenuPlan = { menu_plan_id: menuPlanId, user_id: `user-${getNextId()}`, name: 'Weekly Plan', start_date: new Date().toISOString().split('T')[0], end_date: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; const { planned_meals: mealsOverrides, ...planOverrides } = overrides; const menuPlan = { ...defaultPlan, ...planOverrides }; if (mealsOverrides) { menuPlan.planned_meals = mealsOverrides.map((meal) => createMockPlannedMeal({ menu_plan_id: menuPlanId, ...meal, }), ); } return menuPlan; }; /** * Creates a mock ActivityLogItem object for use in tests. * This factory handles the discriminated union nature of the ActivityLogItem type. * By default, it creates a 'flyer_processed' log item. You can override the 'action' * and 'details' to create other types of log items. * * @param overrides - An object containing properties to override the default mock values. * e.g., `createMockActivityLogItem({ action: 'user_registered', details: { full_name: 'New User' } })` * @returns A complete and type-safe ActivityLogItem object. */ export const createMockActivityLogItem = ( overrides: Partial = {}, ): ActivityLogItem => { const action = overrides.action ?? 'flyer_processed'; const baseLog = { activity_log_id: getNextId(), user_id: `user-${getNextId()}`, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; let specificLog: ActivityLogItem; // Create a default log based on the action, which can then be overridden. switch (action) { case 'recipe_created': specificLog = { ...baseLog, action: 'recipe_created', display_text: 'Created a new recipe: Mock Recipe.', icon: 'chef-hat', details: { recipe_id: 1, recipe_name: 'Mock Recipe' }, }; break; case 'user_registered': specificLog = { ...baseLog, action: 'user_registered', display_text: 'A new user has registered.', icon: 'user-plus', details: { full_name: 'New Mock User' }, }; break; case 'list_shared': specificLog = { ...baseLog, action: 'list_shared', display_text: 'A shopping list was shared.', icon: 'share-2', details: { list_name: 'Mock List', shopping_list_id: 1, shared_with_name: 'Another User' }, }; break; case 'recipe_favorited': specificLog = { ...baseLog, action: 'recipe_favorited', display_text: 'User favorited a recipe.', icon: 'heart', user_full_name: 'Mock User', details: { recipe_name: 'Mock Recipe' }, }; break; case 'flyer_processed': default: specificLog = { ...baseLog, action: 'flyer_processed', display_text: 'Processed a new flyer for Mock Store.', icon: 'file-check', details: { flyer_id: 1, store_name: 'Mock Store' }, }; break; } // Merge the generated log with any specific overrides provided. // This allows for deep merging of the 'details' object. return { ...specificLog, ...overrides, details: { ...specificLog.details, ...overrides.details }, } as ActivityLogItem; }; /** * Creates a mock Achievement object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Achievement object. */ export const createMockAchievement = (overrides: Partial = {}): Achievement => { const defaultAchievement: Achievement = { achievement_id: getNextId(), name: 'Mock Achievement', description: 'A great accomplishment.', icon: 'star', points_value: 10, created_at: new Date().toISOString(), }; return { ...defaultAchievement, ...overrides }; }; /** * Creates a mock object representing a joined UserAchievement and Achievement for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe object representing the joined achievement data. */ export const createMockUserAchievement = ( overrides: Partial = {}, ): UserAchievement & Achievement => { const achievementId = overrides.achievement_id ?? getNextId(); const defaultUserAchievement: UserAchievement & Achievement = { user_id: `user-${getNextId()}`, achievement_id: achievementId, achieved_at: new Date().toISOString(), // from Achievement name: 'Mock User Achievement', description: 'An achievement someone earned.', icon: 'award', points_value: 20, created_at: new Date().toISOString(), }; return { ...defaultUserAchievement, ...overrides }; }; /** * Creates a mock Budget object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Budget object. */ export const createMockBudget = (overrides: Partial = {}): Budget => { const defaultBudget: Budget = { budget_id: getNextId(), user_id: `user-${getNextId()}`, name: 'Monthly Groceries', amount_cents: 50000, period: 'monthly', start_date: new Date().toISOString().split('T')[0], created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultBudget, ...overrides }; }; /** * Creates a mock SpendingByCategory object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe SpendingByCategory object. */ export const createMockSpendingByCategory = ( overrides: Partial = {}, ): SpendingByCategory => { const defaultSpending: SpendingByCategory = { category_id: getNextId(), category_name: 'Produce', total_spent_cents: 15000, }; return { ...defaultSpending, ...overrides }; }; /** * Creates a mock MasterGroceryItem object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe MasterGroceryItem object. */ export const createMockMasterGroceryItem = ( overrides: Partial & { category?: Partial } = {}, ): MasterGroceryItem => { // Ensure category_id is consistent between the item and the nested category object const category = createMockCategory({ ...overrides.category, // Prioritize the top-level category_id if provided category_id: overrides.category_id ?? overrides.category?.category_id, }); const defaultItem: MasterGroceryItem = { master_grocery_item_id: getNextId(), name: 'Mock Master Item', category_id: category.category_id, category_name: category.name, is_allergen: false, allergy_info: null, created_by: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; const { category: _, ...itemOverrides } = overrides; return { ...defaultItem, ...itemOverrides }; }; /** * Creates a mock PantryItem object for use in tests. * @param overrides - An object containing properties to override the default mock values. * Can optionally include a `master_item` to link IDs. * @returns A complete and type-safe PantryItem object. */ export const createMockPantryItem = ( overrides: Partial & { master_item?: Partial } = {}, ): PantryItem => { const pantryItemId = overrides.pantry_item_id ?? getNextId(); const masterItemId = overrides.master_item_id ?? overrides.master_item?.master_grocery_item_id ?? getNextId(); const defaultItem: PantryItem = { pantry_item_id: pantryItemId, user_id: `user-${getNextId()}`, master_item_id: masterItemId, quantity: 1, unit: 'each', updated_at: new Date().toISOString(), }; const { master_item: _, ...itemOverrides } = overrides; return { ...defaultItem, ...itemOverrides }; }; /** * Creates a mock ShoppingList object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe ShoppingList object. */ export const createMockShoppingList = ( overrides: Omit, 'items'> & { items?: (Partial & { master_item?: Partial | null })[]; } = {}, ): ShoppingList => { const shoppingListId = overrides.shopping_list_id ?? getNextId(); const defaultList: ShoppingList = { shopping_list_id: shoppingListId, user_id: `user-${getNextId()}`, name: 'My Mock List', items: [], created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; if (overrides.items) { defaultList.items = overrides.items.map((item) => createMockShoppingListItem({ shopping_list_id: shoppingListId, ...item, }), ); } const { items: _, ...listOverrides } = overrides; return { ...defaultList, ...listOverrides }; }; /** * Creates a mock ShoppingListItem object for use in tests. * @param overrides - An object containing properties to override the default mock values. * Can optionally include a `master_item` to link IDs and populate joined data. * @returns A complete and type-safe ShoppingListItem object. */ export const createMockShoppingListItem = ( overrides: Partial & { master_item?: Partial | null } = {}, ): ShoppingListItem => { const shoppingListItemId = overrides.shopping_list_item_id ?? getNextId(); const shoppingListId = overrides.shopping_list_id ?? getNextId(); const masterItemId = overrides.master_item_id ?? overrides.master_item?.master_grocery_item_id; const defaultItem: ShoppingListItem = { shopping_list_item_id: shoppingListItemId, shopping_list_id: shoppingListId, custom_item_name: 'Mock Shopping List Item', quantity: 1, is_purchased: false, // This was correct added_at: new Date().toISOString(), updated_at: new Date().toISOString(), master_item_id: masterItemId, }; const { master_item: masterItemOverride, ...itemOverrides } = overrides; const result = { ...defaultItem, ...itemOverrides }; if (masterItemOverride && !result.master_item && masterItemOverride.name) { result.master_item = { name: masterItemOverride.name }; } return result; }; /** * Creates a mock ShoppingTripItem object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe ShoppingTripItem object. */ export const createMockShoppingTripItem = ( overrides: Partial & { master_item?: Partial } = {}, ): ShoppingTripItem => { const tripItemId = overrides.shopping_trip_item_id ?? getNextId(); const masterItemId = overrides.master_item_id ?? overrides.master_item?.master_grocery_item_id; const defaultItem: ShoppingTripItem = { shopping_trip_item_id: tripItemId, shopping_trip_id: getNextId(), master_item_id: masterItemId, custom_item_name: masterItemId ? null : 'Custom Trip Item', master_item_name: masterItemId ? (overrides.master_item?.name ?? 'Mock Master Item') : null, quantity: 1, price_paid_cents: 199, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; const { master_item: _, ...itemOverrides } = overrides; return { ...defaultItem, ...itemOverrides }; }; /** * Creates a mock ShoppingTrip object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe ShoppingTrip object. */ export const createMockShoppingTrip = ( overrides: Omit, 'items'> & { items?: Partial[] } = {}, ): ShoppingTrip => { const tripId = overrides.shopping_trip_id ?? getNextId(); const defaultTrip: ShoppingTrip = { shopping_trip_id: tripId, user_id: `user-${getNextId()}`, shopping_list_id: null, completed_at: new Date().toISOString(), total_spent_cents: 0, items: [], updated_at: new Date().toISOString(), }; const { items: itemsOverrides, ...tripOverrides } = overrides; const trip = { ...defaultTrip, ...tripOverrides }; if (itemsOverrides) { trip.items = itemsOverrides.map((item) => createMockShoppingTripItem({ shopping_trip_id: tripId, ...item }), ); if (overrides.total_spent_cents === undefined) { trip.total_spent_cents = trip.items.reduce( (total, item) => total + (item.price_paid_cents ?? 0), 0, ); } } return trip; }; /** * Creates a mock ReceiptItem object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe ReceiptItem object. */ export const createMockReceiptItem = (overrides: Partial = {}): ReceiptItem => { const defaultItem: ReceiptItem = { receipt_item_id: getNextId(), receipt_id: getNextId(), raw_item_description: 'Mock Receipt Item', quantity: 1, price_paid_cents: 199, master_item_id: null, product_id: null, status: 'unmatched', upc_code: null, line_number: null, match_confidence: null, is_discount: false, unit_price_cents: null, unit_type: null, added_to_pantry: false, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultItem, ...overrides }; }; /** * Creates a mock Receipt object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Receipt object. */ export const createMockReceipt = ( overrides: Omit, 'items'> & { items?: Partial[] } = {}, ): Receipt => { const receiptId = overrides.receipt_id ?? getNextId(); const defaultReceipt: Receipt = { receipt_id: receiptId, user_id: `user-${getNextId()}`, store_id: null, receipt_image_url: `/receipts/mock-receipt-${receiptId}.jpg`, transaction_date: new Date().toISOString(), total_amount_cents: null, status: 'pending', raw_text: null, processed_at: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; const { items: itemsOverrides, ...receiptOverrides } = overrides; const receipt = { ...defaultReceipt, ...receiptOverrides }; if (itemsOverrides) { receipt.items = itemsOverrides.map((item) => createMockReceiptItem({ receipt_id: receiptId, ...item }), ); } return receipt; }; /** * Creates a mock DietaryRestriction object for testing. * @param overrides - Optional properties to override the defaults. * @returns A mock DietaryRestriction object. */ export const createMockDietaryRestriction = ( overrides: Partial = {}, ): DietaryRestriction => { return { dietary_restriction_id: 1, name: 'Vegetarian', type: 'diet', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), ...overrides, }; }; /** * Creates a mock DealItem object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe DealItem object. */ export const createMockDealItem = (overrides: Partial = {}): DealItem => { const defaultDealItem: DealItem = { item: 'Mock Deal Item', price_display: '$1.99', price_in_cents: 199, quantity: 'each', storeName: 'Mock Store', master_item_name: 'Mock Master Item', unit_price: null, }; return { ...defaultDealItem, ...overrides }; }; /** * Creates a mock ItemPriceHistory object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe ItemPriceHistory object. */ export const createMockItemPriceHistory = ( overrides: Partial = {}, ): ItemPriceHistory => { const defaultHistory: ItemPriceHistory = { item_price_history_id: getNextId(), master_item_id: getNextId(), summary_date: new Date().toISOString().split('T')[0], min_price_in_cents: 199, max_price_in_cents: 399, avg_price_in_cents: 299, data_points_count: 10, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultHistory, ...overrides }; }; /** * Creates a mock HistoricalPriceDataPoint object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe HistoricalPriceDataPoint object. */ export const createMockHistoricalPriceDataPoint = ( overrides: Partial = {}, ): HistoricalPriceDataPoint => { const defaultPoint: HistoricalPriceDataPoint = { master_item_id: getNextId(), avg_price_in_cents: 250, summary_date: new Date().toISOString().split('T')[0], }; return { ...defaultPoint, ...overrides }; }; /** * Creates a mock ReceiptDeal object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe ReceiptDeal object. */ export const createMockReceiptDeal = (overrides: Partial = {}): ReceiptDeal => { const defaultDeal: ReceiptDeal = { receipt_item_id: getNextId(), master_item_id: getNextId(), item_name: 'Mock Deal Item', price_paid_cents: 199, current_best_price_in_cents: 150, potential_savings_cents: 49, deal_store_name: 'Competitor Store', flyer_id: getNextId(), }; return { ...defaultDeal, ...overrides }; }; /** * Creates a mock RecipeCollection object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe RecipeCollection object. */ export const createMockRecipeCollection = ( overrides: Partial = {}, ): RecipeCollection => { const defaultCollection: RecipeCollection = { recipe_collection_id: getNextId(), user_id: `user-${getNextId()}`, name: 'My Favorite Recipes', description: 'A collection of mock recipes.', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultCollection, ...overrides }; }; /** * Creates a mock SharedShoppingList object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe SharedShoppingList object. */ export const createMockSharedShoppingList = ( overrides: Partial = {}, ): SharedShoppingList => { const defaultSharedList: SharedShoppingList = { shared_shopping_list_id: getNextId(), shopping_list_id: getNextId(), shared_by_user_id: `user-${getNextId()}`, shared_with_user_id: `user-${getNextId()}`, permission_level: 'view', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultSharedList, ...overrides }; }; /** * Creates a mock MostFrequentSaleItem object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe MostFrequentSaleItem object. */ export const createMockMostFrequentSaleItem = ( overrides: Partial = {}, ): MostFrequentSaleItem => { const defaultItem: MostFrequentSaleItem = { master_item_id: getNextId(), item_name: 'Chicken Breast', sale_count: 25, }; return { ...defaultItem, ...overrides }; }; /** * Creates a mock PantryRecipe object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe PantryRecipe object. */ export const createMockPantryRecipe = (overrides: Partial = {}): PantryRecipe => { const defaultRecipe = createMockRecipe({ name: 'Pantry Special', ...overrides }); const pantryRecipe: PantryRecipe = { ...defaultRecipe, missing_ingredients_count: 2, pantry_ingredients_count: 5, }; return { ...pantryRecipe, ...overrides }; }; /** * Creates a mock RecommendedRecipe object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe RecommendedRecipe object. */ export const createMockRecommendedRecipe = ( overrides: Partial = {}, ): RecommendedRecipe => { const defaultRecipe = createMockRecipe({ name: 'Highly Recommended', ...overrides }); const recommendedRecipe: RecommendedRecipe = { ...defaultRecipe, recommendation_score: 0.85, reason: 'Based on your recent activity and pantry items.', }; return { ...recommendedRecipe, ...overrides }; }; /** * Creates a mock UnitPrice object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe UnitPrice object. */ export const createMockUnitPrice = (overrides: Partial = {}): UnitPrice => { const defaultUnitPrice: UnitPrice = { value: 100, unit: 'g', }; return { ...defaultUnitPrice, ...overrides }; }; /** * Creates a mock Source object for use in tests, typically for AI analysis results. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Source object. */ export const createMockSource = (overrides: Partial = {}): Source => { const defaultSource: Source = { uri: 'https://example.com/mock-source', title: 'Mock Source Title', }; return { ...defaultSource, ...overrides }; }; /** * Creates a mock UserAlert object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe UserAlert object. */ export const createMockUserAlert = (overrides: Partial = {}): UserAlert => { const defaultAlert: UserAlert = { user_alert_id: getNextId(), user_watched_item_id: getNextId(), alert_type: 'PRICE_BELOW', threshold_value: 499, is_active: true, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultAlert, ...overrides }; }; /** * Creates a mock UserSubmittedPrice object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe UserSubmittedPrice object. */ export const createMockUserSubmittedPrice = ( overrides: Partial = {}, ): UserSubmittedPrice => { const defaultPrice: UserSubmittedPrice = { user_submitted_price_id: getNextId(), user_id: `user-${getNextId()}`, master_item_id: getNextId(), store_id: getNextId(), price_in_cents: 299, photo_url: null, upvotes: 0, downvotes: 0, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultPrice, ...overrides }; }; /** * Creates a mock RecipeRating object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe RecipeRating object. */ export const createMockRecipeRating = (overrides: Partial = {}): RecipeRating => { const defaultRating: RecipeRating = { recipe_rating_id: getNextId(), recipe_id: getNextId(), user_id: `user-${getNextId()}`, rating: 5, comment: 'Great recipe!', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultRating, ...overrides }; }; /** * Creates a mock Tag object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Tag object. */ export const createMockTag = (overrides: Partial = {}): Tag => { const tagId = overrides.tag_id ?? getNextId(); const defaultTag: Tag = { tag_id: tagId, name: `Tag ${tagId}`, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultTag, ...overrides }; }; /** * Creates a mock PantryLocation object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe PantryLocation object. */ export const createMockPantryLocation = ( overrides: Partial = {}, ): PantryLocation => { const locationId = overrides.pantry_location_id ?? getNextId(); const defaultLocation: PantryLocation = { pantry_location_id: locationId, user_id: `user-${getNextId()}`, name: `Location ${locationId}`, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultLocation, ...overrides }; }; /** * Creates a mock AppStats object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe AppStats object. */ export const createMockAppStats = (overrides: Partial = {}): AppStats => { const defaultStats: AppStats = { userCount: 100, flyerCount: 50, flyerItemCount: 2000, storeCount: 5, pendingCorrectionCount: 0, recipeCount: 500, }; return { ...defaultStats, ...overrides }; }; /** * Creates a mock UserDietaryRestriction object for use in tests. * @param overrides - An object containing properties to override the default mock values. * Can optionally include `user` and `restriction` objects to link IDs. * @returns A complete and type-safe UserDietaryRestriction object. */ export const createMockUserDietaryRestriction = ( overrides: Partial & { user?: Partial; restriction?: Partial; } = {}, ): UserDietaryRestriction => { const userId = overrides.user_id ?? overrides.user?.user_id ?? `user-${getNextId()}`; const restrictionId = overrides.restriction_id ?? overrides.restriction?.dietary_restriction_id ?? getNextId(); const defaultUserRestriction: UserDietaryRestriction = { user_id: userId, restriction_id: restrictionId, created_at: new Date().toISOString(), }; return { ...defaultUserRestriction, ...overrides }; }; /** * Creates a mock UserAppliance object for use in tests. * @param overrides - An object containing properties to override the default mock values. * Can optionally include `user` and `appliance` objects to link IDs. * @returns A complete and type-safe UserAppliance object. */ export const createMockUserAppliance = ( overrides: Partial & { user?: Partial; appliance?: Partial } = {}, ): UserAppliance => { const userId = overrides.user_id ?? overrides.user?.user_id ?? `user-${getNextId()}`; const applianceId = overrides.appliance_id ?? overrides.appliance?.appliance_id ?? getNextId(); const defaultUserAppliance = { user_id: userId, appliance_id: applianceId, created_at: new Date().toISOString(), }; // The 'as UserAppliance' cast is necessary because TypeScript can't guarantee that the spread of a Partial results in a complete T. return { ...defaultUserAppliance, ...overrides } as UserAppliance; }; /** * Creates a mock Address object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Address object. */ export const createMockAddress = (overrides: Partial
= {}): Address => { const defaultAddress: Address = { address_id: getNextId(), address_line_1: '123 Mock St', city: 'Mockville', province_state: 'BC', postal_code: 'V8T 1A1', country: 'CA', // Optional fields address_line_2: null, latitude: null, longitude: null, location: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultAddress, ...overrides }; }; /** * Creates a mock StoreLocation object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe StoreLocation object. */ export const createMockStoreLocation = ( overrides: Partial = {}, ): StoreLocation => { const defaultStoreLocation: StoreLocation = { store_location_id: getNextId(), store_id: overrides.store_id ?? getNextId(), address_id: overrides.address_id ?? getNextId(), created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultStoreLocation, ...overrides }; }; /** * Creates a mock StoreLocationWithAddress object for use in tests. * Includes a full address object nested within the store location. * * @param overrides - An object containing properties to override the default mock values, * including nested properties for the `address`. * e.g., `createMockStoreLocationWithAddress({ address: { city: 'Toronto' } })` * @returns A complete and type-safe StoreLocationWithAddress object. */ export const createMockStoreLocationWithAddress = ( overrides: Omit, 'address'> & { address?: Partial
} = {}, ): StoreLocationWithAddress => { // Create the address first, using the address_id from overrides if provided const address = createMockAddress({ address_id: overrides.address_id, ...overrides.address, }); // Create the store location with the address_id matching the address const storeLocation = createMockStoreLocation({ ...overrides, address_id: address.address_id, }); return { ...storeLocation, address, }; }; /** * Creates a mock StoreWithLocations object for use in tests. * Includes the store data along with an array of store locations with addresses. * * @param overrides - An object containing properties to override the default mock values, * including the `locations` array. * e.g., `createMockStoreWithLocations({ name: 'Walmart', locations: [{ address: { city: 'Toronto' } }] })` * @returns A complete and type-safe StoreWithLocations object. */ export const createMockStoreWithLocations = ( overrides: Omit, 'locations'> & { locations?: Array, 'address'> & { address?: Partial
}>; } = {}, ): StoreWithLocations => { const store = createMockStore(overrides); // If locations are provided, create them; otherwise create one default location const locations = overrides.locations?.map((locOverride) => createMockStoreLocationWithAddress({ ...locOverride, store_id: store.store_id, }), ) ?? [ createMockStoreLocationWithAddress({ store_id: store.store_id, }), ]; return { ...store, locations, }; }; /** * Creates a mock UserWithPasswordHash object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe UserWithPasswordHash object. */ export const createMockUserWithPasswordHash = ( overrides: Partial = {}, ): UserWithPasswordHash => { const userId = overrides.user_id ?? `user-${getNextId()}`; const defaultUser: UserWithPasswordHash = { user_id: userId, email: `${userId}@example.com`, password_hash: 'hashed_password', failed_login_attempts: 0, last_failed_login: null, last_login_ip: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultUser, ...overrides }; }; /** * Creates a mock Profile object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Profile object. */ export const createMockProfile = (overrides: Partial = {}): Profile => { const defaultProfile: Profile = { full_name: 'Mock Profile User', avatar_url: null, address_id: null, points: 0, role: 'user', preferences: {}, created_by: null, updated_by: null, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultProfile, ...overrides }; }; /** * Creates a mock WatchedItemDeal object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe WatchedItemDeal object. */ export const createMockWatchedItemDeal = ( overrides: Partial = {}, ): WatchedItemDeal => { const defaultDeal: WatchedItemDeal = { master_item_id: getNextId(), item_name: 'Mock Deal Item', best_price_in_cents: 599, store: { store_id: getNextId(), name: 'Mock Store', logo_url: null, locations: [], }, flyer_id: getNextId(), valid_to: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(), // 5 days from now }; return { ...defaultDeal, ...overrides }; }; /** * Creates a mock LeaderboardUser object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe LeaderboardUser object. */ export const createMockLeaderboardUser = ( overrides: Partial = {}, ): LeaderboardUser => { const userId = overrides.user_id ?? `user-${getNextId()}`; const defaultUser: LeaderboardUser = { user_id: userId, full_name: 'Leaderboard User', avatar_url: null, points: 500, rank: '10', }; return { ...defaultUser, ...overrides }; }; /** * Creates a mock UnmatchedFlyerItem object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe UnmatchedFlyerItem object. */ export const createMockUnmatchedFlyerItem = ( overrides: Partial = {}, ): UnmatchedFlyerItem => { const defaultItem: UnmatchedFlyerItem = { unmatched_flyer_item_id: getNextId(), status: 'pending', reviewed_at: null, flyer_item_id: getNextId(), flyer_item_name: 'Mystery Product', price_display: '$?.??', flyer_id: getNextId(), store_name: 'Random Store', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultItem, ...overrides }; }; /** * Creates a mock AdminUserView object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe AdminUserView object. */ export const createMockAdminUserView = (overrides: Partial = {}): AdminUserView => { const userId = overrides.user_id ?? `user-${getNextId()}`; const defaultUserView: AdminUserView = { user_id: userId, email: `${userId}@example.com`, role: 'user', full_name: 'Mock User', avatar_url: null, created_at: new Date().toISOString(), }; return { ...defaultUserView, ...overrides }; }; /** * Creates a mock Notification object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe Notification object. */ export const createMockNotification = (overrides: Partial = {}): Notification => { const defaultNotification: Notification = { notification_id: getNextId(), user_id: `user-${getNextId()}`, content: 'This is a mock notification.', link_url: null, is_read: false, created_at: new Date().toISOString(), updated_at: new Date().toISOString(), }; return { ...defaultNotification, ...overrides }; }; /** * Creates a mock ProcessingStage object for use in tests. * @param overrides - An object containing properties to override the default mock values. * @returns A complete and type-safe ProcessingStage object. */ export const createMockProcessingStage = ( overrides: Partial = {}, ): ProcessingStage => { const defaultStage: ProcessingStage = { name: 'Mock Stage', status: 'pending', detail: '', critical: true, }; return { ...defaultStage, ...overrides }; }; export const createMockAppliance = (overrides: Partial = {}): Appliance => { return { appliance_id: 1, name: 'Oven', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), ...overrides, }; }; // src/tests/utils/mockFactories.ts // ... existing factories export const createMockShoppingListItemPayload = ( overrides: Partial<{ masterItemId: number; customItemName: string }> = {}, ): { masterItemId?: number; customItemName?: string } => ({ customItemName: 'Mock Item', ...overrides, }); export const createMockRecipeCommentPayload = ( overrides: Partial<{ content: string; parentCommentId: number }> = {}, ): { content: string; parentCommentId?: number } => ({ content: 'This is a mock comment.', ...overrides, }); export const createMockProfileUpdatePayload = ( overrides: Partial = {}, ): Partial => ({ full_name: 'Mock User', ...overrides, }); export const createMockAddressPayload = (overrides: Partial
= {}): Partial
=> ({ address_line_1: '123 Mock St', city: 'Mockville', province_state: 'MS', postal_code: '12345', country: 'Mockland', ...overrides, }); export const createMockSearchQueryPayload = ( overrides: Partial< Omit > = {}, ): Omit => ({ query_text: 'mock search', result_count: 5, was_successful: true, ...overrides, }); export const createMockWatchedItemPayload = ( overrides: Partial<{ itemName: string; category: string }> = {}, ): { itemName: string; category: string } => ({ itemName: 'Mock Watched Item', category: 'Pantry', ...overrides, }); export const createMockRegisterUserPayload = ( overrides: Partial<{ email: string; password: string; full_name: string; avatar_url: string | undefined; }> = {}, ) => ({ email: 'mock@example.com', password: 'password123', full_name: 'Mock User', avatar_url: undefined, ...overrides, }); export const createMockLoginPayload = ( overrides: Partial<{ email: string; password: string; rememberMe: boolean }> = {}, ) => ({ email: 'mock@example.com', password: 'password123', rememberMe: false, ...overrides, });