From 64c47a2d531a65dfcd771433515ebcb9f521bddc Mon Sep 17 00:00:00 2001 From: WesleyR10 Date: Thu, 28 Dec 2023 15:28:25 -0300 Subject: [PATCH] Implementing method adoption and test e2e --- src/app.ts | 2 + .../controllers/adoption/adoption.spec.ts | 55 +++++++++++++++++++ src/http/controllers/adoption/adoption.ts | 30 ++++++++++ src/http/controllers/adoption/routes.ts | 11 ++++ src/http/controllers/pets/filterPet.spec.ts | 3 - src/http/controllers/pets/filterPet.ts | 10 +++- src/http/controllers/pets/register.ts | 6 +- .../in-memory/in-memory-pets-repository.ts | 14 ++--- src/repositories/pets-repository.ts | 2 +- .../prisma/prisma-pets-repository.ts | 12 +++- src/use-cases/adoptions.ts | 2 +- .../filter-pets-by-characteristics.spec.ts | 8 --- .../filter-pets-by-characteristics.ts | 28 ++++++++-- .../test/create-and-authenticate-user.ts | 5 +- 14 files changed, 152 insertions(+), 36 deletions(-) create mode 100644 src/http/controllers/adoption/adoption.spec.ts create mode 100644 src/http/controllers/adoption/adoption.ts create mode 100644 src/http/controllers/adoption/routes.ts diff --git a/src/app.ts b/src/app.ts index b72c4b6..d7e190a 100644 --- a/src/app.ts +++ b/src/app.ts @@ -6,6 +6,7 @@ import { env } from '@/env' import { usersRoutes } from "@/http/controllers/users/routes"; import { orgsRoutes } from "./http/controllers/orgs/routes"; import { petsRoutes } from "./http/controllers/pets/routes"; +import { adoptionsRoutes } from "./http/controllers/adoption/routes"; export const app = fastify() @@ -24,6 +25,7 @@ app.register(fastifyCookie) app.register(usersRoutes) app.register(orgsRoutes) app.register(petsRoutes) +app.register(adoptionsRoutes) app.setErrorHandler((error, _, reply) => { if (error instanceof ZodError) { diff --git a/src/http/controllers/adoption/adoption.spec.ts b/src/http/controllers/adoption/adoption.spec.ts new file mode 100644 index 0000000..93c67ee --- /dev/null +++ b/src/http/controllers/adoption/adoption.spec.ts @@ -0,0 +1,55 @@ +import request from 'supertest' +import { app } from '@/app' +import { afterAll, beforeAll, describe, expect, it } from 'vitest' +import { createAndAuthenticateOrgs } from '@/utils/test/create-and-authenticate-orgs' +import { createAndAuthenticateUser } from '@/utils/test/create-and-authenticate-user' + +describe('Adoption Pets (e2e)', () => { + beforeAll(async () => { + await app.ready() + }) + + afterAll(async () => { + await app.close() + }) + + it('should be able to adoption pets and after adopting pets appear as unavailable', async () => { + const { token: tokenUser, userId } = await createAndAuthenticateUser(app, true) // true = isAdmin + const { token: tokenOrg, orgId } = await createAndAuthenticateOrgs(app, true) // true = isAdmin + + const pet = await request(app.server) + .post(`/orgs/${orgId}/pets`) + .set('Authorization', `Bearer ${tokenOrg}`) + .send({ + animalType: 'dog', + name: 'max', + breed: 'pit-bull', + size: 'large', + age: 1, + }) + + const petId = pet.body.registeredPet.pet.id + + const response = await request(app.server) + .post(`/${userId}/${petId}/adoption`) + .set('Authorization', `Bearer ${tokenUser}`) + .send() + + expect(response.statusCode).toEqual(201) + expect(response.body).toEqual( + expect.objectContaining({phone: expect.any(String)}), + ) + + const responseFilter = await request(app.server) + .get('/pets/filter') + .query({ + animalType: 'dog', + breed: 'pit-bull', + age: 1, + }) + .set('Authorization', `Bearer ${tokenUser}`) + .send() + + expect(responseFilter.statusCode).toEqual(400) + }) +}) \ No newline at end of file diff --git a/src/http/controllers/adoption/adoption.ts b/src/http/controllers/adoption/adoption.ts new file mode 100644 index 0000000..8887b61 --- /dev/null +++ b/src/http/controllers/adoption/adoption.ts @@ -0,0 +1,30 @@ +import { FastifyReply, FastifyRequest } from 'fastify' +import { z } from "zod" +import { makeAdoptionsUseCase } from '@/use-cases/factories/make-adoptions-use-case' +import { PetNotFoundError } from '@/use-cases/errors/pet-not-found' + +export async function Adoption(request: FastifyRequest, reply: FastifyReply) { + const adoptionParamsSchema = z.object({ + userId: z.string().uuid(), + petId: z.string().uuid(), + }) + + try { + const { userId, petId } = adoptionParamsSchema.parse(request.params) + + const adoptionPetUseCase = makeAdoptionsUseCase() + + const adoptionDetails = await adoptionPetUseCase.execute({ + userId, + petId, + }) + + return reply.status(201).send(adoptionDetails) + } catch (err) { + if(err instanceof PetNotFoundError){ + return reply.status(409).send({ message: err.message}) + } + + throw err + } +} \ No newline at end of file diff --git a/src/http/controllers/adoption/routes.ts b/src/http/controllers/adoption/routes.ts new file mode 100644 index 0000000..e6446ac --- /dev/null +++ b/src/http/controllers/adoption/routes.ts @@ -0,0 +1,11 @@ +import { FastifyInstance } from 'fastify' + +import { verifyJWT } from '@/http/middlewares/verify-jwt' + +import { Adoption } from './adoption' + +export async function adoptionsRoutes(app: FastifyInstance) { + app.addHook('onRequest', verifyJWT) + + app.post('/:userId/:petId/adoption', Adoption) +} \ No newline at end of file diff --git a/src/http/controllers/pets/filterPet.spec.ts b/src/http/controllers/pets/filterPet.spec.ts index d348352..1560b85 100644 --- a/src/http/controllers/pets/filterPet.spec.ts +++ b/src/http/controllers/pets/filterPet.spec.ts @@ -40,11 +40,9 @@ describe('Filter Pets (e2e)', () => { const response = await request(app.server) .get('/pets/filter') .query({ - data: { animalType: 'dog', breed: 'pit-bull', age: 1, - } }) .set('Authorization', `Bearer ${token}`) .send() @@ -52,7 +50,6 @@ describe('Filter Pets (e2e)', () => { expect(response.statusCode).toEqual(200) expect(response.body.pets).toHaveLength(2) - console.log(response.body.pets) expect(response.body.pets).toEqual([ expect.objectContaining( {breed: 'pit-bull',}), expect.objectContaining( {breed: 'pit-bull',}), diff --git a/src/http/controllers/pets/filterPet.ts b/src/http/controllers/pets/filterPet.ts index 246e46c..f056930 100644 --- a/src/http/controllers/pets/filterPet.ts +++ b/src/http/controllers/pets/filterPet.ts @@ -7,27 +7,31 @@ export async function filter(request: FastifyRequest, reply: FastifyReply) { animalType: z.string().optional(), breed: z.string().optional(), size: z.string().optional(), - age: z.number().optional(), + age: z.coerce.number().optional(), name: z.string().optional(), available: z.boolean().optional(), }) + try { const { animalType, breed, age, name, size, available } = filterPetsQuerySchema.parse(request.query) const filterPetsUseCase = makeFilterPetByCharacteristicsUseCase() const { pets } = await filterPetsUseCase.execute({ - data: { animalType, breed, size, age, name, available, - } }) return reply.status(200).send({ pets, }) +} catch (error) { + return reply.status(400).send({ + message: 'Erro na validação dos parâmetros', + }) +} } \ No newline at end of file diff --git a/src/http/controllers/pets/register.ts b/src/http/controllers/pets/register.ts index de07e5f..a931882 100644 --- a/src/http/controllers/pets/register.ts +++ b/src/http/controllers/pets/register.ts @@ -21,7 +21,7 @@ export async function registerPets(request: FastifyRequest, reply: FastifyReply) const registerPetUseCase = makeRegisterPetUseCase() - await registerPetUseCase.execute({ + const registeredPet = await registerPetUseCase.execute({ animalType, name, breed, @@ -29,6 +29,8 @@ export async function registerPets(request: FastifyRequest, reply: FastifyReply) age, orgId, }) + return reply.status(201).send({registeredPet}) + } catch (err) { if(err instanceof OrgNotFoundError){ return reply.status(409).send({ message: err.message}) @@ -36,6 +38,4 @@ export async function registerPets(request: FastifyRequest, reply: FastifyReply) throw err } - - return reply.status(201).send() } \ No newline at end of file diff --git a/src/repositories/in-memory/in-memory-pets-repository.ts b/src/repositories/in-memory/in-memory-pets-repository.ts index 20df227..d9ba04c 100644 --- a/src/repositories/in-memory/in-memory-pets-repository.ts +++ b/src/repositories/in-memory/in-memory-pets-repository.ts @@ -53,16 +53,16 @@ export class InMemoryPetsRepository implements PetsRepository { return pet } - async filterPetByCharacteristics(data: Prisma.PetWhereInput): Promise { + async filterPetByCharacteristics(animalType: string | undefined, breed: string | undefined, size: string | undefined, age: number | undefined, name: string | undefined, available:boolean | undefined): Promise { const filteredPets = this.items.filter((pet) => { - const matchAvailable = data.available === null || data.available === undefined || pet.available === data.available; + const matchAvailable = available === null || available === undefined || pet.available === available; if (pet.available && matchAvailable) { - const matchAnimalType = !data.animalType || pet.animalType === data.animalType; - const matchName = !data.name || pet.name === data.name; - const matchBreed = !data.breed || pet.breed === data.breed; - const matchSize = !data.size || pet.size === data.size; - const matchAge = data.age === undefined || data.age === null || pet.age === data.age; + const matchAnimalType = !animalType || pet.animalType === animalType; + const matchName = !name || pet.name === name; + const matchBreed = !breed || pet.breed === breed; + const matchSize = !size || pet.size === size; + const matchAge = age === undefined || age === null || pet.age === age; return matchAnimalType && matchName && matchBreed && matchSize && matchAge; } diff --git a/src/repositories/pets-repository.ts b/src/repositories/pets-repository.ts index 69d62b8..d618ed1 100644 --- a/src/repositories/pets-repository.ts +++ b/src/repositories/pets-repository.ts @@ -3,6 +3,6 @@ import { Prisma, Pet } from '@prisma/client' export interface PetsRepository { findById(id: string): Promise create(data: Prisma.PetUncheckedCreateInput): Promise - filterPetByCharacteristics(data: Prisma.PetWhereInput): Promise + filterPetByCharacteristics( animalType: string | undefined, breed: string | undefined, size: string | undefined, age: number | undefined, name: string | undefined, available:boolean | undefined ): Promise updateAvailability(petId: string, available: boolean): Promise; } \ No newline at end of file diff --git a/src/repositories/prisma/prisma-pets-repository.ts b/src/repositories/prisma/prisma-pets-repository.ts index 47c431e..50025bd 100644 --- a/src/repositories/prisma/prisma-pets-repository.ts +++ b/src/repositories/prisma/prisma-pets-repository.ts @@ -19,9 +19,16 @@ export class PrismaPetsRepository implements PetsRepository { return pet } - async filterPetByCharacteristics(data: Prisma.PetWhereInput){ + async filterPetByCharacteristics(animalType: string, breed: string, size: string, age: number, name: string, available:boolean){ const pets = await prisma.pet.findMany({ - where: data, + where: { + animalType, + breed, + size, + age, + name, + available, + }, }) return pets } @@ -32,5 +39,4 @@ export class PrismaPetsRepository implements PetsRepository { data: { available }, }); } - } \ No newline at end of file diff --git a/src/use-cases/adoptions.ts b/src/use-cases/adoptions.ts index 077210a..421cc27 100644 --- a/src/use-cases/adoptions.ts +++ b/src/use-cases/adoptions.ts @@ -38,7 +38,7 @@ export class AdoptionUseCase { }) await this.petsRepository.updateAvailability(petId, false); - + const getOrgPhone = await this.orgsRepository.findById(pet.org_id) if (!getOrgPhone) { diff --git a/src/use-cases/filter-pets-by-characteristics.spec.ts b/src/use-cases/filter-pets-by-characteristics.spec.ts index a66d477..34bb507 100644 --- a/src/use-cases/filter-pets-by-characteristics.spec.ts +++ b/src/use-cases/filter-pets-by-characteristics.spec.ts @@ -67,14 +67,12 @@ describe('Filter Pet By Characteristics Use Case', () => { }); const { pets } = await sut.execute({ - data: { animalType: 'Dog', name: 'Max', breed: 'Labrador', size: 'Large', available: true, age: 3, - } }) @@ -125,10 +123,8 @@ describe('Filter Pet By Characteristics Use Case', () => { await expect(() => sut.execute({ - data: { animalType: 'Dog', // Possui o tipo dog mas o available é false - } }), ).rejects.toBeInstanceOf(FilterByPetError) }) @@ -185,10 +181,8 @@ describe('Filter Pet By Characteristics Use Case', () => { }); const { pets } = await sut.execute({ - data: { animalType: 'Dog', // Adicione outras características para filtrar - } }) expect(pets).toHaveLength(2) @@ -241,9 +235,7 @@ describe('Filter Pet By Characteristics Use Case', () => { await expect(() => sut.execute({ - data: { animalType: 'monkey', // Característica que não existe - } }), ).rejects.toBeInstanceOf(FilterByPetError) }) diff --git a/src/use-cases/filter-pets-by-characteristics.ts b/src/use-cases/filter-pets-by-characteristics.ts index d494594..034e724 100644 --- a/src/use-cases/filter-pets-by-characteristics.ts +++ b/src/use-cases/filter-pets-by-characteristics.ts @@ -1,9 +1,14 @@ import { PetsRepository } from '@/repositories/pets-repository'; -import { Pet, Prisma } from '@prisma/client' +import { Pet } from '@prisma/client' import { FilterByPetError } from './errors/filter-by-pet-error'; interface FilterPetsUseCaseRequest { - data: Prisma.PetWhereInput + animalType?: string; + breed?: string; + size?: string; + age?: number; + name?: string; + available?: boolean; } interface FilterPetsUseCaseResponse { @@ -14,11 +19,24 @@ export class FilterPetByCharacteristicsUseCase { constructor(private petsRepository: PetsRepository) {} async execute({ - data + animalType, + breed, + size, + age, + name, + available, }: FilterPetsUseCaseRequest): Promise { - const pets = await this.petsRepository.filterPetByCharacteristics(data) + const pets = await this.petsRepository.filterPetByCharacteristics( + animalType , + breed, + size, + age, + name, + available,) - if (pets.length <= 0 ) { + const petAvailable = pets.some(pet => pet.available === true); + + if (pets.length <= 0 || !petAvailable ) { throw new FilterByPetError() } diff --git a/src/utils/test/create-and-authenticate-user.ts b/src/utils/test/create-and-authenticate-user.ts index 27acdca..b78f88b 100644 --- a/src/utils/test/create-and-authenticate-user.ts +++ b/src/utils/test/create-and-authenticate-user.ts @@ -7,7 +7,7 @@ import request from 'supertest' app: FastifyInstance, isAdmin = false, ) { - await prisma.user.create({ + const user = await prisma.user.create({ data: { name: 'John Doe', email: 'johndoe@example.com', @@ -22,6 +22,7 @@ import request from 'supertest' }) const { token } = authResponse.body + const userId = user.id - return {token} + return {token, userId} } \ No newline at end of file