mock mock mock !
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 57m50s

This commit is contained in:
2025-12-19 20:31:04 -08:00
parent e62739810e
commit a3d3ddd772
48 changed files with 1062 additions and 507 deletions

View File

@@ -1,5 +1,43 @@
// src/tests/utils/mockFactories.ts
import { UserProfile, User, Flyer, Store, SuggestedCorrection, Brand, FlyerItem, MasterGroceryItem, ShoppingList, ShoppingListItem, Achievement, UserAchievement, Budget, SpendingByCategory, Recipe, RecipeComment, ActivityLogItem, DietaryRestriction, Appliance, Notification, UnmatchedFlyerItem, AdminUserView, WatchedItemDeal, LeaderboardUser, UserWithPasswordHash, Profile, Address } from '../../types';
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, MenuPlan, PlannedMeal, PantryItem, Product, ShoppingTrip, ShoppingTripItem, Receipt, ReceiptItem } from '../../types';
// --- 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> = {}): User => {
const userId = overrides.user_id ?? `user-${getNextId()}`;
const defaultUser: User = {
user_id: userId,
email: `${userId}@example.com`,
};
return { ...defaultUser, ...overrides };
};
/**
* Creates a mock UserProfile object for use in tests, ensuring type safety.
@@ -10,24 +48,53 @@ import { UserProfile, User, Flyer, Store, SuggestedCorrection, Brand, FlyerItem,
* @returns A complete and type-safe UserProfile object.
*/
export const createMockUserProfile = (overrides: Partial<UserProfile & { user: Partial<User> }> = {}): UserProfile => {
const userId = overrides.user_id ?? `user-${Math.random().toString(36).substring(2, 9)}`;
// Ensure the user_id is consistent between the profile and the nested user object
const userOverrides: Partial<User> = overrides.user || {};
if (overrides.user_id && !userOverrides.user_id) {
userOverrides.user_id = overrides.user_id;
}
const user = createMockUser(userOverrides);
const defaultProfile: UserProfile = {
user_id: userId,
user_id: user.user_id,
role: 'user',
points: 0,
full_name: 'Test User',
avatar_url: null,
preferences: {},
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
created_by: null,
address: null,
user: {
user_id: userId,
email: `${userId}@example.com`,
...overrides.user, // Apply nested user overrides
},
user,
};
delete (defaultProfile as Partial<UserProfile>).address_id;
// Exclude 'user' from overrides to prevent overwriting the complete user object with a partial one
const { user: _, ...profileOverrides } = overrides;
return { ...defaultProfile, ...profileOverrides, user_id: user.user_id };
};
/**
* 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> = {}): Store => {
const storeId = overrides.store_id ?? getNextId();
const defaultStore: Store = {
store_id: storeId,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
name: 'Mock Store',
logo_url: null,
created_by: null,
};
return { ...defaultProfile, ...overrides };
return { ...defaultStore, ...overrides };
};
/**
@@ -38,33 +105,52 @@ export const createMockUserProfile = (overrides: Partial<UserProfile & { user: P
* e.g., `createMockFlyer({ item_count: 50, store: { name: 'Walmart' } })`
* @returns A complete and type-safe Flyer object.
*/
export const createMockFlyer = (overrides: Partial<Flyer & { store: Partial<Store> }> = {}): Flyer => {
const flyerId = overrides.flyer_id ?? Math.floor(Math.random() * 1000);
const storeId = overrides.store?.store_id ?? Math.floor(Math.random() * 100);
export const createMockFlyer = (overrides: Omit<Partial<Flyer>, 'store'> & { store?: Partial<Store> } = {}): Flyer => {
const flyerId = overrides.flyer_id ?? getNextId();
// Ensure the store_id is consistent between the flyer and the nested store object
const storeOverrides = overrides.store || {};
if (overrides.store_id && !storeOverrides.store_id) {
storeOverrides.store_id = overrides.store_id;
}
const store = createMockStore(storeOverrides);
// 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,
created_at: new Date().toISOString(),
file_name: `flyer-${flyerId}.jpg`,
image_url: `/flyer-images/flyer-${flyerId}.jpg`,
icon_url: `/flyer-images/icons/icon-flyer-${flyerId}.webp`,
checksum: `checksum-${flyerId}`,
store_id: storeId,
updated_at: new Date().toISOString(),
file_name: fileName,
image_url: `/flyer-images/${fileName}`,
icon_url: `/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',
item_count: Math.floor(Math.random() * 100) + 10,
item_count: 50,
uploaded_by: null,
store: {
store_id: storeId,
created_at: new Date().toISOString(),
name: 'Mock Store',
logo_url: null,
},
store,
};
// Deep merge the store object and then merge the top-level properties.
return { ...defaultFlyer, ...overrides, store: { ...defaultFlyer.store, ...overrides.store } as 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 };
};
/**
@@ -74,13 +160,14 @@ export const createMockFlyer = (overrides: Partial<Flyer & { store: Partial<Stor
*/
export const createMockSuggestedCorrection = (overrides: Partial<SuggestedCorrection> = {}): SuggestedCorrection => {
const defaultCorrection: SuggestedCorrection = {
suggested_correction_id: Math.floor(Math.random() * 1000),
flyer_item_id: Math.floor(Math.random() * 10000),
user_id: `user-${Math.random().toString(36).substring(2, 9)}`,
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 };
@@ -92,10 +179,12 @@ export const createMockSuggestedCorrection = (overrides: Partial<SuggestedCorrec
* @returns A complete and type-safe Brand object.
*/
export const createMockBrand = (overrides: Partial<Brand> = {}): Brand => {
const brandId = overrides.brand_id ?? Math.floor(Math.random() * 100);
const brandId = overrides.brand_id ?? getNextId();
const defaultBrand: Brand = {
brand_id: brandId,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
name: `Brand ${brandId}`,
logo_url: null,
store_id: null,
@@ -105,15 +194,62 @@ export const createMockBrand = (overrides: Partial<Brand> = {}): Brand => {
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> = {}): Category => {
const categoryId = overrides.category_id ?? getNextId();
const defaultCategory: Category = {
category_id: categoryId,
name: `Category ${categoryId}`,
};
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<Product> & { master_item?: Partial<MasterGroceryItem>, brand?: Partial<Brand> } = {}): 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<FlyerItem> = {}): FlyerItem => {
export const createMockFlyerItem = (overrides: Partial<FlyerItem> & { flyer?: Partial<Flyer> } = {}): FlyerItem => {
const flyerItemId = overrides.flyer_item_id ?? getNextId();
const flyerId = overrides.flyer_id ?? overrides.flyer?.flyer_id ?? getNextId();
const defaultItem: FlyerItem = {
flyer_item_id: Math.floor(Math.random() * 10000),
flyer_id: Math.floor(Math.random() * 1000),
flyer_item_id: flyerItemId,
flyer_id: flyerId,
created_at: new Date().toISOString(),
item: 'Mock Item',
price_display: '$1.99',
@@ -124,7 +260,9 @@ export const createMockFlyerItem = (overrides: Partial<FlyerItem> = {}): FlyerIt
updated_at: new Date().toISOString(),
};
return { ...defaultItem, ...overrides };
const { flyer: _, ...itemOverrides } = overrides;
return { ...defaultItem, ...itemOverrides };
};
/**
@@ -132,26 +270,71 @@ export const createMockFlyerItem = (overrides: Partial<FlyerItem> = {}): FlyerIt
* @param overrides - An object containing properties to override the default mock values.
* @returns A complete and type-safe Recipe object.
*/
export const createMockRecipe = (overrides: Partial<Recipe> = {}): Recipe => {
const recipeId = overrides.recipe_id ?? Math.floor(Math.random() * 1000);
export const createMockRecipe = (overrides: Omit<Partial<Recipe>, 'comments' | 'ingredients'> & { comments?: Partial<RecipeComment>[], ingredients?: Partial<RecipeIngredient>[] } = {}): Recipe => {
const recipeId = overrides.recipe_id ?? getNextId();
const defaultRecipe: Recipe = {
recipe_id: recipeId,
user_id: `user-${Math.random().toString(36).substring(2, 9)}`,
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: Math.random() * 5,
rating_count: Math.floor(Math.random() * 100),
fork_count: Math.floor(Math.random() * 20),
avg_rating: 4.5,
rating_count: 50,
fork_count: 10,
status: 'public',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
prep_time_minutes: 15,
cook_time_minutes: 30,
servings: 4,
};
return { ...defaultRecipe, ...overrides };
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<RecipeIngredient> & { master_item?: Partial<MasterGroceryItem> } = {}): 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',
};
const { master_item: _, ...itemOverrides } = overrides;
return { ...defaultIngredient, ...itemOverrides };
};
/**
@@ -161,9 +344,9 @@ export const createMockRecipe = (overrides: Partial<Recipe> = {}): Recipe => {
*/
export const createMockRecipeComment = (overrides: Partial<RecipeComment> = {}): RecipeComment => {
const defaultComment: RecipeComment = {
recipe_comment_id: Math.floor(Math.random() * 10000),
recipe_id: Math.floor(Math.random() * 1000),
user_id: `user-${Math.random().toString(36).substring(2, 9)}`,
recipe_comment_id: getNextId(),
recipe_id: getNextId(),
user_id: `user-${getNextId()}`,
content: 'This is a mock comment.',
status: 'visible',
created_at: new Date().toISOString(),
@@ -174,6 +357,56 @@ export const createMockRecipeComment = (overrides: Partial<RecipeComment> = {}):
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> = {}): 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,
};
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<Partial<MenuPlan>, 'planned_meals'> & { planned_meals?: Partial<PlannedMeal>[] } = {}): 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(),
};
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.
@@ -188,9 +421,10 @@ export const createMockActivityLogItem = (overrides: Partial<ActivityLogItem> =
const action = overrides.action ?? 'flyer_processed';
const baseLog = {
activity_log_id: Math.floor(Math.random() * 10000),
user_id: `user-${Math.random().toString(36).substring(2, 9)}`,
activity_log_id: getNextId(),
user_id: `user-${getNextId()}`,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
};
let specificLog: ActivityLogItem;
@@ -248,11 +482,12 @@ export const createMockActivityLogItem = (overrides: Partial<ActivityLogItem> =
*/
export const createMockAchievement = (overrides: Partial<Achievement> = {}): Achievement => {
const defaultAchievement: Achievement = {
achievement_id: Math.floor(Math.random() * 100),
achievement_id: getNextId(),
name: 'Mock Achievement',
description: 'A great accomplishment.',
icon: 'star',
points_value: 10,
created_at: new Date().toISOString(),
};
return { ...defaultAchievement, ...overrides };
};
@@ -263,9 +498,9 @@ export const createMockAchievement = (overrides: Partial<Achievement> = {}): Ach
* @returns A complete and type-safe object representing the joined achievement data.
*/
export const createMockUserAchievement = (overrides: Partial<UserAchievement & Achievement> = {}): UserAchievement & Achievement => {
const achievementId = overrides.achievement_id ?? Math.floor(Math.random() * 100);
const achievementId = overrides.achievement_id ?? getNextId();
const defaultUserAchievement: UserAchievement & Achievement = {
user_id: `user-${Math.random().toString(36).substring(2, 9)}`,
user_id: `user-${getNextId()}`,
achievement_id: achievementId,
achieved_at: new Date().toISOString(),
// from Achievement
@@ -273,6 +508,7 @@ export const createMockUserAchievement = (overrides: Partial<UserAchievement & A
description: 'An achievement someone earned.',
icon: 'award',
points_value: 20,
created_at: new Date().toISOString(),
};
return { ...defaultUserAchievement, ...overrides };
};
@@ -284,12 +520,14 @@ export const createMockUserAchievement = (overrides: Partial<UserAchievement & A
*/
export const createMockBudget = (overrides: Partial<Budget> = {}): Budget => {
const defaultBudget: Budget = {
budget_id: Math.floor(Math.random() * 100),
user_id: `user-${Math.random().toString(36).substring(2, 9)}`,
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 };
};
@@ -301,9 +539,9 @@ export const createMockBudget = (overrides: Partial<Budget> = {}): Budget => {
*/
export const createMockSpendingByCategory = (overrides: Partial<SpendingByCategory> = {}): SpendingByCategory => {
const defaultSpending: SpendingByCategory = {
category_id: Math.floor(Math.random() * 20) + 1,
category_id: getNextId(),
category_name: 'Produce',
total_spent_cents: Math.floor(Math.random() * 20000) + 1000,
total_spent_cents: 15000,
};
return { ...defaultSpending, ...overrides };
};
@@ -313,16 +551,55 @@ export const createMockSpendingByCategory = (overrides: Partial<SpendingByCatego
* @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<MasterGroceryItem> = {}): MasterGroceryItem => {
export const createMockMasterGroceryItem = (overrides: Partial<MasterGroceryItem> & { category?: Partial<Category> } = {}): MasterGroceryItem => {
// Ensure category_id is consistent between the item and the nested category object
const categoryOverrides = overrides.category || {};
if (overrides.category_id && !categoryOverrides.category_id) {
categoryOverrides.category_id = overrides.category_id;
}
const category = createMockCategory(categoryOverrides);
const defaultItem: MasterGroceryItem = {
master_grocery_item_id: Math.floor(Math.random() * 10000),
master_grocery_item_id: getNextId(),
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
name: 'Mock Master Item',
category_id: 1,
category_name: 'Pantry & Dry Goods',
category_id: category.category_id,
category_name: category.name,
is_allergen: false,
allergy_info: null,
created_by: null,
};
return { ...defaultItem, ...overrides };
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<PantryItem> & { master_item?: Partial<MasterGroceryItem> } = {}): 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 };
};
/**
@@ -330,36 +607,173 @@ export const createMockMasterGroceryItem = (overrides: Partial<MasterGroceryItem
* @param overrides - An object containing properties to override the default mock values.
* @returns A complete and type-safe ShoppingList object.
*/
export const createMockShoppingList = (overrides: Partial<ShoppingList> = {}): ShoppingList => {
export const createMockShoppingList = (overrides: Omit<Partial<ShoppingList>, 'items'> & { items?: (Partial<ShoppingListItem> & { master_item?: Partial<MasterGroceryItem> | null })[] } = {}): ShoppingList => {
const shoppingListId = overrides.shopping_list_id ?? getNextId();
const defaultList: ShoppingList = {
shopping_list_id: Math.floor(Math.random() * 100),
user_id: `user-${Math.random().toString(36).substring(2, 9)}`,
shopping_list_id: shoppingListId,
user_id: `user-${getNextId()}`,
name: 'My Mock List',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
items: [],
};
return { ...defaultList, ...overrides };
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<ShoppingListItem> = {}): ShoppingListItem => {
export const createMockShoppingListItem = (overrides: Partial<ShoppingListItem> & { master_item?: Partial<MasterGroceryItem> | 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: Math.floor(Math.random() * 100000),
shopping_list_id: Math.floor(Math.random() * 100),
shopping_list_item_id: shoppingListItemId,
shopping_list_id: shoppingListId,
custom_item_name: 'Mock Shopping List Item',
quantity: 1,
is_purchased: false,
added_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
};
if (masterItemId) {
defaultItem.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<ShoppingTripItem> & { master_item?: Partial<MasterGroceryItem> } = {}): 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,
};
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<Partial<ShoppingTrip>, 'items'> & { items?: Partial<ShoppingTripItem>[] } = {}): 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: [],
};
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> = {}): 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',
};
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<Partial<Receipt>, 'items'> & { items?: Partial<ReceiptItem>[] } = {}): 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,
created_at: new Date().toISOString(),
processed_at: null,
};
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.
@@ -374,6 +788,46 @@ export const createMockDietaryRestriction = (overrides: Partial<DietaryRestricti
};
};
/**
* 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<UserDietaryRestriction> & { user?: Partial<User>; restriction?: Partial<DietaryRestriction> } = {}
): 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,
};
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<UserAppliance> & { user?: Partial<User>; appliance?: Partial<Appliance> } = {}
): UserAppliance => {
const userId = overrides.user_id ?? overrides.user?.user_id ?? `user-${getNextId()}`;
const applianceId = overrides.appliance_id ?? overrides.appliance?.appliance_id ?? getNextId();
const defaultUserAppliance: UserAppliance = {
user_id: userId,
appliance_id: applianceId,
};
return { ...defaultUserAppliance, ...overrides };
};
/**
* Creates a mock Address object for use in tests.
* @param overrides - An object containing properties to override the default mock values.
@@ -381,7 +835,7 @@ export const createMockDietaryRestriction = (overrides: Partial<DietaryRestricti
*/
export const createMockAddress = (overrides: Partial<Address> = {}): Address => {
const defaultAddress: Address = {
address_id: Math.floor(Math.random() * 1000),
address_id: getNextId(),
address_line_1: '123 Mock St',
city: 'Mockville',
province_state: 'BC',
@@ -405,7 +859,7 @@ export const createMockAddress = (overrides: Partial<Address> = {}): Address =>
* @returns A complete and type-safe UserWithPasswordHash object.
*/
export const createMockUserWithPasswordHash = (overrides: Partial<UserWithPasswordHash> = {}): UserWithPasswordHash => {
const userId = overrides.user_id ?? `user-${Math.random().toString(36).substring(2, 9)}`;
const userId = overrides.user_id ?? `user-${getNextId()}`;
const defaultUser: UserWithPasswordHash = {
user_id: userId,
@@ -413,6 +867,9 @@ export const createMockUserWithPasswordHash = (overrides: Partial<UserWithPasswo
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 };
@@ -424,10 +881,11 @@ export const createMockUserWithPasswordHash = (overrides: Partial<UserWithPasswo
* @returns A complete and type-safe Profile object.
*/
export const createMockProfile = (overrides: Partial<Profile> = {}): Profile => {
const userId = overrides.user_id ?? `user-${Math.random().toString(36).substring(2, 9)}`;
const userId = overrides.user_id ?? `user-${getNextId()}`;
const defaultProfile: Profile = {
user_id: userId,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
full_name: 'Mock Profile User',
avatar_url: null,
@@ -435,6 +893,8 @@ export const createMockProfile = (overrides: Partial<Profile> = {}): Profile =>
points: 0,
role: 'user',
preferences: {},
created_by: null,
updated_by: null,
};
return { ...defaultProfile, ...overrides };
@@ -447,11 +907,11 @@ export const createMockProfile = (overrides: Partial<Profile> = {}): Profile =>
*/
export const createMockWatchedItemDeal = (overrides: Partial<WatchedItemDeal> = {}): WatchedItemDeal => {
const defaultDeal: WatchedItemDeal = {
master_item_id: Math.floor(Math.random() * 1000),
master_item_id: getNextId(),
item_name: 'Mock Deal Item',
best_price_in_cents: Math.floor(Math.random() * 1000) + 100,
best_price_in_cents: 599,
store_name: 'Mock Store',
flyer_id: Math.floor(Math.random() * 100),
flyer_id: getNextId(),
valid_to: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(), // 5 days from now
};
@@ -464,14 +924,14 @@ export const createMockWatchedItemDeal = (overrides: Partial<WatchedItemDeal> =
* @returns A complete and type-safe LeaderboardUser object.
*/
export const createMockLeaderboardUser = (overrides: Partial<LeaderboardUser> = {}): LeaderboardUser => {
const userId = overrides.user_id ?? `user-${Math.random().toString(36).substring(2, 9)}`;
const userId = overrides.user_id ?? `user-${getNextId()}`;
const defaultUser: LeaderboardUser = {
user_id: userId,
full_name: 'Leaderboard User',
avatar_url: null,
points: Math.floor(Math.random() * 1000),
rank: String(Math.floor(Math.random() * 100) + 1),
points: 500,
rank: '10',
};
return { ...defaultUser, ...overrides };
@@ -484,13 +944,15 @@ export const createMockLeaderboardUser = (overrides: Partial<LeaderboardUser> =
*/
export const createMockUnmatchedFlyerItem = (overrides: Partial<UnmatchedFlyerItem> = {}): UnmatchedFlyerItem => {
const defaultItem: UnmatchedFlyerItem = {
unmatched_flyer_item_id: Math.floor(Math.random() * 1000),
unmatched_flyer_item_id: getNextId(),
status: 'pending',
created_at: new Date().toISOString(),
flyer_item_id: Math.floor(Math.random() * 10000),
updated_at: new Date().toISOString(),
reviewed_at: null,
flyer_item_id: getNextId(),
flyer_item_name: 'Mystery Product',
price_display: '$?.??',
flyer_id: Math.floor(Math.random() * 100),
flyer_id: getNextId(),
store_name: 'Random Store',
};
@@ -503,7 +965,7 @@ export const createMockUnmatchedFlyerItem = (overrides: Partial<UnmatchedFlyerIt
* @returns A complete and type-safe AdminUserView object.
*/
export const createMockAdminUserView = (overrides: Partial<AdminUserView> = {}): AdminUserView => {
const userId = overrides.user_id ?? `user-${Math.random().toString(36).substring(2, 9)}`;
const userId = overrides.user_id ?? `user-${getNextId()}`;
const defaultUserView: AdminUserView = {
user_id: userId,
@@ -524,12 +986,13 @@ export const createMockAdminUserView = (overrides: Partial<AdminUserView> = {}):
*/
export const createMockNotification = (overrides: Partial<Notification> = {}): Notification => {
const defaultNotification: Notification = {
notification_id: Math.floor(Math.random() * 1000),
user_id: `user-${Math.random().toString(36).substring(2, 9)}`,
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 };