From aaa968b00c62950efc2e3a0f09cc8428d6c3aeb4 Mon Sep 17 00:00:00 2001 From: Ikem Peter Date: Wed, 27 Nov 2024 13:34:25 +0100 Subject: [PATCH 1/5] implemented flag product api --- backend/src/product/product.controller.ts | 14 ++++++++++++++ backend/src/product/product.service.ts | 20 ++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/backend/src/product/product.controller.ts b/backend/src/product/product.controller.ts index 48daa58..aff9b78 100644 --- a/backend/src/product/product.controller.ts +++ b/backend/src/product/product.controller.ts @@ -8,6 +8,7 @@ import { Param, } from '@nestjs/common'; import { ProductDto } from './dto/product.dto'; +import { FlagProductDto } from './dto/flag-product.dto'; import { ProductService } from './product.service'; @Controller('product') @@ -48,4 +49,17 @@ export class ProductController { async getFlaggedProducts() { return this.productService.getFlaggedProducts(); } + + @Post('flag') + async flagProduct(@Body() flagProductDto: FlagProductDto) { + try { + return await this.productService.flagProduct(flagProductDto); + } catch (error) { + console.error('Error:', error.message); + throw new HttpException( + { error: error.message }, + HttpStatus.INTERNAL_SERVER_ERROR + ); + } + } } diff --git a/backend/src/product/product.service.ts b/backend/src/product/product.service.ts index 678cc75..be1e94f 100644 --- a/backend/src/product/product.service.ts +++ b/backend/src/product/product.service.ts @@ -4,9 +4,10 @@ import { Injectable, InternalServerErrorException, } from '@nestjs/common'; -import { PrismaService } from 'src/prisma/prisma.service'; +import { PrismaService } from '../prisma/prisma.service'; import { ProductDto } from './dto/product.dto'; -import { Product } from 'src/interfaces/Product'; +import { FlagProductDto } from './dto/flag-product.dto'; +import { Product } from '../interfaces/Product'; import { generateProductId } from 'src/common/utils/generateProductId'; @Injectable() @@ -122,4 +123,19 @@ export class ProductService { ); } } + + async flagProduct(flagProductDto: FlagProductDto) { + try { + return await this.prisma.flaggedProduct.create({ + data: { + name: flagProductDto.name, + image: flagProductDto.image, + reason: flagProductDto.reason, + }, + }); + } catch (error) { + console.error('Error flagging product:', error); + throw new InternalServerErrorException('Failed to flag product'); + } + } } From 6fc5ab17a6e5d9e30e6c20cc2e49b501fe734a05 Mon Sep 17 00:00:00 2001 From: Ikem Peter Date: Wed, 27 Nov 2024 13:41:46 +0100 Subject: [PATCH 2/5] updated imports --- backend/src/product/product.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/product/product.service.ts b/backend/src/product/product.service.ts index be1e94f..76e5e1d 100644 --- a/backend/src/product/product.service.ts +++ b/backend/src/product/product.service.ts @@ -8,7 +8,7 @@ import { PrismaService } from '../prisma/prisma.service'; import { ProductDto } from './dto/product.dto'; import { FlagProductDto } from './dto/flag-product.dto'; import { Product } from '../interfaces/Product'; -import { generateProductId } from 'src/common/utils/generateProductId'; +import { generateProductId } from '../common/utils/generateProductId'; @Injectable() export class ProductService { From 034bbe1571f6f9a413d03363ec762af92a5ac5b0 Mon Sep 17 00:00:00 2001 From: Ikem Peter Date: Wed, 27 Nov 2024 13:41:58 +0100 Subject: [PATCH 3/5] wrote test --- .../src/product/product.controller.spec.ts | 50 +++++++++++++ backend/src/product/product.service.spec.ts | 72 ++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/backend/src/product/product.controller.spec.ts b/backend/src/product/product.controller.spec.ts index bd5ad5b..108f677 100644 --- a/backend/src/product/product.controller.spec.ts +++ b/backend/src/product/product.controller.spec.ts @@ -1,7 +1,10 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ProductController } from './product.controller'; import { ProductService } from './product.service'; +import { PrismaService } from '../prisma/prisma.service'; import { ProductDto } from './dto/product.dto'; +import { FlagProductDto } from './dto/flag-product.dto'; +import { HttpException, HttpStatus } from '@nestjs/common'; describe('ProductsController', () => { let controller: ProductController; @@ -15,6 +18,8 @@ describe('ProductsController', () => { provide: ProductService, useValue: { submitProduct: jest.fn(), + flagProduct: jest.fn(), + getFlaggedProducts: jest.fn(), }, }, ], @@ -64,4 +69,49 @@ describe('ProductsController', () => { await expect(controller.uploadProduct(dto)).rejects.toThrow(); }); }); + + describe('flagProduct', () => { + const mockFlagProductDto = { + name: 'Suspicious Product', + image: 'http://example.com/suspicious.jpg', + reason: 'Fake product', + }; + + it('should successfully flag a product', async () => { + const mockResponse = { + id: '1', + ...mockFlagProductDto + }; + jest.spyOn(service, 'flagProduct').mockResolvedValue(mockResponse); + + const result = await controller.flagProduct(mockFlagProductDto); + + expect(result).toEqual(mockResponse); + expect(service.flagProduct).toHaveBeenCalledWith(mockFlagProductDto); + }); + + it('should throw HttpException when flagging fails', async () => { + jest.spyOn(service, 'flagProduct').mockRejectedValue(new Error('Failed to flag')); + + await expect(controller.flagProduct(mockFlagProductDto)).rejects.toThrow( + HttpException, + ); + }); + }); + + describe('getFlaggedProducts', () => { + it('should return all flagged products', async () => { + const mockFlaggedProducts = [ + { id: '1', name: 'Product 1', image: 'image1.jpg', reason: 'Fake' }, + { id: '2', name: 'Product 2', image: 'image2.jpg', reason: 'Expired' }, + ]; + + jest.spyOn(service, 'getFlaggedProducts').mockResolvedValue(mockFlaggedProducts); + + const result = await controller.getFlaggedProducts(); + + expect(result).toEqual(mockFlaggedProducts); + expect(service.getFlaggedProducts).toHaveBeenCalled(); + }); + }); }); diff --git a/backend/src/product/product.service.spec.ts b/backend/src/product/product.service.spec.ts index 83a25fd..28c5c76 100644 --- a/backend/src/product/product.service.spec.ts +++ b/backend/src/product/product.service.spec.ts @@ -1,16 +1,31 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ProductService } from './product.service'; import { ProductDto } from './dto/product.dto'; +import { PrismaService } from '../prisma/prisma.service'; +import { InternalServerErrorException } from '@nestjs/common'; describe('ProductsService', () => { let service: ProductService; + let prismaService: PrismaService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [ProductService], + providers: [ + ProductService, + { + provide: PrismaService, + useValue: { + flaggedProduct: { + create: jest.fn(), + findMany: jest.fn(), + }, + }, + }, + ], }).compile(); service = module.get(ProductService); + prismaService = module.get(PrismaService); }); it('should be defined', () => { @@ -53,4 +68,59 @@ describe('ProductsService', () => { ); }); }); + + describe('flagProduct', () => { + const mockFlagProductDto = { + name: 'Suspicious Product', + image: 'http://example.com/suspicious.jpg', + reason: 'Fake product', + }; + + it('should successfully flag a product', async () => { + const mockCreatedProduct = { + id: '1', + ...mockFlagProductDto + }; + jest.spyOn(prismaService.flaggedProduct, 'create').mockResolvedValue(mockCreatedProduct); + + const result = await service.flagProduct(mockFlagProductDto); + + expect(result).toEqual(mockCreatedProduct); + expect(prismaService.flaggedProduct.create).toHaveBeenCalledWith({ + data: mockFlagProductDto, + }); + }); + + it('should throw InternalServerErrorException when flagging fails', async () => { + jest.spyOn(prismaService.flaggedProduct, 'create').mockRejectedValue(new Error('Database error')); + + await expect(service.flagProduct(mockFlagProductDto)).rejects.toThrow( + InternalServerErrorException, + ); + }); + }); + + describe('getFlaggedProducts', () => { + it('should return all flagged products', async () => { + const mockFlaggedProducts = [ + { id: '1', name: 'Product 1', image: 'image1.jpg', reason: 'Fake' }, + { id: '2', name: 'Product 2', image: 'image2.jpg', reason: 'Expired' }, + ]; + + jest.spyOn(prismaService.flaggedProduct, 'findMany').mockResolvedValue(mockFlaggedProducts); + + const result = await service.getFlaggedProducts(); + + expect(result).toEqual(mockFlaggedProducts); + expect(prismaService.flaggedProduct.findMany).toHaveBeenCalled(); + }); + + it('should throw InternalServerErrorException when fetch fails', async () => { + jest.spyOn(prismaService.flaggedProduct, 'findMany').mockRejectedValue(new Error('Database error')); + + await expect(service.getFlaggedProducts()).rejects.toThrow( + InternalServerErrorException, + ); + }); + }); }); From d236bf4708e9a519a4d80d35c3df3a8817d217bb Mon Sep 17 00:00:00 2001 From: Ikem Peter Date: Wed, 27 Nov 2024 14:01:49 +0100 Subject: [PATCH 4/5] removed unused imports --- backend/src/product/product.controller.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/product/product.controller.spec.ts b/backend/src/product/product.controller.spec.ts index 108f677..a50c3d7 100644 --- a/backend/src/product/product.controller.spec.ts +++ b/backend/src/product/product.controller.spec.ts @@ -1,10 +1,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ProductController } from './product.controller'; import { ProductService } from './product.service'; -import { PrismaService } from '../prisma/prisma.service'; import { ProductDto } from './dto/product.dto'; -import { FlagProductDto } from './dto/flag-product.dto'; -import { HttpException, HttpStatus } from '@nestjs/common'; +import { HttpException } from '@nestjs/common'; describe('ProductsController', () => { let controller: ProductController; From 01abcefb1b6dd7f6c6ef2bce1efb01af30487b08 Mon Sep 17 00:00:00 2001 From: Ikem Peter Date: Wed, 27 Nov 2024 14:04:23 +0100 Subject: [PATCH 5/5] formated --- .../src/product/product.controller.spec.ts | 18 +++++++----- backend/src/product/product.controller.ts | 2 +- backend/src/product/product.service.spec.ts | 28 ++++++++++++------- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/backend/src/product/product.controller.spec.ts b/backend/src/product/product.controller.spec.ts index a50c3d7..0f4b982 100644 --- a/backend/src/product/product.controller.spec.ts +++ b/backend/src/product/product.controller.spec.ts @@ -76,23 +76,25 @@ describe('ProductsController', () => { }; it('should successfully flag a product', async () => { - const mockResponse = { + const mockResponse = { id: '1', - ...mockFlagProductDto + ...mockFlagProductDto, }; jest.spyOn(service, 'flagProduct').mockResolvedValue(mockResponse); const result = await controller.flagProduct(mockFlagProductDto); - + expect(result).toEqual(mockResponse); expect(service.flagProduct).toHaveBeenCalledWith(mockFlagProductDto); }); it('should throw HttpException when flagging fails', async () => { - jest.spyOn(service, 'flagProduct').mockRejectedValue(new Error('Failed to flag')); + jest + .spyOn(service, 'flagProduct') + .mockRejectedValue(new Error('Failed to flag')); await expect(controller.flagProduct(mockFlagProductDto)).rejects.toThrow( - HttpException, + HttpException ); }); }); @@ -104,10 +106,12 @@ describe('ProductsController', () => { { id: '2', name: 'Product 2', image: 'image2.jpg', reason: 'Expired' }, ]; - jest.spyOn(service, 'getFlaggedProducts').mockResolvedValue(mockFlaggedProducts); + jest + .spyOn(service, 'getFlaggedProducts') + .mockResolvedValue(mockFlaggedProducts); const result = await controller.getFlaggedProducts(); - + expect(result).toEqual(mockFlaggedProducts); expect(service.getFlaggedProducts).toHaveBeenCalled(); }); diff --git a/backend/src/product/product.controller.ts b/backend/src/product/product.controller.ts index aff9b78..71f5d72 100644 --- a/backend/src/product/product.controller.ts +++ b/backend/src/product/product.controller.ts @@ -49,7 +49,7 @@ export class ProductController { async getFlaggedProducts() { return this.productService.getFlaggedProducts(); } - + @Post('flag') async flagProduct(@Body() flagProductDto: FlagProductDto) { try { diff --git a/backend/src/product/product.service.spec.ts b/backend/src/product/product.service.spec.ts index 28c5c76..836e203 100644 --- a/backend/src/product/product.service.spec.ts +++ b/backend/src/product/product.service.spec.ts @@ -77,14 +77,16 @@ describe('ProductsService', () => { }; it('should successfully flag a product', async () => { - const mockCreatedProduct = { + const mockCreatedProduct = { id: '1', - ...mockFlagProductDto + ...mockFlagProductDto, }; - jest.spyOn(prismaService.flaggedProduct, 'create').mockResolvedValue(mockCreatedProduct); + jest + .spyOn(prismaService.flaggedProduct, 'create') + .mockResolvedValue(mockCreatedProduct); const result = await service.flagProduct(mockFlagProductDto); - + expect(result).toEqual(mockCreatedProduct); expect(prismaService.flaggedProduct.create).toHaveBeenCalledWith({ data: mockFlagProductDto, @@ -92,10 +94,12 @@ describe('ProductsService', () => { }); it('should throw InternalServerErrorException when flagging fails', async () => { - jest.spyOn(prismaService.flaggedProduct, 'create').mockRejectedValue(new Error('Database error')); + jest + .spyOn(prismaService.flaggedProduct, 'create') + .mockRejectedValue(new Error('Database error')); await expect(service.flagProduct(mockFlagProductDto)).rejects.toThrow( - InternalServerErrorException, + InternalServerErrorException ); }); }); @@ -107,19 +111,23 @@ describe('ProductsService', () => { { id: '2', name: 'Product 2', image: 'image2.jpg', reason: 'Expired' }, ]; - jest.spyOn(prismaService.flaggedProduct, 'findMany').mockResolvedValue(mockFlaggedProducts); + jest + .spyOn(prismaService.flaggedProduct, 'findMany') + .mockResolvedValue(mockFlaggedProducts); const result = await service.getFlaggedProducts(); - + expect(result).toEqual(mockFlaggedProducts); expect(prismaService.flaggedProduct.findMany).toHaveBeenCalled(); }); it('should throw InternalServerErrorException when fetch fails', async () => { - jest.spyOn(prismaService.flaggedProduct, 'findMany').mockRejectedValue(new Error('Database error')); + jest + .spyOn(prismaService.flaggedProduct, 'findMany') + .mockRejectedValue(new Error('Database error')); await expect(service.getFlaggedProducts()).rejects.toThrow( - InternalServerErrorException, + InternalServerErrorException ); }); });