From 077e9d08a024f0c6e06c444b0885e3f592262683 Mon Sep 17 00:00:00 2001 From: Lucas Emanuel Pereira Ribeiro Date: Sun, 24 Aug 2025 23:54:56 -0300 Subject: [PATCH 1/3] feat: add unit tests for mail service --- test/mocks/mail/company-mail.mock.ts | 32 ++ test/mocks/mail/job-mail.mock.ts | 50 +++ test/mocks/mail/mail-options.mock.ts | 17 + test/mocks/mail/user-mail.mock.ts | 32 ++ .../mail/services/mail.service.spec.ts | 342 ++++++++++++++++++ 5 files changed, 473 insertions(+) create mode 100644 test/mocks/mail/company-mail.mock.ts create mode 100644 test/mocks/mail/job-mail.mock.ts create mode 100644 test/mocks/mail/mail-options.mock.ts create mode 100644 test/mocks/mail/user-mail.mock.ts create mode 100644 test/modules/mail/services/mail.service.spec.ts diff --git a/test/mocks/mail/company-mail.mock.ts b/test/mocks/mail/company-mail.mock.ts new file mode 100644 index 00000000..6d59d743 --- /dev/null +++ b/test/mocks/mail/company-mail.mock.ts @@ -0,0 +1,32 @@ +import { CompaniesEntity } from '../../../src/database/entities/companies.entity'; + +export const companyMailMock = (): CompaniesEntity => ({ + id: '123e4567-e89b-12d3-a456-426614174000', + companyName: 'Test Company LTDA', + email: 'company@example.com', + cnpj: '12345678000199', + password: 'hashedPassword123', + recoverPasswordToken: 'company-recover-token-456', + mailConfirm: false, + created_at: new Date('2023-01-01T00:00:00.000Z'), + updated_at: new Date('2023-01-01T00:00:00.000Z'), + companyType: 'Tecnologia', + companySize: 'SMALL_SIZE', + uf: 'SP', + otherSite: { + instagran: 'https://instagram.com/testcompany', + linkedin: 'https://linkedin.com/company/testcompany', + twitter: 'https://twitter.com/testcompany', + }, + companySite: 'https://testcompany.com.br', + description: 'Empresa de tecnologia focada em soluções inovadoras', + profile: null, + profileKey: null, + jobs: [], +}); + +export const companyMailWithoutTokenMock = (): CompaniesEntity => ({ + ...companyMailMock(), + recoverPasswordToken: null, + mailConfirm: true, +}); diff --git a/test/mocks/mail/job-mail.mock.ts b/test/mocks/mail/job-mail.mock.ts new file mode 100644 index 00000000..24f70709 --- /dev/null +++ b/test/mocks/mail/job-mail.mock.ts @@ -0,0 +1,50 @@ +import { StatusEnum } from '../../../src/shared/enums/status.enum'; +import { JobsEntity } from '../../../src/database/entities/jobs.entity'; +import { companyMailMock } from './company-mail.mock'; + +export const jobMailMock = (): JobsEntity => ({ + id: '456e7890-e89b-12d3-a456-426614174001', + title: 'Desenvolvedor Frontend Junior', + description: 'Vaga para desenvolvedor frontend com foco em React', + prerequisites: 'Conhecimento em React, JavaScript, HTML, CSS', + benefits: 'Vale alimentação, Vale transporte, Plano de saúde', + type: 'JUNIOR', + typeContract: 'CLT', + contractText: null, + salaryMin: 3000, + salaryMax: 5000, + modality: 'REMOTE', + federalUnit: 'SP', + city: 'São Paulo', + openEndedContract: true, + contractType: null, + affirmative: false, + affirmativeType: null, + status: StatusEnum.ACTIVE, + content: null, + company_id: '123e4567-e89b-12d3-a456-426614174000', + createdAt: new Date('2023-01-01T00:00:00.000Z'), + updatedAt: new Date('2023-01-01T00:00:00.000Z'), + company: companyMailMock(), + applications: [], + savedJobs: [], + comments: [], +}); + +export const jobMailListMock = (): JobsEntity[] => [ + jobMailMock(), + { + ...jobMailMock(), + id: '456e7890-e89b-12d3-a456-426614174002', + title: 'Desenvolvedor Backend Junior', + description: 'Vaga para desenvolvedor backend com foco em Node.js', + prerequisites: 'Conhecimento em Node.js, TypeScript, Banco de dados', + }, + { + ...jobMailMock(), + id: '456e7890-e89b-12d3-a456-426614174003', + title: 'Desenvolvedor Fullstack Junior', + description: 'Vaga para desenvolvedor fullstack', + prerequisites: 'Conhecimento em React, Node.js, TypeScript', + }, +]; diff --git a/test/mocks/mail/mail-options.mock.ts b/test/mocks/mail/mail-options.mock.ts new file mode 100644 index 00000000..bab6b4e9 --- /dev/null +++ b/test/mocks/mail/mail-options.mock.ts @@ -0,0 +1,17 @@ +import { jobMailListMock } from './job-mail.mock'; + +export const mailOptionsMock = () => ({ + subject: 'Teste de Email', + template: './test-template', + context: { + name: 'Usuario Teste', + message: 'Esta é uma mensagem de teste', + url: 'https://example.com/test', + }, + email: 'test@example.com', +}); + +export const jobAlertMailOptionsMock = () => ({ + email: 'candidate@example.com', + jobs: jobMailListMock(), +}); diff --git a/test/mocks/mail/user-mail.mock.ts b/test/mocks/mail/user-mail.mock.ts new file mode 100644 index 00000000..54597f17 --- /dev/null +++ b/test/mocks/mail/user-mail.mock.ts @@ -0,0 +1,32 @@ +import { UsersEntity } from '../../../src/database/entities/users.entity'; + +export const userMailMock = (): UsersEntity => ({ + id: '729c7919-583c-40a5-b0ca-137e282345d4', + name: 'Test User', + email: 'user@example.com', + password: 'hashedPassword123', + recoverPasswordToken: 'recover-token-123', + mailConfirm: false, + type: 'USER', + policies: true, + created_at: new Date('2023-01-01T00:00:00.000Z'), + updated_at: new Date('2023-01-01T00:00:00.000Z'), + ip: '127.0.0.1', + mainPhone: '11999999999', + phone: '1133333333', + city: 'São Paulo', + state: 'SP', + profile: null, + profileKey: null, + personalData: null, + curriculums: [], + applications: [], + candidacies: [], + savedJobs: [], +}); + +export const userMailWithoutTokenMock = (): UsersEntity => ({ + ...userMailMock(), + recoverPasswordToken: null, + mailConfirm: true, +}); diff --git a/test/modules/mail/services/mail.service.spec.ts b/test/modules/mail/services/mail.service.spec.ts new file mode 100644 index 00000000..bdb30e79 --- /dev/null +++ b/test/modules/mail/services/mail.service.spec.ts @@ -0,0 +1,342 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MailerService } from '@nestjs-modules/mailer'; +import { ConfigModule } from '@nestjs/config'; +import { MailService } from '../../../../src/modules/mails/mail.service'; +import { + userMailMock, + userMailWithoutTokenMock, +} from '../../../mocks/mail/user-mail.mock'; +import { + companyMailMock, + companyMailWithoutTokenMock, +} from '../../../mocks/mail/company-mail.mock'; +import { + jobMailListMock, + jobMailMock, +} from '../../../mocks/mail/job-mail.mock'; +import { mailOptionsMock } from '../../../mocks/mail/mail-options.mock'; + +const mailerServiceMock = { + sendMail: jest.fn(), +}; + +describe('MailService', () => { + let service: MailService; + let mailerService: { sendMail: jest.Mock }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [ConfigModule.forRoot()], + providers: [ + MailService, + { + provide: MailerService, + useValue: mailerServiceMock, + }, + ], + }).compile(); + + service = module.get(MailService); + mailerService = module.get(MailerService); + + process.env.FRONTEND_URL = 'https://frontend.test.com'; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); + + describe('sendUserConfirmation', () => { + it('should send password recovery email when user has recoverPasswordToken', async () => { + const user = userMailMock(); + mailerService.sendMail.mockResolvedValue(undefined); + + await service.sendUserConfirmation(user); + + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + expect(mailerService.sendMail).toHaveBeenCalledWith({ + to: user.email, + subject: 'Recuperação de Senha!', + template: './send', + context: { + name: user.name, + url: `${process.env.FRONTEND_URL}/recovery-password?token=${user.recoverPasswordToken}&type=USER`, + }, + }); + }); + + it('should send password update confirmation email when user has no recoverPasswordToken', async () => { + const user = userMailWithoutTokenMock(); + mailerService.sendMail.mockResolvedValue(undefined); + + await service.sendUserConfirmation(user); + + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + expect(mailerService.sendMail).toHaveBeenCalledWith({ + to: user.email, + subject: 'Senha alterada com Sucesso!', + template: './passwordupdate', + context: { + name: user.name, + url: `${process.env.FRONTEND_URL}/recovery-password?token=null&type=USER`, + }, + }); + }); + }); + + describe('sendUserCreationConfirmation', () => { + it('should send user creation confirmation email', async () => { + const user = userMailMock(); + mailerService.sendMail.mockResolvedValue(undefined); + + await service.sendUserCreationConfirmation(user); + + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + expect(mailerService.sendMail).toHaveBeenCalledWith({ + to: user.email, + subject: 'Usuário criado!', + template: './confirmEmailUser', + context: { + name: user.name, + url: `${process.env.FRONTEND_URL}/userconfirmation?id=${user.id}&type=USER`, + }, + }); + }); + + it('should return void when email is sent successfully', async () => { + const user = userMailMock(); + mailerService.sendMail.mockResolvedValue(undefined); + const result = await service.sendUserCreationConfirmation(user); + expect(result).toBeUndefined(); + }); + }); + + describe('sendCompanyConfirmation', () => { + it('should send password recovery email when company has recoverPasswordToken', async () => { + const company = companyMailMock(); + mailerService.sendMail.mockResolvedValue(undefined); + + await service.sendCompanyConfirmation(company); + + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + expect(mailerService.sendMail).toHaveBeenCalledWith({ + to: company.email, + subject: 'Recuperação de Senha!', + template: './send', + context: { + name: company.companyName, + url: `${process.env.FRONTEND_URL}/recovery-password?token=${company.recoverPasswordToken}&type=COMPANY`, + }, + }); + }); + + it('should send password update confirmation email when company has no recoverPasswordToken', async () => { + const company = companyMailWithoutTokenMock(); + mailerService.sendMail.mockResolvedValue(undefined); + + await service.sendCompanyConfirmation(company); + + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + expect(mailerService.sendMail).toHaveBeenCalledWith({ + to: company.email, + subject: 'Senha alterada com Sucesso!', + template: './passwordupdate', + context: { + name: company.companyName, + }, + }); + }); + }); + + describe('sendCompanyCreationConfirmation', () => { + it('should send company creation confirmation email', async () => { + const company = companyMailMock(); + mailerService.sendMail.mockResolvedValue(undefined); + + await service.sendCompanyCreationConfirmation(company); + + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + expect(mailerService.sendMail).toHaveBeenCalledWith({ + to: company.email, + subject: 'Empresa criado!', + template: './confirmEmailCompany', + context: { + name: company.companyName, + url: `${process.env.FRONTEND_URL}/companyconfirmation?id=${company.id}&type=COMPANY`, + }, + }); + }); + + it('should return void when email is sent successfully', async () => { + const company = companyMailMock(); + mailerService.sendMail.mockResolvedValue(undefined); + const result = await service.sendCompanyCreationConfirmation(company); + expect(result).toBeUndefined(); + }); + }); + + describe('sendJobAlerts', () => { + it('should send job alerts email with jobs list', async () => { + const email = 'candidate@example.com'; + const jobs = jobMailListMock(); + mailerService.sendMail.mockResolvedValue(undefined); + + await service.sendJobAlerts(email, jobs); + + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + expect(mailerService.sendMail).toHaveBeenCalledWith({ + to: email, + subject: 'Vagas Relevantes para Você', + template: './jobsAlert', + context: { + jobs, + }, + }); + }); + + it('should send job alerts email with empty jobs list', async () => { + const email = 'candidate@example.com'; + const jobs = []; + mailerService.sendMail.mockResolvedValue(undefined); + + await service.sendJobAlerts(email, jobs); + + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + expect(mailerService.sendMail).toHaveBeenCalledWith({ + to: email, + subject: 'Vagas Relevantes para Você', + template: './jobsAlert', + context: { + jobs, + }, + }); + }); + + it('should send job alerts email with single job', async () => { + const email = 'candidate@example.com'; + const jobs = [jobMailMock()]; + mailerService.sendMail.mockResolvedValue(undefined); + + await service.sendJobAlerts(email, jobs); + + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + expect(mailerService.sendMail).toHaveBeenCalledWith({ + to: email, + subject: 'Vagas Relevantes para Você', + template: './jobsAlert', + context: { + jobs, + }, + }); + }); + }); + + describe('sendMail', () => { + it('should send generic email with provided parameters', async () => { + const mailOptions = mailOptionsMock(); + mailerService.sendMail.mockResolvedValue(undefined); + + await service.sendMail(mailOptions); + + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + expect(mailerService.sendMail).toHaveBeenCalledWith({ + to: mailOptions.email, + subject: mailOptions.subject, + template: mailOptions.template, + context: mailOptions.context, + }); + }); + + it('should return void when email is sent successfully', async () => { + const mailOptions = mailOptionsMock(); + mailerService.sendMail.mockResolvedValue(undefined); + const result = await service.sendMail(mailOptions); + expect(result).toBeUndefined(); + }); + }); + + describe('error handling', () => { + it('should handle mailer service errors in sendUserConfirmation', async () => { + const user = userMailMock(); + const error = new Error('SMTP connection failed'); + mailerService.sendMail.mockRejectedValue(error); + + await expect(service.sendUserConfirmation(user)).rejects.toThrow( + 'SMTP connection failed', + ); + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + }); + + it('should handle mailer service errors in sendUserCreationConfirmation', async () => { + const user = userMailMock(); + const error = new Error('Invalid email template'); + mailerService.sendMail.mockRejectedValue(error); + + await expect(service.sendUserCreationConfirmation(user)).rejects.toThrow( + 'Invalid email template', + ); + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + }); + + it('should handle mailer service errors in sendCompanyConfirmation', async () => { + const company = companyMailMock(); + const error = new Error('Authentication failed'); + mailerService.sendMail.mockRejectedValue(error); + + await expect(service.sendCompanyConfirmation(company)).rejects.toThrow( + 'Authentication failed', + ); + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + }); + + it('should handle mailer service errors in sendCompanyCreationConfirmation', async () => { + const company = companyMailMock(); + const error = new Error('Template not found'); + mailerService.sendMail.mockRejectedValue(error); + + await expect( + service.sendCompanyCreationConfirmation(company), + ).rejects.toThrow('Template not found'); + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + }); + + it('should handle mailer service errors in sendJobAlerts', async () => { + const email = 'candidate@example.com'; + const jobs = jobMailListMock(); + const error = new Error('Rate limit exceeded'); + mailerService.sendMail.mockRejectedValue(error); + + await expect(service.sendJobAlerts(email, jobs)).rejects.toThrow( + 'Rate limit exceeded', + ); + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + }); + + it('should handle mailer service errors in sendMail', async () => { + const mailOptions = mailOptionsMock(); + const error = new Error('Invalid recipient email'); + mailerService.sendMail.mockRejectedValue(error); + + await expect(service.sendMail(mailOptions)).rejects.toThrow( + 'Invalid recipient email', + ); + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + }); + + it('should handle network timeout errors', async () => { + const user = userMailMock(); + const error = new Error('Network timeout'); + error.name = 'TimeoutError'; + mailerService.sendMail.mockRejectedValue(error); + + await expect(service.sendUserConfirmation(user)).rejects.toThrow( + 'Network timeout', + ); + expect(mailerService.sendMail).toHaveBeenCalledTimes(1); + }); + }); +}); From a723c06753fedfa6f75c2fed69c2f4a9598adcd2 Mon Sep 17 00:00:00 2001 From: Lucas Emanuel Pereira Ribeiro Date: Sun, 24 Aug 2025 23:57:12 -0300 Subject: [PATCH 2/3] fix: remove invalid savedJobs property from job mock --- test/mocks/mail/job-mail.mock.ts | 1 - test/mocks/mail/user-mail.mock.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/test/mocks/mail/job-mail.mock.ts b/test/mocks/mail/job-mail.mock.ts index 24f70709..c63d6138 100644 --- a/test/mocks/mail/job-mail.mock.ts +++ b/test/mocks/mail/job-mail.mock.ts @@ -27,7 +27,6 @@ export const jobMailMock = (): JobsEntity => ({ updatedAt: new Date('2023-01-01T00:00:00.000Z'), company: companyMailMock(), applications: [], - savedJobs: [], comments: [], }); diff --git a/test/mocks/mail/user-mail.mock.ts b/test/mocks/mail/user-mail.mock.ts index 54597f17..48e28662 100644 --- a/test/mocks/mail/user-mail.mock.ts +++ b/test/mocks/mail/user-mail.mock.ts @@ -22,7 +22,6 @@ export const userMailMock = (): UsersEntity => ({ curriculums: [], applications: [], candidacies: [], - savedJobs: [], }); export const userMailWithoutTokenMock = (): UsersEntity => ({ From a7cc78a50f8ea76ee531956bfa839e19aa356a1b Mon Sep 17 00:00:00 2001 From: Lucas Emanuel Pereira Ribeiro Date: Sat, 1 Nov 2025 21:49:26 -0300 Subject: [PATCH 3/3] fix: corrigir imports do MailService e atualizar mensagens dos testes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Corrigir imports relativos em mail.service.spec.ts e update-password-by-email.service.ts - Adicionar MailService mock em update-password-by-email.service.spec.ts - Atualizar mensagens esperadas de inglês para português nos testes --- .DS_Store | Bin 0 -> 6148 bytes docker-compose.yml | 30 ++++++++++------- package-lock.json | 28 +++++++++------- src/database/entities/companies.entity.ts | 2 +- src/modules/mails/mail.module.ts | 2 +- src/modules/mails/mail.service.ts | 2 +- src/modules/user/dtos/create-user.dto.ts | 11 +++---- .../update-password-by-email.service.ts | 2 +- test/mocks/mail/company-mail.mock.ts | 31 +++++++++++------- test/mocks/mail/job-mail.mock.ts | 4 +-- test/mocks/mail/user-mail.mock.ts | 4 +-- .../mail/services/mail.service.spec.ts | 4 +-- ...recovery-password-by-email.service.spec.ts | 4 +-- .../update-password-by-email.service.spec.ts | 17 ++++++++-- 14 files changed, 84 insertions(+), 57 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5bfcd8bf3af6e416100b22e282b1a6bc6d343102 GIT binary patch literal 6148 zcmeHKu}&N@5PjxW0tJLFG=QL3zVPqeb%mb0jQN>rvcOfOt1+iAK1NM;+Iyk zF*~zJRI*0D+t_I5gZ)xuoQMiU1^!0`$eYbpKW}l4FN5OK z>GIXG@9(_o-|so^hV&0pF+Qq`9)@Un)iHijj_^6);x5x1f^`PD%cLsG0bb1!f9 zpA@g60#Si?rGQGB8_gBol092XHz#Lp%C^NOCUJ#9ox;u@$9f<~@f@2r`cfGXGmC{m R&d}nAfR-V4QGtJ|z$4+C+uQ&E literal 0 HcmV?d00001 diff --git a/docker-compose.yml b/docker-compose.yml index 502e0ed3..e7f7a0f6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,38 +1,46 @@ version: '3.9' services: - database_soujunior: - image: postgres - container_name: database_soujunior + postgres: + image: postgres:16-alpine + container_name: postgres restart: always ports: - 5432:5432 environment: - - POSTGRES_USER=docker - - POSTGRES_PASSWORD=ignite - - POSTGRES_DB=linkedin_backend + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=123 + - POSTGRES_DB=vagas-api volumes: - - pgdata:/data/postgres + - pgdata:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready"] + test: ['CMD-SHELL', 'pg_isready -U postgres'] interval: 10s timeout: 5s retries: 5 + mailhog: + image: mailhog/mailhog + container_name: mailhog + restart: always + ports: + - 1025:1025 + - 8025:8025 + app: build: . - container_name: linkedIn_Backend + container_name: vagas_api restart: always ports: - 3000:3000 volumes: - .:/user/app depends_on: - database_soujunior: + postgres: condition: service_healthy env_file: - .env volumes: pgdata: - driver: local \ No newline at end of file + driver: local diff --git a/package-lock.json b/package-lock.json index f18d68a3..474f3f82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "@nestjs/schematics": "^10.0.2", "@nestjs/testing": "^10.2.6", "@types/bcrypt": "^5.0.0", + "@types/chance": "^1.1.7", "@types/express": "^4.17.18", "@types/jest": "29.5.5", "@types/node": "^20.8.2", @@ -58,6 +59,7 @@ "@types/uuid": "^9.0.4", "@typescript-eslint/eslint-plugin": "^6.7.4", "@typescript-eslint/parser": "^6.7.4", + "chance": "^1.1.13", "eslint": "^8.50.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", @@ -2715,6 +2717,13 @@ "@types/node": "*" } }, + "node_modules/@types/chance": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@types/chance/-/chance-1.1.7.tgz", + "integrity": "sha512-40you9610GTQPJyvjMBgmj9wiDO6qXhbfjizNYod/fmvLSfUUxURAJMTD8tjmbcZSsyYE5iEUox61AAcCjW/wQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -2736,18 +2745,6 @@ "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", "optional": true }, - "node_modules/@types/eslint": { - "version": "8.56.3", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.3.tgz", - "integrity": "sha512-PvSf1wfv2wJpVIFUMSb+i4PvqNYkB9Rkp9ZDO3oaWzq4SKhsQk4mrMBr3ZH06I0hKrVGLBacmgl8JM4WVjb9dg==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -4189,6 +4186,13 @@ "node": ">=8" } }, + "node_modules/chance": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.13.tgz", + "integrity": "sha512-V6lQCljcLznE7tUYUM9EOAnnKXbctE6j/rdQkYOHIWbfGQbrzTsAXNW9CdU5XCo4ArXQCj/rb6HgxPlmGJcaUg==", + "dev": true, + "license": "MIT" + }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", diff --git a/src/database/entities/companies.entity.ts b/src/database/entities/companies.entity.ts index 13d78484..e7abfd2b 100644 --- a/src/database/entities/companies.entity.ts +++ b/src/database/entities/companies.entity.ts @@ -63,7 +63,7 @@ export class CompaniesEntity { @Column({ type: 'json', nullable: true }) otherSite: { - instagran: string; + instagram: string; linkedin: string; twitter: string; }; diff --git a/src/modules/mails/mail.module.ts b/src/modules/mails/mail.module.ts index a25829de..656fb3a8 100644 --- a/src/modules/mails/mail.module.ts +++ b/src/modules/mails/mail.module.ts @@ -12,7 +12,7 @@ import { MailService } from './mail.service'; useFactory: async (config: ConfigService) => ({ transport: { host: config.get('MAIL_HOST'), - port: config.get('MAIL_PORT'), + port: config.get('MAIL_PORT'), // secure: true, // secure: false, auth: { diff --git a/src/modules/mails/mail.service.ts b/src/modules/mails/mail.service.ts index d6abb58d..c30b688a 100644 --- a/src/modules/mails/mail.service.ts +++ b/src/modules/mails/mail.service.ts @@ -87,7 +87,7 @@ export class MailService { await this.mailerService.sendMail({ to: email, - subject: 'Empresa criado!', + subject: 'Empresa criada!', template: './confirmEmailCompany', context: { name: companyName, diff --git a/src/modules/user/dtos/create-user.dto.ts b/src/modules/user/dtos/create-user.dto.ts index 7f5c940d..8ef8ab92 100644 --- a/src/modules/user/dtos/create-user.dto.ts +++ b/src/modules/user/dtos/create-user.dto.ts @@ -34,13 +34,10 @@ export class CreateUserDto { @IsNotEmpty() @IsString() - @Matches( - /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W)[a-zA-Z\d\W]{8,}$/, - { - message: - 'Senha inválida. Deve conter pelo menos 8 caracteres, uma letra maiúscula, uma letra minúscula, um número e um caractere especial.', - }, - ) + @Matches(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W)[a-zA-Z\d\W]{8,}$/, { + message: + 'Senha inválida. Deve conter pelo menos 8 caracteres, uma letra maiúscula, uma letra minúscula, um número e um caractere especial.', + }) @ApiProperty({ description: 'Senha de Login', example: 'Abcd@1234', diff --git a/src/modules/user/services/update-password-by-email.service.ts b/src/modules/user/services/update-password-by-email.service.ts index c13ab1c3..640650bc 100644 --- a/src/modules/user/services/update-password-by-email.service.ts +++ b/src/modules/user/services/update-password-by-email.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import * as bcrypt from 'bcrypt'; -import { MailService } from 'src/modules/mails/mail.service'; +import { MailService } from '../../mails/mail.service'; import { CreatePasswordHashDto } from '../dtos/update-my-password.dto'; import { UserRepository } from '../repository/user.repository'; diff --git a/test/mocks/mail/company-mail.mock.ts b/test/mocks/mail/company-mail.mock.ts index 6d59d743..31b50b35 100644 --- a/test/mocks/mail/company-mail.mock.ts +++ b/test/mocks/mail/company-mail.mock.ts @@ -1,25 +1,32 @@ +import { Chance } from 'chance'; import { CompaniesEntity } from '../../../src/database/entities/companies.entity'; +const chance = new Chance(); + export const companyMailMock = (): CompaniesEntity => ({ - id: '123e4567-e89b-12d3-a456-426614174000', - companyName: 'Test Company LTDA', - email: 'company@example.com', + id: chance.guid(), + companyName: chance.company(), + email: chance.email(), cnpj: '12345678000199', - password: 'hashedPassword123', - recoverPasswordToken: 'company-recover-token-456', + password: chance.hash({ length: 60 }), + recoverPasswordToken: chance.string({ + length: 32, + alpha: true, + numeric: true, + }), mailConfirm: false, - created_at: new Date('2023-01-01T00:00:00.000Z'), - updated_at: new Date('2023-01-01T00:00:00.000Z'), + created_at: new Date(), + updated_at: new Date(), companyType: 'Tecnologia', companySize: 'SMALL_SIZE', uf: 'SP', otherSite: { - instagran: 'https://instagram.com/testcompany', - linkedin: 'https://linkedin.com/company/testcompany', - twitter: 'https://twitter.com/testcompany', + instagram: chance.url(), + linkedin: chance.url(), + twitter: chance.url(), }, - companySite: 'https://testcompany.com.br', - description: 'Empresa de tecnologia focada em soluções inovadoras', + companySite: chance.url(), + description: chance.sentence(), profile: null, profileKey: null, jobs: [], diff --git a/test/mocks/mail/job-mail.mock.ts b/test/mocks/mail/job-mail.mock.ts index c63d6138..61d5e3fe 100644 --- a/test/mocks/mail/job-mail.mock.ts +++ b/test/mocks/mail/job-mail.mock.ts @@ -23,8 +23,8 @@ export const jobMailMock = (): JobsEntity => ({ status: StatusEnum.ACTIVE, content: null, company_id: '123e4567-e89b-12d3-a456-426614174000', - createdAt: new Date('2023-01-01T00:00:00.000Z'), - updatedAt: new Date('2023-01-01T00:00:00.000Z'), + createdAt: new Date(), + updatedAt: new Date(), company: companyMailMock(), applications: [], comments: [], diff --git a/test/mocks/mail/user-mail.mock.ts b/test/mocks/mail/user-mail.mock.ts index 48e28662..77225973 100644 --- a/test/mocks/mail/user-mail.mock.ts +++ b/test/mocks/mail/user-mail.mock.ts @@ -9,8 +9,8 @@ export const userMailMock = (): UsersEntity => ({ mailConfirm: false, type: 'USER', policies: true, - created_at: new Date('2023-01-01T00:00:00.000Z'), - updated_at: new Date('2023-01-01T00:00:00.000Z'), + created_at: new Date(), + updated_at: new Date(), ip: '127.0.0.1', mainPhone: '11999999999', phone: '1133333333', diff --git a/test/modules/mail/services/mail.service.spec.ts b/test/modules/mail/services/mail.service.spec.ts index bdb30e79..73a99691 100644 --- a/test/modules/mail/services/mail.service.spec.ts +++ b/test/modules/mail/services/mail.service.spec.ts @@ -1,7 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { MailerService } from '@nestjs-modules/mailer'; import { ConfigModule } from '@nestjs/config'; -import { MailService } from '../../../../src/modules/mails/mail.service'; import { userMailMock, userMailWithoutTokenMock, @@ -15,6 +14,7 @@ import { jobMailMock, } from '../../../mocks/mail/job-mail.mock'; import { mailOptionsMock } from '../../../mocks/mail/mail-options.mock'; +import { MailService } from '../../../../src/modules/mails/mail.service'; const mailerServiceMock = { sendMail: jest.fn(), @@ -162,7 +162,7 @@ describe('MailService', () => { expect(mailerService.sendMail).toHaveBeenCalledTimes(1); expect(mailerService.sendMail).toHaveBeenCalledWith({ to: company.email, - subject: 'Empresa criado!', + subject: 'Empresa criada!', template: './confirmEmailCompany', context: { name: company.companyName, diff --git a/test/modules/user/services/recovery-password-by-email.service.spec.ts b/test/modules/user/services/recovery-password-by-email.service.spec.ts index feb57de2..bad3c3b0 100644 --- a/test/modules/user/services/recovery-password-by-email.service.spec.ts +++ b/test/modules/user/services/recovery-password-by-email.service.spec.ts @@ -55,7 +55,7 @@ describe('RecoveryPasswordByEmail', () => { ); const { status, data } = await service.execute('teste@teste.com'); const result = { - message: 'If email exists a email to recovery password was send', + message: 'Caso esse e-mail esteja cadastrado no sistema, será encaminhado para ele uma mensagem de orientação sobre os próximos passos para a redefinição da senha.', }; expect(status).toEqual(200); expect(data).toEqual(result); @@ -77,7 +77,7 @@ describe('RecoveryPasswordByEmail', () => { ); const { status, data } = await service.execute('teste@teste.com'); const result = { - message: 'If email exists a email to recovery password was send', + message: 'Caso esse e-mail esteja cadastrado no sistema, será encaminhado para ele uma mensagem de orientação sobre os próximos passos para a redefinição da senha.', }; expect(status).toEqual(200); expect(data).toEqual(result); diff --git a/test/modules/user/services/update-password-by-email.service.spec.ts b/test/modules/user/services/update-password-by-email.service.spec.ts index a9741b6e..93004e03 100644 --- a/test/modules/user/services/update-password-by-email.service.spec.ts +++ b/test/modules/user/services/update-password-by-email.service.spec.ts @@ -1,4 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; +import { MailService } from '../../../../src/modules/mails/mail.service'; import { UserRepository } from '../../../../src/modules/user/repository/user.repository'; import { UpdatePasswordByEmailService } from '../../../../src/modules/user/services/update-password-by-email.service'; import { userMock } from '../../../mocks/user/user.mock'; @@ -8,9 +9,14 @@ class UserRepositoryMock { updatePassword = jest.fn(); } +class MailServiceMock { + sendUserConfirmation = jest.fn().mockResolvedValue(''); +} + describe('UpdatePasswordByEmailService', () => { let service: UpdatePasswordByEmailService; let userRepository: UserRepositoryMock; + let mailService: MailServiceMock; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -20,11 +26,16 @@ describe('UpdatePasswordByEmailService', () => { provide: UserRepository, useClass: UserRepositoryMock, }, + { + provide: MailService, + useClass: MailServiceMock, + }, ], }).compile(); service = module.get(UpdatePasswordByEmailService); userRepository = module.get(UserRepository); + mailService = module.get(MailService); }); it('should be defined', () => { @@ -42,7 +53,7 @@ describe('UpdatePasswordByEmailService', () => { confirmPassword: 'password', }); expect(status).toEqual(400); - expect(data).toEqual({ message: 'User not found' }); + expect(data).toEqual({ message: 'Usuário não encontrado!' }); expect(findByTokenSpy).toBeCalled(); expect(findByTokenSpy).toBeCalledTimes(1); expect(updatePassword).not.toBeCalled(); @@ -58,7 +69,7 @@ describe('UpdatePasswordByEmailService', () => { confirmPassword: 'teste', }); expect(status).toEqual(400); - expect(data).toEqual({ message: 'Password mismatch' }); + expect(data).toEqual({ message: 'As senhas não conferem!' }); expect(findByTokenSpy).toBeCalled(); expect(findByTokenSpy).toBeCalledTimes(1); expect(updatePassword).not.toBeCalled(); @@ -75,7 +86,7 @@ describe('UpdatePasswordByEmailService', () => { confirmPassword: 'password', }); expect(status).toEqual(200); - expect(data).toEqual(userMock()); + expect(data).toEqual({ message: 'Senha redefinida com sucesso!' }); expect(findByTokenSpy).toBeCalled(); expect(findByTokenSpy).toBeCalledTimes(1); expect(updatePassword).toBeCalled();