Files
flyer-crawler.projectium.com/src/services/db/personalization.test.ts
Torben Sorensen 5c214fb6f4
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m24s
Refactor tests and services for improved type safety and error handling
- Updated FlyerCorrectionTool tests to remove unused error notification.
- Enhanced ProfileManager tests and component to include points in user profile.
- Fixed error handling in ProfileManager to correctly log error messages.
- Adjusted AI routes tests to ensure proper mocking and added missing properties in mock responses.
- Refined AI routes to improve error message extraction and payload handling.
- Cleaned up gamification routes tests by removing unnecessary parameters.
- Simplified public routes by removing unused parameters in async handlers.
- Improved system routes tests to handle exec command callbacks more robustly.
- Updated user routes tests to remove unnecessary middleware parameters.
- Enhanced AI API client tests to use File objects for simulating uploads.
- Modified AI service tests to improve type safety and mock implementations.
- Refined database service tests to ensure proper type assertions and mock setups.
- Updated express type definitions for better clarity and organization.
- Cleaned up notification service tests to mock local re-exports instead of library directly.
2025-12-04 12:46:12 -08:00

211 lines
8.4 KiB
TypeScript

// src/services/db/personalization.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
getWatchedItems,
addWatchedItem,
removeWatchedItem,
findRecipesFromPantry,
recommendRecipesForUser,
getBestSalePricesForUser,
suggestPantryItemConversions,
findPantryItemOwner,
getDietaryRestrictions,
getUserDietaryRestrictions,
setUserDietaryRestrictions,
getAppliances,
getUserAppliances,
setUserAppliances,
getRecipesForUserDiets,
} from './personalization';
import type { MasterGroceryItem } from '../../types';
import { getPool } from './connection';
import { Pool } from 'pg';
const mockQuery = vi.fn();
const mockRelease = vi.fn();
const mockConnect = vi.fn().mockResolvedValue({ query: mockQuery, release: mockRelease });
// Mock the logger to prevent console output during tests
vi.mock('../logger', () => ({
logger: {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
},
}));
describe('Personalization DB Service', () => {
beforeEach(() => {
mockQuery.mockReset();
vi.mocked(Pool).mockImplementation(function() {
console.log('[DEBUG] personalization.test.ts: Local Pool mock instantiated via "new"');
return {
query: mockQuery,
connect: mockConnect,
release: mockRelease,
on: vi.fn(),
end: vi.fn(),
totalCount: 0,
idleCount: 0,
waitingCount: 0,
} as unknown as Pool;
});
vi.clearAllMocks();
});
describe('getWatchedItems', () => {
it('should execute the correct query and return watched items', async () => {
const mockItems: MasterGroceryItem[] = [{ master_grocery_item_id: 1, name: 'Apples', created_at: '' }];
mockQuery.mockResolvedValue({ rows: mockItems });
const result = await getWatchedItems('user-123');
expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining('FROM public.master_grocery_items mgi'), ['user-123']);
expect(result).toEqual(mockItems);
});
});
describe('addWatchedItem', () => {
it('should execute a transaction to add a watched item', async () => {
const mockItem: MasterGroceryItem = { master_grocery_item_id: 1, name: 'New Item', created_at: '' };
// Provide mock responses for all queries in the transaction: BEGIN, SELECT, SELECT, INSERT, COMMIT
mockQuery
.mockResolvedValueOnce({ rows: [] }) // BEGIN
.mockResolvedValueOnce({ rows: [{ category_id: 1 }] }) // Find category
.mockResolvedValueOnce({ rows: [mockItem] }) // Find/create master item
.mockResolvedValueOnce({ rows: [] }) // Insert into watchlist
.mockResolvedValueOnce({ rows: [] }); // COMMIT
await addWatchedItem('user-123', 'New Item', 'Produce');
expect(mockConnect).toHaveBeenCalled();
expect(mockQuery).toHaveBeenCalledWith('BEGIN');
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('SELECT category_id FROM public.categories'), expect.any(Array));
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('SELECT * FROM public.master_grocery_items'), expect.any(Array));
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.user_watched_items'), expect.any(Array));
expect(mockQuery).toHaveBeenCalledWith('COMMIT');
});
});
describe('removeWatchedItem', () => {
it('should execute a DELETE query', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await removeWatchedItem('user-123', 1);
expect(getPool().query).toHaveBeenCalledWith('DELETE FROM public.user_watched_items WHERE user_id = $1 AND master_item_id = $2', ['user-123', 1]);
});
});
describe('findRecipesFromPantry', () => {
it('should call the correct database function', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await findRecipesFromPantry('user-123');
expect(getPool().query).toHaveBeenCalledWith('SELECT * FROM public.find_recipes_from_pantry($1)', ['user-123']);
});
});
describe('recommendRecipesForUser', () => {
it('should call the correct database function', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await recommendRecipesForUser('user-123', 5);
expect(getPool().query).toHaveBeenCalledWith('SELECT * FROM public.recommend_recipes_for_user($1, $2)', ['user-123', 5]);
});
});
describe('getBestSalePricesForUser', () => {
it('should call the correct database function', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await getBestSalePricesForUser('user-123');
expect(getPool().query).toHaveBeenCalledWith('SELECT * FROM public.get_best_sale_prices_for_user($1)', ['user-123']);
});
});
describe('suggestPantryItemConversions', () => {
it('should call the correct database function', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await suggestPantryItemConversions(1);
expect(getPool().query).toHaveBeenCalledWith('SELECT * FROM public.suggest_pantry_item_conversions($1)', [1]);
});
});
describe('findPantryItemOwner', () => {
it('should execute a SELECT query to find the owner', async () => {
mockQuery.mockResolvedValue({ rows: [{ user_id: 'user-123' }] });
const result = await findPantryItemOwner(1);
expect(getPool().query).toHaveBeenCalledWith('SELECT user_id FROM public.pantry_items WHERE pantry_item_id = $1', [1]);
expect(result?.user_id).toBe('user-123');
});
});
describe('getDietaryRestrictions', () => {
it('should execute a SELECT query to get all restrictions', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await getDietaryRestrictions();
expect(getPool().query).toHaveBeenCalledWith('SELECT * FROM public.dietary_restrictions ORDER BY type, name');
});
});
describe('getUserDietaryRestrictions', () => {
it('should execute a SELECT query with a JOIN', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await getUserDietaryRestrictions('user-123');
expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining('FROM public.dietary_restrictions dr'), ['user-123']);
});
});
describe('setUserDietaryRestrictions', () => {
it('should execute a transaction to set restrictions', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await setUserDietaryRestrictions('user-123', [1, 2]);
expect(mockConnect).toHaveBeenCalled();
expect(mockQuery).toHaveBeenCalledWith('BEGIN');
expect(mockQuery).toHaveBeenCalledWith('DELETE FROM public.user_dietary_restrictions WHERE user_id = $1', ['user-123']);
// FIX: Make assertion robust for array parameters
expect(mockQuery).toHaveBeenCalledWith(
expect.stringContaining('INSERT INTO public.user_dietary_restrictions'),
['user-123', [1, 2]]
);
expect(mockQuery).toHaveBeenCalledWith('COMMIT');
});
});
describe('getAppliances', () => {
it('should execute a SELECT query to get all appliances', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await getAppliances();
expect(getPool().query).toHaveBeenCalledWith('SELECT * FROM public.appliances ORDER BY name');
});
});
describe('getUserAppliances', () => {
it('should execute a SELECT query with a JOIN', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await getUserAppliances('user-123');
expect(getPool().query).toHaveBeenCalledWith(expect.stringContaining('FROM public.appliances a'), ['user-123']);
});
});
describe('setUserAppliances', () => {
it('should execute a transaction to set appliances', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await setUserAppliances('user-123', [1, 2]);
expect(mockConnect).toHaveBeenCalled();
expect(mockQuery).toHaveBeenCalledWith('BEGIN');
expect(mockQuery).toHaveBeenCalledWith('DELETE FROM public.user_appliances WHERE user_id = $1', ['user-123']);
expect(mockQuery).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.user_appliances'), expect.any(Array));
expect(mockQuery).toHaveBeenCalledWith('COMMIT');
});
});
describe('getRecipesForUserDiets', () => {
it('should call the correct database function', async () => {
mockQuery.mockResolvedValue({ rows: [] });
await getRecipesForUserDiets('user-123');
expect(getPool().query).toHaveBeenCalledWith('SELECT * FROM public.get_recipes_for_user_diets($1)', ['user-123']);
});
});
});