import { LoadFacebookUser , TokenGenerator } from '@/domain/contracts/gateways' ; import { LoadUserAccount , SaveFacebookAccount } from '@/domain/contracts/repos' ; import { AccessToken , FacebookAccount , AuthenticationError } from '@/domain/entities' ; import { setupFacebookAuthentication , FacebookAuthentication } from '@/domain/use-cases' ; import { mocked } from 'ts-jest/utils' ; import { mock , MockProxy } from 'jest-mock-extended' ; jest . mock ( '@/domain/entities/facebook-account' ) ; describe ( 'FacebookAuthentication' , ( ) => { let facebookApi ; let crypto ; let userAccountRepo ; let sut ; let token ; beforeAll ( ( ) => { token = 'any_token' ; facebookApi = mock ( ) ; facebookApi . loadUser . mockResolvedValue ( { name : 'any_fb_name' , email : 'any_fb_email' , facebookId : 'any_fb_id' } ) ; userAccountRepo = mock ( ) ; userAccountRepo . load . mockResolvedValue ( undefined ) ; userAccountRepo . saveWithFacebook . mockResolvedValue ( { id : 'any_account_id' } ) ; crypto = mock ( ) ; crypto . generate . mockResolvedValue ( 'any_generated_token' ) ; } ) ; beforeEach ( ( ) => { sut = setupFacebookAuthentication ( facebookApi , userAccountRepo , crypto ) ; } ) ; it ( 'should call LoadFacebookUser with correct input' , async ( ) => { await sut ( { token } ) ; expect ( facebookApi . loadUser ) . toHaveBeenCalledWith ( { token } ) ; expect ( facebookApi . loadUser ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should throw AuthenticationError when LoadFacebookUser returns undefined' , async ( ) => { facebookApi . loadUser . mockResolvedValueOnce ( undefined ) ; const promise = sut ( { token } ) ; await expect ( promise ) . rejects . toThrow ( new AuthenticationError ( ) ) ; } ) ; it ( 'should call LoadUserAccountRepo when LoadFacebookUser returns data' , async ( ) => { await sut ( { token } ) ; expect ( userAccountRepo . load ) . toHaveBeenCalledWith ( { email : 'any_fb_email' } ) ; expect ( userAccountRepo . load ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should call SaveFacebookAccount with FacebookAccount' , async ( ) => { await sut ( { token } ) ; expect ( userAccountRepo . saveWithFacebook ) . toHaveBeenCalledWith ( ... mocked ( FacebookAccount ) . mock . instances ) ; expect ( userAccountRepo . saveWithFacebook ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should call TokenGenerator with correct input' , async ( ) => { await sut ( { token } ) ; expect ( crypto . generate ) . toHaveBeenCalledWith ( { key : 'any_account_id' , expirationInMs : AccessToken . expirationInMs } ) ; expect ( crypto . generate ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should return an AccessToken on success' , async ( ) => { const authOutput = await sut ( { token } ) ; expect ( authOutput ) . toEqual ( { accessToken : 'any_generated_token' } ) ; } ) ; it ( 'should rethrow if LoadFacebookUser throws' , async ( ) => { facebookApi . loadUser . mockRejectedValueOnce ( new Error ( 'fb_error' ) ) ; const promise = sut ( { token } ) ; await expect ( promise ) . rejects . toThrow ( new Error ( 'fb_error' ) ) ; } ) ; it ( 'should rethrow if LoadUserAccount throws' , async ( ) => { userAccountRepo . load . mockRejectedValueOnce ( new Error ( 'load_error' ) ) ; const promise = sut ( { token } ) ; await expect ( promise ) . rejects . toThrow ( new Error ( 'load_error' ) ) ; } ) ; it ( 'should rethrow if SaveFacebookAccount throws' , async ( ) => { userAccountRepo . saveWithFacebook . mockRejectedValueOnce ( new Error ( 'save_error' ) ) ; const promise = sut ( { token } ) ; await expect ( promise ) . rejects . toThrow ( new Error ( 'save_error' ) ) ; } ) ; it ( 'should rethrow if TokenGenerator throws' , async ( ) => { crypto . generate . mockRejectedValueOnce ( new Error ( 'token_error' ) ) ; const promise = sut ( { token } ) ; await expect ( promise ) . rejects . toThrow ( new Error ( 'token_error' ) ) ; } ) ; } ) ;