import { ForbiddenException, NotFoundException } from '@nestjs/common'; import { Prisma } from '@prisma/client'; import { ShoppingListService } from '../shopping-list/shopping-list.service'; describe('ShoppingListService', () => { const prismaMock = { shoppingListItem: { findMany: jest.fn(), findUnique: jest.fn(), update: jest.fn(), findFirst: jest.fn(), create: jest.fn(), }, flyerSelection: { findMany: jest.fn(), }, $transaction: jest.fn(), }; const createService = () => new ShoppingListService(prismaMock as any); beforeEach(() => { jest.clearAllMocks(); }); it('throws when updating another users shopping item', async () => { prismaMock.shoppingListItem.findUnique.mockResolvedValue({ id: 1, userId: 99 }); const service = createService(); await expect(service.updateCheckedStatus(1, 1, true)).rejects.toBeInstanceOf(ForbiddenException); }); it('throws when shopping item is missing', async () => { prismaMock.shoppingListItem.findUnique.mockResolvedValue(null); const service = createService(); await expect(service.updateCheckedStatus(1, 1, true)).rejects.toBeInstanceOf(NotFoundException); }); it('deduplicates by productId+unit when planning from flyer selections', async () => { prismaMock.flyerSelection.findMany.mockResolvedValue([ { id: 10, plannedQuantity: new Prisma.Decimal(1), plannedUnit: 'kg', item: { id: 100, rawName: 'Tomat', matchedProductId: 7, categoryId: 22, priceUnit: 'kg', }, }, { id: 11, plannedQuantity: new Prisma.Decimal(2), plannedUnit: 'kg', item: { id: 101, rawName: 'Tomat', matchedProductId: 7, categoryId: 22, priceUnit: 'kg', }, }, ]); prismaMock.$transaction.mockImplementation(async (cb: any) => cb(prismaMock)); prismaMock.shoppingListItem.findFirst .mockResolvedValueOnce(null) .mockResolvedValueOnce({ id: 999 }); const service = createService(); const result = await service.upsertFromFlyerSelections(1, 1, [100, 101]); expect(result.created).toBe(1); expect(result.updated).toBe(1); expect(prismaMock.shoppingListItem.create).toHaveBeenCalledTimes(1); expect(prismaMock.shoppingListItem.update).toHaveBeenCalledTimes(1); }); });