import { Test, TestingModule } from '@nestjs/testing' import { datatype } from 'faker' import { plainToClass } from 'class-transformer' import { MockedModule } from '../../../test/types' import { Person, SmartfitApi, SmartfitHelper } from '../smartfit' import { factory } from '../../../test/factories' import { Patient, SmartfitUnit } from '../../models' import { LanguageCountryCode } from '../../utils' import { TeleconsultationsCreditService } from '../credits/teleconsultations_credit.service' import { UnauthorizedError, UserNotFoundError } from '../../errors' import { PatientRepository } from '../../repositories/patient.repository' import { SmartfitUnitRepository } from '../../repositories/smartfit_unit.repository' import { AuthenticationService } from './authentication.service' import { AccountService, SMARTFIT_TENANT } from './account.service' describe('AccountService', () => { let accountService: AccountService let patientRepository: MockedModule let authenticationService: MockedModule let smartfitAPI: MockedModule let teleconsultationsCreditService: MockedModule let smartfitUnitRepository: MockedModule let patient: Patient beforeAll(async () => { patientRepository = { findByLogin: jest.fn(), create: jest.fn(), updateBasicData: jest.fn(), } authenticationService = { delete: jest.fn(), getUserByEmail: jest.fn(), createPatient: jest.fn(), getUser: jest.fn(), } smartfitAPI = { getByLogin: jest.fn(), getPersonByToken: jest.fn(), getPersonByLogin: jest.fn(), } teleconsultationsCreditService = { createTransaction: jest.fn(), } smartfitUnitRepository = { findByAcronym: jest.fn(), } const app: TestingModule = await Test.createTestingModule({ providers: [ { provide: PatientRepository, useValue: patientRepository, }, { provide: AuthenticationService, useValue: authenticationService, }, { provide: SmartfitApi, useValue: smartfitAPI, }, { provide: TeleconsultationsCreditService, useValue: teleconsultationsCreditService, }, { provide: SmartfitUnitRepository, useValue: smartfitUnitRepository, }, AccountService, ], }).compile() accountService = app.get(AccountService) }) afterEach(() => { jest.clearAllMocks() }) afterAll(() => { jest.restoreAllMocks() }) describe('#createOrUpdatePatient', () => { const login = datatype.uuid() beforeAll(() => { process.env.STAGE = 'prod' }) describe('when the patient exists', () => { let person: Person beforeEach(async () => { patient = await factory.build('patient', { tenant: SMARTFIT_TENANT }) person = await factory.build('smartfit:person', { email: patient.email, phone: patient.cell_phone }) smartfitAPI.getPersonByLogin.mockResolvedValue(person) patientRepository.findByLogin.mockResolvedValue(patient) authenticationService.createPatient.mockResolvedValue({ uid: patient.id }) }) describe("and there isn't an authentication user", () => { beforeEach(() => { authenticationService.getUser.mockResolvedValue(null) }) describe("and already has an authentication with the patient's email", () => { beforeEach(() => { authenticationService.getUserByEmail.mockResolvedValue({}) }) it('creates an user on authentication service', async () => { await accountService.createOrUpdatePatient(login) expect(authenticationService.createPatient).toHaveBeenCalledWith({ id: patient.id, name: patient.name, country_code: patient.country_code, }) }) }) describe("and hasn't an authentication with the patient's email", () => { beforeEach(() => { authenticationService.getUserByEmail.mockResolvedValue(null) }) it('creates an user on authentication service', async () => { await accountService.createOrUpdatePatient(login) expect(authenticationService.createPatient).toHaveBeenCalledWith({ id: patient.id, email: patient.email, name: patient.name, country_code: patient.country_code, }) }) }) }) describe('and patient isn`t a smartfit person', () => { beforeEach(() => { person = undefined smartfitAPI.getPersonByLogin.mockResolvedValue(person) }) it('doens`t updates the patient', () => { expect(patientRepository.updateBasicData).not.toBeCalled() }) }) describe('and person phone is not same patient cell phone', () => { beforeEach(async () => { person = await factory.build('smartfit:person', { email: patient.email }) smartfitAPI.getPersonByLogin.mockResolvedValue(person) }) it('updates patient`s cell_phone', async () => { await accountService.createOrUpdatePatient(login) const patient = plainToClass(Patient, { cell_phone: person.phone, country_code: LanguageCountryCode.PT_BR }) expect(patientRepository.updateBasicData).toBeCalledWith( expect.objectContaining({ cell_phone: patient.cell_phone }), ) }) describe('when is different production environment', () => { beforeEach(() => { process.env.STAGE = 'dev' }) afterEach(() => { process.env.STAGE = 'prod' }) it('doens`t updates the patient', async () => { await accountService.createOrUpdatePatient(login) expect(patientRepository.updateBasicData).not.toBeCalled() }) }) }) describe('and person email isn`t same as patient email', () => { beforeEach(async () => { person = await factory.build('smartfit:person') smartfitAPI.getPersonByLogin.mockResolvedValue(person) }) describe('and patient is BR', () => { beforeEach(async () => { patient = await factory.build('patient', { tenant: SMARTFIT_TENANT, country_code: LanguageCountryCode.PT_BR, cell_phone: person.phone, }) patientRepository.findByLogin.mockResolvedValue(patient) patientRepository.updateBasicData.mockRestore() await accountService.createOrUpdatePatient(login) }) it('updates patient email', () => { expect(patientRepository.updateBasicData).toBeCalledWith(expect.objectContaining({ email: person.email })) }) describe('when is different production environment', () => { beforeAll(() => { process.env.STAGE = 'dev' }) afterAll(() => { process.env.STAGE = 'prod' }) it('doens`t updates the patient', async () => { expect(patientRepository.updateBasicData).not.toBeCalled() }) }) }) describe('and patient is MX', () => { beforeEach(async () => { patient = await factory.build('patient', { tenant: SMARTFIT_TENANT, country_code: LanguageCountryCode.ES_MX, cell_phone: person.phone, }) patientRepository.findByLogin.mockResolvedValue(patient) patientRepository.updateBasicData.mockRestore() await accountService.createOrUpdatePatient(login) }) it('doesn`t updates patient`s email', () => { expect(patientRepository.updateBasicData).not.toBeCalled() }) }) }) }) describe('when the patient doest not exist', () => { let person: Person beforeEach(() => { patientRepository.findByLogin.mockResolvedValue(null) }) describe('and he`s a Smartfit student', () => { const uid = datatype.uuid() beforeEach(async () => { person = await factory.build('smartfit:person') patient = new Patient({ cell_phone: person.phone, email: person.email, name: person.name, country_code: SmartfitHelper.getCountryCodeByLocationCountry(person.locationCountry), }) smartfitAPI.getByLogin.mockResolvedValue({ token: datatype.uuid() }) smartfitAPI.getPersonByToken.mockResolvedValue(person) authenticationService.createPatient.mockResolvedValue({ uid }) patientRepository.create.mockResolvedValue(datatype.uuid()) }) describe("and already has an authentication with the patient's email", () => { beforeEach(() => { authenticationService.getUserByEmail.mockResolvedValue({}) }) it('creates an user on authentication service', async () => { await accountService.createOrUpdatePatient(login) expect(authenticationService.createPatient).toHaveBeenCalledWith({ name: patient.name, country_code: patient.country_code, }) }) }) describe("and hasn't an authentication with the patient's email", () => { beforeEach(() => { authenticationService.getUserByEmail.mockResolvedValue(null) }) it('creates an user on authentication service', async () => { await accountService.createOrUpdatePatient(login) expect(authenticationService.createPatient).toHaveBeenCalledWith({ email: patient.email, name: patient.name, country_code: patient.country_code, }) }) }) describe('when patient has location acronym', () => { let smartfitUnit: SmartfitUnit beforeEach(() => { smartfitUnitRepository.findByAcronym.mockResolvedValue(smartfitUnit) }) describe('and smartfit unit id is already registered', () => { beforeAll(async () => { smartfitUnit = await factory.build('smartfit_unit') }) beforeEach(() => { smartfitUnit.acronym = person.locationAcronym }) it('creates the patient with a smartfit_unit_acronym', async () => { await accountService.createOrUpdatePatient(login) expect(patientRepository.create).toHaveBeenCalledWith( expect.objectContaining({ smartfit_unit_acronym: smartfitUnit.acronym, }), ) }) }) describe("and smartfit unit id isn't already registered", () => { beforeAll(async () => { smartfitUnit = null }) it('creates the patient with a null smartfit_unit_acronym', async () => { await accountService.createOrUpdatePatient(login) expect(patientRepository.create).toHaveBeenCalledWith( expect.objectContaining({ smartfit_unit_acronym: null, }), ) }) }) }) it('creates the patient with the generated token', async () => { await accountService.createOrUpdatePatient(login) expect(patientRepository.create).toHaveBeenCalledWith( expect.objectContaining({ email: person.email, document_number: login, }), ) }) }) describe('and he is not an Smart student', () => { beforeEach(async () => { person = await factory.build('smartfit:person') patient = new Patient({ cell_phone: person.phone, email: person.email }) smartfitAPI.getByLogin.mockImplementationOnce(async (_login: string) => Promise.reject(new Error())) }) it('throws an UserNotFoundError', async () => { await expect(accountService.createOrUpdatePatient(login)).rejects.toThrowError(UserNotFoundError) }) }) }) }) describe('#createPatientAndAuth', () => { beforeEach(async () => { patient = await factory.build('patient') }) describe('when the patient does not exist', () => { const uid = datatype.uuid() beforeEach(() => { authenticationService.createPatient.mockResolvedValue({ uid }) patientRepository.create.mockResolvedValue(datatype.uuid()) patientRepository.findByLogin.mockResolvedValue(null) }) it('creates the patient with the generated token', async () => { await accountService.createPatientAndAuth(patient) expect(patientRepository.create).toHaveBeenCalledWith( expect.objectContaining({ id: uid, email: patient.email, document_number: patient.document_number, }), ) }) describe('when patient is a collaborator', () => { beforeEach(() => { patient.collaborator = true }) it('add teleconsultations credit', async () => { await accountService.createPatientAndAuth(patient) expect(teleconsultationsCreditService.createTransaction).toBeCalledWith( expect.objectContaining({ reason: 'collaborator_signup' }), ) }) }) }) describe('when the patient already exists', () => { beforeEach(() => { patientRepository.findByLogin.mockResolvedValue(patient) }) it('throws an error', async () => { await expect(accountService.createPatientAndAuth(patient)).rejects.toThrowError(UnauthorizedError) }) }) }) })