import { Test, TestingModule } from '@nestjs/testing' import { datatype } from 'faker' import { sample } from 'lodash' import { factory } from '../../../test/factories' import { MockedModule } from '../../../test/types' import { Patient } from '../../models' import { LanguageCountryCode } from '../../utils' import { SMSArgs } from '../../models/types' import { PatientRepository } from '../../repositories' import { MailService } from '../mail' import { AwsService } from '../aws' import { ComteleService } from '../comtele' import { OS } from '../../params' import { AuthToken, AuthTokenHelper } from './helpers' import { ANDROID_REVIEWERS, IOS_REVIEWERS, REVIEWER_TOKEN, SendTo, TokenService } from './token.service' import { TranslateAuthenticationHelper } from './helpers/i18n' import { SMSTokenOptions } from '.' describe('TokenService', () => { let tokenService: TokenService let patientRepository: MockedModule let awsService: MockedModule let comteleService: MockedModule | any let mailService: MockedModule let authToken: AuthToken let patient: Patient let sendTo: SendTo let smsOptions: SMSTokenOptions const translateAuthenticationHelper = new TranslateAuthenticationHelper() beforeAll(async () => { patientRepository = { update: jest.fn(), } awsService = { sendSMS: jest.fn(), } comteleService = { sendSMS: jest.fn(), } mailService = { send: jest.fn(), } const app: TestingModule = await Test.createTestingModule({ providers: [ { provide: PatientRepository, useValue: patientRepository, }, { provide: AwsService, useValue: awsService, }, { provide: ComteleService, useValue: comteleService, }, { provide: MailService, useValue: mailService, }, { provide: TranslateAuthenticationHelper, useValue: translateAuthenticationHelper, }, TokenService, ], }).compile() tokenService = app.get(TokenService) }) beforeEach(() => { smsOptions = { appSignature: datatype.uuid(), os: sample([OS.ANDROID, OS.IOS]) } }) afterEach(() => { jest.clearAllMocks() }) afterAll(() => { jest.restoreAllMocks() }) describe('#sendAuthToken', () => { const originalBuildAuthToken = AuthTokenHelper.buildAuthToken let sendToSmsAndEmailSpy: jest.SpyInstance, [AuthToken, Patient, SendTo?, SMSTokenOptions?]> let buildAuthToken: jest.Mock beforeAll(async () => { sendToSmsAndEmailSpy = jest.spyOn(tokenService, 'sendToSmsAndEmail') patient = await factory.build('patient') sendTo = { sms: datatype.boolean(), email: datatype.boolean() } authToken = { code: datatype.uuid(), encryptedCode: datatype.uuid() } AuthTokenHelper.buildAuthToken = buildAuthToken = jest.fn().mockReturnValue(authToken) }) afterAll(() => { sendToSmsAndEmailSpy.mockRestore() buildAuthToken.mockRestore() }) it('updates the patient with the generated token', async () => { await tokenService.sendAuthToken(patient, sendTo, smsOptions) expect(patientRepository.update).toHaveBeenCalledWith( expect.objectContaining({ auth_token: authToken.encryptedCode, }), ) }) it('sends the code to sms and/or email', async () => { await tokenService.sendAuthToken(patient, sendTo, smsOptions) expect(sendToSmsAndEmailSpy).toHaveBeenCalledWith(authToken, patient, sendTo, smsOptions) }) it("returns the user's email and cell phone", async () => { expect(await tokenService.sendAuthToken(patient, sendTo, smsOptions)).toMatchObject({ email: patient.email, cellPhone: patient.cell_phone, }) }) describe('when the patient is a mobile reviewer', () => { beforeAll(() => { AuthTokenHelper.buildAuthToken = originalBuildAuthToken }) beforeEach(() => { patient.email = sample([...ANDROID_REVIEWERS, ...IOS_REVIEWERS]) }) it('updates the patient with the encripted reviewer token', async () => { await tokenService.sendAuthToken(patient, sendTo, smsOptions) const lastCall = patientRepository.update.mock.calls[patientRepository.update.mock.calls.length - 1] expect(AuthTokenHelper['tokenMatch'](REVIEWER_TOKEN, lastCall[0].auth_token)).toBeTruthy() }) }) }) describe('#sendToSmsAndEmail', () => { beforeAll(() => { authToken = { code: datatype.uuid(), encryptedCode: undefined } }) describe('when sms is true', () => { let smsParameters: SMSArgs beforeAll(() => { sendTo.sms = true }) afterAll(() => { sendTo.sms = undefined }) beforeEach(() => { smsParameters = { message: translateAuthenticationHelper.get(patient.country_code, 'sms.auth_token', { params: { code: authToken.code }, }), phoneNumber: patient.cell_phone, controlId: `${patient.id}/${authToken.code}`, } }) const checkAutoFill = () => { describe('when OS is Android', () => { beforeEach(() => { smsParameters.message = `${smsParameters.message} ${smsOptions.appSignature}` smsOptions.os = OS.ANDROID }) it('sends with autofill code', async () => { await tokenService.sendToSmsAndEmail(authToken, patient, sendTo, smsOptions) expect( (!comteleService.isEnabled || patient.country_code === LanguageCountryCode.ES_MX ? awsService : comteleService)['sendSMS'], ).toHaveBeenCalledWith(smsParameters) }) }) } describe('when patient is BR', () => { beforeAll(async () => { patient = await factory.build('patient', { country_code: LanguageCountryCode.PT_BR }) }) describe('when comtele is disabled', () => { beforeEach(() => { comteleService.isEnabled = false }) it('sends by AWS', async () => { await tokenService.sendToSmsAndEmail(authToken, patient, sendTo) expect(awsService.sendSMS).toHaveBeenCalledWith(smsParameters) }) checkAutoFill() }) describe('when comtele is enabled', () => { beforeEach(() => { comteleService.isEnabled = true }) it('sends by Comtele', async () => { await tokenService.sendToSmsAndEmail(authToken, patient, sendTo) expect(comteleService.sendSMS).toHaveBeenCalledWith(smsParameters) }) checkAutoFill() }) }) describe('when patient is MX', () => { beforeAll(async () => { patient = await factory.build('patient', { country_code: LanguageCountryCode.ES_MX }) }) it('sends by AWS', async () => { await tokenService.sendToSmsAndEmail(authToken, patient, sendTo) expect(awsService.sendSMS).toHaveBeenCalledWith(smsParameters) }) checkAutoFill() }) }) describe('when email is true', () => { let templateId: string beforeAll(async () => { sendTo.email = true patient = await factory.build('patient') templateId = translateAuthenticationHelper.get(patient.country_code, `email_templates.auth_token`) }) afterAll(() => { sendTo.email = undefined }) it(`send mail with correct templateId`, async () => { await tokenService.sendToSmsAndEmail(authToken, patient, sendTo) expect(mailService.send).toHaveBeenCalledWith(expect.objectContaining({ templateId })) }) }) describe.each` value ${null} ${undefined} ${false} `('when email parameter is "$value"', ({ value }) => { beforeAll(() => { sendTo.email = value }) it('doesn`t send email', async () => { await tokenService.sendToSmsAndEmail(authToken, patient, sendTo) expect(mailService.send).not.toHaveBeenCalled() }) }) describe.each` value ${null} ${undefined} ${false} `('when sms parameter is "$value"', ({ value }) => { beforeAll(() => { sendTo.sms = value }) it('doesn`t send SMS', async () => { await tokenService.sendToSmsAndEmail(authToken, patient, sendTo) expect(awsService.sendSMS).not.toHaveBeenCalled() expect(comteleService.sendSMS).not.toHaveBeenCalled() }) }) }) })