diff --git a/backend/src/product/product.controller.spec.ts b/backend/src/product/product.controller.spec.ts index bd5ad5b..0f4b982 100644 --- a/backend/src/product/product.controller.spec.ts +++ b/backend/src/product/product.controller.spec.ts @@ -2,6 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ProductController } from './product.controller'; import { ProductService } from './product.service'; import { ProductDto } from './dto/product.dto'; +import { HttpException } from '@nestjs/common'; describe('ProductsController', () => { let controller: ProductController; @@ -15,6 +16,8 @@ describe('ProductsController', () => { provide: ProductService, useValue: { submitProduct: jest.fn(), + flagProduct: jest.fn(), + getFlaggedProducts: jest.fn(), }, }, ], @@ -64,4 +67,53 @@ 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.controller.ts b/backend/src/product/product.controller.ts index 48daa58..71f5d72 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.spec.ts b/backend/src/product/product.service.spec.ts index 83a25fd..836e203 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,67 @@ 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 + ); + }); + }); }); diff --git a/backend/src/product/product.service.ts b/backend/src/product/product.service.ts index 678cc75..76e5e1d 100644 --- a/backend/src/product/product.service.ts +++ b/backend/src/product/product.service.ts @@ -4,10 +4,11 @@ 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 { generateProductId } from 'src/common/utils/generateProductId'; +import { FlagProductDto } from './dto/flag-product.dto'; +import { Product } from '../interfaces/Product'; +import { generateProductId } from '../common/utils/generateProductId'; @Injectable() export class ProductService { @@ -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'); + } + } }