From e00990feffb37f779c4d1745021917a5e60a3158 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 09:49:12 -0300 Subject: [PATCH 01/19] chore: removed deprecated version and put especifc version on docker compose file --- docker-compose.dev.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index deb5d05f..c356c440 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,8 +1,6 @@ -version: '3.8' - services: db: - image: postgres + image: postgres:16-alpine container_name: petjournal_db restart: always ports: From defc19d3a51d52729cacce23f015cbf89227dbe6 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 09:49:54 -0300 Subject: [PATCH 02/19] feat: add image field on database --- .../migrations/20260207183158_add_image_guardian/migration.sql | 2 ++ src/infra/repos/postgresql/prisma/schema.prisma | 1 + 2 files changed, 3 insertions(+) create mode 100644 src/infra/repos/postgresql/prisma/migrations/20260207183158_add_image_guardian/migration.sql diff --git a/src/infra/repos/postgresql/prisma/migrations/20260207183158_add_image_guardian/migration.sql b/src/infra/repos/postgresql/prisma/migrations/20260207183158_add_image_guardian/migration.sql new file mode 100644 index 00000000..765fb8fa --- /dev/null +++ b/src/infra/repos/postgresql/prisma/migrations/20260207183158_add_image_guardian/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "guardians" ADD COLUMN "image" TEXT NOT NULL DEFAULT ''; diff --git a/src/infra/repos/postgresql/prisma/schema.prisma b/src/infra/repos/postgresql/prisma/schema.prisma index 9278fe1e..79ea4dee 100644 --- a/src/infra/repos/postgresql/prisma/schema.prisma +++ b/src/infra/repos/postgresql/prisma/schema.prisma @@ -19,6 +19,7 @@ model Guardian { verificationToken String @map("verification_token") verificationTokenCreatedAt DateTime @default(now()) @map("verification_token_created_at") emailConfirmation Boolean @default(false) @map("email_confirmation") + image String @default("") pets Pet[] tags Tag[] scheduler Scheduler[] From 7acfb119b952ab9784df1c81ac1827a92f41e18b Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 09:53:01 -0300 Subject: [PATCH 03/19] feat: create update image on guardian repository --- src/data/protocols/db/guardian/index.ts | 1 + .../update-guardian-image-repository.ts | 19 +++++++++++++++ .../postgresql/guardian-account-repository.ts | 24 ++++++++++++++++++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/data/protocols/db/guardian/update-guardian-image-repository.ts diff --git a/src/data/protocols/db/guardian/index.ts b/src/data/protocols/db/guardian/index.ts index a6237ba9..a7b35cd9 100644 --- a/src/data/protocols/db/guardian/index.ts +++ b/src/data/protocols/db/guardian/index.ts @@ -7,3 +7,4 @@ export * from './update-verification-token-repository' export * from './update-guardian-password-repository' export * from './save-token-repository' export * from './update-email-confirmation-repository' +export * from './update-guardian-image-repository' diff --git a/src/data/protocols/db/guardian/update-guardian-image-repository.ts b/src/data/protocols/db/guardian/update-guardian-image-repository.ts new file mode 100644 index 00000000..98561453 --- /dev/null +++ b/src/data/protocols/db/guardian/update-guardian-image-repository.ts @@ -0,0 +1,19 @@ +export interface UpdateGuardianImageRepository { + updateImage: (params: UpdateGuardianImageRepository.Params) => Promise +} + +export namespace UpdateGuardianImageRepository { + export type Params = { + guardianId: string + image?: string + } + + export type Result = { + id: string + firstName: string + lastName: string + email: string + phone: string + image: string + } | undefined +} diff --git a/src/infra/repos/postgresql/guardian-account-repository.ts b/src/infra/repos/postgresql/guardian-account-repository.ts index 21c6860f..24de7352 100644 --- a/src/infra/repos/postgresql/guardian-account-repository.ts +++ b/src/infra/repos/postgresql/guardian-account-repository.ts @@ -1,3 +1,4 @@ +import { type UpdateGuardianImageRepository } from '@/data/protocols/db/guardian/update-guardian-image-repository' import { prisma as db } from './prisma' import { type AddGuardianRepository, @@ -15,7 +16,8 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository, UpdateAccessTokenRepository, UpdateGuardianPasswordRepository, UpdateVerificationTokenRepository, - UpdateEmailConfirmationRepository { + UpdateEmailConfirmationRepository, + UpdateGuardianImageRepository { async add ( guardianData: AddGuardianRepository.Params ): Promise { @@ -136,4 +138,24 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository, return result.emailConfirmation } + + async updateImage (params: UpdateGuardianImageRepository.Params): Promise { + const { guardianId, image } = params + const result = await db.guardian.update({ + where: { id: guardianId }, + data: { image }, + select: { + id: true, + firstName: true, + lastName: true, + email: true, + phone: true, + verificationToken: false, + emailConfirmation: true, + image: true + } + }) + + return result + } } From 5a237d6d452cf3d939bc5e3581cae85a0f91bf10 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 09:54:38 -0300 Subject: [PATCH 04/19] refactor: change pet image field name --- src/data/use-cases/pet/db-add-pet.ts | 8 ++++---- src/domain/use-cases/pet/add-pet.ts | 2 +- src/main/factories/usecases/pet/db-add-pet-factory.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/data/use-cases/pet/db-add-pet.ts b/src/data/use-cases/pet/db-add-pet.ts index 8b28cca2..2aa09220 100644 --- a/src/data/use-cases/pet/db-add-pet.ts +++ b/src/data/use-cases/pet/db-add-pet.ts @@ -17,20 +17,20 @@ export class DbAddPet implements AddPet { private readonly petRepository: AddPetRepository & UpdatePetRepository private readonly appointPet: AppointPet private readonly fileStorage: FileStorage - private readonly defaultImageUrl: string + private readonly defaultPetImageUrl: string constructor ({ guardianRepository, petRepository, appointPet, fileStorage, - defaultImageUrl + defaultPetImageUrl }: AddPet.Dependencies) { this.guardianRepository = guardianRepository this.petRepository = petRepository this.appointPet = appointPet this.fileStorage = fileStorage - this.defaultImageUrl = defaultImageUrl + this.defaultPetImageUrl = defaultPetImageUrl } async add (petData: AddPet.Params): Promise { @@ -96,7 +96,7 @@ export class DbAddPet implements AddPet { size: pet?.size as Size & { id: string }, castrated: pet?.castrated as boolean, dateOfBirth: pet?.dateOfBirth as Date, - image: imageUrl || this.defaultImageUrl + image: imageUrl || this.defaultPetImageUrl } } } diff --git a/src/domain/use-cases/pet/add-pet.ts b/src/domain/use-cases/pet/add-pet.ts index 5a5c5fdd..4e013753 100644 --- a/src/domain/use-cases/pet/add-pet.ts +++ b/src/domain/use-cases/pet/add-pet.ts @@ -58,6 +58,6 @@ export namespace AddPet { petRepository: AddPetRepository & UpdatePetRepository appointPet: AppointPet fileStorage: FileStorage - defaultImageUrl: string + defaultPetImageUrl: string } } diff --git a/src/main/factories/usecases/pet/db-add-pet-factory.ts b/src/main/factories/usecases/pet/db-add-pet-factory.ts index 525c5e38..baad021f 100644 --- a/src/main/factories/usecases/pet/db-add-pet-factory.ts +++ b/src/main/factories/usecases/pet/db-add-pet-factory.ts @@ -10,13 +10,13 @@ export const makeDbAddPet = (): AddPet => { const petRepository = new PetRepository() const appointPet = makeDbAppointPet() const fileStorage = new FirebaseStorageAdapter(env.firebase.projectId, env.firebase.storageBucket) - const defaultImageUrl = env.firebase.defaultImageUrl + const defaultPetImageUrl = env.firebase.defaultPetImageUrl const addPet = new DbAddPet({ guardianRepository, petRepository, appointPet, fileStorage, - defaultImageUrl + defaultPetImageUrl }) return addPet } From c2e04c1e192b4c14e85c3b9b3d56dbc8b95edbb7 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 09:56:00 -0300 Subject: [PATCH 05/19] feat: add image field and new dependencies on add guardian use case --- src/domain/use-cases/add-guardian.ts | 9 +++++++-- src/main/config/env.ts | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/domain/use-cases/add-guardian.ts b/src/domain/use-cases/add-guardian.ts index b82631b6..108df38b 100644 --- a/src/domain/use-cases/add-guardian.ts +++ b/src/domain/use-cases/add-guardian.ts @@ -1,4 +1,5 @@ -import { type AddGuardianRepository, type HashGenerator } from '@/data/protocols' +import { type FileStorage, type AddGuardianRepository, type HashGenerator } from '@/data/protocols' +import { type UpdateGuardianImageRepository } from '@/data/protocols/db/guardian/update-guardian-image-repository' export interface AddGuardian { add: (guardianData: AddGuardian.Params) => Promise @@ -12,6 +13,7 @@ export namespace AddGuardian { phone: string password: string verificationToken: string + image: Buffer | null } export type Result = { @@ -20,10 +22,13 @@ export namespace AddGuardian { lastName: string email: string phone: string + image: string } | undefined export type Dependencies = { - guardianRepository: AddGuardianRepository + guardianRepository: AddGuardianRepository & UpdateGuardianImageRepository hashService: HashGenerator + fileStorage: FileStorage + defaultGuardianImageUrl: string } } diff --git a/src/main/config/env.ts b/src/main/config/env.ts index c1279585..8e7aa0be 100644 --- a/src/main/config/env.ts +++ b/src/main/config/env.ts @@ -14,7 +14,8 @@ export default { firebase: { projectId: process.env.FIREBASE_PROJECT_ID ?? '', storageBucket: process.env.FIREBASE_STORAGE_BUCKET ?? '', - defaultImageUrl: process.env.FIREBASE_DEFAULT_IMAGE_URL ?? '' + defaultPetImageUrl: process.env.FIREBASE_DEFAULT_PET_IMAGE_URL ?? '', + defaultGuardianImageUrl: process.env.FIREBASE_DEFAULT_GUARDIAN_IMAGE_URL ?? '' }, mailerooApiKey: process.env.MAILEROO_API_KEY ?? '', mailerooApiSenderUrl: process.env.MAILEROO_API_URL ?? '', From 3826a63a8a40c415da176ddfdbb96129ca8df580 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 09:58:49 -0300 Subject: [PATCH 06/19] feat: updated to receive image on create guardian use case implementation --- src/data/use-cases/db-add-guardian.ts | 30 +++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/data/use-cases/db-add-guardian.ts b/src/data/use-cases/db-add-guardian.ts index 4e84e346..a13bccc4 100644 --- a/src/data/use-cases/db-add-guardian.ts +++ b/src/data/use-cases/db-add-guardian.ts @@ -1,17 +1,39 @@ import { type AddGuardian } from '@/domain/use-cases' -import { type AddGuardianRepository, type HashGenerator } from '@/data/protocols' +import { type FileStorage, type AddGuardianRepository, type HashGenerator, type UpdateGuardianImageRepository } from '@/data/protocols' export class DbAddGuardian implements AddGuardian { - private readonly guardianRepository: AddGuardianRepository + private readonly guardianRepository: AddGuardianRepository & UpdateGuardianImageRepository private readonly hashService: HashGenerator + private readonly fileStorage: FileStorage + private readonly defaultGuardianImageUrl: string - constructor ({ guardianRepository, hashService }: AddGuardian.Dependencies) { + constructor ({ guardianRepository, hashService, fileStorage, defaultGuardianImageUrl }: AddGuardian.Dependencies) { this.guardianRepository = guardianRepository this.hashService = hashService + this.fileStorage = fileStorage + this.defaultGuardianImageUrl = defaultGuardianImageUrl } async add (guardianData: AddGuardian.Params): Promise { const hashedPassword = await this.hashService.encrypt({ value: guardianData.password }) - return await this.guardianRepository.add(Object.assign({}, guardianData, { password: hashedPassword })) + const guardian = await this.guardianRepository.add(Object.assign({}, { ...guardianData, image: '' }, { password: hashedPassword })) + + let imageUrl: string = '' + if (guardianData.image) { + imageUrl = await this.fileStorage.save({ file: guardianData.image, fileName: `images/guardian-${guardian?.id as string}` }) + } + + if (imageUrl) { + await this.guardianRepository.updateImage({ guardianId: guardian?.id as string, image: imageUrl }) + } + + return { + id: guardian?.id as string, + email: guardian?.email as string, + firstName: guardian?.firstName as string, + lastName: guardian?.lastName as string, + phone: guardian?.phone as string, + image: imageUrl ?? this.defaultGuardianImageUrl + } } } From 1f704b1805899feb2625c19a75b23a4b75293326 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 10:01:38 -0300 Subject: [PATCH 07/19] feat: updated to get image on signup controller --- src/application/controllers/signup.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/application/controllers/signup.ts b/src/application/controllers/signup.ts index 82cfd86b..e2a1ea60 100644 --- a/src/application/controllers/signup.ts +++ b/src/application/controllers/signup.ts @@ -29,13 +29,15 @@ export class SignUpController implements Controller { } const { firstName, lastName, email, phone, password } = httpRequest.body + const image = httpRequest.file ?? null const guardian = await this.addGuardian.add({ firstName, lastName, email, phone, password, - verificationToken: '' + verificationToken: '', + image }) if (!guardian) { From 78f5c1c08265c52dc2ec30bb1d15f9cc8528e3ce Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 10:02:16 -0300 Subject: [PATCH 08/19] feat: add upload middleware on signup route --- src/main/routes/signup-routes.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/routes/signup-routes.ts b/src/main/routes/signup-routes.ts index cf9a7409..3cbfd468 100644 --- a/src/main/routes/signup-routes.ts +++ b/src/main/routes/signup-routes.ts @@ -1,7 +1,8 @@ import { type Router } from 'express' import { adaptRoute } from '@/main/adapters' import { makeSignUpController } from '@/main/factories' +import { upload } from '../middlewares' export default (router: Router): void => { - router.post('/signup', adaptRoute(makeSignUpController())) + router.post('/signup', upload, adaptRoute(makeSignUpController())) } From 8bb1b158ebcd3779255edd3b832bc06eb4989244 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 10:03:10 -0300 Subject: [PATCH 09/19] feat: updated add guardian use case implementation factory --- src/main/factories/usecases/db-add-guardian-factory.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/factories/usecases/db-add-guardian-factory.ts b/src/main/factories/usecases/db-add-guardian-factory.ts index 0318a7fc..0475d9a3 100644 --- a/src/main/factories/usecases/db-add-guardian-factory.ts +++ b/src/main/factories/usecases/db-add-guardian-factory.ts @@ -3,14 +3,19 @@ import { type AddGuardian } from '@/domain/use-cases' import { BcryptAdapter } from '@/infra/cryptography' import { GuardianAccountRepository } from '@/infra/repos/postgresql' import { DbAddGuardian } from '@/data/use-cases' +import { FirebaseStorageAdapter } from '@/infra/repos/firebase' export const makeDbAddGuardian = (): AddGuardian => { const salt = Number(env.salt) const hashService = new BcryptAdapter(salt) const guardianRepository = new GuardianAccountRepository() + const fileStorage = new FirebaseStorageAdapter(env.firebase.projectId, env.firebase.storageBucket) + const defaultGuardianImageUrl = env.firebase.defaultGuardianImageUrl const addGuardian = new DbAddGuardian({ + fileStorage, guardianRepository, - hashService + hashService, + defaultGuardianImageUrl }) return addGuardian } From a448a610fd5d830b4339cd286ab6ea41e0c2de32 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 10:03:53 -0300 Subject: [PATCH 10/19] docs: update documentation with new type of request on signup route --- src/main/docs/paths/signup-path.ts | 44 +++++++++++++++---- src/main/docs/schemas/guardian-schema.ts | 3 ++ src/main/docs/schemas/signup-params-schema.ts | 3 ++ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/main/docs/paths/signup-path.ts b/src/main/docs/paths/signup-path.ts index a965e7e3..c59bf01b 100644 --- a/src/main/docs/paths/signup-path.ts +++ b/src/main/docs/paths/signup-path.ts @@ -3,14 +3,42 @@ import { DocBuilder } from '../utils/doc-builder' export const signUpPath = DocBuilder.postBuilder() .addTags(['guardian']) .addSummary('adds a new guardian') - .addJsonBody('#/schemas/signUpParams', true, { - firstName: 'John', - lastName: 'Doe', - email: 'johndoe@email.com', - password: 'Teste@123', - passwordConfirmation: 'Teste@123', - phone: '11987654321', - isPrivacyPolicyAccepted: true + .addMultipartFormDataBody({ + type: 'object', + properties: { + firstName: { + type: 'string', + example: 'John' + }, + lastName: { + type: 'string', + example: 'Doe' + }, + email: { + type: 'string', + example: 'johndoe@email.com' + }, + password: { + type: 'string', + example: 'Teste@123' + }, + passwordConfirmation: { + type: 'string', + example: 'Teste@123' + }, + phone: { + type: 'string', + example: '11987654321' + }, + isPrivacyPolicyAccepted: { + type: 'boolean', + example: true + }, + image: { + type: 'string', + format: 'binary' + } + } }) .addResponse(201, { description: 'Success', diff --git a/src/main/docs/schemas/guardian-schema.ts b/src/main/docs/schemas/guardian-schema.ts index 46b1b297..1ec2ce8f 100644 --- a/src/main/docs/schemas/guardian-schema.ts +++ b/src/main/docs/schemas/guardian-schema.ts @@ -13,6 +13,9 @@ export const guardianSchema = { }, phone: { type: 'string' + }, + image: { + type: 'binary' } } } diff --git a/src/main/docs/schemas/signup-params-schema.ts b/src/main/docs/schemas/signup-params-schema.ts index c91661a9..8dd161fb 100644 --- a/src/main/docs/schemas/signup-params-schema.ts +++ b/src/main/docs/schemas/signup-params-schema.ts @@ -21,6 +21,9 @@ export const signUpParamsSchema = { }, isPrivacyPolicyAccepted: { type: 'boolean' + }, + image: { + type: 'binary' } } } From f6d44dbeadb65cd312427e9f515817fe028a0f71 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 12:51:22 -0300 Subject: [PATCH 11/19] fix: add verification if guardian already exists --- src/data/use-cases/db-add-guardian.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/data/use-cases/db-add-guardian.ts b/src/data/use-cases/db-add-guardian.ts index a13bccc4..c8414903 100644 --- a/src/data/use-cases/db-add-guardian.ts +++ b/src/data/use-cases/db-add-guardian.ts @@ -18,21 +18,25 @@ export class DbAddGuardian implements AddGuardian { const hashedPassword = await this.hashService.encrypt({ value: guardianData.password }) const guardian = await this.guardianRepository.add(Object.assign({}, { ...guardianData, image: '' }, { password: hashedPassword })) + if (!guardian) { + return undefined + } + let imageUrl: string = '' if (guardianData.image) { - imageUrl = await this.fileStorage.save({ file: guardianData.image, fileName: `images/guardian-${guardian?.id as string}` }) + imageUrl = await this.fileStorage.save({ file: guardianData.image, fileName: `images/guardian-${guardian?.id}` }) } if (imageUrl) { - await this.guardianRepository.updateImage({ guardianId: guardian?.id as string, image: imageUrl }) + await this.guardianRepository.updateImage({ guardianId: guardian?.id, image: imageUrl }) } return { - id: guardian?.id as string, - email: guardian?.email as string, - firstName: guardian?.firstName as string, - lastName: guardian?.lastName as string, - phone: guardian?.phone as string, + id: guardian?.id, + email: guardian?.email, + firstName: guardian?.firstName, + lastName: guardian?.lastName, + phone: guardian?.phone, image: imageUrl ?? this.defaultGuardianImageUrl } } From 05c9cee1fd39a6ca734ab1c04e76cbaff5f18022 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 12:52:58 -0300 Subject: [PATCH 12/19] test: add methods in mocks and stubs to test add guardian image field --- tests/helpers/prisma-helper.ts | 3 ++- tests/utils/images/guardian.jpg | Bin 0 -> 21495 bytes tests/utils/mocks/entities.mocks.ts | 15 ++++++++++++++- tests/utils/mocks/request.mock.ts | 3 ++- tests/utils/stubs/service.stub.ts | 16 ++++++++++++---- tests/utils/stubs/use-case.stub.ts | 6 ++++-- tests/utils/types/request.type.ts | 1 + 7 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 tests/utils/images/guardian.jpg diff --git a/tests/helpers/prisma-helper.ts b/tests/helpers/prisma-helper.ts index 8a6b13b0..fbb38004 100644 --- a/tests/helpers/prisma-helper.ts +++ b/tests/helpers/prisma-helper.ts @@ -75,7 +75,8 @@ export const PrismaHelper = { password: await bcryptAdapter.encrypt({ value: 'Test@1234' }), phone: '11987654321', emailConfirmation: true, - verificationToken: '' + verificationToken: '', + image: '' } }) }, diff --git a/tests/utils/images/guardian.jpg b/tests/utils/images/guardian.jpg new file mode 100644 index 0000000000000000000000000000000000000000..de182163d942c624b8e8161ed974d030dd3ff46f GIT binary patch literal 21495 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 Q@yC{EIIrqypVx%{*-4-lzyJUM literal 0 HcmV?d00001 diff --git a/tests/utils/mocks/entities.mocks.ts b/tests/utils/mocks/entities.mocks.ts index 4e09ea3c..8e609b6f 100644 --- a/tests/utils/mocks/entities.mocks.ts +++ b/tests/utils/mocks/entities.mocks.ts @@ -10,7 +10,8 @@ import { type LoadDogBreedsRepository, type LoadPetByGuardianIdRepository, type LoadPetByIdRepository, - type DeletePetByIdRepository + type DeletePetByIdRepository, + type UpdateGuardianImageRepository } from '@/data/protocols' import { type AppointPet } from '@/domain/use-cases' import { type Guardian } from '@/tests/utils/types' @@ -41,6 +42,17 @@ const mockFakeGuardianAdded = (): Exclude => { + return { + id: 'any_id', + firstName: 'any_first_name', + lastName: 'any_last_name', + email: 'any_email@mail.com', + phone: 'any_phone', + image: 'any_image' + } +} + const mockFakePetAdded = (): AddPetRepository.Result => { return { id: 'any_id', @@ -195,6 +207,7 @@ const mockFakeGuardianLoaded = (): Exclude { passwordConfirmation: mockGuardianRequest.passwordConfirmation, phone: mockGuardianRequest.phone } + const file = Buffer.from('any_image') - return { body } + return { body, file } } const makeFakeForgetPasswordRequest = (): ForgetPasswordRequest => { diff --git a/tests/utils/stubs/service.stub.ts b/tests/utils/stubs/service.stub.ts index fd9f9228..01a4e530 100644 --- a/tests/utils/stubs/service.stub.ts +++ b/tests/utils/stubs/service.stub.ts @@ -7,7 +7,8 @@ import { mockFakePetByGuardianIdLoaded, mockFakePetByIdLoaded, mockFakePetUpdated, - mockFakePetByIdDeleted + mockFakePetByIdDeleted, + mockFakeGuardianUpdated } from '@/tests/utils' import { type EmailService, @@ -47,7 +48,8 @@ import { type DeleteSchedulerByIdRepository, type LoadSettingsRepository, type UpdateSettingsRepository, - type LoadNextTasksByPetIdAndTagIdRepository + type LoadNextTasksByPetIdAndTagIdRepository, + type UpdateGuardianImageRepository } 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' @@ -69,7 +71,8 @@ LoadGuardianByIdRepository & UpdateAccessTokenRepository & UpdateGuardianPasswordRepository & UpdateVerificationTokenRepository & -UpdateEmailConfirmationRepository => { +UpdateEmailConfirmationRepository & +UpdateGuardianImageRepository => { class GuardianRepositoryStub implements AddGuardianRepository, LoadGuardianByEmailRepository, @@ -77,7 +80,8 @@ UpdateEmailConfirmationRepository => { UpdateAccessTokenRepository, UpdateGuardianPasswordRepository, UpdateVerificationTokenRepository, - UpdateEmailConfirmationRepository { + UpdateEmailConfirmationRepository, + UpdateGuardianImageRepository { async add (guardian: AddGuardianRepository.Params): Promise { return mockFakeGuardianAdded() } @@ -105,6 +109,10 @@ UpdateEmailConfirmationRepository => { async updateEmailConfirmation (userId: UpdateEmailConfirmationRepository.Params): Promise { return true } + + async updateImage (params: UpdateGuardianImageRepository.Params): Promise { + return mockFakeGuardianUpdated() + } } return new GuardianRepositoryStub() } diff --git a/tests/utils/stubs/use-case.stub.ts b/tests/utils/stubs/use-case.stub.ts index 801a6169..2266b9a3 100644 --- a/tests/utils/stubs/use-case.stub.ts +++ b/tests/utils/stubs/use-case.stub.ts @@ -38,7 +38,8 @@ const mockGuardianUseCase = { email: 'any_email@mail.com', password: 'any_password', phone: 'any_phone', - accessToken: 'any_token' + accessToken: 'any_token', + image: '' } const makeFakeAddGuardianUseCase = (): AddGuardian => { @@ -50,7 +51,8 @@ const makeFakeAddGuardianUseCase = (): AddGuardian => { lastName: mockGuardianUseCase.lastName, email: mockGuardianUseCase.email, password: mockGuardianUseCase.password, - phone: mockGuardianUseCase.phone + phone: mockGuardianUseCase.phone, + image: mockGuardianUseCase.image } return result } diff --git a/tests/utils/types/request.type.ts b/tests/utils/types/request.type.ts index a033c017..c2f214b6 100644 --- a/tests/utils/types/request.type.ts +++ b/tests/utils/types/request.type.ts @@ -18,6 +18,7 @@ interface SignUpRequest { passwordConfirmation: string phone: string } + file?: Buffer } interface ForgetPasswordRequest { From 98c9ed5c88c10d8e6dd41b29664b3417183ee801 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 12:53:52 -0300 Subject: [PATCH 13/19] test: add image field on signup --- tests/src/application/controllers/signup.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/src/application/controllers/signup.spec.ts b/tests/src/application/controllers/signup.spec.ts index 7c6e5fd7..995bb694 100644 --- a/tests/src/application/controllers/signup.spec.ts +++ b/tests/src/application/controllers/signup.spec.ts @@ -59,7 +59,8 @@ describe('SignUp Controller', () => { email: httpRequest.body.email, password: httpRequest.body.password, phone: httpRequest.body.phone, - verificationToken: '' + verificationToken: '', + image: httpRequest.file }) }) }) From ccbd100b913d39ec582777b1d7a03bb58063eaee Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 12:55:00 -0300 Subject: [PATCH 14/19] test: add image field and filestorage dependencies on add guardian use case --- .../data/use-cases/db-add-guardian.spec.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tests/src/data/use-cases/db-add-guardian.spec.ts b/tests/src/data/use-cases/db-add-guardian.spec.ts index 273a2702..ef6082af 100644 --- a/tests/src/data/use-cases/db-add-guardian.spec.ts +++ b/tests/src/data/use-cases/db-add-guardian.spec.ts @@ -1,7 +1,7 @@ import { type AddGuardian } from '@/domain/use-cases' import { type AddGuardianRepository, type HashGenerator } from '@/data/protocols' import { DbAddGuardian } from '@/data/use-cases' -import { makeFakeGuardianRepository, makeFakeHashService, mockHashService } from '@/tests/utils' +import { makeFakeFileStorage, makeFakeGuardianRepository, makeFakeHashService, mockHashService } from '@/tests/utils' interface SutTypes { sut: DbAddGuardian @@ -12,9 +12,13 @@ interface SutTypes { const makeSut = (): SutTypes => { const guardianRepositoryStub = makeFakeGuardianRepository() const hashServiceStub = makeFakeHashService() + const defaultGuardianImageUrlStub = 'any_url' + const fileStorageStub = makeFakeFileStorage() const dependencies: AddGuardian.Dependencies = { hashService: hashServiceStub, - guardianRepository: guardianRepositoryStub + guardianRepository: guardianRepositoryStub, + fileStorage: fileStorageStub, + defaultGuardianImageUrl: defaultGuardianImageUrlStub } const sut = new DbAddGuardian(dependencies) return { @@ -25,13 +29,14 @@ const makeSut = (): SutTypes => { } describe('DbAddGuardian use case', () => { - const params: AddGuardianRepository.Params = { + const params: AddGuardian.Params = { firstName: 'any_first_name', lastName: 'any_last_name', email: 'any_email@mail.com', password: 'any_password', phone: 'any_phone', - verificationToken: 'any_verification' + verificationToken: 'any_verification', + image: null } describe('HashService', () => { @@ -61,7 +66,8 @@ describe('DbAddGuardian use case', () => { email: params.email, password: mockHashService.hashedValue, phone: params.phone, - verificationToken: params.verificationToken + verificationToken: params.verificationToken, + image: '' }) }) @@ -80,7 +86,7 @@ describe('DbAddGuardian use case', () => { }) }) - test('Should return a guardian when saving the user successfully', async () => { + it('Should return a guardian when saving the user successfully', async () => { const { sut } = makeSut() const result = await sut.add(params) as any expect(result).toEqual({ @@ -88,7 +94,8 @@ describe('DbAddGuardian use case', () => { firstName: params.firstName, lastName: params.lastName, email: params.email, - phone: params.phone + phone: params.phone, + image: '' }) }) }) From 634c85dc5fcae6d83dfedd3b5d2edfcf27f905d6 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 12:55:53 -0300 Subject: [PATCH 15/19] test: add default image field on add pet use case --- tests/src/data/use-cases/pet/db-add-pet.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/data/use-cases/pet/db-add-pet.spec.ts b/tests/src/data/use-cases/pet/db-add-pet.spec.ts index 7e9b190d..155b3f38 100644 --- a/tests/src/data/use-cases/pet/db-add-pet.spec.ts +++ b/tests/src/data/use-cases/pet/db-add-pet.spec.ts @@ -28,14 +28,14 @@ const makeSut = (): SutTypes => { const petRepositoryStub = makeFakePetRepository() const appointPetStub = makeFakeAppointPetUseCase() const fileStorageStub = makeFakeFileStorage() - const defaultImageUrl = 'any_url' + const defaultPetImageUrl = 'any_url' const sut = new DbAddPet({ guardianRepository: guardianRepositoryStub, petRepository: petRepositoryStub, appointPet: appointPetStub, fileStorage: fileStorageStub, - defaultImageUrl + defaultPetImageUrl }) return { From d960a824cdcc16b813a51e54ba26df01c8e8b5cb Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 12:57:06 -0300 Subject: [PATCH 16/19] test: delete property of fakeUser in pet route --- tests/src/main/routes/pet-routes.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/main/routes/pet-routes.test.ts b/tests/src/main/routes/pet-routes.test.ts index e9a3bc1e..4e7fbb38 100644 --- a/tests/src/main/routes/pet-routes.test.ts +++ b/tests/src/main/routes/pet-routes.test.ts @@ -150,6 +150,7 @@ describe('Pet Routes', () => { Reflect.deleteProperty(fakeUser, 'accessToken') Reflect.deleteProperty(fakeUser, 'verificationToken') Reflect.deleteProperty(fakeUser, 'verificationTokenCreatedAt') + Reflect.deleteProperty(fakeUser, 'image') }) beforeEach(async () => { await prisma.pet.deleteMany() }) From 032abcaa4c1b7f0a57450f7ff9dd7199bb126810 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Mon, 9 Feb 2026 12:59:07 -0300 Subject: [PATCH 17/19] test: change test structure to multpart form data in signup route --- tests/src/main/routes/signup-routes.test.ts | 36 ++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/src/main/routes/signup-routes.test.ts b/tests/src/main/routes/signup-routes.test.ts index 1222ebd3..60e410b6 100644 --- a/tests/src/main/routes/signup-routes.test.ts +++ b/tests/src/main/routes/signup-routes.test.ts @@ -1,5 +1,6 @@ import request from 'supertest' import app from '@/main/config/app' +import path from 'node:path' import { PrismaHelper } from '@/tests/helpers/prisma-helper' describe('SignUp Routes', () => { @@ -9,35 +10,34 @@ describe('SignUp Routes', () => { afterAll(async () => { await PrismaHelper.disconnect() }) + const image = path.join(__dirname, '..', '..', '..', 'utils', 'images', 'guardian.jpg') it('Should return 409 if guardian already exists on database', async () => { await PrismaHelper.createGuardian() await request(app) .post('/api/signup') - .send({ - firstName: 'John', - lastName: 'Doe', - email: 'johndoe@email.com', - password: 'Test@1234', - passwordConfirmation: 'Test@1234', - phone: '11987654321', - isPrivacyPolicyAccepted: true - }) + .field('firstName', 'John') + .field('lastName', 'Doe') + .field('email', 'johndoe@email.com') + .field('password', 'Test@1234') + .field('passwordConfirmation', 'Test@1234') + .field('phone', '11987654321') + .field('isPrivacyPolicyAccepted', 'true') + .attach('image', image) .expect(409) }) it('Should return a guardian account on success', async () => { await request(app) .post('/api/signup') - .send({ - firstName: 'John', - lastName: 'Doe', - email: 'johndoe@email.com', - password: 'Test@1234', - passwordConfirmation: 'Test@1234', - phone: '11987654321', - isPrivacyPolicyAccepted: true - }) + .field('firstName', 'John') + .field('lastName', 'Doe') + .field('email', 'johndoe@email.com') + .field('password', 'Test@1234') + .field('passwordConfirmation', 'Test@1234') + .field('phone', '11987654321') + .field('isPrivacyPolicyAccepted', 'true') + .attach('image', image) .expect(201) }) }) From 74f150ab8e3055dcaca41b9fd3ac9cdb3b0b228a Mon Sep 17 00:00:00 2001 From: Anderson Sales Date: Thu, 12 Feb 2026 13:59:51 -0300 Subject: [PATCH 18/19] Update src/infra/repos/postgresql/guardian-account-repository.ts Co-authored-by: Matheus de Gondra <65628233+matheusgondra@users.noreply.github.com> --- .../postgresql/guardian-account-repository.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/infra/repos/postgresql/guardian-account-repository.ts b/src/infra/repos/postgresql/guardian-account-repository.ts index 24de7352..7249c01c 100644 --- a/src/infra/repos/postgresql/guardian-account-repository.ts +++ b/src/infra/repos/postgresql/guardian-account-repository.ts @@ -140,22 +140,20 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository, } async updateImage (params: UpdateGuardianImageRepository.Params): Promise { + const { guardianId, image } = params const { guardianId, image } = params const result = await db.guardian.update({ where: { id: guardianId }, data: { image }, - select: { - id: true, - firstName: true, - lastName: true, - email: true, - phone: true, - verificationToken: false, - emailConfirmation: true, - image: true + omit: { + password: true, + verificationToken: true, + verificationTokenCreatedAt: true, } }) return result + + return result } } From a3a41aeb9a810590e06b784f07dd478813780012 Mon Sep 17 00:00:00 2001 From: andersonnaz Date: Thu, 12 Feb 2026 14:06:27 -0300 Subject: [PATCH 19/19] refactor: change select to omit in prisma update query --- src/infra/repos/postgresql/guardian-account-repository.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/infra/repos/postgresql/guardian-account-repository.ts b/src/infra/repos/postgresql/guardian-account-repository.ts index 7249c01c..b86bcfb5 100644 --- a/src/infra/repos/postgresql/guardian-account-repository.ts +++ b/src/infra/repos/postgresql/guardian-account-repository.ts @@ -140,7 +140,6 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository, } async updateImage (params: UpdateGuardianImageRepository.Params): Promise { - const { guardianId, image } = params const { guardianId, image } = params const result = await db.guardian.update({ where: { id: guardianId }, @@ -148,12 +147,10 @@ implements AddGuardianRepository, LoadGuardianByEmailRepository, omit: { password: true, verificationToken: true, - verificationTokenCreatedAt: true, + verificationTokenCreatedAt: true } }) return result - - return result } }