@@ -16,7 +16,9 @@ import {
1616 applyRemoteConfiguration ,
1717 buildEndpoint ,
1818 fetchRemoteConfiguration ,
19+ getRemoteConfiguration ,
1920} from './remoteConfiguration'
21+ import { buildCacheKey } from './remoteConfigurationCache'
2022
2123const DEFAULT_INIT_CONFIGURATION : RumInitConfiguration = {
2224 clientToken : 'xxx' ,
@@ -749,4 +751,121 @@ describe('remoteConfiguration', () => {
749751 expect ( buildEndpoint ( { remoteConfigurationProxy : '/config' } as RumInitConfiguration ) ) . toEqual ( '/config' )
750752 } )
751753 } )
754+
755+ describe ( 'async loading (getRemoteConfiguration)' , ( ) => {
756+ const REMOTE_CONFIGURATION_ID = 'rc-test-id'
757+ const CACHE_KEY = buildCacheKey ( REMOTE_CONFIGURATION_ID )
758+ const FRESH_RUM_CONFIG : RumRemoteConfiguration = { applicationId : 'fresh-app' }
759+ const CACHED_RUM_CONFIG : RumRemoteConfiguration = { applicationId : 'cached-app' }
760+
761+ let initConfiguration : RumInitConfiguration
762+ let supportedContextManagers : {
763+ user : ReturnType < typeof createContextManager >
764+ context : ReturnType < typeof createContextManager >
765+ }
766+ let interceptor : ReturnType < typeof interceptRequests >
767+ let displaySpy : jasmine . Spy
768+
769+ function withCachedEntry ( config : RumRemoteConfiguration ) {
770+ localStorage . setItem ( CACHE_KEY , JSON . stringify ( { version : 1 , config, fetchedAt : 1000 } ) )
771+ }
772+
773+ function withFetchSuccess ( config : RumRemoteConfiguration = FRESH_RUM_CONFIG ) {
774+ interceptor . withFetch ( ( ) => Promise . resolve ( { ok : true , json : ( ) => Promise . resolve ( { rum : config } ) } ) )
775+ }
776+
777+ function withFetchFailure ( ) {
778+ interceptor . withFetch ( ( ) => Promise . reject ( new Error ( 'Network error' ) ) )
779+ }
780+
781+ async function flushBackgroundSync ( ) {
782+ await interceptor . waitForAllFetchCalls ( )
783+ await new Promise < void > ( ( resolve ) => setTimeout ( resolve ) )
784+ }
785+
786+ beforeEach ( ( ) => {
787+ initConfiguration = {
788+ ...DEFAULT_INIT_CONFIGURATION ,
789+ applicationId : 'init-app' ,
790+ remoteConfiguration : { id : REMOTE_CONFIGURATION_ID } ,
791+ }
792+ supportedContextManagers = { user : createContextManager ( ) , context : createContextManager ( ) }
793+ interceptor = interceptRequests ( )
794+ displaySpy = spyOn ( display , 'error' )
795+
796+ registerCleanupTask ( ( ) => {
797+ localStorage . clear ( )
798+ } )
799+ } )
800+
801+ it ( 'should return init configuration on cache miss' , async ( ) => {
802+ withFetchSuccess ( )
803+
804+ const result = getRemoteConfiguration ( initConfiguration , supportedContextManagers )
805+
806+ expect ( result ) . toBe ( initConfiguration )
807+ await flushBackgroundSync ( )
808+ } )
809+
810+ it ( 'should apply cached configuration to init on cache hit' , async ( ) => {
811+ withCachedEntry ( CACHED_RUM_CONFIG )
812+ withFetchSuccess ( )
813+
814+ const result = getRemoteConfiguration ( initConfiguration , supportedContextManagers )
815+
816+ expect ( result . applicationId ) . toBe ( 'cached-app' )
817+ expect ( result . clientToken ) . toBe ( 'xxx' )
818+ await flushBackgroundSync ( )
819+ } )
820+
821+ it ( 'should return init configuration on cache error and remove the corrupted entry' , async ( ) => {
822+ localStorage . setItem ( CACHE_KEY , 'not-json' )
823+ withFetchSuccess ( )
824+
825+ const result = getRemoteConfiguration ( initConfiguration , supportedContextManagers )
826+
827+ expect ( result ) . toBe ( initConfiguration )
828+ expect ( localStorage . getItem ( CACHE_KEY ) ) . toBeNull ( )
829+ await flushBackgroundSync ( )
830+ } )
831+
832+ it ( 'should write the fetched configuration to cache on background fetch success' , async ( ) => {
833+ withFetchSuccess ( )
834+
835+ getRemoteConfiguration ( initConfiguration , supportedContextManagers )
836+ await flushBackgroundSync ( )
837+
838+ const stored = JSON . parse ( localStorage . getItem ( CACHE_KEY ) ! )
839+ expect ( stored . config ) . toEqual ( FRESH_RUM_CONFIG )
840+ expect ( stored . version ) . toBe ( 1 )
841+ } )
842+
843+ it ( 'should not overwrite cache when background fetch fails' , async ( ) => {
844+ withCachedEntry ( CACHED_RUM_CONFIG )
845+ withFetchFailure ( )
846+
847+ getRemoteConfiguration ( initConfiguration , supportedContextManagers )
848+ await flushBackgroundSync ( )
849+
850+ const stored = JSON . parse ( localStorage . getItem ( CACHE_KEY ) ! )
851+ expect ( stored . config ) . toEqual ( CACHED_RUM_CONFIG )
852+ expect ( displaySpy ) . toHaveBeenCalled ( )
853+ } )
854+
855+ it ( 'should always trigger a background fetch regardless of cache state' , async ( ) => {
856+ withCachedEntry ( CACHED_RUM_CONFIG )
857+ const fetchSpy = withFetchSuccessReturningSpy ( )
858+
859+ getRemoteConfiguration ( initConfiguration , supportedContextManagers )
860+ await flushBackgroundSync ( )
861+
862+ expect ( fetchSpy ) . toHaveBeenCalledTimes ( 1 )
863+
864+ function withFetchSuccessReturningSpy ( ) {
865+ return interceptor . withFetch ( ( ) =>
866+ Promise . resolve ( { ok : true , json : ( ) => Promise . resolve ( { rum : FRESH_RUM_CONFIG } ) } )
867+ )
868+ }
869+ } )
870+ } )
752871} )
0 commit comments