From f1a25877a8e01c4c64722b50cf8036c56653edd7 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sun, 14 Sep 2025 01:38:07 -0400 Subject: [PATCH 01/14] Made initial routes --- apps/backend/src/orders/order.controller.ts | 8 +++++-- apps/backend/src/orders/order.service.ts | 25 +++++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/apps/backend/src/orders/order.controller.ts b/apps/backend/src/orders/order.controller.ts index 0d35f0c2..6f2ce0ec 100644 --- a/apps/backend/src/orders/order.controller.ts +++ b/apps/backend/src/orders/order.controller.ts @@ -6,6 +6,7 @@ import { Param, ParseIntPipe, Body, + Query, } from '@nestjs/common'; import { OrdersService } from './order.service'; import { Order } from './order.entity'; @@ -19,8 +20,11 @@ export class OrdersController { constructor(private ordersService: OrdersService) {} @Get('/get-all-orders') - async getAllOrders(): Promise { - return this.ordersService.getAll(); + async getAllOrders( + @Query('status') status?: string, + @Query('pantryName') pantryName?: string, + ): Promise { + return this.ordersService.getAll({ status, pantryName }); } @Get('/get-current-orders') diff --git a/apps/backend/src/orders/order.service.ts b/apps/backend/src/orders/order.service.ts index c8d32705..64c96a0b 100644 --- a/apps/backend/src/orders/order.service.ts +++ b/apps/backend/src/orders/order.service.ts @@ -11,8 +11,29 @@ import { Donation } from '../donations/donations.entity'; export class OrdersService { constructor(@InjectRepository(Order) private repo: Repository) {} - async getAll() { - return this.repo.find(); + async getAll(filters?: { status?: string; pantryName?: string }) { + const qb = this.repo + .createQueryBuilder('order') + .leftJoinAndSelect('order.pantry', 'pantry') + .select([ + 'order.orderId', + 'order.status', + 'order.createdAt', + 'order.shippedAt', + 'order.deliveredAt', + ]); + + if (filters?.status) { + qb.andWhere('order.status = :status', { status: filters.status }); + } + + if (filters?.pantryName) { + qb.andWhere('pantry.pantryName = :pantryName', { + pantryName: filters.pantryName, + }); + } + + return qb.getMany(); } async getCurrentOrders() { From 5b88900f4386f68ef861594c1038a07269f7af78 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sun, 14 Sep 2025 02:03:23 -0400 Subject: [PATCH 02/14] Wrote tests --- apps/backend/src/orders/order.service.spec.ts | 106 ++++++++++++++++++ package.json | 1 + yarn.lock | 12 ++ 3 files changed, 119 insertions(+) create mode 100644 apps/backend/src/orders/order.service.spec.ts diff --git a/apps/backend/src/orders/order.service.spec.ts b/apps/backend/src/orders/order.service.spec.ts new file mode 100644 index 00000000..be828355 --- /dev/null +++ b/apps/backend/src/orders/order.service.spec.ts @@ -0,0 +1,106 @@ +import { Test } from '@nestjs/testing'; +import { getRepositoryToken } from '@nestjs/typeorm'; +import { Repository, SelectQueryBuilder } from 'typeorm'; +import { Order } from './order.entity'; +import { OrdersService } from './order.service'; +import { mock } from 'jest-mock-extended'; + +const mockOrdersRepository = mock>(); +const qb = mock>(); + +describe('OrdersService', () => { + let service: OrdersService; + + beforeAll(async () => { + jest.resetAllMocks(); + + const app = await Test.createTestingModule({ + providers: [ + OrdersService, + { + provide: getRepositoryToken(Order), + useValue: mockOrdersRepository, + }, + ], + }).compile(); + + service = app.get(OrdersService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + describe('getAll', () => { + beforeEach(() => { + qb.leftJoin.mockReturnThis(); + qb.select.mockReturnThis(); + qb.andWhere.mockReturnThis(); + qb.getMany.mockResolvedValue([]); + + mockOrdersRepository.createQueryBuilder.mockReturnValue(qb); + }); + + it('should return orders filtered by status', async () => { + const mockOrders = [ + { orderId: 1, status: 'pending' } as Order, + { orderId: 2, status: 'delivered' } as Order, + ]; + + qb.getMany.mockResolvedValue(mockOrders); + + const result = await service.getAll({ status: 'pending' }); + + expect(result).toEqual([mockOrders[0]]); + expect(qb.andWhere).toHaveBeenCalledWith('order.status = :status', { + status: 'pending', + }); + }); + + it('should return empty array when no status filters match', async () => { + qb.getMany.mockResolvedValue([]); + + const result = await service.getAll({ status: 'invalid status' }); + + expect(result).toEqual([]); + expect(qb.andWhere).toHaveBeenCalledWith('order.status = :status', { + status: 'invalid status', + }); + }); + + it('should return orders filtered by pantryName', async () => { + const mockOrders = [ + { + orderId: 3, + status: 'delivered', + pantry: { pantryName: 'Test Pantry' }, + } as Order, + { + orderId: 3, + status: 'delivered', + pantry: { pantryName: 'Test Pantry 2' }, + } as Order, + ]; + + qb.getMany.mockResolvedValue(mockOrders); + + const result = await service.getAll({ pantryName: 'Test Pantry' }); + + expect(result).toEqual([mockOrders[0]]); + expect(qb.andWhere).toHaveBeenCalledWith('pantry.name = :pantryName', { + pantryName: 'Test Pantry', + }); + }); + + it('should return empty array when no pantryName filters match', async () => { + qb.getMany.mockResolvedValue([]); + + const result = await service.getAll({ pantryName: 'Nonexistent Pantry' }); + + expect(result).toEqual([]); + expect(qb.andWhere).toHaveBeenCalledWith('pantry.name = :pantryName', { + pantryName: 'Nonexistent Pantry', + }); + }); + }); +}); diff --git a/package.json b/package.json index 83e1b96d..a69ffe4a 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "framer-motion": "^11.5.6", "global": "^4.4.0", "google-libphonenumber": "^3.2.40", + "jest-mock-extended": "^4.0.0", "jwks-rsa": "^3.1.0", "mongodb": "^6.1.0", "multer": "^1.4.5-lts.1", diff --git a/yarn.lock b/yarn.lock index 7a038925..ca4c7ff5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10135,6 +10135,13 @@ jest-message-util@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" +jest-mock-extended@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jest-mock-extended/-/jest-mock-extended-4.0.0.tgz#fe8cfa686c7ada4be2e7f7a3eced794b1338c18b" + integrity sha512-7BZpfuvLam+/HC+NxifIi9b+5VXj/utUDMPUqrDJehGWVuXPtLS9Jqlob2mJLrI/pg2k1S8DMfKDvEB88QNjaQ== + dependencies: + ts-essentials "^10.0.2" + jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz" @@ -13728,6 +13735,11 @@ ts-api-utils@^2.0.1: resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz" integrity sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w== +ts-essentials@^10.0.2: + version "10.1.1" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-10.1.1.tgz#4e1d29b7c9b33c1a2744482376634c4fafba5210" + integrity sha512-4aTB7KLHKmUvkjNj8V+EdnmuVTiECzn3K+zIbRthumvHu+j44x3w63xpfs0JL3NGIzGXqoQ7AV591xHO+XrOTw== + ts-jest@^29.1.0: version "29.1.1" resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz" From 518c2f67f2084f84d1a43f76d5a2df560988b7b4 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sun, 14 Sep 2025 16:53:02 -0400 Subject: [PATCH 03/14] Final commit --- .../src/allocations/allocations.controller.ts | 7 -- .../src/allocations/allocations.module.ts | 1 + apps/backend/src/auth/auth.controller.spec.ts | 18 +++++ .../src/orders/order.controller.spec.ts | 76 +++++++++++++++++++ apps/backend/src/orders/order.controller.ts | 13 +++- apps/backend/src/orders/order.module.ts | 3 +- apps/backend/src/orders/order.service.spec.ts | 69 ++++++++++------- apps/backend/src/orders/order.service.ts | 1 + apps/backend/src/pantries/pantries.entity.ts | 2 +- 9 files changed, 151 insertions(+), 39 deletions(-) create mode 100644 apps/backend/src/orders/order.controller.spec.ts diff --git a/apps/backend/src/allocations/allocations.controller.ts b/apps/backend/src/allocations/allocations.controller.ts index 44776a3d..d8d2324d 100644 --- a/apps/backend/src/allocations/allocations.controller.ts +++ b/apps/backend/src/allocations/allocations.controller.ts @@ -5,11 +5,4 @@ import { Allocation } from './allocations.entity'; @Controller('allocations') export class AllocationsController { constructor(private allocationsService: AllocationsService) {} - - @Get(':orderId/get-all-allocations') - async getAllAllocationsByOrder( - @Param('orderId', ParseIntPipe) orderId: number, - ): Promise { - return this.allocationsService.getAllAllocationsByOrder(orderId); - } } diff --git a/apps/backend/src/allocations/allocations.module.ts b/apps/backend/src/allocations/allocations.module.ts index 26e36b74..fed7360b 100644 --- a/apps/backend/src/allocations/allocations.module.ts +++ b/apps/backend/src/allocations/allocations.module.ts @@ -10,5 +10,6 @@ import { JwtStrategy } from '../auth/jwt.strategy'; imports: [TypeOrmModule.forFeature([Allocation])], controllers: [AllocationsController], providers: [AllocationsService, AuthService, JwtStrategy], + exports: [AllocationsService], }) export class AllocationModule {} diff --git a/apps/backend/src/auth/auth.controller.spec.ts b/apps/backend/src/auth/auth.controller.spec.ts index 27a31e61..1d439c1f 100644 --- a/apps/backend/src/auth/auth.controller.spec.ts +++ b/apps/backend/src/auth/auth.controller.spec.ts @@ -1,5 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; +import { UsersService } from '../users/users.service'; describe('AuthController', () => { let controller: AuthController; @@ -7,6 +9,22 @@ describe('AuthController', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [AuthController], + providers: [ + { + provide: AuthService, + useValue: { + login: jest.fn(), + register: jest.fn(), + }, + }, + { + provide: UsersService, + useValue: { + findOne: jest.fn(), + create: jest.fn(), + }, + }, + ], }).compile(); controller = module.get(AuthController); diff --git a/apps/backend/src/orders/order.controller.spec.ts b/apps/backend/src/orders/order.controller.spec.ts new file mode 100644 index 00000000..01280d9e --- /dev/null +++ b/apps/backend/src/orders/order.controller.spec.ts @@ -0,0 +1,76 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { OrdersController } from './order.controller'; +import { OrdersService } from './order.service'; +import { AllocationsService } from '../allocations/allocations.service'; +import { Order } from './order.entity'; +import { Allocation } from '../allocations/allocations.entity'; + +describe('OrdersController', () => { + let controller: OrdersController; + let ordersService: OrdersService; + let allocationsService: AllocationsService; + + const mockOrders: Order[] = [ + { orderId: 1, status: 'pending' } as Order, + { orderId: 2, status: 'delivered' } as Order, + ]; + + const mockAllocations = [ + { allocationId: 1, orderId: 1 } as Allocation, + { allocationId: 2, orderId: 1 } as Allocation, + { allocationId: 3, orderId: 2 } as Allocation, + ]; + + const mockOrdersService = { + getAll: jest.fn().mockResolvedValue(mockOrders[0]), + }; + + const mockAllocationsService = { + getAllAllocationsByOrder: jest + .fn() + .mockResolvedValue(mockAllocations.slice(0, 2)), + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [OrdersController], + providers: [ + { provide: OrdersService, useValue: mockOrdersService }, + { provide: AllocationsService, useValue: mockAllocationsService }, + ], + }).compile(); + + controller = module.get(OrdersController); + ordersService = module.get(OrdersService); + allocationsService = module.get(AllocationsService); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); + + describe('getAllOrders', () => { + it('should call ordersService.getAll and return orders', async () => { + const status = 'pending'; + const pantryName = 'Test Pantry'; + + const result = await controller.getAllOrders(status, pantryName); + + expect(result).toEqual(mockOrders[0]); + expect(ordersService.getAll).toHaveBeenCalledWith({ status, pantryName }); + }); + }); + + describe('getAllAllocationsByOrder', () => { + it('should call allocationsService.getAllAllocationsByOrder and return allocations', async () => { + const orderId = 1; + + const result = await controller.getAllAllocationsByOrder(orderId); + + expect(result).toEqual(mockAllocations.slice(0, 2)); + expect(allocationsService.getAllAllocationsByOrder).toHaveBeenCalledWith( + orderId, + ); + }); + }); +}); diff --git a/apps/backend/src/orders/order.controller.ts b/apps/backend/src/orders/order.controller.ts index 6f2ce0ec..6e313f65 100644 --- a/apps/backend/src/orders/order.controller.ts +++ b/apps/backend/src/orders/order.controller.ts @@ -14,10 +14,14 @@ import { Pantry } from '../pantries/pantries.entity'; import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity'; import { FoodRequest } from '../foodRequests/request.entity'; import { Donation } from '../donations/donations.entity'; +import { AllocationsService } from '../allocations/allocations.service'; @Controller('orders') export class OrdersController { - constructor(private ordersService: OrdersService) {} + constructor( + private readonly ordersService: OrdersService, + private readonly allocationsService: AllocationsService, + ) {} @Get('/get-all-orders') async getAllOrders( @@ -79,6 +83,13 @@ export class OrdersController { return this.ordersService.findOrderByRequest(orderId); } + @Get(':orderId/get-all-allocations') + async getAllAllocationsByOrder( + @Param('orderId', ParseIntPipe) orderId: number, + ) { + return this.allocationsService.getAllAllocationsByOrder(orderId); + } + @Patch('/update-status/:orderId') async updateStatus( @Param('orderId', ParseIntPipe) orderId: number, diff --git a/apps/backend/src/orders/order.module.ts b/apps/backend/src/orders/order.module.ts index fe2d5584..bf07f1cd 100644 --- a/apps/backend/src/orders/order.module.ts +++ b/apps/backend/src/orders/order.module.ts @@ -5,9 +5,10 @@ import { Order } from './order.entity'; import { OrdersService } from './order.service'; import { JwtStrategy } from '../auth/jwt.strategy'; import { AuthService } from '../auth/auth.service'; +import { AllocationModule } from '../allocations/allocations.module'; @Module({ - imports: [TypeOrmModule.forFeature([Order])], + imports: [TypeOrmModule.forFeature([Order]), AllocationModule], controllers: [OrdersController], providers: [OrdersService, AuthService, JwtStrategy], }) diff --git a/apps/backend/src/orders/order.service.spec.ts b/apps/backend/src/orders/order.service.spec.ts index be828355..8b3a8339 100644 --- a/apps/backend/src/orders/order.service.spec.ts +++ b/apps/backend/src/orders/order.service.spec.ts @@ -3,18 +3,17 @@ import { getRepositoryToken } from '@nestjs/typeorm'; import { Repository, SelectQueryBuilder } from 'typeorm'; import { Order } from './order.entity'; import { OrdersService } from './order.service'; -import { mock } from 'jest-mock-extended'; - -const mockOrdersRepository = mock>(); -const qb = mock>(); describe('OrdersService', () => { let service: OrdersService; + let mockOrdersRepository: jest.Mocked>; beforeAll(async () => { - jest.resetAllMocks(); + mockOrdersRepository = { + createQueryBuilder: jest.fn(), + } as unknown as jest.Mocked>; - const app = await Test.createTestingModule({ + const module = await Test.createTestingModule({ providers: [ OrdersService, { @@ -24,7 +23,18 @@ describe('OrdersService', () => { ], }).compile(); - service = app.get(OrdersService); + service = module.get(OrdersService); + }); + + beforeEach(() => { + const qb: SelectQueryBuilder = { + leftJoinAndSelect: jest.fn().mockReturnThis(), + select: jest.fn().mockReturnThis(), + andWhere: jest.fn().mockReturnThis(), + getMany: jest.fn().mockResolvedValue([]), + } as unknown as SelectQueryBuilder; + + mockOrdersRepository.createQueryBuilder.mockReturnValue(qb); }); it('should be defined', () => { @@ -32,33 +42,26 @@ describe('OrdersService', () => { }); describe('getAll', () => { - beforeEach(() => { - qb.leftJoin.mockReturnThis(); - qb.select.mockReturnThis(); - qb.andWhere.mockReturnThis(); - qb.getMany.mockResolvedValue([]); - - mockOrdersRepository.createQueryBuilder.mockReturnValue(qb); - }); - it('should return orders filtered by status', async () => { const mockOrders = [ { orderId: 1, status: 'pending' } as Order, { orderId: 2, status: 'delivered' } as Order, ]; - qb.getMany.mockResolvedValue(mockOrders); + const qb = mockOrdersRepository.createQueryBuilder(); + (qb.getMany as jest.Mock).mockResolvedValue(mockOrders); const result = await service.getAll({ status: 'pending' }); - expect(result).toEqual([mockOrders[0]]); + expect(result).toEqual(mockOrders); expect(qb.andWhere).toHaveBeenCalledWith('order.status = :status', { status: 'pending', }); }); it('should return empty array when no status filters match', async () => { - qb.getMany.mockResolvedValue([]); + const qb = mockOrdersRepository.createQueryBuilder(); + (qb.getMany as jest.Mock).mockResolvedValue([]); const result = await service.getAll({ status: 'invalid status' }); @@ -76,31 +79,39 @@ describe('OrdersService', () => { pantry: { pantryName: 'Test Pantry' }, } as Order, { - orderId: 3, + orderId: 4, status: 'delivered', pantry: { pantryName: 'Test Pantry 2' }, } as Order, ]; - qb.getMany.mockResolvedValue(mockOrders); + const qb = mockOrdersRepository.createQueryBuilder(); + (qb.getMany as jest.Mock).mockResolvedValue(mockOrders); const result = await service.getAll({ pantryName: 'Test Pantry' }); - expect(result).toEqual([mockOrders[0]]); - expect(qb.andWhere).toHaveBeenCalledWith('pantry.name = :pantryName', { - pantryName: 'Test Pantry', - }); + expect(result).toEqual(mockOrders); + expect(qb.andWhere).toHaveBeenCalledWith( + 'pantry.pantryName = :pantryName', + { + pantryName: 'Test Pantry', + }, + ); }); it('should return empty array when no pantryName filters match', async () => { - qb.getMany.mockResolvedValue([]); + const qb = mockOrdersRepository.createQueryBuilder(); + (qb.getMany as jest.Mock).mockResolvedValue([]); const result = await service.getAll({ pantryName: 'Nonexistent Pantry' }); expect(result).toEqual([]); - expect(qb.andWhere).toHaveBeenCalledWith('pantry.name = :pantryName', { - pantryName: 'Nonexistent Pantry', - }); + expect(qb.andWhere).toHaveBeenCalledWith( + 'pantry.pantryName = :pantryName', + { + pantryName: 'Nonexistent Pantry', + }, + ); }); }); }); diff --git a/apps/backend/src/orders/order.service.ts b/apps/backend/src/orders/order.service.ts index 64c96a0b..0ba874b5 100644 --- a/apps/backend/src/orders/order.service.ts +++ b/apps/backend/src/orders/order.service.ts @@ -21,6 +21,7 @@ export class OrdersService { 'order.createdAt', 'order.shippedAt', 'order.deliveredAt', + 'pantry.pantryName', ]); if (filters?.status) { diff --git a/apps/backend/src/pantries/pantries.entity.ts b/apps/backend/src/pantries/pantries.entity.ts index 354d0caf..1b4fd689 100644 --- a/apps/backend/src/pantries/pantries.entity.ts +++ b/apps/backend/src/pantries/pantries.entity.ts @@ -29,7 +29,7 @@ export class Pantry { reserveFoodForAllergic: boolean; @Column({ name: 'reservation_explanation', type: 'text' }) - reservationExplanation: Text; + reservationExplanation: string; @Column({ name: 'dedicated_allergy_friendly', type: 'varchar', length: 255 }) dedicatedAllergyFriendly: string; From b78edc5d05c9c5477cf561b90148449de4fdc185 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Mon, 15 Sep 2025 22:48:54 -0400 Subject: [PATCH 04/14] Final commit --- apps/backend-e2e/.eslintrc.json | 18 ---------- apps/backend-e2e/jest.config.ts | 19 ----------- apps/backend-e2e/project.json | 23 ------------- .../src/apps/backend/apps/backend.spec.ts | 10 ------ apps/backend-e2e/src/support/global-setup.ts | 10 ------ .../src/support/global-teardown.ts | 7 ---- apps/backend-e2e/src/support/test-setup.ts | 10 ------ apps/backend-e2e/tsconfig.json | 13 -------- apps/backend-e2e/tsconfig.spec.json | 9 ----- apps/frontend-e2e/.eslintrc.json | 10 ------ apps/frontend-e2e/cypress.config.ts | 8 ----- apps/frontend-e2e/project.json | 33 ------------------- apps/frontend-e2e/src/e2e/app.cy.ts | 13 -------- apps/frontend-e2e/src/fixtures/example.json | 4 --- apps/frontend-e2e/src/support/app.po.ts | 1 - apps/frontend-e2e/src/support/commands.ts | 33 ------------------- apps/frontend-e2e/src/support/e2e.ts | 17 ---------- apps/frontend-e2e/tsconfig.json | 10 ------ 18 files changed, 248 deletions(-) delete mode 100644 apps/backend-e2e/.eslintrc.json delete mode 100644 apps/backend-e2e/jest.config.ts delete mode 100644 apps/backend-e2e/project.json delete mode 100644 apps/backend-e2e/src/apps/backend/apps/backend.spec.ts delete mode 100644 apps/backend-e2e/src/support/global-setup.ts delete mode 100644 apps/backend-e2e/src/support/global-teardown.ts delete mode 100644 apps/backend-e2e/src/support/test-setup.ts delete mode 100644 apps/backend-e2e/tsconfig.json delete mode 100644 apps/backend-e2e/tsconfig.spec.json delete mode 100644 apps/frontend-e2e/.eslintrc.json delete mode 100644 apps/frontend-e2e/cypress.config.ts delete mode 100644 apps/frontend-e2e/project.json delete mode 100644 apps/frontend-e2e/src/e2e/app.cy.ts delete mode 100644 apps/frontend-e2e/src/fixtures/example.json delete mode 100644 apps/frontend-e2e/src/support/app.po.ts delete mode 100644 apps/frontend-e2e/src/support/commands.ts delete mode 100644 apps/frontend-e2e/src/support/e2e.ts delete mode 100644 apps/frontend-e2e/tsconfig.json diff --git a/apps/backend-e2e/.eslintrc.json b/apps/backend-e2e/.eslintrc.json deleted file mode 100644 index 9d9c0db5..00000000 --- a/apps/backend-e2e/.eslintrc.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": ["../../.eslintrc.json"], - "ignorePatterns": ["!**/*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - }, - { - "files": ["*.ts", "*.tsx"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "rules": {} - } - ] -} diff --git a/apps/backend-e2e/jest.config.ts b/apps/backend-e2e/jest.config.ts deleted file mode 100644 index befc444b..00000000 --- a/apps/backend-e2e/jest.config.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable */ -export default { - displayName: 'apps/backend-e2e', - preset: '../../jest.preset.js', - globalSetup: '/src/support/global-setup.ts', - globalTeardown: '/src/support/global-teardown.ts', - setupFiles: ['/src/support/test-setup.ts'], - testEnvironment: 'node', - transform: { - '^.+\\.[tj]s$': [ - 'ts-jest', - { - tsconfig: '/tsconfig.spec.json', - }, - ], - }, - moduleFileExtensions: ['ts', 'js', 'html'], - coverageDirectory: '../../coverage/apps/backend-e2e', -}; diff --git a/apps/backend-e2e/project.json b/apps/backend-e2e/project.json deleted file mode 100644 index 28491170..00000000 --- a/apps/backend-e2e/project.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "apps/backend-e2e", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "implicitDependencies": ["apps/backend"], - "projectType": "application", - "targets": { - "e2e": { - "executor": "@nx/jest:jest", - "outputs": ["{workspaceRoot}/coverage/{e2eProjectRoot}"], - "options": { - "jestConfig": "apps/backend-e2e/jest.config.ts", - "passWithNoTests": true - } - }, - "lint": { - "executor": "@nx/linter:eslint", - "outputs": ["{options.outputFile}"], - "options": { - "lintFilePatterns": ["apps/backend-e2e/**/*.{js,ts}"] - } - } - } -} diff --git a/apps/backend-e2e/src/apps/backend/apps/backend.spec.ts b/apps/backend-e2e/src/apps/backend/apps/backend.spec.ts deleted file mode 100644 index e8ac2a6c..00000000 --- a/apps/backend-e2e/src/apps/backend/apps/backend.spec.ts +++ /dev/null @@ -1,10 +0,0 @@ -import axios from 'axios'; - -describe('GET /api', () => { - it('should return a message', async () => { - const res = await axios.get(`/api`); - - expect(res.status).toBe(200); - expect(res.data).toEqual({ message: 'Hello API' }); - }); -}); diff --git a/apps/backend-e2e/src/support/global-setup.ts b/apps/backend-e2e/src/support/global-setup.ts deleted file mode 100644 index c1f51444..00000000 --- a/apps/backend-e2e/src/support/global-setup.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable */ -var __TEARDOWN_MESSAGE__: string; - -module.exports = async function () { - // Start services that that the app needs to run (e.g. database, docker-compose, etc.). - console.log('\nSetting up...\n'); - - // Hint: Use `globalThis` to pass variables to global teardown. - globalThis.__TEARDOWN_MESSAGE__ = '\nTearing down...\n'; -}; diff --git a/apps/backend-e2e/src/support/global-teardown.ts b/apps/backend-e2e/src/support/global-teardown.ts deleted file mode 100644 index 32ea345c..00000000 --- a/apps/backend-e2e/src/support/global-teardown.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable */ - -module.exports = async function () { - // Put clean up logic here (e.g. stopping services, docker-compose, etc.). - // Hint: `globalThis` is shared between setup and teardown. - console.log(globalThis.__TEARDOWN_MESSAGE__); -}; diff --git a/apps/backend-e2e/src/support/test-setup.ts b/apps/backend-e2e/src/support/test-setup.ts deleted file mode 100644 index 07f28703..00000000 --- a/apps/backend-e2e/src/support/test-setup.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable */ - -import axios from 'axios'; - -module.exports = async function () { - // Configure axios for tests to use. - const host = process.env.HOST ?? 'localhost'; - const port = process.env.PORT ?? '3000'; - axios.defaults.baseURL = `http://${host}:${port}`; -}; diff --git a/apps/backend-e2e/tsconfig.json b/apps/backend-e2e/tsconfig.json deleted file mode 100644 index ed633e1d..00000000 --- a/apps/backend-e2e/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.spec.json" - } - ], - "compilerOptions": { - "esModuleInterop": true - } -} diff --git a/apps/backend-e2e/tsconfig.spec.json b/apps/backend-e2e/tsconfig.spec.json deleted file mode 100644 index d7f9cf20..00000000 --- a/apps/backend-e2e/tsconfig.spec.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "../../dist/out-tsc", - "module": "commonjs", - "types": ["jest", "node"] - }, - "include": ["jest.config.ts", "src/**/*.ts"] -} diff --git a/apps/frontend-e2e/.eslintrc.json b/apps/frontend-e2e/.eslintrc.json deleted file mode 100644 index 696cb8b1..00000000 --- a/apps/frontend-e2e/.eslintrc.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], - "ignorePatterns": ["!**/*"], - "overrides": [ - { - "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], - "rules": {} - } - ] -} diff --git a/apps/frontend-e2e/cypress.config.ts b/apps/frontend-e2e/cypress.config.ts deleted file mode 100644 index a45b4fd6..00000000 --- a/apps/frontend-e2e/cypress.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { defineConfig } from 'cypress'; -import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset'; - -export default defineConfig({ - e2e: nxE2EPreset(__dirname, { - bundler: 'vite', - }), -}); diff --git a/apps/frontend-e2e/project.json b/apps/frontend-e2e/project.json deleted file mode 100644 index eac6b111..00000000 --- a/apps/frontend-e2e/project.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "frontend-e2e", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/frontend-e2e/src", - "projectType": "application", - "targets": { - "e2e": { - "executor": "@nx/cypress:cypress", - "options": { - "cypressConfig": "apps/frontend-e2e/cypress.config.ts", - "devServerTarget": "frontend:serve:development", - "testingType": "e2e" - }, - "configurations": { - "production": { - "devServerTarget": "frontend:serve:production" - }, - "ci": { - "devServerTarget": "frontend:serve-static" - } - } - }, - "lint": { - "executor": "@nx/linter:eslint", - "outputs": ["{options.outputFile}"], - "options": { - "lintFilePatterns": ["apps/frontend-e2e/**/*.{js,ts}"] - } - } - }, - "tags": [], - "implicitDependencies": ["frontend"] -} diff --git a/apps/frontend-e2e/src/e2e/app.cy.ts b/apps/frontend-e2e/src/e2e/app.cy.ts deleted file mode 100644 index c37b7f7c..00000000 --- a/apps/frontend-e2e/src/e2e/app.cy.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { getGreeting } from '../support/app.po'; - -describe('frontend', () => { - beforeEach(() => cy.visit('/')); - - it('should display welcome message', () => { - // Custom command example, see `../support/commands.ts` file - cy.login('my-email@something.com', 'myPassword'); - - // Function helper example, see `../support/app.po.ts` file - getGreeting().contains('Welcome frontend'); - }); -}); diff --git a/apps/frontend-e2e/src/fixtures/example.json b/apps/frontend-e2e/src/fixtures/example.json deleted file mode 100644 index 294cbed6..00000000 --- a/apps/frontend-e2e/src/fixtures/example.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io" -} diff --git a/apps/frontend-e2e/src/support/app.po.ts b/apps/frontend-e2e/src/support/app.po.ts deleted file mode 100644 index 32934246..00000000 --- a/apps/frontend-e2e/src/support/app.po.ts +++ /dev/null @@ -1 +0,0 @@ -export const getGreeting = () => cy.get('h1'); diff --git a/apps/frontend-e2e/src/support/commands.ts b/apps/frontend-e2e/src/support/commands.ts deleted file mode 100644 index 310f1fa0..00000000 --- a/apps/frontend-e2e/src/support/commands.ts +++ /dev/null @@ -1,33 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** - -// eslint-disable-next-line @typescript-eslint/no-namespace -declare namespace Cypress { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - interface Chainable { - login(email: string, password: string): void; - } -} -// -// -- This is a parent command -- -Cypress.Commands.add('login', (email, password) => { - console.log('Custom command example: Login', email, password); -}); -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/frontend-e2e/src/support/e2e.ts b/apps/frontend-e2e/src/support/e2e.ts deleted file mode 100644 index 3d469a6b..00000000 --- a/apps/frontend-e2e/src/support/e2e.ts +++ /dev/null @@ -1,17 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands'; diff --git a/apps/frontend-e2e/tsconfig.json b/apps/frontend-e2e/tsconfig.json deleted file mode 100644 index cc509a73..00000000 --- a/apps/frontend-e2e/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "sourceMap": false, - "outDir": "../../dist/out-tsc", - "allowJs": true, - "types": ["cypress", "node"] - }, - "include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"] -} From 0690c6fec1de198d8dc9fc64f7e1c3cefa236941 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Mon, 15 Sep 2025 23:00:10 -0400 Subject: [PATCH 05/14] Final commit --- apps/backend/src/allocations/allocations.service.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/allocations/allocations.service.ts b/apps/backend/src/allocations/allocations.service.ts index a2797376..d5bbf460 100644 --- a/apps/backend/src/allocations/allocations.service.ts +++ b/apps/backend/src/allocations/allocations.service.ts @@ -11,10 +11,17 @@ export class AllocationsService { async getAllAllocationsByOrder( orderId: number, - ): Promise { + ): Promise[]> { return this.repo.find({ - where: { orderId: orderId }, + where: { orderId }, relations: ['item'], + select: { + allocationId: true, + allocatedQuantity: true, + reservedAt: true, + fulfilledAt: true, + status: true, + }, }); } } From e9c58e5175ddcb30085f37d24093690c99e106fc Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Wed, 17 Sep 2025 22:51:41 -0400 Subject: [PATCH 06/14] Adjusted unit tests --- apps/backend/src/orders/order.service.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/orders/order.service.spec.ts b/apps/backend/src/orders/order.service.spec.ts index 8b3a8339..293f863e 100644 --- a/apps/backend/src/orders/order.service.spec.ts +++ b/apps/backend/src/orders/order.service.spec.ts @@ -49,11 +49,11 @@ describe('OrdersService', () => { ]; const qb = mockOrdersRepository.createQueryBuilder(); - (qb.getMany as jest.Mock).mockResolvedValue(mockOrders); + (qb.getMany as jest.Mock).mockResolvedValue(mockOrders[0]); const result = await service.getAll({ status: 'pending' }); - expect(result).toEqual(mockOrders); + expect(result).toEqual(mockOrders[0]); expect(qb.andWhere).toHaveBeenCalledWith('order.status = :status', { status: 'pending', }); From 06d3f47ef989a3a795c394db5421297703baab8b Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Wed, 17 Sep 2025 23:00:08 -0400 Subject: [PATCH 07/14] Added test for both --- apps/backend/src/orders/order.service.spec.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/apps/backend/src/orders/order.service.spec.ts b/apps/backend/src/orders/order.service.spec.ts index 293f863e..16b855de 100644 --- a/apps/backend/src/orders/order.service.spec.ts +++ b/apps/backend/src/orders/order.service.spec.ts @@ -113,5 +113,44 @@ describe('OrdersService', () => { }, ); }); + + it('should return orders filtered by both status and pantryName', async () => { + const mockOrders = [ + { + orderId: 3, + status: 'delivered', + pantry: { pantryName: 'Test Pantry' }, + } as Order, + { + orderId: 4, + status: 'delivered', + pantry: { pantryName: 'Test Pantry 2' }, + } as Order, + { + orderId: 5, + status: 'delivered', + pantry: { pantryName: 'Test Pantry 2' }, + }, + ]; + + const qb = mockOrdersRepository.createQueryBuilder(); + (qb.getMany as jest.Mock).mockResolvedValue(mockOrders.slice(1, 3)); + + const result = await service.getAll({ + status: 'delivered', + pantryName: 'Test Pantry 2', + }); + + expect(result).toEqual(mockOrders.slice(1, 3)); + expect(qb.andWhere).toHaveBeenCalledWith('order.status = :status', { + status: 'delivered', + }); + expect(qb.andWhere).toHaveBeenCalledWith( + 'pantry.pantryName = :pantryName', + { + pantryName: 'Test Pantry 2', + }, + ); + }); }); }); From 69b82b230742d651f9c146c48328c9b7c4e098c4 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sun, 21 Sep 2025 21:00:19 -0400 Subject: [PATCH 08/14] Altered pantryNames to allow for multiple to be passed in for querying purposes --- .../src/orders/order.controller.spec.ts | 9 ++++-- apps/backend/src/orders/order.controller.ts | 7 +++-- apps/backend/src/orders/order.service.spec.ts | 31 ++++++++++++------- apps/backend/src/orders/order.service.ts | 8 ++--- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/apps/backend/src/orders/order.controller.spec.ts b/apps/backend/src/orders/order.controller.spec.ts index 01280d9e..744d24e0 100644 --- a/apps/backend/src/orders/order.controller.spec.ts +++ b/apps/backend/src/orders/order.controller.spec.ts @@ -52,12 +52,15 @@ describe('OrdersController', () => { describe('getAllOrders', () => { it('should call ordersService.getAll and return orders', async () => { const status = 'pending'; - const pantryName = 'Test Pantry'; + const pantryNames = ['Test Pantry', 'Test Pantry 2']; - const result = await controller.getAllOrders(status, pantryName); + const result = await controller.getAllOrders(status, pantryNames); expect(result).toEqual(mockOrders[0]); - expect(ordersService.getAll).toHaveBeenCalledWith({ status, pantryName }); + expect(ordersService.getAll).toHaveBeenCalledWith({ + status, + pantryNames, + }); }); }); diff --git a/apps/backend/src/orders/order.controller.ts b/apps/backend/src/orders/order.controller.ts index 6e313f65..596f7fbd 100644 --- a/apps/backend/src/orders/order.controller.ts +++ b/apps/backend/src/orders/order.controller.ts @@ -26,9 +26,12 @@ export class OrdersController { @Get('/get-all-orders') async getAllOrders( @Query('status') status?: string, - @Query('pantryName') pantryName?: string, + @Query('pantryName') pantryNames?: string | string[], ): Promise { - return this.ordersService.getAll({ status, pantryName }); + if (typeof pantryNames === 'string') { + pantryNames = [pantryNames]; + } + return this.ordersService.getAll({ status, pantryNames }); } @Get('/get-current-orders') diff --git a/apps/backend/src/orders/order.service.spec.ts b/apps/backend/src/orders/order.service.spec.ts index 16b855de..e6c8c79a 100644 --- a/apps/backend/src/orders/order.service.spec.ts +++ b/apps/backend/src/orders/order.service.spec.ts @@ -83,18 +83,25 @@ describe('OrdersService', () => { status: 'delivered', pantry: { pantryName: 'Test Pantry 2' }, } as Order, + { + orderId: 5, + status: 'delivered', + pantry: { pantryName: 'Test Pantry 3' }, + } as Order, ]; const qb = mockOrdersRepository.createQueryBuilder(); - (qb.getMany as jest.Mock).mockResolvedValue(mockOrders); + (qb.getMany as jest.Mock).mockResolvedValue(mockOrders.slice(0, 2)); - const result = await service.getAll({ pantryName: 'Test Pantry' }); + const result = await service.getAll({ + pantryNames: ['Test Pantry', 'Test Pantry 2'], + }); - expect(result).toEqual(mockOrders); + expect(result).toEqual(mockOrders.slice(0, 2)); expect(qb.andWhere).toHaveBeenCalledWith( - 'pantry.pantryName = :pantryName', + 'pantry.pantryName IN (:...pantryNames)', { - pantryName: 'Test Pantry', + pantryNames: ['Test Pantry', 'Test Pantry 2'], }, ); }); @@ -103,13 +110,15 @@ describe('OrdersService', () => { const qb = mockOrdersRepository.createQueryBuilder(); (qb.getMany as jest.Mock).mockResolvedValue([]); - const result = await service.getAll({ pantryName: 'Nonexistent Pantry' }); + const result = await service.getAll({ + pantryNames: ['Nonexistent Pantry'], + }); expect(result).toEqual([]); expect(qb.andWhere).toHaveBeenCalledWith( - 'pantry.pantryName = :pantryName', + 'pantry.pantryName IN (:...pantryNames)', { - pantryName: 'Nonexistent Pantry', + pantryNames: ['Nonexistent Pantry'], }, ); }); @@ -138,7 +147,7 @@ describe('OrdersService', () => { const result = await service.getAll({ status: 'delivered', - pantryName: 'Test Pantry 2', + pantryNames: ['Test Pantry 2'], }); expect(result).toEqual(mockOrders.slice(1, 3)); @@ -146,9 +155,9 @@ describe('OrdersService', () => { status: 'delivered', }); expect(qb.andWhere).toHaveBeenCalledWith( - 'pantry.pantryName = :pantryName', + 'pantry.pantryName IN (:...pantryNames)', { - pantryName: 'Test Pantry 2', + pantryNames: ['Test Pantry 2'], }, ); }); diff --git a/apps/backend/src/orders/order.service.ts b/apps/backend/src/orders/order.service.ts index 0ba874b5..f1dfdf81 100644 --- a/apps/backend/src/orders/order.service.ts +++ b/apps/backend/src/orders/order.service.ts @@ -11,7 +11,7 @@ import { Donation } from '../donations/donations.entity'; export class OrdersService { constructor(@InjectRepository(Order) private repo: Repository) {} - async getAll(filters?: { status?: string; pantryName?: string }) { + async getAll(filters?: { status?: string; pantryNames?: string[] }) { const qb = this.repo .createQueryBuilder('order') .leftJoinAndSelect('order.pantry', 'pantry') @@ -28,9 +28,9 @@ export class OrdersService { qb.andWhere('order.status = :status', { status: filters.status }); } - if (filters?.pantryName) { - qb.andWhere('pantry.pantryName = :pantryName', { - pantryName: filters.pantryName, + if (filters?.pantryNames) { + qb.andWhere('pantry.pantryName IN (:...pantryNames)', { + pantryNames: filters.pantryNames, }); } From 3f81e79aaa22a1945410496ed5d6c9822d6334bb Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Tue, 7 Oct 2025 22:55:22 -0400 Subject: [PATCH 09/14] Fixed testing format --- .../src/orders/order.controller.spec.ts | 32 +++++++---------- apps/backend/src/orders/order.service.spec.ts | 36 ++++++++----------- 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/apps/backend/src/orders/order.controller.spec.ts b/apps/backend/src/orders/order.controller.spec.ts index 744d24e0..5434504a 100644 --- a/apps/backend/src/orders/order.controller.spec.ts +++ b/apps/backend/src/orders/order.controller.spec.ts @@ -4,11 +4,13 @@ import { OrdersService } from './order.service'; import { AllocationsService } from '../allocations/allocations.service'; import { Order } from './order.entity'; import { Allocation } from '../allocations/allocations.entity'; +import { mock } from 'jest-mock-extended'; + +const mockOrdersService = mock(); +const mockAllocationsService = mock(); describe('OrdersController', () => { let controller: OrdersController; - let ordersService: OrdersService; - let allocationsService: AllocationsService; const mockOrders: Order[] = [ { orderId: 1, status: 'pending' } as Order, @@ -21,16 +23,6 @@ describe('OrdersController', () => { { allocationId: 3, orderId: 2 } as Allocation, ]; - const mockOrdersService = { - getAll: jest.fn().mockResolvedValue(mockOrders[0]), - }; - - const mockAllocationsService = { - getAllAllocationsByOrder: jest - .fn() - .mockResolvedValue(mockAllocations.slice(0, 2)), - }; - beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [OrdersController], @@ -41,8 +33,6 @@ describe('OrdersController', () => { }).compile(); controller = module.get(OrdersController); - ordersService = module.get(OrdersService); - allocationsService = module.get(AllocationsService); }); it('should be defined', () => { @@ -53,11 +43,12 @@ describe('OrdersController', () => { it('should call ordersService.getAll and return orders', async () => { const status = 'pending'; const pantryNames = ['Test Pantry', 'Test Pantry 2']; + mockOrdersService.getAll.mockResolvedValueOnce([mockOrders[0]]); const result = await controller.getAllOrders(status, pantryNames); - expect(result).toEqual(mockOrders[0]); - expect(ordersService.getAll).toHaveBeenCalledWith({ + expect(result).toEqual([mockOrders[0]]); + expect(mockOrdersService.getAll).toHaveBeenCalledWith({ status, pantryNames, }); @@ -67,13 +58,16 @@ describe('OrdersController', () => { describe('getAllAllocationsByOrder', () => { it('should call allocationsService.getAllAllocationsByOrder and return allocations', async () => { const orderId = 1; + mockAllocationsService.getAllAllocationsByOrder.mockResolvedValueOnce( + mockAllocations.slice(0, 2), + ); const result = await controller.getAllAllocationsByOrder(orderId); expect(result).toEqual(mockAllocations.slice(0, 2)); - expect(allocationsService.getAllAllocationsByOrder).toHaveBeenCalledWith( - orderId, - ); + expect( + mockAllocationsService.getAllAllocationsByOrder, + ).toHaveBeenCalledWith(orderId); }); }); }); diff --git a/apps/backend/src/orders/order.service.spec.ts b/apps/backend/src/orders/order.service.spec.ts index e6c8c79a..09924d4c 100644 --- a/apps/backend/src/orders/order.service.spec.ts +++ b/apps/backend/src/orders/order.service.spec.ts @@ -3,15 +3,17 @@ import { getRepositoryToken } from '@nestjs/typeorm'; import { Repository, SelectQueryBuilder } from 'typeorm'; import { Order } from './order.entity'; import { OrdersService } from './order.service'; +import { mock } from 'jest-mock-extended'; + +const mockOrdersRepository = mock>(); describe('OrdersService', () => { let service: OrdersService; - let mockOrdersRepository: jest.Mocked>; + let qb: SelectQueryBuilder; beforeAll(async () => { - mockOrdersRepository = { - createQueryBuilder: jest.fn(), - } as unknown as jest.Mocked>; + // Reset the mock repository before compiling module + mockOrdersRepository.createQueryBuilder.mockReset(); const module = await Test.createTestingModule({ providers: [ @@ -27,7 +29,8 @@ describe('OrdersService', () => { }); beforeEach(() => { - const qb: SelectQueryBuilder = { + // Fresh query builder mock for each test + qb = { leftJoinAndSelect: jest.fn().mockReturnThis(), select: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), @@ -48,19 +51,17 @@ describe('OrdersService', () => { { orderId: 2, status: 'delivered' } as Order, ]; - const qb = mockOrdersRepository.createQueryBuilder(); - (qb.getMany as jest.Mock).mockResolvedValue(mockOrders[0]); + (qb.getMany as jest.Mock).mockResolvedValue([mockOrders[0]]); const result = await service.getAll({ status: 'pending' }); - expect(result).toEqual(mockOrders[0]); + expect(result).toEqual([mockOrders[0]]); expect(qb.andWhere).toHaveBeenCalledWith('order.status = :status', { status: 'pending', }); }); it('should return empty array when no status filters match', async () => { - const qb = mockOrdersRepository.createQueryBuilder(); (qb.getMany as jest.Mock).mockResolvedValue([]); const result = await service.getAll({ status: 'invalid status' }); @@ -90,7 +91,6 @@ describe('OrdersService', () => { } as Order, ]; - const qb = mockOrdersRepository.createQueryBuilder(); (qb.getMany as jest.Mock).mockResolvedValue(mockOrders.slice(0, 2)); const result = await service.getAll({ @@ -100,14 +100,11 @@ describe('OrdersService', () => { expect(result).toEqual(mockOrders.slice(0, 2)); expect(qb.andWhere).toHaveBeenCalledWith( 'pantry.pantryName IN (:...pantryNames)', - { - pantryNames: ['Test Pantry', 'Test Pantry 2'], - }, + { pantryNames: ['Test Pantry', 'Test Pantry 2'] }, ); }); it('should return empty array when no pantryName filters match', async () => { - const qb = mockOrdersRepository.createQueryBuilder(); (qb.getMany as jest.Mock).mockResolvedValue([]); const result = await service.getAll({ @@ -117,9 +114,7 @@ describe('OrdersService', () => { expect(result).toEqual([]); expect(qb.andWhere).toHaveBeenCalledWith( 'pantry.pantryName IN (:...pantryNames)', - { - pantryNames: ['Nonexistent Pantry'], - }, + { pantryNames: ['Nonexistent Pantry'] }, ); }); @@ -139,10 +134,9 @@ describe('OrdersService', () => { orderId: 5, status: 'delivered', pantry: { pantryName: 'Test Pantry 2' }, - }, + } as Order, ]; - const qb = mockOrdersRepository.createQueryBuilder(); (qb.getMany as jest.Mock).mockResolvedValue(mockOrders.slice(1, 3)); const result = await service.getAll({ @@ -156,9 +150,7 @@ describe('OrdersService', () => { }); expect(qb.andWhere).toHaveBeenCalledWith( 'pantry.pantryName IN (:...pantryNames)', - { - pantryNames: ['Test Pantry 2'], - }, + { pantryNames: ['Test Pantry 2'] }, ); }); }); From c4489fdb9a4afc502558b540f1ed5ac08f13ffa3 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sun, 12 Oct 2025 16:33:37 -0400 Subject: [PATCH 10/14] Resolved Sam comments --- apps/backend/src/orders/order.controller.ts | 4 ++-- apps/frontend/src/api/apiClient.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/backend/src/orders/order.controller.ts b/apps/backend/src/orders/order.controller.ts index 596f7fbd..d4de39f6 100644 --- a/apps/backend/src/orders/order.controller.ts +++ b/apps/backend/src/orders/order.controller.ts @@ -23,7 +23,7 @@ export class OrdersController { private readonly allocationsService: AllocationsService, ) {} - @Get('/get-all-orders') + @Get('/orders') async getAllOrders( @Query('status') status?: string, @Query('pantryName') pantryNames?: string | string[], @@ -86,7 +86,7 @@ export class OrdersController { return this.ordersService.findOrderByRequest(orderId); } - @Get(':orderId/get-all-allocations') + @Get(':orderId/allocations') async getAllAllocationsByOrder( @Param('orderId', ParseIntPipe) orderId: number, ) { diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index c9dc3c78..0e993b23 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -141,7 +141,7 @@ export class ApiClient { public async getAllOrders(): Promise { return this.axiosInstance - .get('/api/orders/get-all-orders') + .get('/api/orders/orders') .then((response) => response.data); } @@ -169,7 +169,7 @@ export class ApiClient { async getAllAllocationsByOrder(orderId: number): Promise { return this.axiosInstance - .get(`api/allocations/${orderId}/get-all-allocations`) + .get(`api/allocations/${orderId}/allocations`) .then((response) => response.data); } From d34d15b3a1a94c4aa5d7cbf36df95c18376f6381 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sun, 12 Oct 2025 17:21:04 -0400 Subject: [PATCH 11/14] Added partial type casting --- .../src/orders/order.controller.spec.ts | 18 +++-- apps/backend/src/orders/order.service.spec.ts | 76 +++++++++++++------ apps/frontend/src/api/apiClient.ts | 2 +- 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/apps/backend/src/orders/order.controller.spec.ts b/apps/backend/src/orders/order.controller.spec.ts index 5434504a..3f15bd85 100644 --- a/apps/backend/src/orders/order.controller.spec.ts +++ b/apps/backend/src/orders/order.controller.spec.ts @@ -12,15 +12,15 @@ const mockAllocationsService = mock(); describe('OrdersController', () => { let controller: OrdersController; - const mockOrders: Order[] = [ - { orderId: 1, status: 'pending' } as Order, - { orderId: 2, status: 'delivered' } as Order, + const mockOrders: Partial[] = [ + { orderId: 1, status: 'pending' }, + { orderId: 2, status: 'delivered' }, ]; - const mockAllocations = [ - { allocationId: 1, orderId: 1 } as Allocation, - { allocationId: 2, orderId: 1 } as Allocation, - { allocationId: 3, orderId: 2 } as Allocation, + const mockAllocations: Partial[] = [ + { allocationId: 1, orderId: 1 }, + { allocationId: 2, orderId: 1 }, + { allocationId: 3, orderId: 2 }, ]; beforeEach(async () => { @@ -43,7 +43,9 @@ describe('OrdersController', () => { it('should call ordersService.getAll and return orders', async () => { const status = 'pending'; const pantryNames = ['Test Pantry', 'Test Pantry 2']; - mockOrdersService.getAll.mockResolvedValueOnce([mockOrders[0]]); + mockOrdersService.getAll.mockResolvedValueOnce([ + mockOrders[0], + ] as Order[]); const result = await controller.getAllOrders(status, pantryNames); diff --git a/apps/backend/src/orders/order.service.spec.ts b/apps/backend/src/orders/order.service.spec.ts index 09924d4c..586da4c2 100644 --- a/apps/backend/src/orders/order.service.spec.ts +++ b/apps/backend/src/orders/order.service.spec.ts @@ -4,15 +4,40 @@ import { Repository, SelectQueryBuilder } from 'typeorm'; import { Order } from './order.entity'; import { OrdersService } from './order.service'; import { mock } from 'jest-mock-extended'; +import { Pantry } from '../pantries/pantries.entity'; +import { User } from '../users/user.entity'; const mockOrdersRepository = mock>(); +const mockPantry: Pantry = { + pantryId: 1, + pantryName: 'Test Pantry', + address: '123 Test St', + allergenClients: '', + refrigeratedDonation: '', + reserveFoodForAllergic: false, + reservationExplanation: '', + dedicatedAllergyFriendly: '', + clientVisitFrequency: '', + identifyAllergensConfidence: '', + serveAllergicChildren: '', + newsletterSubscription: false, + restrictions: [], + ssfRepresentative: null as unknown as User, + pantryRepresentative: null as unknown as User, + status: 'active', + dateApplied: new Date(), + activities: '', + questions: null, + itemsInStock: '', + needMoreOptions: '', +}; + describe('OrdersService', () => { let service: OrdersService; let qb: SelectQueryBuilder; beforeAll(async () => { - // Reset the mock repository before compiling module mockOrdersRepository.createQueryBuilder.mockReset(); const module = await Test.createTestingModule({ @@ -29,7 +54,6 @@ describe('OrdersService', () => { }); beforeEach(() => { - // Fresh query builder mock for each test qb = { leftJoinAndSelect: jest.fn().mockReturnThis(), select: jest.fn().mockReturnThis(), @@ -46,12 +70,12 @@ describe('OrdersService', () => { describe('getAll', () => { it('should return orders filtered by status', async () => { - const mockOrders = [ - { orderId: 1, status: 'pending' } as Order, - { orderId: 2, status: 'delivered' } as Order, + const mockOrders: Partial[] = [ + { orderId: 1, status: 'pending' }, + { orderId: 2, status: 'delivered' }, ]; - (qb.getMany as jest.Mock).mockResolvedValue([mockOrders[0]]); + (qb.getMany as jest.Mock).mockResolvedValue([mockOrders[0] as Order]); const result = await service.getAll({ status: 'pending' }); @@ -73,31 +97,33 @@ describe('OrdersService', () => { }); it('should return orders filtered by pantryName', async () => { - const mockOrders = [ + const mockOrders: Partial[] = [ { orderId: 3, status: 'delivered', - pantry: { pantryName: 'Test Pantry' }, - } as Order, + pantry: { ...mockPantry, pantryName: 'Test Pantry' }, + }, { orderId: 4, status: 'delivered', - pantry: { pantryName: 'Test Pantry 2' }, - } as Order, + pantry: { ...mockPantry, pantryName: 'Test Pantry 2' }, + }, { orderId: 5, status: 'delivered', - pantry: { pantryName: 'Test Pantry 3' }, - } as Order, + pantry: { ...mockPantry, pantryName: 'Test Pantry 3' }, + }, ]; - (qb.getMany as jest.Mock).mockResolvedValue(mockOrders.slice(0, 2)); + (qb.getMany as jest.Mock).mockResolvedValue( + mockOrders.slice(0, 2) as Order[], + ); const result = await service.getAll({ pantryNames: ['Test Pantry', 'Test Pantry 2'], }); - expect(result).toEqual(mockOrders.slice(0, 2)); + expect(result).toEqual(mockOrders.slice(0, 2) as Order[]); expect(qb.andWhere).toHaveBeenCalledWith( 'pantry.pantryName IN (:...pantryNames)', { pantryNames: ['Test Pantry', 'Test Pantry 2'] }, @@ -119,32 +145,34 @@ describe('OrdersService', () => { }); it('should return orders filtered by both status and pantryName', async () => { - const mockOrders = [ + const mockOrders: Partial[] = [ { orderId: 3, status: 'delivered', - pantry: { pantryName: 'Test Pantry' }, - } as Order, + pantry: { ...mockPantry, pantryName: 'Test Pantry 1' }, + }, { orderId: 4, status: 'delivered', - pantry: { pantryName: 'Test Pantry 2' }, - } as Order, + pantry: { ...mockPantry, pantryName: 'Test Pantry 2' }, + }, { orderId: 5, status: 'delivered', - pantry: { pantryName: 'Test Pantry 2' }, - } as Order, + pantry: { ...mockPantry, pantryName: 'Test Pantry 2' }, + }, ]; - (qb.getMany as jest.Mock).mockResolvedValue(mockOrders.slice(1, 3)); + (qb.getMany as jest.Mock).mockResolvedValue( + mockOrders.slice(1, 3) as Order[], + ); const result = await service.getAll({ status: 'delivered', pantryNames: ['Test Pantry 2'], }); - expect(result).toEqual(mockOrders.slice(1, 3)); + expect(result).toEqual(mockOrders.slice(1, 3) as Order[]); expect(qb.andWhere).toHaveBeenCalledWith('order.status = :status', { status: 'delivered', }); diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 0e993b23..50c853b9 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -169,7 +169,7 @@ export class ApiClient { async getAllAllocationsByOrder(orderId: number): Promise { return this.axiosInstance - .get(`api/allocations/${orderId}/allocations`) + .get(`api/orders/${orderId}/allocations`) .then((response) => response.data); } From 3fb772431c1f19d33e64d9c46d45ac16c7a5f703 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sun, 12 Oct 2025 17:22:12 -0400 Subject: [PATCH 12/14] Final --- apps/backend/src/orders/order.controller.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/orders/order.controller.spec.ts b/apps/backend/src/orders/order.controller.spec.ts index 3f15bd85..b03201ee 100644 --- a/apps/backend/src/orders/order.controller.spec.ts +++ b/apps/backend/src/orders/order.controller.spec.ts @@ -49,7 +49,7 @@ describe('OrdersController', () => { const result = await controller.getAllOrders(status, pantryNames); - expect(result).toEqual([mockOrders[0]]); + expect(result).toEqual([mockOrders[0]] as Order[]); expect(mockOrdersService.getAll).toHaveBeenCalledWith({ status, pantryNames, @@ -61,12 +61,12 @@ describe('OrdersController', () => { it('should call allocationsService.getAllAllocationsByOrder and return allocations', async () => { const orderId = 1; mockAllocationsService.getAllAllocationsByOrder.mockResolvedValueOnce( - mockAllocations.slice(0, 2), + mockAllocations.slice(0, 2) as Allocation[], ); const result = await controller.getAllAllocationsByOrder(orderId); - expect(result).toEqual(mockAllocations.slice(0, 2)); + expect(result).toEqual(mockAllocations.slice(0, 2) as Allocation[]); expect( mockAllocationsService.getAllAllocationsByOrder, ).toHaveBeenCalledWith(orderId); From 27f626d8b8a1671d54313467d84c0a9e77e286c6 Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Sun, 12 Oct 2025 17:51:20 -0400 Subject: [PATCH 13/14] Added documentation for parameter querying --- apps/backend/src/orders/order.controller.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/backend/src/orders/order.controller.ts b/apps/backend/src/orders/order.controller.ts index d4de39f6..921bedf8 100644 --- a/apps/backend/src/orders/order.controller.ts +++ b/apps/backend/src/orders/order.controller.ts @@ -23,6 +23,8 @@ export class OrdersController { private readonly allocationsService: AllocationsService, ) {} + // Called like: /orders?status=pending&pantryName=Test%20Pantry&pantryName=Test%20Pantry%2 + // %20 is the URL encoded space character @Get('/orders') async getAllOrders( @Query('status') status?: string, From 663297866fb69c1599d61c7f9e2b5a3b81b7d26e Mon Sep 17 00:00:00 2001 From: Dalton Burkhart Date: Tue, 14 Oct 2025 19:13:51 -0400 Subject: [PATCH 14/14] Fixed route naming --- apps/backend/src/orders/order.controller.ts | 4 ++-- apps/frontend/src/api/apiClient.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/orders/order.controller.ts b/apps/backend/src/orders/order.controller.ts index 921bedf8..de2e4616 100644 --- a/apps/backend/src/orders/order.controller.ts +++ b/apps/backend/src/orders/order.controller.ts @@ -23,9 +23,9 @@ export class OrdersController { private readonly allocationsService: AllocationsService, ) {} - // Called like: /orders?status=pending&pantryName=Test%20Pantry&pantryName=Test%20Pantry%2 + // Called like: /?status=pending&pantryName=Test%20Pantry&pantryName=Test%20Pantry%2 // %20 is the URL encoded space character - @Get('/orders') + @Get('/') async getAllOrders( @Query('status') status?: string, @Query('pantryName') pantryNames?: string | string[], diff --git a/apps/frontend/src/api/apiClient.ts b/apps/frontend/src/api/apiClient.ts index 50c853b9..5347c8c8 100644 --- a/apps/frontend/src/api/apiClient.ts +++ b/apps/frontend/src/api/apiClient.ts @@ -141,7 +141,7 @@ export class ApiClient { public async getAllOrders(): Promise { return this.axiosInstance - .get('/api/orders/orders') + .get('/api/orders/') .then((response) => response.data); }