Some checks failed
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Failing after 5m45s
164 lines
7.4 KiB
TypeScript
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.');
|
|
});
|
|
});
|
|
}); |