diff --git a/apps/backend/src/coordinators/coordinators.controller.spec.ts b/apps/backend/src/coordinators/coordinators.controller.spec.ts index 5d8f27e43..1760e99ae 100644 --- a/apps/backend/src/coordinators/coordinators.controller.spec.ts +++ b/apps/backend/src/coordinators/coordinators.controller.spec.ts @@ -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 }; + 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(); + }); + }); }); diff --git a/apps/backend/src/coordinators/coordinators.controller.ts b/apps/backend/src/coordinators/coordinators.controller.ts index bbbddfd39..1cee6c24b 100644 --- a/apps/backend/src/coordinators/coordinators.controller.ts +++ b/apps/backend/src/coordinators/coordinators.controller.ts @@ -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 { + validateId(id, 'Coordinator'); + + return this.coordinatorsService.deactivate(id); + } + + @Patch('/:id/activate') + async activate( + @Param('id', ParseIntPipe) id: number, + ): Promise { + validateId(id, 'Coordinator'); + + return this.coordinatorsService.activate(id); + } } diff --git a/apps/backend/src/coordinators/coordinators.service.spec.ts b/apps/backend/src/coordinators/coordinators.service.spec.ts index 2003f702f..76423322e 100644 --- a/apps/backend/src/coordinators/coordinators.service.spec.ts +++ b/apps/backend/src/coordinators/coordinators.service.spec.ts @@ -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 () => { + 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(); + }); + }); }); diff --git a/apps/backend/src/coordinators/coordinators.service.ts b/apps/backend/src/coordinators/coordinators.service.ts index 81fb74d9f..6b4ab0002 100644 --- a/apps/backend/src/coordinators/coordinators.service.ts +++ b/apps/backend/src/coordinators/coordinators.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { FosterCoordinator } from './coordinators.entity'; @@ -9,4 +9,32 @@ export class CoordinatorsService { @InjectRepository(FosterCoordinator) private repo: Repository, ) {} + + async deactivate(id: number): Promise { + const coordinator = await this.repo.findOne({ + where: { coordinatorId: id }, + }); + + if (!coordinator) { + throw new NotFoundException(`Coordinator with ID ${id} not found`); + } + + coordinator.active = false; + + return this.repo.save(coordinator); + } + + async activate(id: number): Promise { + const coordinator = await this.repo.findOne({ + where: { coordinatorId: id }, + }); + + if (!coordinator) { + throw new NotFoundException(`Coordinator with ID ${id} not found`); + } + + coordinator.active = true; + + return this.repo.save(coordinator); + } } diff --git a/apps/backend/src/volunteers/volunteers.controller.spec.ts b/apps/backend/src/volunteers/volunteers.controller.spec.ts index a3cdb3daa..0dccf3090 100644 --- a/apps/backend/src/volunteers/volunteers.controller.spec.ts +++ b/apps/backend/src/volunteers/volunteers.controller.spec.ts @@ -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 }; + 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 }; + 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(); + }); + }); }); diff --git a/apps/backend/src/volunteers/volunteers.controller.ts b/apps/backend/src/volunteers/volunteers.controller.ts index d2c246fc8..cb7fbf8fa 100644 --- a/apps/backend/src/volunteers/volunteers.controller.ts +++ b/apps/backend/src/volunteers/volunteers.controller.ts @@ -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 { // return this.usersService.findOne(userId); // } + + @Patch('/:id/deactivate') + async deactivate( + @Param('id', ParseIntPipe) id: number, + ): Promise { + validateId(id, 'Volunteer'); + + return this.volunteersService.deactivate(id); + } + + @Patch('/:id/activate') + async activate( + @Param('id', ParseIntPipe) id: number, + ): Promise { + validateId(id, 'Volunteer'); + + return this.volunteersService.activate(id); + } } diff --git a/apps/backend/src/volunteers/volunteers.service.spec.ts b/apps/backend/src/volunteers/volunteers.service.spec.ts index fda546818..10888a681 100644 --- a/apps/backend/src/volunteers/volunteers.service.spec.ts +++ b/apps/backend/src/volunteers/volunteers.service.spec.ts @@ -1,18 +1,26 @@ import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; +import { NotFoundException } from '@nestjs/common'; import { VolunteersService } from './volunteers.service'; import { FosterVolunteer } from './volunteers.entity'; describe('VolunteersService', () => { let service: VolunteersService; + 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: [ VolunteersService, { provide: getRepositoryToken(FosterVolunteer), - useValue: {}, + useValue: repo, }, ], }).compile(); @@ -23,4 +31,63 @@ describe('VolunteersService', () => { it('should be defined', () => { expect(service).toBeDefined(); }); + + describe('deactivate', () => { + it('should set active to false and save the volunteer', async () => { + const volunteer = { volunteerId: 1, active: true } as FosterVolunteer; + repo.findOne.mockResolvedValue(volunteer); + repo.save.mockResolvedValue({ ...volunteer, active: false }); + + const result = await service.deactivate(1); + + expect(repo.findOne).toHaveBeenCalledWith({ + where: { volunteerId: 1 }, + }); + expect(repo.save).toHaveBeenCalledWith({ ...volunteer, active: false }); + expect(result.active).toBe(false); + }); + + it('should throw NotFoundException if volunteer does not exist', async () => { + repo.findOne.mockResolvedValue(null); + + await expect(service.deactivate(999)).rejects.toThrow( + new NotFoundException('Volunteer with ID 999 not found'), + ); + expect(repo.save).not.toHaveBeenCalled(); + }); + it('should not delete the volunteer record', async () => { + const volunteer = { volunteerId: 1, active: true } as FosterVolunteer; + repo.findOne.mockResolvedValue(volunteer); + repo.save.mockResolvedValue({ ...volunteer, active: false }); + + await service.deactivate(1); + + expect(repo.delete).not.toHaveBeenCalled(); + }); + }); + + describe('activate', () => { + it('should set active to true and save the volunteer', async () => { + const volunteer = { volunteerId: 1, active: false } as FosterVolunteer; + repo.findOne.mockResolvedValue(volunteer); + repo.save.mockResolvedValue({ ...volunteer, active: true }); + + const result = await service.activate(1); + + expect(repo.findOne).toHaveBeenCalledWith({ + where: { volunteerId: 1 }, + }); + expect(repo.save).toHaveBeenCalledWith({ ...volunteer, active: true }); + expect(result.active).toBe(true); + }); + + it('should throw NotFoundException if volunteer does not exist', async () => { + repo.findOne.mockResolvedValue(null); + + await expect(service.activate(999)).rejects.toThrow( + new NotFoundException('Volunteer with ID 999 not found'), + ); + expect(repo.save).not.toHaveBeenCalled(); + }); + }); }); diff --git a/apps/backend/src/volunteers/volunteers.service.ts b/apps/backend/src/volunteers/volunteers.service.ts index 1c6f17a61..1a9667a68 100644 --- a/apps/backend/src/volunteers/volunteers.service.ts +++ b/apps/backend/src/volunteers/volunteers.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { FosterVolunteer } from './volunteers.entity'; @@ -26,4 +26,28 @@ export class VolunteersService { // return this.repo.save(user); // } + + async deactivate(id: number): Promise { + const volunteer = await this.repo.findOne({ where: { volunteerId: id } }); + + if (!volunteer) { + throw new NotFoundException(`Volunteer with ID ${id} not found`); + } + + volunteer.active = false; + + return this.repo.save(volunteer); + } + + async activate(id: number): Promise { + const volunteer = await this.repo.findOne({ where: { volunteerId: id } }); + + if (!volunteer) { + throw new NotFoundException(`Volunteer with ID ${id} not found`); + } + + volunteer.active = true; + + return this.repo.save(volunteer); + } }