Files
flyer-crawler.projectium.com/src/services/db/shopping.db.test.ts
Torben Sorensen b48445b713
Some checks failed
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Failing after 5m45s
moar unit test !
2025-12-07 14:54:49 -08:00

164 lines
7.4 KiB
TypeScript

// src/services/db/shopping.db.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { mockPoolInstance } from '../../tests/setup/tests-setup-unit';
import { createMockShoppingList, createMockShoppingListItem } from '../../tests/utils/mockFactories';
// Un-mock the module we are testing to ensure we use the real implementation.
vi.unmock('./shopping.db');
import {
getShoppingLists,
createShoppingList,
deleteShoppingList,
addShoppingListItem,
updateShoppingListItem,
removeShoppingListItem,
completeShoppingList,
} from './shopping.db';
// 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('Shopping DB Service', () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe('getShoppingLists', () => {
it('should execute the correct query and return shopping lists', async () => {
const mockLists = [createMockShoppingList({ user_id: 'user-1' })];
mockPoolInstance.query.mockResolvedValue({ rows: mockLists });
const result = await getShoppingLists('user-1');
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('FROM public.shopping_lists sl'), ['user-1']);
expect(result).toEqual(mockLists);
});
it('should throw an error if the database query fails', async () => {
mockPoolInstance.query.mockRejectedValue(new Error('DB Connection Error'));
await expect(getShoppingLists('user-1')).rejects.toThrow('Failed to retrieve shopping lists.');
});
});
describe('createShoppingList', () => {
it('should insert a new shopping list and return it', async () => {
const mockList = createMockShoppingList({ name: 'New List' });
mockPoolInstance.query.mockResolvedValue({ rows: [mockList] });
const result = await createShoppingList('user-1', 'New List');
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.shopping_lists'), ['user-1', 'New List']);
expect(result).toEqual(mockList);
});
it('should throw ForeignKeyConstraintError if user does not exist', async () => {
const dbError = new Error('insert or update on table "shopping_lists" violates foreign key constraint "shopping_lists_user_id_fkey"');
(dbError as any).code = '23503';
mockPoolInstance.query.mockRejectedValue(dbError);
await expect(createShoppingList('non-existent-user', 'Wont work')).rejects.toThrow('The specified user does not exist.');
});
});
describe('deleteShoppingList', () => {
it('should delete a shopping list if rowCount is 1', async () => {
mockPoolInstance.query.mockResolvedValue({ rowCount: 1, rows: [], command: 'DELETE' });
await expect(deleteShoppingList(1, 'user-1')).resolves.toBeUndefined();
expect(mockPoolInstance.query).toHaveBeenCalledWith('DELETE FROM public.shopping_lists WHERE shopping_list_id = $1 AND user_id = $2', [1, 'user-1']);
});
it('should throw an error if no rows are deleted (list not found or wrong user)', async () => {
mockPoolInstance.query.mockResolvedValue({ rowCount: 0, rows: [], command: 'DELETE' });
await expect(deleteShoppingList(999, 'user-1')).rejects.toThrow('Shopping list not found or user does not have permission to delete.');
});
});
describe('addShoppingListItem', () => {
it('should add a custom item to a shopping list', async () => {
const mockItem = createMockShoppingListItem({ custom_item_name: 'Custom Item' });
mockPoolInstance.query.mockResolvedValue({ rows: [mockItem] });
const result = await addShoppingListItem(1, { customItemName: 'Custom Item' });
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.shopping_list_items'), [1, null, 'Custom Item']);
expect(result).toEqual(mockItem);
});
it('should add a master item to a shopping list', async () => {
const mockItem = createMockShoppingListItem({ master_item_id: 123 });
mockPoolInstance.query.mockResolvedValue({ rows: [mockItem] });
const result = await addShoppingListItem(1, { masterItemId: 123 });
expect(mockPoolInstance.query).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO public.shopping_list_items'), [1, 123, null]);
expect(result).toEqual(mockItem);
});
it('should throw an error if both masterItemId and customItemName are missing', async () => {
await expect(addShoppingListItem(1, {})).rejects.toThrow('Either masterItemId or customItemName must be provided.');
});
it('should throw ForeignKeyConstraintError if list or master item does not exist', async () => {
const dbError = new Error('violates foreign key constraint');
(dbError as any).code = '23503';
mockPoolInstance.query.mockRejectedValue(dbError);
await expect(addShoppingListItem(999, { masterItemId: 999 })).rejects.toThrow('The specified shopping list or master item does not exist.');
});
});
describe('updateShoppingListItem', () => {
it('should update an item and return the updated record', async () => {
const mockItem = createMockShoppingListItem({ shopping_list_item_id: 1, is_purchased: true });
mockPoolInstance.query.mockResolvedValue({ rows: [mockItem], rowCount: 1 });
const result = await updateShoppingListItem(1, { is_purchased: true });
expect(mockPoolInstance.query).toHaveBeenCalledWith(
'UPDATE public.shopping_list_items SET is_purchased = $1 WHERE shopping_list_item_id = $2 RETURNING *',
[true, 1]
);
expect(result).toEqual(mockItem);
});
it('should throw an error if the item to update is not found', async () => {
mockPoolInstance.query.mockResolvedValue({ rowCount: 0, rows: [], command: 'UPDATE' });
await expect(updateShoppingListItem(999, { quantity: 5 })).rejects.toThrow('Shopping list item not found.');
});
});
describe('removeShoppingListItem', () => {
it('should delete an item if rowCount is 1', async () => {
mockPoolInstance.query.mockResolvedValue({ rowCount: 1, rows: [], command: 'DELETE' });
await expect(removeShoppingListItem(1)).resolves.toBeUndefined();
expect(mockPoolInstance.query).toHaveBeenCalledWith('DELETE FROM public.shopping_list_items WHERE shopping_list_item_id = $1', [1]);
});
it('should throw an error if no rows are deleted (item not found)', async () => {
mockPoolInstance.query.mockResolvedValue({ rowCount: 0, rows: [], command: 'DELETE' });
await expect(removeShoppingListItem(999)).rejects.toThrow('Shopping list item not found.');
});
});
describe('completeShoppingList', () => {
it('should call the complete_shopping_list database function', async () => {
mockPoolInstance.query.mockResolvedValue({ rows: [{ complete_shopping_list: 1 }] });
const result = await completeShoppingList(1, 'user-123', 5000);
expect(result).toBe(1);
expect(mockPoolInstance.query).toHaveBeenCalledWith('SELECT public.complete_shopping_list($1, $2, $3)', [1, 'user-123', 5000]);
});
it('should throw ForeignKeyConstraintError if the shopping list does not exist', async () => {
const dbError = new Error('violates foreign key constraint');
(dbError as any).code = '23503';
mockPoolInstance.query.mockRejectedValue(dbError);
await expect(completeShoppingList(999, 'user-123')).rejects.toThrow('The specified shopping list does not exist.');
});
});
});