import { mocked } from 'jest-mock' ; import { lastValueFrom } from 'rxjs' ; import { Test , TestingModule } from '@nestjs/testing' ; import { validateDto } from '@fc/common' ; import { ConfigService } from '@fc/config' ; import { LoggerService } from '@fc/logger' ; import { BridgeHttpProxyProtocolDto } from '../dto' ; import { BridgeHttpProxyMissingVariableException , BridgeHttpProxyRabbitmqException , } from '../exceptions' ; import { BridgeHttpProxyService } from './bridge-http-proxy.service' ; jest . mock ( 'rxjs' ) ; jest . mock ( '@fc/common' ) ; describe ( 'BridgeHttpProxyService' , ( ) => { let service ; const loggerServiceMock = { debug : jest . fn ( ) , setContext : jest . fn ( ) , trace : jest . fn ( ) , } as unknown as LoggerService ; const configMock = { get : jest . fn ( ) , } ; const brokerMock = { send : jest . fn ( ) , } ; const messageMock = { pipe : jest . fn ( ) , } ; const brokerResponseMock = 'brokerResponseMock' ; const originalUrlMock = '/bizz/bud' ; const headersMock = { host : 'foo.fr' , 'x-forwarded-proto' : 'https' , } ; const axiosResponseResolvedMock = { headers : { host : 'host' } , data : 'foo' , status : 200 , } ; const validatorOptions = { forbidNonWhitelisted : true , forbidUnknownValues : true , skipMissingProperties : false , whitelist : true , } ; let lastValueMock ; let validateDtoMock ; beforeEach ( async ( ) => { jest . resetAllMocks ( ) ; jest . restoreAllMocks ( ) ; const module = await Test . createTestingModule ( { providers : [ BridgeHttpProxyService , LoggerService , ConfigService , { provide : 'BridgeProxyBroker' , useValue : brokerMock , } , ] , } ) . overrideProvider ( ConfigService ) . useValue ( configMock ) . overrideProvider ( LoggerService ) . useValue ( loggerServiceMock ) . compile ( ) ; service = module . get < BridgeHttpProxyService > ( BridgeHttpProxyService ) ; configMock . get . mockReturnValueOnce ( { requestTimeout : 6000 , } ) ; lastValueMock = mocked ( lastValueFrom ) ; validateDtoMock = mocked ( validateDto , true ) ; } ) ; it ( 'should be defined' , ( ) => { expect ( service ) . toBeDefined ( ) ; } ) ; describe ( 'proxyRequest' , ( ) => { const method = 'GET' ; describe ( '' , ( ) => { beforeEach ( ( ) => { brokerMock . send . mockReturnValue ( messageMock ) ; messageMock . pipe . mockReturnValue ( brokerResponseMock ) ; lastValueMock . mockResolvedValueOnce ( axiosResponseResolvedMock ) ; validateDtoMock . mockResolvedValueOnce ( [ ] ) ; } ) ; it ( 'should call once createMessage() private method' , async ( ) => { service [ 'createMessage' ] = jest . fn ( ) ; const bodyMock = 'toto=titi' ; await service . proxyRequest ( originalUrlMock , method , headersMock , bodyMock ) ; expect ( service [ 'createMessage' ] ) . toHaveBeenCalledTimes ( 1 ) ; expect ( service [ 'createMessage' ] ) . toHaveBeenCalledWith ( originalUrlMock , 'GET' , { host : 'foo.fr' , 'x-forwarded-proto' : 'https' , } , 'toto=titi' ) ; } ) ; it ( 'should log method proxyRequest() called' , async ( ) => { await service . proxyRequest ( originalUrlMock , method , headersMock ) ; expect ( loggerServiceMock . debug ) . toHaveBeenCalledTimes ( 1 ) ; expect ( loggerServiceMock . debug ) . toHaveBeenCalledWith ( 'BrokerProxyController.proxyRequest()' ) ; } ) ; it ( 'should validate the format of the identity received from broker thanks to the dto' , async ( ) => { await service . proxyRequest ( originalUrlMock , method , headersMock ) ; expect ( validateDtoMock ) . toHaveBeenCalledTimes ( 1 ) ; expect ( validateDtoMock ) . toHaveBeenCalledWith ( axiosResponseResolvedMock , BridgeHttpProxyProtocolDto , validatorOptions ) ; } ) ; it ( 'should return result received from broker' , async ( ) => { const expected = { headers : { host : 'host' } , data : 'foo' , status : 200 , } ; const result = await service . proxyRequest ( originalUrlMock , method , headersMock ) ; expect ( result ) . toStrictEqual ( expected ) ; } ) ; } ) ; it ( 'should throw an exception if data are missing' , async ( ) => { const axiosResponseResolvedMock = { headers : { host : 'host' } , status : 200 , } ; brokerMock . send . mockReturnValue ( messageMock ) ; messageMock . pipe . mockReturnValue ( brokerResponseMock ) ; lastValueMock . mockResolvedValueOnce ( axiosResponseResolvedMock ) ; validateDtoMock . mockResolvedValueOnce ( [ { property : 'invalid param' } ] ) ; await expect ( service . proxyRequest ( originalUrlMock , method , headersMock ) ) . rejects . toThrow ( BridgeHttpProxyMissingVariableException ) ; expect ( loggerServiceMock . trace ) . toHaveBeenCalledTimes ( 1 ) ; expect ( loggerServiceMock . trace ) . toHaveBeenCalledWith ( { dtoProtocolErrors : [ { property : 'invalid param' } ] , } ) ; } ) ; it ( 'should throw an exception if an error occurs when calling the broker' , async ( ) => { brokerMock . send . mockReturnValue ( messageMock ) ; messageMock . pipe . mockReturnValue ( brokerResponseMock ) ; lastValueMock . mockRejectedValue ( { message : 'error message' , status : 400 , } ) ; validateDtoMock . mockResolvedValueOnce ( [ ] ) ; await expect ( service . proxyRequest ( originalUrlMock , method , headersMock ) ) . rejects . toThrow ( BridgeHttpProxyRabbitmqException ) ; expect ( loggerServiceMock . trace ) . toHaveBeenCalledTimes ( 1 ) ; expect ( loggerServiceMock . trace ) . toHaveBeenCalledWith ( { error : { message : 'error message' , status : 400 } , } ) ; } ) ; } ) ; describe ( 'createMessage' , ( ) => { it ( 'should prepare and create message for the broker call' , ( ) => { const method = 'GET' ; const bodyMock = 'toto=titi' ; const expected = { headers : { host : 'foo.fr' , 'x-forwarded-proto' : 'https' , } , method : 'GET' , data : 'toto=titi' , url : 'https://foo.fr/bizz/bud' , } ; const result = service [ 'createMessage' ] ( originalUrlMock , method , headersMock , bodyMock ) ; expect ( result ) . toStrictEqual ( expected ) ; } ) ; it ( 'should prepare and create message for the broker call, even if body is an empty object' , ( ) => { const method = 'POST' ; const expected = { data : undefined , headers : { host : 'foo.fr' , 'x-forwarded-proto' : 'https' , } , method : 'POST' , url : 'https://foo.fr/bizz/bud' , } ; const result = service [ 'createMessage' ] ( originalUrlMock , method , headersMock ) ; expect ( result ) . toStrictEqual ( expected ) ; } ) ; } ) ; describe ( 'setHeaders' , ( ) => { it ( 'should set headers value' , ( ) => { const resMock = { set : jest . fn ( ) , } ; service . setHeaders ( resMock , headersMock ) ; expect ( resMock . set ) . toHaveBeenCalledTimes ( 2 ) ; expect ( resMock . set ) . toHaveBeenCalledWith ( 'host' , 'foo.fr' ) ; expect ( resMock . set ) . toHaveBeenCalledWith ( 'x-forwarded-proto' , 'https' ) ; } ) ; } ) ; } ) ;