-
Notifications
You must be signed in to change notification settings - Fork 0
MSPCA-7: Add activate/deactivate endpoints for volunteers and coordin… #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,24 @@ | ||
| import { Test, TestingModule } from '@nestjs/testing'; | ||
| import { BadRequestException } from '@nestjs/common'; | ||
| import { CoordinatorsController } from './coordinators.controller'; | ||
| import { CoordinatorsService } from './coordinators.service'; | ||
|
|
||
| describe('CoordinatorsController', () => { | ||
| let controller: CoordinatorsController; | ||
| let service: { deactivate: jest.Mock; activate: jest.Mock }; | ||
|
|
||
| beforeEach(async () => { | ||
| service = { | ||
| deactivate: jest.fn(), | ||
| activate: jest.fn(), | ||
| }; | ||
|
|
||
| const module: TestingModule = await Test.createTestingModule({ | ||
| controllers: [CoordinatorsController], | ||
| providers: [ | ||
| { | ||
| provide: CoordinatorsService, | ||
| useValue: {}, | ||
| useValue: service, | ||
| }, | ||
| ], | ||
| }).compile(); | ||
|
|
@@ -22,4 +29,40 @@ describe('CoordinatorsController', () => { | |
| it('should be defined', () => { | ||
| expect(controller).toBeDefined(); | ||
| }); | ||
|
|
||
| describe('deactivate', () => { | ||
| it('should call service.deactivate with the parsed id', async () => { | ||
| const coordinator = { coordinatorId: 1, active: false }; | ||
| service.deactivate.mockResolvedValue(coordinator); | ||
|
|
||
| const result = await controller.deactivate(1); | ||
|
|
||
| expect(service.deactivate).toHaveBeenCalledWith(1); | ||
| expect(result).toEqual(coordinator); | ||
| }); | ||
|
|
||
| it('should throw BadRequestException for an invalid id', async () => { | ||
| await expect(controller.deactivate(0)).rejects.toThrow( | ||
| BadRequestException, | ||
| ); | ||
| expect(service.deactivate).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('activate', () => { | ||
| it('should call service.activate with the parsed id', async () => { | ||
| const coordinator = { coordinatorId: 1, active: true }; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type cast |
||
| service.activate.mockResolvedValue(coordinator); | ||
|
|
||
| const result = await controller.activate(1); | ||
|
|
||
| expect(service.activate).toHaveBeenCalledWith(1); | ||
| expect(result).toEqual(coordinator); | ||
| }); | ||
|
|
||
| it('should throw BadRequestException for an invalid id', async () => { | ||
| await expect(controller.activate(0)).rejects.toThrow(BadRequestException); | ||
| expect(service.activate).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,27 @@ | ||
| import { Controller } from '@nestjs/common'; | ||
| import { Controller, Param, ParseIntPipe, Patch } from '@nestjs/common'; | ||
| import { CoordinatorsService } from './coordinators.service'; | ||
| import { validateId } from '../utils/validation.utils'; | ||
| import { FosterCoordinator } from './coordinators.entity'; | ||
|
|
||
| @Controller('coordinators') | ||
| export class CoordinatorsController { | ||
| constructor(private coordinatorsService: CoordinatorsService) {} | ||
|
|
||
| @Patch('/:id/deactivate') | ||
| async deactivate( | ||
| @Param('id', ParseIntPipe) id: number, | ||
| ): Promise<FosterCoordinator> { | ||
| validateId(id, 'Coordinator'); | ||
|
|
||
| return this.coordinatorsService.deactivate(id); | ||
| } | ||
|
|
||
| @Patch('/:id/activate') | ||
| async activate( | ||
| @Param('id', ParseIntPipe) id: number, | ||
| ): Promise<FosterCoordinator> { | ||
| validateId(id, 'Coordinator'); | ||
|
|
||
| return this.coordinatorsService.activate(id); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,26 @@ | ||
| import { Test, TestingModule } from '@nestjs/testing'; | ||
| import { getRepositoryToken } from '@nestjs/typeorm'; | ||
| import { NotFoundException } from '@nestjs/common'; | ||
| import { CoordinatorsService } from './coordinators.service'; | ||
| import { FosterCoordinator } from './coordinators.entity'; | ||
|
|
||
| describe('CoordinatorsService', () => { | ||
| let service: CoordinatorsService; | ||
| let repo: { findOne: jest.Mock; save: jest.Mock; delete: jest.Mock }; | ||
|
|
||
| beforeEach(async () => { | ||
| repo = { | ||
| findOne: jest.fn(), | ||
| save: jest.fn(), | ||
| delete: jest.fn(), | ||
| }; | ||
|
|
||
| const module: TestingModule = await Test.createTestingModule({ | ||
| providers: [ | ||
| CoordinatorsService, | ||
| { | ||
| provide: getRepositoryToken(FosterCoordinator), | ||
| useValue: {}, | ||
| useValue: repo, | ||
| }, | ||
| ], | ||
| }).compile(); | ||
|
|
@@ -23,4 +31,75 @@ describe('CoordinatorsService', () => { | |
| it('should be defined', () => { | ||
| expect(service).toBeDefined(); | ||
| }); | ||
|
|
||
| describe('deactivate', () => { | ||
| it('should set active to false and save the coordinator', async () => { | ||
| const coordinator = { | ||
| coordinatorId: 1, | ||
| active: true, | ||
| } as FosterCoordinator; | ||
| repo.findOne.mockResolvedValue(coordinator); | ||
| repo.save.mockResolvedValue({ ...coordinator, active: false }); | ||
|
|
||
| const result = await service.deactivate(1); | ||
|
|
||
| expect(repo.findOne).toHaveBeenCalledWith({ | ||
| where: { coordinatorId: 1 }, | ||
| }); | ||
| expect(repo.save).toHaveBeenCalledWith({ | ||
| ...coordinator, | ||
| active: false, | ||
| }); | ||
| expect(result.active).toBe(false); | ||
| }); | ||
|
|
||
| it('should throw NotFoundException if coordinator does not exist', async () => { | ||
| repo.findOne.mockResolvedValue(null); | ||
|
|
||
| await expect(service.deactivate(999)).rejects.toThrow( | ||
| new NotFoundException('Coordinator with ID 999 not found'), | ||
| ); | ||
| expect(repo.save).not.toHaveBeenCalled(); | ||
| }); | ||
| it('should not delete the coordinator record', async () => { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same thing here as the volunteer service |
||
| const coordinator = { | ||
| coordinatorId: 1, | ||
| active: true, | ||
| } as FosterCoordinator; | ||
| repo.findOne.mockResolvedValue(coordinator); | ||
| repo.save.mockResolvedValue({ ...coordinator, active: false }); | ||
|
|
||
| await service.deactivate(1); | ||
|
|
||
| expect(repo.delete).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('activate', () => { | ||
| it('should set active to true and save the coordinator', async () => { | ||
| const coordinator = { | ||
| coordinatorId: 1, | ||
| active: false, | ||
| } as FosterCoordinator; | ||
| repo.findOne.mockResolvedValue(coordinator); | ||
| repo.save.mockResolvedValue({ ...coordinator, active: true }); | ||
|
|
||
| const result = await service.activate(1); | ||
|
|
||
| expect(repo.findOne).toHaveBeenCalledWith({ | ||
| where: { coordinatorId: 1 }, | ||
| }); | ||
| expect(repo.save).toHaveBeenCalledWith({ ...coordinator, active: true }); | ||
| expect(result.active).toBe(true); | ||
| }); | ||
|
|
||
| it('should throw NotFoundException if coordinator does not exist', async () => { | ||
| repo.findOne.mockResolvedValue(null); | ||
|
|
||
| await expect(service.activate(999)).rejects.toThrow( | ||
| new NotFoundException('Coordinator with ID 999 not found'), | ||
| ); | ||
| expect(repo.save).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,24 @@ | ||
| import { Test, TestingModule } from '@nestjs/testing'; | ||
| import { BadRequestException } from '@nestjs/common'; | ||
| import { VolunteersController } from './volunteers.controller'; | ||
| import { VolunteersService } from './volunteers.service'; | ||
|
|
||
| describe('VolunteersController', () => { | ||
| let controller: VolunteersController; | ||
| let service: { deactivate: jest.Mock; activate: jest.Mock }; | ||
|
|
||
| beforeEach(async () => { | ||
| service = { | ||
| deactivate: jest.fn(), | ||
| activate: jest.fn(), | ||
| }; | ||
|
|
||
| const module: TestingModule = await Test.createTestingModule({ | ||
| controllers: [VolunteersController], | ||
| providers: [ | ||
| { | ||
| provide: VolunteersService, | ||
| useValue: {}, | ||
| useValue: service, | ||
| }, | ||
| ], | ||
| }).compile(); | ||
|
|
@@ -22,4 +29,40 @@ describe('VolunteersController', () => { | |
| it('should be defined', () => { | ||
| expect(controller).toBeDefined(); | ||
| }); | ||
|
|
||
| describe('deactivate', () => { | ||
| it('should call service.deactivate with the parsed id', async () => { | ||
| const volunteer = { volunteerId: 1, active: false }; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we type cast this to a FosterVolunteer |
||
| service.deactivate.mockResolvedValue(volunteer); | ||
|
|
||
| const result = await controller.deactivate(1); | ||
|
|
||
| expect(service.deactivate).toHaveBeenCalledWith(1); | ||
| expect(result).toEqual(volunteer); | ||
| }); | ||
|
|
||
| it('should throw BadRequestException for an invalid id', async () => { | ||
| await expect(controller.deactivate(0)).rejects.toThrow( | ||
| BadRequestException, | ||
| ); | ||
| expect(service.deactivate).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
|
|
||
| describe('activate', () => { | ||
| it('should call service.activate with the parsed id', async () => { | ||
| const volunteer = { volunteerId: 1, active: true }; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
| service.activate.mockResolvedValue(volunteer); | ||
|
|
||
| const result = await controller.activate(1); | ||
|
|
||
| expect(service.activate).toHaveBeenCalledWith(1); | ||
| expect(result).toEqual(volunteer); | ||
| }); | ||
|
|
||
| it('should throw BadRequestException for an invalid id', async () => { | ||
| await expect(controller.activate(0)).rejects.toThrow(BadRequestException); | ||
| expect(service.activate).not.toHaveBeenCalled(); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| import { Controller } from '@nestjs/common'; | ||
| import { Controller, Param, ParseIntPipe, Patch } from '@nestjs/common'; | ||
| import { VolunteersService } from './volunteers.service'; | ||
| import { validateId } from '../utils/validation.utils'; | ||
| import { FosterVolunteer } from './volunteers.entity'; | ||
|
|
||
| // @ApiTags('Volunteers') | ||
| // @ApiBearerAuth() | ||
|
|
@@ -12,4 +14,22 @@ export class VolunteersController { | |
| // async getUser(@Param('userId', ParseIntPipe) userId: number): Promise<User> { | ||
| // return this.usersService.findOne(userId); | ||
| // } | ||
|
|
||
| @Patch('/:id/deactivate') | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should've specified this in the ticket, but we are going to be documenting all of our endpoints so that they can be easily read within Swagger without having to go into our repo. To do this, we are gonna add Tags from NestJs. For all your endpoints, can you add an @apioperation, @ApiParam, and just one @apiresponse for the happy path, including the status code, description, and type it returns? We will do this for all API endpoints moving forward. |
||
| async deactivate( | ||
| @Param('id', ParseIntPipe) id: number, | ||
| ): Promise<FosterVolunteer> { | ||
| validateId(id, 'Volunteer'); | ||
|
|
||
| return this.volunteersService.deactivate(id); | ||
| } | ||
|
|
||
| @Patch('/:id/activate') | ||
| async activate( | ||
| @Param('id', ParseIntPipe) id: number, | ||
| ): Promise<FosterVolunteer> { | ||
| validateId(id, 'Volunteer'); | ||
|
|
||
| return this.volunteersService.activate(id); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type cast