From 9bcbd648a2ff735c277eead777afeca0bf1858c2 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:21:11 -0300 Subject: [PATCH 01/36] feat: add image field in Load Guardian By Id Repository Result --- src/data/protocols/db/guardian/load-guardian-by-id-repository.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/protocols/db/guardian/load-guardian-by-id-repository.ts b/src/data/protocols/db/guardian/load-guardian-by-id-repository.ts index ff6c5a08..54908441 100644 --- a/src/data/protocols/db/guardian/load-guardian-by-id-repository.ts +++ b/src/data/protocols/db/guardian/load-guardian-by-id-repository.ts @@ -16,5 +16,6 @@ export namespace LoadGuardianByIdRepository { verificationToken: string verificationTokenCreatedAt: Date emailConfirmation: boolean + image: string } | null } From 332e159c786aefc020bbb429375365c4f6e1666c Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:23:43 -0300 Subject: [PATCH 02/36] feat: add update guardian repository protocol --- src/data/protocols/db/guardian/index.ts | 1 + .../db/guardian/update-guardian-repository.ts | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/data/protocols/db/guardian/update-guardian-repository.ts diff --git a/src/data/protocols/db/guardian/index.ts b/src/data/protocols/db/guardian/index.ts index a7b35cd9..c8024d28 100644 --- a/src/data/protocols/db/guardian/index.ts +++ b/src/data/protocols/db/guardian/index.ts @@ -8,3 +8,4 @@ export * from './update-guardian-password-repository' export * from './save-token-repository' export * from './update-email-confirmation-repository' export * from './update-guardian-image-repository' +export * from './update-guardian-repository' diff --git a/src/data/protocols/db/guardian/update-guardian-repository.ts b/src/data/protocols/db/guardian/update-guardian-repository.ts new file mode 100644 index 00000000..e3c9ac0a --- /dev/null +++ b/src/data/protocols/db/guardian/update-guardian-repository.ts @@ -0,0 +1,23 @@ +export interface UpdateGuardianRepository { + update: (params: UpdateGuardianRepository.Params) => Promise +} + +export namespace UpdateGuardianRepository { + export type Params = { + guardianId: string + firstName?: string + lastName?: string + phone?: string + image?: string + } + + export type Result = { + id: string + firstName: string + lastName: string + email: string + phone: string + image: string + } | undefined + +} From 99ed15ac181132f449b26f5ebe791fc31f59e0ca Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:26:18 -0300 Subject: [PATCH 03/36] feat: create update guardian use case --- src/domain/use-cases/guardian/index.ts | 1 + .../use-cases/guardian/update-guardian.ts | 26 +++++++++++++++++++ src/domain/use-cases/index.ts | 1 + 3 files changed, 28 insertions(+) create mode 100644 src/domain/use-cases/guardian/index.ts create mode 100644 src/domain/use-cases/guardian/update-guardian.ts diff --git a/src/domain/use-cases/guardian/index.ts b/src/domain/use-cases/guardian/index.ts new file mode 100644 index 00000000..36269a0e --- /dev/null +++ b/src/domain/use-cases/guardian/index.ts @@ -0,0 +1 @@ +export * from './update-guardian' diff --git a/src/domain/use-cases/guardian/update-guardian.ts b/src/domain/use-cases/guardian/update-guardian.ts new file mode 100644 index 00000000..2aa099f7 --- /dev/null +++ b/src/domain/use-cases/guardian/update-guardian.ts @@ -0,0 +1,26 @@ +import { type DeleteFileStorage, type FileStorage, type LoadGuardianByIdRepository, type UpdateGuardianRepository } from '@/data/protocols' + +export interface UpdateGuardian { + update: (params: UpdateGuardian.Params) => Promise +} + +export namespace UpdateGuardian { + export type Params = { + guardianId: string + firstName?: string + lastName?: string + phone?: string + image?: Buffer | null + } + + export type Result = { + isSuccess: boolean + error?: Error + data?: UpdateGuardianRepository.Result + } + + export type Dependencies = { + guardianRepository: LoadGuardianByIdRepository & UpdateGuardianRepository + fileStorage: FileStorage & DeleteFileStorage + } +} diff --git a/src/domain/use-cases/index.ts b/src/domain/use-cases/index.ts index 98ae3629..b12e6419 100644 --- a/src/domain/use-cases/index.ts +++ b/src/domain/use-cases/index.ts @@ -11,3 +11,4 @@ export * from './send-email' export * from './scheduler' export * from './tasks' export * from './settings' +export * from './guardian' From 954cd1ae787a78a60d87f2b786d750d73eb37b71 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:29:24 -0300 Subject: [PATCH 04/36] feat: add update method in guardian repository --- .../postgresql/guardian-account-repository.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/infra/repos/postgresql/guardian-account-repository.ts b/src/infra/repos/postgresql/guardian-account-repository.ts index b86bcfb5..a09fba74 100644 --- a/src/infra/repos/postgresql/guardian-account-repository.ts +++ b/src/infra/repos/postgresql/guardian-account-repository.ts @@ -9,6 +9,7 @@ import { type UpdateVerificationTokenRepository, type UpdateEmailConfirmationRepository } from '@/data/protocols' +import { type UpdateGuardianRepository } from '@/data/protocols/db/guardian/update-guardian-repository' export class GuardianAccountRepository implements AddGuardianRepository, LoadGuardianByEmailRepository, @@ -17,7 +18,8 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository, UpdateGuardianPasswordRepository, UpdateVerificationTokenRepository, UpdateEmailConfirmationRepository, - UpdateGuardianImageRepository { + UpdateGuardianImageRepository, + UpdateGuardianRepository { async add ( guardianData: AddGuardianRepository.Params ): Promise { @@ -153,4 +155,19 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository, return result } + + async update (params: UpdateGuardianRepository.Params): Promise { + const { guardianId, ...updateData } = params + const result = await db.guardian.update({ + where: { id: guardianId }, + data: updateData, + omit: { + password: true, + verificationToken: true, + verificationTokenCreatedAt: true + } + }) + + return result + } } From 16defedf005981aa467bf93d82a1d263bed796a3 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:33:47 -0300 Subject: [PATCH 05/36] feat: implemented update guardian use case --- .../use-cases/guardian/db-update-guardian.ts | 55 +++++++++++++++++++ src/data/use-cases/guardian/index.ts | 1 + src/data/use-cases/index.ts | 1 + 3 files changed, 57 insertions(+) create mode 100644 src/data/use-cases/guardian/db-update-guardian.ts create mode 100644 src/data/use-cases/guardian/index.ts diff --git a/src/data/use-cases/guardian/db-update-guardian.ts b/src/data/use-cases/guardian/db-update-guardian.ts new file mode 100644 index 00000000..9165fac7 --- /dev/null +++ b/src/data/use-cases/guardian/db-update-guardian.ts @@ -0,0 +1,55 @@ +import { NotAcceptableError } from '@/application/errors' +import { type DeleteFileStorage, type FileStorage, type LoadGuardianByIdRepository, type UpdateGuardianRepository } from '@/data/protocols' +import { type UpdateGuardian } from '@/domain/use-cases' + +export class DbUpdateGuardian implements UpdateGuardian { + private readonly guardianRepository: LoadGuardianByIdRepository & UpdateGuardianRepository + private readonly fileStorage: FileStorage & DeleteFileStorage + + constructor ({ guardianRepository, fileStorage }: UpdateGuardian.Dependencies) { + this.guardianRepository = guardianRepository + this.fileStorage = fileStorage + } + + async update (guardianData: UpdateGuardian.Params): Promise { + const guardian = await this.guardianRepository.loadById(guardianData.guardianId) + if (!guardian) { + return { + isSuccess: false, + error: new NotAcceptableError('userId') + } + } + + let finalImage: string = '' + if (guardianData.image) { + const currentImage = guardian.image + const updatedImage = await this.fileStorage.save({ file: guardianData.image, fileName: `images/guardian-${guardian?.id}-${Math.trunc(Date.now() / 1000)}` }) + if (!updatedImage) { + return { + isSuccess: false, + error: new NotAcceptableError('update image failed') + } + } + if (currentImage) { + await this.fileStorage.delete({ fileUrlOrPath: currentImage }) + } + finalImage = updatedImage + } + if (!guardianData.image) { + finalImage = guardian.image + } + + const guardianUpdateResult = await this.guardianRepository.update({ + guardianId: guardian.id, + firstName: guardianData.firstName ? guardianData.firstName : guardian.firstName, + lastName: guardianData.lastName ? guardianData.lastName : guardian.lastName, + phone: guardianData.phone ? guardianData.phone : guardian.phone, + image: finalImage + }) + + return { + isSuccess: true, + data: guardianUpdateResult + } + } +} diff --git a/src/data/use-cases/guardian/index.ts b/src/data/use-cases/guardian/index.ts new file mode 100644 index 00000000..a495c45b --- /dev/null +++ b/src/data/use-cases/guardian/index.ts @@ -0,0 +1 @@ +export * from './db-update-guardian' diff --git a/src/data/use-cases/index.ts b/src/data/use-cases/index.ts index 82f1a712..42900d08 100644 --- a/src/data/use-cases/index.ts +++ b/src/data/use-cases/index.ts @@ -11,3 +11,4 @@ export * from './db-send-email' export * from './scheduler' export * from './tasks' export * from './settings' +export * from './guardian' From 4d8133ec8afde6b52a4f9403e1db4d6ca5d66c8d Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:36:11 -0300 Subject: [PATCH 06/36] feat: add update guardian controller --- src/application/controllers/guardian/index.ts | 1 + .../controllers/guardian/update-guardian.ts | 45 +++++++++++++++++++ src/application/controllers/index.ts | 1 + 3 files changed, 47 insertions(+) create mode 100644 src/application/controllers/guardian/index.ts create mode 100644 src/application/controllers/guardian/update-guardian.ts diff --git a/src/application/controllers/guardian/index.ts b/src/application/controllers/guardian/index.ts new file mode 100644 index 00000000..36269a0e --- /dev/null +++ b/src/application/controllers/guardian/index.ts @@ -0,0 +1 @@ +export * from './update-guardian' diff --git a/src/application/controllers/guardian/update-guardian.ts b/src/application/controllers/guardian/update-guardian.ts new file mode 100644 index 00000000..17dde1b1 --- /dev/null +++ b/src/application/controllers/guardian/update-guardian.ts @@ -0,0 +1,45 @@ +import { badRequest, notAcceptable, serverError, success, type HttpRequest, type HttpResponse } from '@/application/helpers' +import { type Controller, type Validation } from '@/application/protocols' +import { type UpdateGuardian } from '@/domain/use-cases' + +export class UpdateGuardianController implements Controller { + private readonly validation: Validation + private readonly UpdateGuardian: UpdateGuardian + + constructor ({ validation, updateGuardian }: UpdateGuardianController.Dependencies) { + this.validation = validation + this.UpdateGuardian = updateGuardian + } + + async handle (httpRequest: HttpRequest): Promise { + try { + const error = this.validation.validate({ ...httpRequest.body, ...httpRequest.params }) + if (error) { + return badRequest(error) + } + const { guardianId } = httpRequest.params + const image = httpRequest.file ?? null + const updateData = { ...httpRequest.body, guardianId, image } + const result = await this.UpdateGuardian.update(updateData) + if (!result.isSuccess) { + return notAcceptable(result.error as Error) + } + return success({ + id: result.data?.id, + firstName: result.data?.firstName, + lastName: result.data?.lastName, + phone: result.data?.phone, + image: result.data?.image + }) + } catch (error) { + return serverError(error as Error) + } + } +} + +export namespace UpdateGuardianController { + export interface Dependencies { + validation: Validation + updateGuardian: UpdateGuardian + } +} diff --git a/src/application/controllers/index.ts b/src/application/controllers/index.ts index 23f30a88..de3b9278 100644 --- a/src/application/controllers/index.ts +++ b/src/application/controllers/index.ts @@ -9,3 +9,4 @@ export * from './email-confirmation' export * from './scheduler' export * from './tasks' export * from './settings' +export * from './guardian' From 0cb95eed1d9638ac85e9980964cb22e271850168 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:40:53 -0300 Subject: [PATCH 07/36] feat: add update guardian use case factory --- .../guardian/db-update-guardian-factory.ts | 15 +++++++++++++++ src/main/factories/usecases/guardian/index.ts | 1 + src/main/factories/usecases/index.ts | 1 + 3 files changed, 17 insertions(+) create mode 100644 src/main/factories/usecases/guardian/db-update-guardian-factory.ts create mode 100644 src/main/factories/usecases/guardian/index.ts diff --git a/src/main/factories/usecases/guardian/db-update-guardian-factory.ts b/src/main/factories/usecases/guardian/db-update-guardian-factory.ts new file mode 100644 index 00000000..44e7f47a --- /dev/null +++ b/src/main/factories/usecases/guardian/db-update-guardian-factory.ts @@ -0,0 +1,15 @@ +import { DbUpdateGuardian } from '@/data/use-cases' +import { type UpdateGuardian } from '@/domain/use-cases' +import { FirebaseStorageAdapter } from '@/infra/repos/firebase' +import { GuardianAccountRepository } from '@/infra/repos/postgresql' +import env from '@/main/config/env' + +export const makeDbUpdateGuardian = (): UpdateGuardian => { + const guardianRepository = new GuardianAccountRepository() + const fileStorage = new FirebaseStorageAdapter(env.firebase.projectId, env.firebase.storageBucket, env.firebase.serviceAccount) + const dependencies: UpdateGuardian.Dependencies = { + guardianRepository, + fileStorage + } + return new DbUpdateGuardian(dependencies) +} diff --git a/src/main/factories/usecases/guardian/index.ts b/src/main/factories/usecases/guardian/index.ts new file mode 100644 index 00000000..9ab66515 --- /dev/null +++ b/src/main/factories/usecases/guardian/index.ts @@ -0,0 +1 @@ +export * from './db-update-guardian-factory' diff --git a/src/main/factories/usecases/index.ts b/src/main/factories/usecases/index.ts index 782c9e2f..65ddf602 100644 --- a/src/main/factories/usecases/index.ts +++ b/src/main/factories/usecases/index.ts @@ -11,3 +11,4 @@ export * from './db-send-email-factory' export * from './scheduler' export * from './tasks' export * from './settings' +export * from './guardian' From 19b52a7704fe49fd6ead26ec0bf4cd60682e8b12 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:47:45 -0300 Subject: [PATCH 08/36] feat: add update guardian controller factory --- .../factories/controllers/guardian/index.ts | 1 + .../guardian/update-guardian-factory.ts | 19 +++++++++++++++++++ src/main/factories/controllers/index.ts | 1 + 3 files changed, 21 insertions(+) create mode 100644 src/main/factories/controllers/guardian/index.ts create mode 100644 src/main/factories/controllers/guardian/update-guardian-factory.ts diff --git a/src/main/factories/controllers/guardian/index.ts b/src/main/factories/controllers/guardian/index.ts new file mode 100644 index 00000000..55344c6c --- /dev/null +++ b/src/main/factories/controllers/guardian/index.ts @@ -0,0 +1 @@ +export * from './update-guardian-factory' diff --git a/src/main/factories/controllers/guardian/update-guardian-factory.ts b/src/main/factories/controllers/guardian/update-guardian-factory.ts new file mode 100644 index 00000000..30f15806 --- /dev/null +++ b/src/main/factories/controllers/guardian/update-guardian-factory.ts @@ -0,0 +1,19 @@ +import { UpdateGuardianController } from '@/application/controllers' +import { type Controller } from '@/application/protocols' +import { makeDbUpdateGuardian } from '../../usecases' +import { makeUpdateGuardianValidation } from '../../validations' +import { LoggerPgRepository } from '@/infra/repos/postgresql' +import { DevLoggerControllerDecorator, LoggerControllerDecorator } from '@/main/decorators' + +export const makeUpdateGuardianController = (): Controller => { + const updateGuardian = makeDbUpdateGuardian() + const validation = makeUpdateGuardianValidation() + const dependencies: UpdateGuardianController.Dependencies = { + updateGuardian, + validation + } + const updateGuardianController = new UpdateGuardianController(dependencies) + const loggerPgRepository = new LoggerPgRepository() + const loggerControllerDecorator = new LoggerControllerDecorator(updateGuardianController, loggerPgRepository) + return new DevLoggerControllerDecorator(loggerControllerDecorator) +} diff --git a/src/main/factories/controllers/index.ts b/src/main/factories/controllers/index.ts index 03d24f50..6f9bfa22 100644 --- a/src/main/factories/controllers/index.ts +++ b/src/main/factories/controllers/index.ts @@ -9,3 +9,4 @@ export * from './email-confirmation-factory' export * from './scheduler' export * from './tasks' export * from './settings' +export * from './guardian' From 67532958db8ce67ee4213a25878221e1102e025a Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:51:25 -0300 Subject: [PATCH 09/36] feat: add update guardian validations factory --- src/main/factories/validations/guardian/index.ts | 1 + .../validations/guardian/update-guardian-factory.ts | 13 +++++++++++++ src/main/factories/validations/index.ts | 1 + 3 files changed, 15 insertions(+) create mode 100644 src/main/factories/validations/guardian/index.ts create mode 100644 src/main/factories/validations/guardian/update-guardian-factory.ts diff --git a/src/main/factories/validations/guardian/index.ts b/src/main/factories/validations/guardian/index.ts new file mode 100644 index 00000000..55344c6c --- /dev/null +++ b/src/main/factories/validations/guardian/index.ts @@ -0,0 +1 @@ +export * from './update-guardian-factory' diff --git a/src/main/factories/validations/guardian/update-guardian-factory.ts b/src/main/factories/validations/guardian/update-guardian-factory.ts new file mode 100644 index 00000000..aca3a7d8 --- /dev/null +++ b/src/main/factories/validations/guardian/update-guardian-factory.ts @@ -0,0 +1,13 @@ +import { type Validation } from '@/application/protocols' +import { NameValidation, OptionalFieldValidation, ValidationComposite } from '@/application/validation' +import { NameValidatorAdapter } from '@/infra/validators' + +export const makeUpdateGuardianValidation = (): ValidationComposite => { + const validations: Validation[] = [] + + validations.push(new OptionalFieldValidation('firstName', new NameValidation('firstName', new NameValidatorAdapter()))) + validations.push(new OptionalFieldValidation('lastName', new NameValidation('lastName', new NameValidatorAdapter()))) + validations.push(new OptionalFieldValidation('phone', new NameValidation('phone', new NameValidatorAdapter()))) + + return new ValidationComposite(validations) +} diff --git a/src/main/factories/validations/index.ts b/src/main/factories/validations/index.ts index 1bf94e87..d6af77cb 100644 --- a/src/main/factories/validations/index.ts +++ b/src/main/factories/validations/index.ts @@ -5,3 +5,4 @@ export * from './change-password-validation-factory' export * from './waiting-code-validation-factory' export * from './pet' export * from './scheduler' +export * from './guardian' From 1e38e01b2cc6b67e0974bbe8f8067b37a7428d38 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:55:25 -0300 Subject: [PATCH 10/36] feat: create update guardian route --- src/main/routes/guardian-routes.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/routes/guardian-routes.ts diff --git a/src/main/routes/guardian-routes.ts b/src/main/routes/guardian-routes.ts new file mode 100644 index 00000000..2f80fac6 --- /dev/null +++ b/src/main/routes/guardian-routes.ts @@ -0,0 +1,8 @@ +import { type Router } from 'express' +import { accountConfirmation, auth, upload } from '../middlewares' +import { adaptRoute } from '../adapters' +import { makeUpdateGuardianController } from '../factories' + +export default (router: Router): void => { + router.put('/guardian/:guardianId', auth, accountConfirmation, upload, adaptRoute(makeUpdateGuardianController())) +} From 2ab865b40922d0582c7071cab6bc0bf9532e46f5 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 10:57:48 -0300 Subject: [PATCH 11/36] test: add image field in guardian mock --- tests/utils/mocks/entities.mocks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/utils/mocks/entities.mocks.ts b/tests/utils/mocks/entities.mocks.ts index 8e609b6f..cafcce38 100644 --- a/tests/utils/mocks/entities.mocks.ts +++ b/tests/utils/mocks/entities.mocks.ts @@ -197,6 +197,7 @@ const mockFakeGuardianLoaded = (): Exclude Date: Mon, 9 Mar 2026 11:00:44 -0300 Subject: [PATCH 12/36] docs: add update guardian route in documentation --- src/main/docs/paths.ts | 4 +- src/main/docs/paths/guardian/index.ts | 1 + .../paths/guardian/update-guardian-path.ts | 42 +++++++++++++++++++ src/main/docs/paths/index.ts | 1 + 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/main/docs/paths/guardian/index.ts create mode 100644 src/main/docs/paths/guardian/update-guardian-path.ts diff --git a/src/main/docs/paths.ts b/src/main/docs/paths.ts index 5ff48e24..26958b5e 100644 --- a/src/main/docs/paths.ts +++ b/src/main/docs/paths.ts @@ -28,7 +28,8 @@ import { loadPetByIdPath, loadNextTasksByPetIdPath, loadPreviousTasksByPetIdPath, - loadNextTasksByPetIdAndTagIdPath + loadNextTasksByPetIdAndTagIdPath, + updateGuardianPath } from './paths/' export default { @@ -37,6 +38,7 @@ export default { '/forget-password': forgetPasswordPath, '/guardian/change-password': changePasswordPath, '/guardian/email-confirmation/{userId}': emailConfirmationPath, + '/guardian/{guardianId}': updateGuardianPath, '/waiting-code': waitingCodePath, '/pet': { ...petRegistryPath, ...loadPetsPath }, '/pet/{petId}': { ...loadPetByIdPath, ...updatePetPath, ...deletePetPath }, diff --git a/src/main/docs/paths/guardian/index.ts b/src/main/docs/paths/guardian/index.ts new file mode 100644 index 00000000..04c6b43d --- /dev/null +++ b/src/main/docs/paths/guardian/index.ts @@ -0,0 +1 @@ +export * from './update-guardian-path' diff --git a/src/main/docs/paths/guardian/update-guardian-path.ts b/src/main/docs/paths/guardian/update-guardian-path.ts new file mode 100644 index 00000000..85a4591a --- /dev/null +++ b/src/main/docs/paths/guardian/update-guardian-path.ts @@ -0,0 +1,42 @@ +import { DocBuilder } from '../../utils/doc-builder' + +export const updateGuardianPath = DocBuilder.putBuilder() + .addTags(['guardian']) + .addSummary('Update an existing guardian') + .addJwtAuthSecurity() + .addPathParameter('guardianId', 'Guardian ID', 'string') + .addMultipartFormDataBody({ + type: 'object', + properties: { + firstName: { + type: 'string', + example: 'John' + }, + lastName: { + type: 'string', + example: 'Doe' + }, + phone: { + type: 'string', + example: '11987654321' + }, + image: { + type: 'string', + format: 'binary' + } + } + }) + .addResponse(200, { + description: 'Success', + content: { + 'application/json': { + schema: { + $ref: '#/schemas/guardian' + } + } + } + }) + .addBadRequestResponse() + .addNotAcceptableResponse() + .addServerErrorResponse() + .build() diff --git a/src/main/docs/paths/index.ts b/src/main/docs/paths/index.ts index c715c1ad..873e2a91 100644 --- a/src/main/docs/paths/index.ts +++ b/src/main/docs/paths/index.ts @@ -13,3 +13,4 @@ export * from './email-confirmation-path' export * from './scheduler' export * from './tasks' export * from './settings' +export * from './guardian' From 9815d9e3d412899c6c75300743fec38bfadeb2c6 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 11:44:21 -0300 Subject: [PATCH 13/36] test: ensure return a guardian when image id updated --- .../repos/postgresql/guardian-account.spec.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/src/infra/repos/postgresql/guardian-account.spec.ts b/tests/src/infra/repos/postgresql/guardian-account.spec.ts index 37d628ff..00a0d686 100644 --- a/tests/src/infra/repos/postgresql/guardian-account.spec.ts +++ b/tests/src/infra/repos/postgresql/guardian-account.spec.ts @@ -218,4 +218,22 @@ describe('GuardianAccountRepository', () => { expect(response).toBe(true) }) }) + + describe('UpdateImage', () => { + it('Should return a guardian when image is updated', async () => { + const sut = makeSut() + const { id } = await sut.add(input) as any + const response = await sut.updateImage({ guardianId: id, image: 'any_image' }) + expect(response).toEqual({ + id: expect.any(String), + firstName: 'any_first_name', + lastName: 'any_last_name', + phone: 'any_phone', + email: 'any_email@gmail.com', + image: 'any_image', + emailConfirmation: false, + accessToken: 'any_token' + }) + }) + }) }) From 3201b21e3995df08b4d2b31df1abedc0d0bd606a Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 14:39:22 -0300 Subject: [PATCH 14/36] refactor: add a trycatch on updateImage method in guardian account repository --- .../postgresql/guardian-account-repository.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/infra/repos/postgresql/guardian-account-repository.ts b/src/infra/repos/postgresql/guardian-account-repository.ts index a09fba74..69e50d9d 100644 --- a/src/infra/repos/postgresql/guardian-account-repository.ts +++ b/src/infra/repos/postgresql/guardian-account-repository.ts @@ -142,18 +142,21 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository, } async updateImage (params: UpdateGuardianImageRepository.Params): Promise { - const { guardianId, image } = params - const result = await db.guardian.update({ - where: { id: guardianId }, - data: { image }, - omit: { - password: true, - verificationToken: true, - verificationTokenCreatedAt: true - } - }) - - return result + try { + const { guardianId, image } = params + const result = await db.guardian.update({ + where: { id: guardianId }, + data: { image }, + omit: { + password: true, + verificationToken: true, + verificationTokenCreatedAt: true + } + }) + return result + } catch (error) { + return undefined + } } async update (params: UpdateGuardianRepository.Params): Promise { From b81501109df5b945305822f851cecccc4ffc6b2a Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 14:40:02 -0300 Subject: [PATCH 15/36] test: ensure return undefined if a invalid guardian id is provided --- tests/src/infra/repos/postgresql/guardian-account.spec.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/src/infra/repos/postgresql/guardian-account.spec.ts b/tests/src/infra/repos/postgresql/guardian-account.spec.ts index 00a0d686..85ec3f45 100644 --- a/tests/src/infra/repos/postgresql/guardian-account.spec.ts +++ b/tests/src/infra/repos/postgresql/guardian-account.spec.ts @@ -235,5 +235,13 @@ describe('GuardianAccountRepository', () => { accessToken: 'any_token' }) }) + + it('Should return undefined if a invalid guardian id is provided', async () => { + const sut = makeSut() + await sut.add(input) as any + const invalidId = 'invalid_id' + const response = await sut.updateImage({ guardianId: invalidId, image: 'any_image' }) + expect(response).toBeUndefined() + }) }) }) From e918e39d2d5e14dfab24f425e6be22f73eb52709 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 14:56:48 -0300 Subject: [PATCH 16/36] refactor: removed unused field on result --- src/data/protocols/db/guardian/update-guardian-repository.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/data/protocols/db/guardian/update-guardian-repository.ts b/src/data/protocols/db/guardian/update-guardian-repository.ts index e3c9ac0a..2a193138 100644 --- a/src/data/protocols/db/guardian/update-guardian-repository.ts +++ b/src/data/protocols/db/guardian/update-guardian-repository.ts @@ -15,7 +15,6 @@ export namespace UpdateGuardianRepository { id: string firstName: string lastName: string - email: string phone: string image: string } | undefined From c36a27c215ac6f7a4348cb194f4d3aa3ddd41e97 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 14:57:29 -0300 Subject: [PATCH 17/36] refactor: add trycatch on updateImage and update methods --- .../postgresql/guardian-account-repository.ts | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/infra/repos/postgresql/guardian-account-repository.ts b/src/infra/repos/postgresql/guardian-account-repository.ts index 69e50d9d..6e293f98 100644 --- a/src/infra/repos/postgresql/guardian-account-repository.ts +++ b/src/infra/repos/postgresql/guardian-account-repository.ts @@ -150,7 +150,9 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository, omit: { password: true, verificationToken: true, - verificationTokenCreatedAt: true + verificationTokenCreatedAt: true, + emailConfirmation: true, + accessToken: true } }) return result @@ -160,17 +162,23 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository, } async update (params: UpdateGuardianRepository.Params): Promise { - const { guardianId, ...updateData } = params - const result = await db.guardian.update({ - where: { id: guardianId }, - data: updateData, - omit: { - password: true, - verificationToken: true, - verificationTokenCreatedAt: true - } - }) - - return result + try { + const { guardianId, ...updateData } = params + const result = await db.guardian.update({ + where: { id: guardianId }, + data: updateData, + omit: { + password: true, + verificationToken: true, + verificationTokenCreatedAt: true, + accessToken: true, + email: true, + emailConfirmation: true + } + }) + return result + } catch (error) { + return undefined + } } } From 692a69e5a71f0cb35b62239092a8223e30446c7c Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 14:58:09 -0300 Subject: [PATCH 18/36] test: ensure return a guardian when update success --- .../repos/postgresql/guardian-account.spec.ts | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/tests/src/infra/repos/postgresql/guardian-account.spec.ts b/tests/src/infra/repos/postgresql/guardian-account.spec.ts index 85ec3f45..dd10a191 100644 --- a/tests/src/infra/repos/postgresql/guardian-account.spec.ts +++ b/tests/src/infra/repos/postgresql/guardian-account.spec.ts @@ -230,18 +230,37 @@ describe('GuardianAccountRepository', () => { lastName: 'any_last_name', phone: 'any_phone', email: 'any_email@gmail.com', - image: 'any_image', - emailConfirmation: false, - accessToken: 'any_token' + image: 'any_image' }) }) it('Should return undefined if a invalid guardian id is provided', async () => { const sut = makeSut() - await sut.add(input) as any const invalidId = 'invalid_id' const response = await sut.updateImage({ guardianId: invalidId, image: 'any_image' }) expect(response).toBeUndefined() }) }) + + describe('Update', () => { + it('Should return a guardian when update success', async () => { + const sut = makeSut() + const guardian = await sut.add(input) + const updateGuardianInput = { + guardianId: guardian?.id as string, + firstName: input.firstName, + lastName: input.lastName, + phone: input.phone, + image: 'any_image' + } + const response = await sut.update(updateGuardianInput) + expect(response).toEqual({ + id: expect.any(String), + firstName: 'any_first_name', + lastName: 'any_last_name', + phone: 'any_phone', + image: 'any_image' + }) + }) + }) }) From b8f2a3dc298ab5b655b13bcd9e765cc911a401e1 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 15:26:26 -0300 Subject: [PATCH 19/36] test: ensure call loadById method with correct values --- .../guardian/db-update-guardian.spec.ts | 47 +++++++++++++++++++ tests/utils/mocks/entities.mocks.ts | 16 ++++++- tests/utils/stubs/service.stub.ts | 16 +++++-- 3 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 tests/src/data/use-cases/guardian/db-update-guardian.spec.ts diff --git a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts new file mode 100644 index 00000000..47c03157 --- /dev/null +++ b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts @@ -0,0 +1,47 @@ +import { type DeleteFileStorage, type FileStorage, type LoadGuardianByIdRepository, type UpdateGuardianRepository } from '@/data/protocols' +import { DbUpdateGuardian } from '@/data/use-cases' +import { type UpdateGuardian } from '@/domain/use-cases' +import { makeFakeFileStorage, makeFakeGuardianRepository } from '@/tests/utils' + +interface SutTypes { + sut: DbUpdateGuardian + guardianRepositoryStub: LoadGuardianByIdRepository & UpdateGuardianRepository + fileStorageStub: FileStorage & DeleteFileStorage +} + +const makeSut = (): SutTypes => { + const guardianRepositoryStub = makeFakeGuardianRepository() + const fileStorageStub = makeFakeFileStorage() + const dependencies: UpdateGuardian.Dependencies = { + guardianRepository: guardianRepositoryStub, + fileStorage: fileStorageStub + } + const sut = new DbUpdateGuardian(dependencies) + return { + sut, + guardianRepositoryStub, + fileStorageStub + } +} + +describe('DbUpdateGuardian use case', () => { + const params: UpdateGuardian.Params = { + guardianId: 'any_guardian_id', + firstName: 'any_first_name', + lastName: 'any_last_name', + phone: 'any_phone', + image: null + } + + describe('GuardianRepository', () => { + it('Should call loadById method with correct values', async () => { + const { sut, guardianRepositoryStub } = makeSut() + const loadByIdSpy = jest.spyOn(guardianRepositoryStub, 'loadById') + await sut.update(params) + expect(loadByIdSpy).toHaveBeenCalledWith(params.guardianId) + }) + }) + describe('FileStorage', () => { + + }) +}) diff --git a/tests/utils/mocks/entities.mocks.ts b/tests/utils/mocks/entities.mocks.ts index cafcce38..fe0d4ed7 100644 --- a/tests/utils/mocks/entities.mocks.ts +++ b/tests/utils/mocks/entities.mocks.ts @@ -11,7 +11,8 @@ import { type LoadPetByGuardianIdRepository, type LoadPetByIdRepository, type DeletePetByIdRepository, - type UpdateGuardianImageRepository + type UpdateGuardianImageRepository, + type UpdateGuardianRepository } from '@/data/protocols' import { type AppointPet } from '@/domain/use-cases' import { type Guardian } from '@/tests/utils/types' @@ -42,7 +43,7 @@ const mockFakeGuardianAdded = (): Exclude => { +const mockFakeGuardianImageUpdated = (): Exclude => { return { id: 'any_id', firstName: 'any_first_name', @@ -53,6 +54,16 @@ const mockFakeGuardianUpdated = (): Exclude => { + return { + id: 'any_id', + firstName: 'any_first_name', + lastName: 'any_last_name', + phone: 'any_phone', + image: 'any_image' + } +} + const mockFakePetAdded = (): AddPetRepository.Result => { return { id: 'any_id', @@ -209,6 +220,7 @@ export { makeFakeGuardianData, mockFakeGuardianAdded, mockFakeGuardianUpdated, + mockFakeGuardianImageUpdated, mockFakeGuardianLoaded, mockFakePetAdded, mockFakePetByGuardianIdLoaded, diff --git a/tests/utils/stubs/service.stub.ts b/tests/utils/stubs/service.stub.ts index 01a4e530..1dc3c105 100644 --- a/tests/utils/stubs/service.stub.ts +++ b/tests/utils/stubs/service.stub.ts @@ -8,7 +8,8 @@ import { mockFakePetByIdLoaded, mockFakePetUpdated, mockFakePetByIdDeleted, - mockFakeGuardianUpdated + mockFakeGuardianUpdated, + mockFakeGuardianImageUpdated } from '@/tests/utils' import { type EmailService, @@ -49,7 +50,8 @@ import { type LoadSettingsRepository, type UpdateSettingsRepository, type LoadNextTasksByPetIdAndTagIdRepository, - type UpdateGuardianImageRepository + type UpdateGuardianImageRepository, + type UpdateGuardianRepository } from '@/data/protocols' import { type LoadCatSizesRepository } from '@/data/protocols/db/size/load-cat-sizes-repository' import { type LoadDogSizesRepository } from '@/data/protocols/db/size/load-dog-sizes-repository' @@ -72,7 +74,8 @@ UpdateAccessTokenRepository & UpdateGuardianPasswordRepository & UpdateVerificationTokenRepository & UpdateEmailConfirmationRepository & -UpdateGuardianImageRepository => { +UpdateGuardianImageRepository & +UpdateGuardianRepository => { class GuardianRepositoryStub implements AddGuardianRepository, LoadGuardianByEmailRepository, @@ -81,7 +84,8 @@ UpdateGuardianImageRepository => { UpdateGuardianPasswordRepository, UpdateVerificationTokenRepository, UpdateEmailConfirmationRepository, - UpdateGuardianImageRepository { + UpdateGuardianImageRepository, + UpdateGuardianRepository { async add (guardian: AddGuardianRepository.Params): Promise { return mockFakeGuardianAdded() } @@ -111,6 +115,10 @@ UpdateGuardianImageRepository => { } async updateImage (params: UpdateGuardianImageRepository.Params): Promise { + return mockFakeGuardianImageUpdated() + } + + async update (params: UpdateGuardianRepository.Params): Promise { return mockFakeGuardianUpdated() } } From 5d2ea121caaf36e0df81a448aaf8f175c8705552 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 15:27:28 -0300 Subject: [PATCH 20/36] test: ensure return Not Acceptable Error if incorrect guardianId is provided --- .../use-cases/guardian/db-update-guardian.spec.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts index 47c03157..32a0be44 100644 --- a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts +++ b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts @@ -1,3 +1,4 @@ +import { NotAcceptableError } from '@/application/errors' import { type DeleteFileStorage, type FileStorage, type LoadGuardianByIdRepository, type UpdateGuardianRepository } from '@/data/protocols' import { DbUpdateGuardian } from '@/data/use-cases' import { type UpdateGuardian } from '@/domain/use-cases' @@ -40,6 +41,16 @@ describe('DbUpdateGuardian use case', () => { await sut.update(params) expect(loadByIdSpy).toHaveBeenCalledWith(params.guardianId) }) + + it('Should return Not Acceptable Error if incorrect guardianId is provided', async () => { + const { sut, guardianRepositoryStub } = makeSut() + jest.spyOn(guardianRepositoryStub, 'loadById').mockResolvedValueOnce(null) + const result = await sut.update(params) + expect(result).toEqual({ + isSuccess: false, + error: new NotAcceptableError('userId') + }) + }) }) describe('FileStorage', () => { From 68a69e9bee0d020a452a5c2c5d7594fb836d9c54 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 15:28:17 -0300 Subject: [PATCH 21/36] test: ensure throw if loadById method throws --- .../src/data/use-cases/guardian/db-update-guardian.spec.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts index 32a0be44..12cd0ade 100644 --- a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts +++ b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts @@ -51,6 +51,13 @@ describe('DbUpdateGuardian use case', () => { error: new NotAcceptableError('userId') }) }) + + it('Should throw if loadById method throws', async () => { + const { sut, guardianRepositoryStub } = makeSut() + jest.spyOn(guardianRepositoryStub, 'loadById').mockRejectedValue(new Error()) + const promise = sut.update(params) + await expect(promise).rejects.toThrow() + }) }) describe('FileStorage', () => { From 11f38521db0e6c8d6dbe9acbf9444487705fe9b9 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 15:39:05 -0300 Subject: [PATCH 22/36] test: ensure call save method with correct values --- .../use-cases/guardian/db-update-guardian.spec.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts index 12cd0ade..2b6539ee 100644 --- a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts +++ b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts @@ -2,7 +2,7 @@ import { NotAcceptableError } from '@/application/errors' import { type DeleteFileStorage, type FileStorage, type LoadGuardianByIdRepository, type UpdateGuardianRepository } from '@/data/protocols' import { DbUpdateGuardian } from '@/data/use-cases' import { type UpdateGuardian } from '@/domain/use-cases' -import { makeFakeFileStorage, makeFakeGuardianRepository } from '@/tests/utils' +import { makeFakeFileStorage, makeFakeGuardianRepository, mockFakeGuardianUpdated } from '@/tests/utils' interface SutTypes { sut: DbUpdateGuardian @@ -31,7 +31,7 @@ describe('DbUpdateGuardian use case', () => { firstName: 'any_first_name', lastName: 'any_last_name', phone: 'any_phone', - image: null + image: Buffer.from('any_image') } describe('GuardianRepository', () => { @@ -60,6 +60,14 @@ describe('DbUpdateGuardian use case', () => { }) }) describe('FileStorage', () => { - + it('Should call save method with correct values', async () => { + const { sut, fileStorageStub } = makeSut() + const saveSpy = jest.spyOn(fileStorageStub, 'save') + await sut.update(params) + expect(saveSpy).toHaveBeenCalledWith({ + file: params.image, + fileName: `images/guardian-${mockFakeGuardianUpdated()?.id}-${Math.trunc(Date.now() / 1000)}` + }) + }) }) }) From ca349a37d607742763bcf9846e0b1b3ad3115132 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 15:41:29 -0300 Subject: [PATCH 23/36] test: ensure throw if save method throws --- .../src/data/use-cases/guardian/db-update-guardian.spec.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts index 2b6539ee..6e174e7c 100644 --- a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts +++ b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts @@ -69,5 +69,12 @@ describe('DbUpdateGuardian use case', () => { fileName: `images/guardian-${mockFakeGuardianUpdated()?.id}-${Math.trunc(Date.now() / 1000)}` }) }) + + it('Should throw if save method throws', async () => { + const { sut, fileStorageStub } = makeSut() + jest.spyOn(fileStorageStub, 'save').mockRejectedValueOnce(new Error()) + const promise = sut.update(params) + await expect(promise).rejects.toThrow() + }) }) }) From c3d429777dc8ac97e5884d4e4d9de1f001d8e928 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 15:45:12 -0300 Subject: [PATCH 24/36] test: ensure call delete method with correct values --- .../data/use-cases/guardian/db-update-guardian.spec.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts index 6e174e7c..d0030274 100644 --- a/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts +++ b/tests/src/data/use-cases/guardian/db-update-guardian.spec.ts @@ -76,5 +76,14 @@ describe('DbUpdateGuardian use case', () => { const promise = sut.update(params) await expect(promise).rejects.toThrow() }) + + it('Should call delete method with correct values', async () => { + const { sut, fileStorageStub } = makeSut() + const deleteSpy = jest.spyOn(fileStorageStub, 'delete') + await sut.update(params) + expect(deleteSpy).toHaveBeenCalledWith({ + fileUrlOrPath: 'any_image' + }) + }) }) }) From 00bb7fe03b0ab6a16a1cd884a5534ff16a55d776 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 16:29:05 -0300 Subject: [PATCH 25/36] test: ensure reutrn 406 (NotAcceptable) if invalid data is provided --- .../guardian/update-guardian.spec.ts | 48 +++++++++++++++++++ tests/utils/mocks/request.mock.ts | 19 +++++++- tests/utils/stubs/use-case.stub.ts | 23 ++++++++- tests/utils/types/request.type.ts | 15 +++++- 4 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 tests/src/application/controllers/guardian/update-guardian.spec.ts diff --git a/tests/src/application/controllers/guardian/update-guardian.spec.ts b/tests/src/application/controllers/guardian/update-guardian.spec.ts new file mode 100644 index 00000000..35a39dfe --- /dev/null +++ b/tests/src/application/controllers/guardian/update-guardian.spec.ts @@ -0,0 +1,48 @@ +import { UpdateGuardianController } from '@/application/controllers' +import { InvalidParamError } from '@/application/errors' +import { notAcceptable } from '@/application/helpers' +import { type Validation } from '@/application/protocols' +import { type UpdateGuardian } from '@/domain/use-cases' +import { makeFakeUpdateGuardianRequest, makeFakeUpdateGuardianUseCase, makeFakeValidation } from '@/tests/utils' + +interface SutTypes { + sut: UpdateGuardianController + updateGuardianStub: UpdateGuardian + validationStub: Validation + +} + +const makeSut = (): SutTypes => { + const updateGuardianStub = makeFakeUpdateGuardianUseCase() + const validationStub = makeFakeValidation() + const dependencies: UpdateGuardianController.Dependencies = { + updateGuardian: updateGuardianStub, + validation: validationStub + } + const sut = new UpdateGuardianController(dependencies) + return { + sut, + updateGuardianStub, + validationStub + } +} + +describe('UpdateGuardian Controller', () => { + const httpRequest = makeFakeUpdateGuardianRequest() + + describe('UpdateGuardian', () => { + it('Should return 406 (NotAcceptable) if invalid data is provided', async () => { + const { sut, updateGuardianStub } = makeSut() + jest.spyOn(updateGuardianStub, 'update').mockResolvedValue({ + isSuccess: false, + error: new InvalidParamError('any_field') + }) + const httpResponse = await sut.handle(httpRequest) + expect(httpResponse).toEqual(notAcceptable(new InvalidParamError('any_field'))) + }) + }) + + describe('Validations', () => { + + }) +}) diff --git a/tests/utils/mocks/request.mock.ts b/tests/utils/mocks/request.mock.ts index 2d0bcb98..7f0d8ea6 100644 --- a/tests/utils/mocks/request.mock.ts +++ b/tests/utils/mocks/request.mock.ts @@ -12,7 +12,8 @@ import { type DeletePetRequest, type EmailConfirmationRequest, type AddTagRequest, - type AddSchedulerRequest + type AddSchedulerRequest, + type UpdateGuardianRequest } from '@/tests/utils' const mockGuardianRequest = { @@ -124,6 +125,19 @@ const makeFakeUpdatePetRequest = (): UpdatePetRequest => { return { body, params, userId, file } } +const makeFakeUpdateGuardianRequest = (): UpdateGuardianRequest => { + const body = { + firstName: 'any_first_name', + lastName: 'any_last_name', + phone: 'any_phone' + } + const params = { + guardianId: 'any_guardian_id' + } + const file = Buffer.from('any_image') + return { body, params, file } +} + const makeFakeDeletePetRequest = (): DeletePetRequest => { const userId = 'valid_guardian_id' const params = { @@ -178,5 +192,6 @@ export { makeFakeDeletePetRequest, makeFakeEmailConfirmationRequest, makeFakeAddTagRequest, - makeFakeAddSchedulerRequest + makeFakeAddSchedulerRequest, + makeFakeUpdateGuardianRequest } diff --git a/tests/utils/stubs/use-case.stub.ts b/tests/utils/stubs/use-case.stub.ts index 2266b9a3..e1c8055b 100644 --- a/tests/utils/stubs/use-case.stub.ts +++ b/tests/utils/stubs/use-case.stub.ts @@ -24,7 +24,8 @@ import { type LoadSettings, type UpdateSettings, type LoadPetById, - type LoadNextTasksByPetIdAndTagId + type LoadNextTasksByPetIdAndTagId, + type UpdateGuardian } from '@/domain/use-cases' import { mockTokenService } from '@/tests/utils/stubs/service.stub' import { mockFakeAppointPet, mockFakePetUpdated, mockFakePetByGuardianIdLoaded, mockFakeSpecieAdded, makeFakeGuardianData, mockFakeBreedAdded, mockFakeSizeAdded, mockFakePetByIdLoaded } from '../mocks' @@ -60,6 +61,25 @@ const makeFakeAddGuardianUseCase = (): AddGuardian => { return new AddGuardianStub() } +const makeFakeUpdateGuardianUseCase = (): UpdateGuardian => { + class UpdateGuardianStub implements UpdateGuardian { + async update (params: UpdateGuardian.Params): Promise { + const result = { + id: mockGuardianUseCase.id, + firstName: mockGuardianUseCase.firstName, + lastName: mockGuardianUseCase.lastName, + phone: mockGuardianUseCase.phone, + image: mockGuardianUseCase.image + } + return { + isSuccess: true, + data: result + } + } + } + return new UpdateGuardianStub() +} + const makeFakeEmailConfirmationUseCase = (): EmailConfirmation => { class EmailConfirmationStub implements EmailConfirmation { async confirm (userId: EmailConfirmation.Params): Promise { @@ -472,6 +492,7 @@ const makeFakeUpdateSettingsUseCase = (): UpdateSettings => { export { makeFakeAddGuardianUseCase, makeFakeAddPetUseCase, + makeFakeUpdateGuardianUseCase, makeFakeLoadPetsUseCase, makeFakeLoadPetByIdUseCase, makeFakeUpdatePetUseCase, diff --git a/tests/utils/types/request.type.ts b/tests/utils/types/request.type.ts index c2f214b6..91b249d4 100644 --- a/tests/utils/types/request.type.ts +++ b/tests/utils/types/request.type.ts @@ -74,6 +74,18 @@ interface UpdatePetRequest { file?: Buffer } +interface UpdateGuardianRequest { + body: { + firstName: string + lastName: string + phone: string + } + params: { + guardianId: string + } + file?: Buffer +} + interface DeletePetRequest { userId: string params: { @@ -122,5 +134,6 @@ export { type DeletePetRequest, type EmailConfirmationRequest, type AddTagRequest, - type AddSchedulerRequest + type AddSchedulerRequest, + type UpdateGuardianRequest } From 76e3d3a4a0c5d50fb473618d3b8259e70dc097ae Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 16:32:04 -0300 Subject: [PATCH 26/36] test: ensure return 500 (ServerError) if update throws --- .../controllers/guardian/update-guardian.spec.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/src/application/controllers/guardian/update-guardian.spec.ts b/tests/src/application/controllers/guardian/update-guardian.spec.ts index 35a39dfe..4a8d8d5a 100644 --- a/tests/src/application/controllers/guardian/update-guardian.spec.ts +++ b/tests/src/application/controllers/guardian/update-guardian.spec.ts @@ -3,7 +3,7 @@ import { InvalidParamError } from '@/application/errors' import { notAcceptable } from '@/application/helpers' import { type Validation } from '@/application/protocols' import { type UpdateGuardian } from '@/domain/use-cases' -import { makeFakeUpdateGuardianRequest, makeFakeUpdateGuardianUseCase, makeFakeValidation } from '@/tests/utils' +import { makeFakeServerError, makeFakeUpdateGuardianRequest, makeFakeUpdateGuardianUseCase, makeFakeValidation } from '@/tests/utils' interface SutTypes { sut: UpdateGuardianController @@ -40,6 +40,13 @@ describe('UpdateGuardian Controller', () => { const httpResponse = await sut.handle(httpRequest) expect(httpResponse).toEqual(notAcceptable(new InvalidParamError('any_field'))) }) + + it('Should return 500 (ServerError) if update throws', async () => { + const { sut, updateGuardianStub } = makeSut() + jest.spyOn(updateGuardianStub, 'update').mockRejectedValue(new Error()) + const promise = await sut.handle(httpRequest) + expect(promise).toEqual(makeFakeServerError()) + }) }) describe('Validations', () => { From c38e70fb50fb5abfefa51cd15acbc9b575d9d872 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 16:37:30 -0300 Subject: [PATCH 27/36] test: ensure call update with correct values --- .../controllers/guardian/update-guardian.spec.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/src/application/controllers/guardian/update-guardian.spec.ts b/tests/src/application/controllers/guardian/update-guardian.spec.ts index 4a8d8d5a..c91d332b 100644 --- a/tests/src/application/controllers/guardian/update-guardian.spec.ts +++ b/tests/src/application/controllers/guardian/update-guardian.spec.ts @@ -47,6 +47,19 @@ describe('UpdateGuardian Controller', () => { const promise = await sut.handle(httpRequest) expect(promise).toEqual(makeFakeServerError()) }) + + it('Should call update with correct values', async () => { + const { sut, updateGuardianStub } = makeSut() + const updateSpy = jest.spyOn(updateGuardianStub, 'update') + await sut.handle(httpRequest) + expect(updateSpy).toHaveBeenCalledWith({ + guardianId: httpRequest.params.guardianId, + firstName: httpRequest.body.firstName, + lastName: httpRequest.body.lastName, + phone: httpRequest.body.phone, + image: httpRequest.file + }) + }) }) describe('Validations', () => { From 66d2a57d2c7a150244c3bf7676dfbf71699f9664 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 16:49:11 -0300 Subject: [PATCH 28/36] test: ensure return 400(BadRequest) if validation returns an error --- .../controllers/guardian/update-guardian.spec.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/src/application/controllers/guardian/update-guardian.spec.ts b/tests/src/application/controllers/guardian/update-guardian.spec.ts index c91d332b..80f729c6 100644 --- a/tests/src/application/controllers/guardian/update-guardian.spec.ts +++ b/tests/src/application/controllers/guardian/update-guardian.spec.ts @@ -1,6 +1,6 @@ import { UpdateGuardianController } from '@/application/controllers' import { InvalidParamError } from '@/application/errors' -import { notAcceptable } from '@/application/helpers' +import { badRequest, notAcceptable } from '@/application/helpers' import { type Validation } from '@/application/protocols' import { type UpdateGuardian } from '@/domain/use-cases' import { makeFakeServerError, makeFakeUpdateGuardianRequest, makeFakeUpdateGuardianUseCase, makeFakeValidation } from '@/tests/utils' @@ -63,6 +63,13 @@ describe('UpdateGuardian Controller', () => { }) describe('Validations', () => { + it('Should return 400 (BadRequest) if Validation returns an error', async () => { + const { sut, validationStub } = makeSut() + jest.spyOn(validationStub, 'validate').mockReturnValueOnce(new Error()) + const httpResponse = await sut.handle(httpRequest) + + expect(httpResponse).toEqual(badRequest(new Error())) + }) }) }) From 6ba4ff287440226479f785595180898455222b83 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 16:49:51 -0300 Subject: [PATCH 29/36] test: ensure call validation with correct values --- .../controllers/guardian/update-guardian.spec.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/src/application/controllers/guardian/update-guardian.spec.ts b/tests/src/application/controllers/guardian/update-guardian.spec.ts index 80f729c6..d6169c09 100644 --- a/tests/src/application/controllers/guardian/update-guardian.spec.ts +++ b/tests/src/application/controllers/guardian/update-guardian.spec.ts @@ -71,5 +71,14 @@ describe('UpdateGuardian Controller', () => { expect(httpResponse).toEqual(badRequest(new Error())) }) + + it('Should call Validation with correct values', async () => { + const { sut, validationStub } = makeSut() + const validateSpy = jest.spyOn(validationStub, 'validate') + + await sut.handle(httpRequest) + + expect(validateSpy).toHaveBeenCalledWith({ ...httpRequest.body, ...httpRequest.params }) + }) }) }) From a0987e5892682787508aa1a9d2903e9f11f3f371 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 16:53:23 -0300 Subject: [PATCH 30/36] test: ensure reutrn 200 (success) if empty data is provided --- .../controllers/guardian/update-guardian.spec.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/src/application/controllers/guardian/update-guardian.spec.ts b/tests/src/application/controllers/guardian/update-guardian.spec.ts index d6169c09..f1e15833 100644 --- a/tests/src/application/controllers/guardian/update-guardian.spec.ts +++ b/tests/src/application/controllers/guardian/update-guardian.spec.ts @@ -1,9 +1,9 @@ import { UpdateGuardianController } from '@/application/controllers' import { InvalidParamError } from '@/application/errors' -import { badRequest, notAcceptable } from '@/application/helpers' +import { badRequest, notAcceptable, success } from '@/application/helpers' import { type Validation } from '@/application/protocols' import { type UpdateGuardian } from '@/domain/use-cases' -import { makeFakeServerError, makeFakeUpdateGuardianRequest, makeFakeUpdateGuardianUseCase, makeFakeValidation } from '@/tests/utils' +import { makeFakeServerError, makeFakeUpdateGuardianRequest, makeFakeUpdateGuardianUseCase, makeFakeValidation, mockFakeGuardianAdded, mockFakeGuardianUpdated } from '@/tests/utils' interface SutTypes { sut: UpdateGuardianController @@ -80,5 +80,14 @@ describe('UpdateGuardian Controller', () => { expect(validateSpy).toHaveBeenCalledWith({ ...httpRequest.body, ...httpRequest.params }) }) + + it('should return 200 (success) if empty data is provided', async () => { + const { sut } = makeSut() + const { body, ...httpRequestWithoutBody } = { ...httpRequest } + + const httpResponse = await sut.handle(httpRequestWithoutBody) + + expect(httpResponse).toEqual(success({ ...mockFakeGuardianUpdated(), image: '' })) + }) }) }) From 682af8665d28b0860c02e56ad3b7f05e3d010dea Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Mar 2026 16:56:58 -0300 Subject: [PATCH 31/36] test: ensure return 200 (success) if valid data is provided --- .../guardian/update-guardian.spec.ts | 17 ++++++++++++++++- tests/utils/mocks/entities.mocks.ts | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/src/application/controllers/guardian/update-guardian.spec.ts b/tests/src/application/controllers/guardian/update-guardian.spec.ts index f1e15833..e91f4d18 100644 --- a/tests/src/application/controllers/guardian/update-guardian.spec.ts +++ b/tests/src/application/controllers/guardian/update-guardian.spec.ts @@ -3,7 +3,7 @@ import { InvalidParamError } from '@/application/errors' import { badRequest, notAcceptable, success } from '@/application/helpers' import { type Validation } from '@/application/protocols' import { type UpdateGuardian } from '@/domain/use-cases' -import { makeFakeServerError, makeFakeUpdateGuardianRequest, makeFakeUpdateGuardianUseCase, makeFakeValidation, mockFakeGuardianAdded, mockFakeGuardianUpdated } from '@/tests/utils' +import { makeFakeServerError, makeFakeUpdateGuardianRequest, makeFakeUpdateGuardianUseCase, makeFakeValidation, mockFakeGuardianUpdated } from '@/tests/utils' interface SutTypes { sut: UpdateGuardianController @@ -89,5 +89,20 @@ describe('UpdateGuardian Controller', () => { expect(httpResponse).toEqual(success({ ...mockFakeGuardianUpdated(), image: '' })) }) + + it('should return 200 (success) if valid data is provided', async () => { + const { sut } = makeSut() + + const entries = Object.entries(httpRequest.body) + const f = async (prefix: any, entries: any): Promise => { + for (let i = 0; i < entries.length; i++) { + Object.assign(httpRequest, { body: { ...Object.fromEntries([...prefix, entries[i]]) } }) + const httpResponse = await sut.handle(httpRequest) + expect(httpResponse).toEqual(success(mockFakeGuardianUpdated())) + await f([...prefix, entries[i]], entries.slice(i + 1)) + } + } + await f([], entries) + }) }) }) diff --git a/tests/utils/mocks/entities.mocks.ts b/tests/utils/mocks/entities.mocks.ts index fe0d4ed7..ba5d04c4 100644 --- a/tests/utils/mocks/entities.mocks.ts +++ b/tests/utils/mocks/entities.mocks.ts @@ -60,7 +60,7 @@ const mockFakeGuardianUpdated = (): Exclude Date: Thu, 12 Mar 2026 16:07:50 -0300 Subject: [PATCH 32/36] fix: change class to validate phone --- .../validations/guardian/update-guardian-factory.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/factories/validations/guardian/update-guardian-factory.ts b/src/main/factories/validations/guardian/update-guardian-factory.ts index aca3a7d8..0ade181c 100644 --- a/src/main/factories/validations/guardian/update-guardian-factory.ts +++ b/src/main/factories/validations/guardian/update-guardian-factory.ts @@ -1,13 +1,13 @@ import { type Validation } from '@/application/protocols' -import { NameValidation, OptionalFieldValidation, ValidationComposite } from '@/application/validation' -import { NameValidatorAdapter } from '@/infra/validators' +import { NameValidation, OptionalFieldValidation, PhoneValidation, ValidationComposite } from '@/application/validation' +import { NameValidatorAdapter, PhoneValidatorAdapter } from '@/infra/validators' export const makeUpdateGuardianValidation = (): ValidationComposite => { const validations: Validation[] = [] validations.push(new OptionalFieldValidation('firstName', new NameValidation('firstName', new NameValidatorAdapter()))) validations.push(new OptionalFieldValidation('lastName', new NameValidation('lastName', new NameValidatorAdapter()))) - validations.push(new OptionalFieldValidation('phone', new NameValidation('phone', new NameValidatorAdapter()))) + validations.push(new OptionalFieldValidation('phone', new PhoneValidation('phone', new PhoneValidatorAdapter()))) return new ValidationComposite(validations) } From db2b5106a2d8c878dd7b69a5fbac9b3e6b42cc8f Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Thu, 12 Mar 2026 16:09:04 -0300 Subject: [PATCH 33/36] test: ensure return a updated guardian in route PUT guardian --- .../routes/update-guardian-routes.test.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/src/main/routes/update-guardian-routes.test.ts diff --git a/tests/src/main/routes/update-guardian-routes.test.ts b/tests/src/main/routes/update-guardian-routes.test.ts new file mode 100644 index 00000000..3b9f6990 --- /dev/null +++ b/tests/src/main/routes/update-guardian-routes.test.ts @@ -0,0 +1,46 @@ +import request from 'supertest' +import app from '@/main/config/app' +import { PrismaHelper } from '@/tests/helpers/prisma-helper' + +jest.setTimeout(30000) +describe('Update Guardian Routes', () => { + beforeAll(async () => { + await PrismaHelper.connect() + }) + + afterAll(async () => { + await PrismaHelper.clearGuardian() + await PrismaHelper.disconnect() + }) + + describe('PUT - /api/guardian/:guardianId route', () => { + it('Should update a guardian', async () => { + const guardian = await PrismaHelper.createGuardian() + + const { body } = await request(app) + .post('/api/login') + .send({ + email: 'johndoe@email.com', + password: 'Test@1234' + }) + + const response = await request(app) + .put(`/api/guardian/${guardian.id}`) + .set('Authorization', body.accessToken) + .field('firstName', 'John Updated') + .field('lastName', 'Doe') + .field('phone', '11987654322') + .attach('image', '') + + expect(response.status).toBe(200) + expect(response.body).toEqual({ + id: guardian.id, + firstName: 'John Updated', + lastName: 'Doe', + phone: '11987654322', + image: '' + + }) + }) + }) +}) From bc70e1230c8b25105042f34ce84e6b9678a09370 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Thu, 12 Mar 2026 16:12:24 -0300 Subject: [PATCH 34/36] test: ensure return 400 if no access token is provided --- tests/src/main/routes/update-guardian-routes.test.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/src/main/routes/update-guardian-routes.test.ts b/tests/src/main/routes/update-guardian-routes.test.ts index 3b9f6990..4901fd60 100644 --- a/tests/src/main/routes/update-guardian-routes.test.ts +++ b/tests/src/main/routes/update-guardian-routes.test.ts @@ -42,5 +42,12 @@ describe('Update Guardian Routes', () => { }) }) + + it('Should return 400 if no access token is provided', async () => { + await request(app) + .put('/api/guardian/:guardianId') + .set('Authorization', '') + .expect(400) + }) }) }) From 5df8245c215dacad857a732089d1d462f0a38372 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Thu, 12 Mar 2026 16:19:23 -0300 Subject: [PATCH 35/36] test: ensure return 406 (NotAcceptable) if invalid guardianId is provided --- .../routes/update-guardian-routes.test.ts | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/src/main/routes/update-guardian-routes.test.ts b/tests/src/main/routes/update-guardian-routes.test.ts index 4901fd60..9be323f1 100644 --- a/tests/src/main/routes/update-guardian-routes.test.ts +++ b/tests/src/main/routes/update-guardian-routes.test.ts @@ -8,8 +8,11 @@ describe('Update Guardian Routes', () => { await PrismaHelper.connect() }) - afterAll(async () => { + afterEach(async () => { await PrismaHelper.clearGuardian() + }) + + afterAll(async () => { await PrismaHelper.disconnect() }) @@ -49,5 +52,22 @@ describe('Update Guardian Routes', () => { .set('Authorization', '') .expect(400) }) + + it('Should return 406 (NotAcceptable) if invalid guardianId is provided', async () => { + await PrismaHelper.createGuardian() + + const { body } = await request(app) + .post('/api/login') + .send({ + email: 'johndoe@email.com', + password: 'Test@1234' + }) + + const response = await request(app) + .put('/api/guardian/b1e64ea1-0f6f-4cad-b3d6-434468cb2c5d') + .set('Authorization', body.accessToken) + + expect(response.status).toBe(406) + }) }) }) From 1a3377c945672559c54f679e50187330db319f3b Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Thu, 12 Mar 2026 16:49:55 -0300 Subject: [PATCH 36/36] test: removed unused timeout --- tests/src/main/routes/update-guardian-routes.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/src/main/routes/update-guardian-routes.test.ts b/tests/src/main/routes/update-guardian-routes.test.ts index 9be323f1..694f4b88 100644 --- a/tests/src/main/routes/update-guardian-routes.test.ts +++ b/tests/src/main/routes/update-guardian-routes.test.ts @@ -2,7 +2,6 @@ import request from 'supertest' import app from '@/main/config/app' import { PrismaHelper } from '@/tests/helpers/prisma-helper' -jest.setTimeout(30000) describe('Update Guardian Routes', () => { beforeAll(async () => { await PrismaHelper.connect() @@ -44,7 +43,7 @@ describe('Update Guardian Routes', () => { image: '' }) - }) + }, 30000) it('Should return 400 if no access token is provided', async () => { await request(app)