All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 4m24s
- 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.
211 lines
8.4 KiB
TypeScript
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']);
|
|
});
|
|
});
|
|
}); |