import { mocked } from 'jest-mock' ; import { Test , TestingModule } from '@nestjs/testing' ; import { PartialExcept , validateDto } from '@fc/common' ; import { LoggerService } from '@fc/logger' ; import { IOidcIdentity } from '@fc/oidc' ; import { IEventContext , TrackingService } from '@fc/tracking' ; import { OidcClientTokenEvent , OidcClientUserinfoEvent } from '../events' ; import { OidcClientUserinfosFailedException } from '../exceptions' ; import { ExtraTokenParams , TokenParams , UserInfosParams } from '../interfaces' ; import { OidcClientService } from './oidc-client.service' ; import { OidcClientUtilsService } from './oidc-client-utils.service' ; jest . mock ( '@fc/common' , ( ) => ( { ... ( jest . requireActual ( '@fc/common' ) as any ) , validateDto : jest . fn ( ) , } ) ) ; describe ( 'OidcClientService' , ( ) => { let service ; let validateDtoMock ; const postLogoutRedirectUriMock = 'https://postLogoutRedirectUriMock' ; const idpIdMock = 'idpIdMockValue' ; const idpStateMock = 'idpStateMockValue' ; const idpNonceMock = 'idpNonceMockValue' ; const acrMock = 'acrMockValue' ; const amrMock = [ 'amrMockValue' ] ; const accessTokenMock = 'accessTokenMockValue' ; const idTokenMock = 'idTokenMockValue' ; const contextMock = { hello : 'world' , } ; const loggerServiceMock = { setContext : jest . fn ( ) , trace : jest . fn ( ) , debug : jest . fn ( ) , error : jest . fn ( ) , businessEvent : jest . fn ( ) , } as unknown as LoggerService ; const trackingServiceMock = { track : jest . fn ( ) , } ; const oidcClientUtilsServiceMock = { getTokenSet : jest . fn ( ) , getUserInfo : jest . fn ( ) , getEndSessionUrl : jest . fn ( ) , } ; const claimsMock = jest . fn ( ) ; const tokenResultMock = { access_token : accessTokenMock , id_token : idTokenMock , claims : claimsMock , } ; const errorMock = new Error ( 'Unknown Error' ) ; const tokenParamsMock = { state : idpStateMock , nonce : idpNonceMock , } ; const extraParamsMock = { foo : 'bar' , } ; const identityMock = { sub : 'xxxxxxyyyyy1122334455667788' , given_name : 'jean-paul' , family_name : 'rive' , } ; const userInfosParamsMock = { accessToken : accessTokenMock , idpId : idpIdMock , } ; beforeEach ( async ( ) => { jest . resetAllMocks ( ) ; jest . restoreAllMocks ( ) ; const module = await Test . createTestingModule ( { providers : [ LoggerService , TrackingService , OidcClientUtilsService , OidcClientService , ] , } ) . overrideProvider ( OidcClientUtilsService ) . useValue ( oidcClientUtilsServiceMock ) . overrideProvider ( TrackingService ) . useValue ( trackingServiceMock ) . overrideProvider ( LoggerService ) . useValue ( loggerServiceMock ) . compile ( ) ; service = module . get < OidcClientService > ( OidcClientService ) ; validateDtoMock = mocked ( validateDto ) ; } ) ; describe ( 'constructor' , ( ) => { it ( 'should be defined' , ( ) => { expect ( service ) . toBeDefined ( ) ; } ) ; } ) ; describe ( 'getTokenFromProvider' , ( ) => { beforeEach ( ( ) => { claimsMock . mockReturnValueOnce ( { acr : acrMock , amr : amrMock } ) ; oidcClientUtilsServiceMock . getTokenSet . mockResolvedValueOnce ( tokenResultMock ) ; validateDtoMock . mockResolvedValueOnce ( [ ] ) . mockResolvedValueOnce ( [ ] ) ; } ) ; it ( 'should call getTokenSet with token params' , async ( ) => { await service . getTokenFromProvider ( idpIdMock , tokenParamsMock , contextMock , extraParamsMock ) ; expect ( oidcClientUtilsServiceMock . getTokenSet ) . toHaveBeenCalledTimes ( 1 ) ; expect ( oidcClientUtilsServiceMock . getTokenSet ) . toHaveBeenCalledWith ( contextMock , idpIdMock , tokenParamsMock , extraParamsMock ) ; } ) ; it ( 'should get the the tokenSet from the provider' , async ( ) => { const resultMock = { accessToken : accessTokenMock , idToken : idTokenMock , acr : acrMock , amr : amrMock , } ; const result = await service . getTokenFromProvider ( idpIdMock , tokenParamsMock , contextMock ) ; expect ( result ) . toStrictEqual ( resultMock ) ; } ) ; todo it . skip ( 'should failed if the params for token are wrong' , async ( ) => { validateDtoMock . mockReset ( ) . mockReturnValueOnce ( [ errorMock ] ) ; await expect ( ( ) => service . getTokenFromProvider ( idpIdMock , tokenParamsMock , contextMock ) ) . rejects . toThrow ( '"{"providerUid":"providerUidMockValue","idpState":"idpStateMockValue","idpNonce":"idpNonceMockValue"}" input was wrong from the result at DTO validation: [{}]' ) ; expect ( oidcClientUtilsServiceMock . getTokenSet ) . toHaveBeenCalledTimes ( 0 ) ; } ) ; it ( 'should failed if the token is wrong and DTO blocked' , async ( ) => { const expectedError = new Error ( '"{"acr":"acrMockValue","amr":["amrMockValue"],"accessToken":"accessTokenMockValue","idToken":"idTokenMockValue"}" input was wrong from the result at DTO validation: [{}]' ) ; validateDtoMock . mockReset ( ) . mockReturnValueOnce ( [ errorMock ] ) ; await expect ( ( ) => service . getTokenFromProvider ( idpIdMock , tokenParamsMock , contextMock ) ) . rejects . toThrow ( expectedError ) ; expect ( oidcClientUtilsServiceMock . getTokenSet ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should track the token event' , async ( ) => { await service . getTokenFromProvider ( idpIdMock , tokenParamsMock , contextMock ) ; expect ( trackingServiceMock . track ) . toHaveBeenCalledTimes ( 1 ) ; expect ( trackingServiceMock . track ) . toHaveBeenCalledWith ( OidcClientTokenEvent , contextMock ) ; } ) ; it ( 'should get claims from token' , async ( ) => { const { acr } = await service . getTokenFromProvider ( idpIdMock , tokenParamsMock , contextMock ) ; expect ( claimsMock ) . toHaveBeenCalledTimes ( 1 ) ; expect ( acr ) . toStrictEqual ( acrMock ) ; } ) ; } ) ; describe ( 'getUserInfosFromProvider' , ( ) => { beforeEach ( ( ) => { oidcClientUtilsServiceMock . getUserInfo . mockResolvedValueOnce ( identityMock ) ; validateDtoMock . mockResolvedValueOnce ( [ ] ) . mockResolvedValueOnce ( [ ] ) ; } ) ; it ( 'should get the user infos' , async ( ) => { const result = await service . getUserInfosFromProvider ( userInfosParamsMock , contextMock ) ; expect ( result ) . toStrictEqual ( identityMock ) ; } ) ; it ( 'should get the user infos with access token params' , async ( ) => { await service . getUserInfosFromProvider ( userInfosParamsMock , contextMock ) ; expect ( oidcClientUtilsServiceMock . getUserInfo ) . toHaveBeenCalledTimes ( 1 ) ; expect ( oidcClientUtilsServiceMock . getUserInfo ) . toHaveBeenCalledWith ( accessTokenMock , idpIdMock ) ; } ) ; it ( 'should failed if userinfos failed' , async ( ) => { const errorMock = new Error ( 'Unknown Error' ) ; oidcClientUtilsServiceMock . getUserInfo . mockReset ( ) . mockRejectedValueOnce ( errorMock ) ; await expect ( ( ) => service . getUserInfosFromProvider ( userInfosParamsMock , contextMock ) ) . rejects . toThrow ( OidcClientUserinfosFailedException ) ; expect ( oidcClientUtilsServiceMock . getUserInfo ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should failed if the token is wrong and DTO blocked' , async ( ) => { validateDtoMock . mockReset ( ) . mockReturnValueOnce ( [ errorMock ] ) ; await expect ( ( ) => service . getUserInfosFromProvider ( userInfosParamsMock , contextMock ) ) . rejects . toThrow ( '"idpIdMockValue" doesn\'t provide a minimum identity information: [{}]' ) ; expect ( oidcClientUtilsServiceMock . getUserInfo ) . toHaveBeenCalledTimes ( 1 ) ; } ) ; it ( 'should track the userinfos event' , async ( ) => { await service . getUserInfosFromProvider ( userInfosParamsMock , contextMock ) ; expect ( trackingServiceMock . track ) . toHaveBeenCalledTimes ( 1 ) ; expect ( trackingServiceMock . track ) . toHaveBeenCalledWith ( OidcClientUserinfoEvent , contextMock ) ; } ) ; } ) ; describe ( 'getEndSessionUrlFromProvider' , ( ) => { it ( 'should call oidcClientUtilsServiceMock.getEndSessionUrl() with given parameters' , async ( ) => { await service . getEndSessionUrlFromProvider ( idpIdMock , idpStateMock , idTokenMock , postLogoutRedirectUriMock ) ; expect ( oidcClientUtilsServiceMock . getEndSessionUrl ) . toHaveBeenCalledTimes ( 1 ) ; expect ( oidcClientUtilsServiceMock . getEndSessionUrl ) . toHaveBeenCalledWith ( idpIdMock , idpStateMock , idTokenMock , postLogoutRedirectUriMock ) ; } ) ; } ) ; } ) ;