Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
322 changes: 322 additions & 0 deletions apps/backend/src/foodRequests/request.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
import { RequestsService } from './request.service';
import { RequestsController } from './request.controller';
import { Test, TestingModule } from '@nestjs/testing';
import { mock } from 'jest-mock-extended';
import { AWSS3Service } from '../aws/aws-s3.service';
import { OrdersService } from '../orders/order.service';
import { Readable } from 'stream';
import { FoodRequest } from './request.entity';
import { RequestSize } from './types';
import { OrderStatus } from '../orders/types';

const mockRequestsService = mock<RequestsService>();
const mockOrdersService = mock<OrdersService>();
const mockAWSS3Service = mock<AWSS3Service>();

const foodRequest: Partial<FoodRequest> = {
requestId: 1,
pantryId: 1,
};

describe('RequestsController', () => {
let controller: RequestsController;

beforeEach(async () => {
mockRequestsService.findOne.mockReset();
mockRequestsService.find.mockReset();
mockRequestsService.create.mockReset();
mockRequestsService.updateDeliveryDetails?.mockReset();
mockAWSS3Service.upload.mockReset();
mockOrdersService.updateStatus.mockReset();

const module: TestingModule = await Test.createTestingModule({
controllers: [RequestsController],
providers: [
{
provide: RequestsService,
useValue: mockRequestsService,
},
{
provide: OrdersService,
useValue: mockOrdersService,
},
{
provide: AWSS3Service,
useValue: mockAWSS3Service,
},
],
}).compile();

controller = module.get<RequestsController>(RequestsController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});

describe('GET /:requestId', () => {
it('should call requestsService.findOne and return a specific food request', async () => {
const requestId = 1;

mockRequestsService.findOne.mockResolvedValueOnce(
foodRequest as FoodRequest,
);

const result = await controller.getRequest(requestId);

expect(result).toEqual(foodRequest);
expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId);
});
});

describe('GET /get-all-requests/:pantryId', () => {
it('should call requestsService.find and return all food requests for a specific pantry', async () => {
const foodRequests: Partial<FoodRequest>[] = [
foodRequest,
{
requestId: 2,
pantryId: 1,
},
];
const pantryId = 1;

mockRequestsService.find.mockResolvedValueOnce(
foodRequests as FoodRequest[],
);

const result = await controller.getAllPantryRequests(pantryId);

expect(result).toEqual(foodRequests);
expect(mockRequestsService.find).toHaveBeenCalledWith(pantryId);
});
});

describe('POST /create', () => {
it('should call requestsService.create and return the created food request', async () => {
const createBody: Partial<FoodRequest> = {
pantryId: 1,
requestedSize: RequestSize.MEDIUM,
requestedItems: ['Test item 1', 'Test item 2'],
additionalInformation: 'Test information.',
dateReceived: null,
feedback: null,
photos: null,
};

const createdRequest: Partial<FoodRequest> = {
requestId: 1,
...createBody,
requestedAt: new Date(),
order: null,
};

mockRequestsService.create.mockResolvedValueOnce(
createdRequest as FoodRequest,
);

const result = await controller.createRequest(createBody as FoodRequest);

expect(result).toEqual(createdRequest);
expect(mockRequestsService.create).toHaveBeenCalledWith(
createBody.pantryId,
createBody.requestedSize,
createBody.requestedItems,
createBody.additionalInformation,
createBody.dateReceived,
createBody.feedback,
createBody.photos,
);
});
});

describe('POST /:requestId/confirm-delivery', () => {
it('should upload photos, update the order, then update the request', async () => {
const requestId = 1;

const body = {
dateReceived: new Date().toISOString(),
feedback: 'Nice delivery!',
};

// Mock Photos
const mockStream = new Readable();
mockStream._read = () => {};

const photos: Express.Multer.File[] = [
{
fieldname: 'photos',
originalname: 'photo1.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
buffer: Buffer.from('image1'),
size: 1000,
destination: '',
filename: '',
path: '',
stream: mockStream,
},
{
fieldname: 'photos',
originalname: 'photo2.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
buffer: Buffer.from('image2'),
size: 2000,
destination: '',
filename: '',
path: '',
stream: mockStream,
},
];

const uploadedUrls = [
'https://fake-s3/photo1.jpg',
'https://fake-s3/photo2.jpg',
];

// Mock AWS upload
mockAWSS3Service.upload.mockResolvedValue(uploadedUrls);

// Mock RequestsService.findOne
mockRequestsService.findOne.mockResolvedValue({
requestId,
pantryId: 1,
order: { orderId: 99 },
} as FoodRequest);

mockOrdersService.updateStatus.mockResolvedValue();

const updatedRequest: Partial<FoodRequest> = {
requestId,
pantryId: 1,
dateReceived: new Date(body.dateReceived),
feedback: body.feedback,
photos: uploadedUrls,
};

mockRequestsService.updateDeliveryDetails.mockResolvedValue(
updatedRequest as FoodRequest,
);

const result = await controller.confirmDelivery(requestId, body, photos);

expect(mockAWSS3Service.upload).toHaveBeenCalledWith(photos);

expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId);

expect(mockOrdersService.updateStatus).toHaveBeenCalledWith(
99,
OrderStatus.DELIVERED,
);

expect(mockRequestsService.updateDeliveryDetails).toHaveBeenCalledWith(
requestId,
new Date(body.dateReceived),
body.feedback,
uploadedUrls,
);

expect(result).toEqual(updatedRequest);
});

it('should handle no photos being uploaded', async () => {
const requestId = 1;

const body = {
dateReceived: new Date().toISOString(),
feedback: 'No photos delivery!',
};

mockRequestsService.findOne.mockResolvedValue({
requestId,
pantryId: 1,
order: { orderId: 100 },
} as FoodRequest);

mockOrdersService.updateStatus.mockResolvedValue();

const updatedRequest: Partial<FoodRequest> = {
requestId,
pantryId: 1,
dateReceived: new Date(body.dateReceived),
feedback: body.feedback,
photos: [],
};

mockRequestsService.updateDeliveryDetails.mockResolvedValue(
updatedRequest as FoodRequest,
);

const result = await controller.confirmDelivery(requestId, body);

expect(mockAWSS3Service.upload).not.toHaveBeenCalled();
expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId);
expect(mockOrdersService.updateStatus).toHaveBeenCalledWith(
100,
OrderStatus.DELIVERED,
);
expect(mockRequestsService.updateDeliveryDetails).toHaveBeenCalledWith(
requestId,
new Date(body.dateReceived),
body.feedback,
[],
);
expect(result).toEqual(updatedRequest);
});

it('should handle empty photos array', async () => {
const requestId = 1;

const body = {
dateReceived: new Date().toISOString(),
feedback: 'Empty photos array delivery!',
};

mockRequestsService.findOne.mockResolvedValue({
requestId,
pantryId: 1,
order: { orderId: 101 },
} as FoodRequest);

mockOrdersService.updateStatus.mockResolvedValue();

const updatedRequest: Partial<FoodRequest> = {
requestId,
pantryId: 1,
dateReceived: new Date(body.dateReceived),
feedback: body.feedback,
photos: [],
};

mockRequestsService.updateDeliveryDetails.mockResolvedValue(
updatedRequest as FoodRequest,
);

const result = await controller.confirmDelivery(requestId, body, []);

expect(mockAWSS3Service.upload).not.toHaveBeenCalled();
expect(mockRequestsService.findOne).toHaveBeenCalledWith(requestId);
expect(mockOrdersService.updateStatus).toHaveBeenCalledWith(
101,
OrderStatus.DELIVERED,
);
expect(mockRequestsService.updateDeliveryDetails).toHaveBeenCalledWith(
requestId,
new Date(body.dateReceived),
body.feedback,
[],
);
expect(result).toEqual(updatedRequest);
});

it('should throw an error for invalid date', async () => {
await expect(
controller.confirmDelivery(
1,
{ dateReceived: 'bad-date', feedback: '' },
[],
),
).rejects.toThrow('Invalid date format for deliveryDate');
});
});
});
10 changes: 1 addition & 9 deletions apps/backend/src/foodRequests/request.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { OrderStatus } from '../orders/types';

@Controller('requests')
// @UseInterceptors()
export class FoodRequestsController {
export class RequestsController {
constructor(
private requestsService: RequestsService,
private awsS3Service: AWSS3Service,
Expand All @@ -43,14 +43,6 @@ export class FoodRequestsController {
return this.requestsService.find(pantryId);
}

@Get('get-order/:requestId')
async getOrderByRequestId(
@Param('requestId', ParseIntPipe) requestId: number,
): Promise<Order> {
const request = await this.requestsService.findOne(requestId);
return request.order;
}

@Post('/create')
@ApiBody({
description: 'Details for creating a food request',
Expand Down
7 changes: 4 additions & 3 deletions apps/backend/src/foodRequests/request.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FoodRequestsController } from './request.controller';
import { RequestsController } from './request.controller';
import { FoodRequest } from './request.entity';
import { RequestsService } from './request.service';
import { JwtStrategy } from '../auth/jwt.strategy';
Expand All @@ -9,14 +9,15 @@ import { AWSS3Module } from '../aws/aws-s3.module';
import { MulterModule } from '@nestjs/platform-express';
import { OrdersService } from '../orders/order.service';
import { Order } from '../orders/order.entity';
import { Pantry } from '../pantries/pantries.entity';

@Module({
imports: [
AWSS3Module,
MulterModule.register({ dest: './uploads' }),
TypeOrmModule.forFeature([FoodRequest, Order]),
TypeOrmModule.forFeature([FoodRequest, Order, Pantry]),
],
controllers: [FoodRequestsController],
controllers: [RequestsController],
providers: [RequestsService, OrdersService, AuthService, JwtStrategy],
})
export class RequestsModule {}
Loading
Loading