import { TestBed } from '@/testing'; import { lastValueFrom, of } from 'rxjs'; import { vi } from 'vitest'; import { LoggerService } from '../logging/logger.service'; import { EventTypes } from '../public-events/event-types'; import { PublicEventsService } from '../public-events/public-events.service'; import { StoragePersistenceService } from '../storage/storage-persistence.service'; import { mockAbstractProvider, mockProvider } from '../testing/mock'; import { PlatformProvider } from '../utils/platform-provider/platform.provider'; import { AuthWellKnownService } from './auth-well-known/auth-well-known.service'; import { ConfigurationService } from './config.service'; import { StsConfigLoader, StsConfigStaticLoader } from './loader/config-loader'; import type { OpenIdConfiguration } from './openid-configuration'; import { ConfigValidationService } from './validation/config-validation.service'; describe('Configuration Service', () => { let configService: ConfigurationService; let publicEventsService: PublicEventsService; let authWellKnownService: AuthWellKnownService; let storagePersistenceService: StoragePersistenceService; let configValidationService: ConfigValidationService; let platformProvider: PlatformProvider; let stsConfigLoader: StsConfigLoader; beforeEach(() => { TestBed.configureTestingModule({ providers: [ ConfigurationService, mockProvider(LoggerService), PublicEventsService, mockProvider(StoragePersistenceService), ConfigValidationService, mockProvider(PlatformProvider), mockProvider(AuthWellKnownService), mockAbstractProvider(StsConfigLoader, StsConfigStaticLoader), ], }); configService = TestBed.inject(ConfigurationService); publicEventsService = TestBed.inject(PublicEventsService); authWellKnownService = TestBed.inject(AuthWellKnownService); storagePersistenceService = TestBed.inject(StoragePersistenceService); stsConfigLoader = TestBed.inject(StsConfigLoader); platformProvider = TestBed.inject(PlatformProvider); configValidationService = TestBed.inject(ConfigValidationService); }); it('should create', () => { expect(configService).toBeTruthy(); }); describe('hasManyConfigs', () => { it('returns true if many configs are stored', () => { (configService as any).configsInternal = { configId1: { configId: 'configId1' }, configId2: { configId: 'configId2' }, }; const result = configService.hasManyConfigs(); expect(result).toBe(true); }); it('returns false if only one config is stored', () => { (configService as any).configsInternal = { configId1: { configId: 'configId1' }, }; const result = configService.hasManyConfigs(); expect(result).toBe(false); }); }); describe('getAllConfigurations', () => { it('returns all configs as array', () => { (configService as any).configsInternal = { configId1: { configId: 'configId1' }, configId2: { configId: 'configId2' }, }; const result = configService.getAllConfigurations(); expect(Array.isArray(result)).toBe(true); expect(result.length).toBe(2); }); }); describe('getOpenIDConfiguration', () => { it(`if config is already saved 'loadConfigs' is not called`, async () => { (configService as any).configsInternal = { configId1: { configId: 'configId1' }, configId2: { configId: 'configId2' }, }; const spy = vi.spyOn(configService as any, 'loadConfigs'); const config = await lastValueFrom( configService.getOpenIDConfiguration('configId1') ); expect(config).toBeTruthy(); expect(spy).not.toHaveBeenCalled(); }); it(`if config is NOT already saved 'loadConfigs' is called`, async () => { const configs = [{ configId: 'configId1' }, { configId: 'configId2' }]; const spy = vi .spyOn(configService as any, 'loadConfigs') .mockReturnValue(of(configs)); vi.spyOn(configValidationService, 'validateConfig').mockReturnValue(true); const config = await lastValueFrom( configService.getOpenIDConfiguration('configId1') ); expect(config).toBeTruthy(); expect(spy).toHaveBeenCalled(); }); it('returns null if config is not valid', async () => { const configs = [{ configId: 'configId1' }]; vi.spyOn(configService as any, 'loadConfigs').mockReturnValue( of(configs) ); vi.spyOn(configValidationService, 'validateConfig').mockReturnValue( false ); const consoleSpy = vi.spyOn(console, 'warn'); const config = await lastValueFrom( configService.getOpenIDConfiguration('configId1') ); expect(config).toBeNull(); expect(consoleSpy).toHaveBeenCalledExactlyOnceWith( `[oidc-client-rx] No configuration found for config id 'configId1'.` ); }); it('returns null if configs are stored but not existing ID is passed', async () => { (configService as any).configsInternal = { configId1: { configId: 'configId1' }, configId2: { configId: 'configId2' }, }; const config = await lastValueFrom( configService.getOpenIDConfiguration('notExisting') ); expect(config).toBeNull(); }); it('sets authWellKnownEndPoints on config if authWellKnownEndPoints is stored', async () => { const configs = [{ configId: 'configId1' }]; vi.spyOn(configService as any, 'loadConfigs').mockReturnValue( of(configs) ); vi.spyOn(configValidationService, 'validateConfig').mockReturnValue(true); const consoleSpy = vi.spyOn(console, 'warn'); vi.spyOn(storagePersistenceService, 'read').mockReturnValue({ issuer: 'auth-well-known', }); const config = await lastValueFrom( configService.getOpenIDConfiguration('configId1') ); expect(config?.authWellknownEndpoints).toEqual({ issuer: 'auth-well-known', }); expect(consoleSpy).not.toHaveBeenCalled(); }); it('fires ConfigLoaded if authWellKnownEndPoints is stored', async () => { const configs = [{ configId: 'configId1' }]; vi.spyOn(configService as any, 'loadConfigs').mockReturnValue( of(configs) ); vi.spyOn(configValidationService, 'validateConfig').mockReturnValue(true); vi.spyOn(storagePersistenceService, 'read').mockReturnValue({ issuer: 'auth-well-known', }); const spy = vi.spyOn(publicEventsService, 'fireEvent'); await lastValueFrom(configService.getOpenIDConfiguration('configId1')); expect(spy).toHaveBeenCalledExactlyOnceWith( EventTypes.ConfigLoaded, expect.anything() ); }); it('stores, uses and fires event when authwellknownendpoints are passed', async () => { const configs = [ { configId: 'configId1', authWellknownEndpoints: { issuer: 'auth-well-known' }, }, ]; vi.spyOn(configService as any, 'loadConfigs').mockReturnValue( of(configs) ); vi.spyOn(configValidationService, 'validateConfig').mockReturnValue(true); vi.spyOn(storagePersistenceService, 'read').mockReturnValue(null); const fireEventSpy = vi.spyOn(publicEventsService, 'fireEvent'); const storeWellKnownEndpointsSpy = vi.spyOn( authWellKnownService, 'storeWellKnownEndpoints' ); const config = await lastValueFrom( configService.getOpenIDConfiguration('configId1') ); expect(config).toBeTruthy(); expect(fireEventSpy).toHaveBeenCalledExactlyOnceWith( EventTypes.ConfigLoaded, expect.anything() ); expect(storeWellKnownEndpointsSpy).toHaveBeenCalledExactlyOnceWith( config as OpenIdConfiguration, { issuer: 'auth-well-known', } ); }); }); describe('getOpenIDConfigurations', () => { it('returns correct result', async () => { vi.spyOn(stsConfigLoader, 'loadConfigs').mockReturnValue( of([ { configId: 'configId1' } as OpenIdConfiguration, { configId: 'configId2' } as OpenIdConfiguration, ]) ); vi.spyOn(configValidationService, 'validateConfig').mockReturnValue(true); const result = await lastValueFrom( configService.getOpenIDConfigurations('configId1') ); expect(result.allConfigs.length).toEqual(2); expect(result.currentConfig).toBeTruthy(); }); it('created configId when configId is not set', async () => { vi.spyOn(stsConfigLoader, 'loadConfigs').mockReturnValue( of([ { clientId: 'clientId1' } as OpenIdConfiguration, { clientId: 'clientId2' } as OpenIdConfiguration, ]) ); vi.spyOn(configValidationService, 'validateConfig').mockReturnValue(true); const result = await lastValueFrom( configService.getOpenIDConfigurations() ); expect(result.allConfigs.length).toEqual(2); const allConfigIds = result.allConfigs.map((x) => x.configId); expect(allConfigIds).toEqual(['0-clientId1', '1-clientId2']); expect(result.currentConfig).toBeTruthy(); expect(result.currentConfig?.configId).toBeTruthy(); }); it('returns empty array if config is not valid', async () => { vi.spyOn(stsConfigLoader, 'loadConfigs').mockReturnValue( of([ { configId: 'configId1' } as OpenIdConfiguration, { configId: 'configId2' } as OpenIdConfiguration, ]) ); vi.spyOn(configValidationService, 'validateConfigs').mockReturnValue( false ); const { allConfigs, currentConfig } = await lastValueFrom( configService.getOpenIDConfigurations() ); expect(allConfigs).toEqual([]); expect(currentConfig).toBeNull(); }); }); describe('setSpecialCases', () => { it('should set special cases when current platform is browser', () => { vi.spyOn(platformProvider, 'isBrowser').mockReturnValue(false); const config = { configId: 'configId1' } as OpenIdConfiguration; (configService as any).setSpecialCases(config); expect(config).toEqual({ configId: 'configId1', startCheckSession: false, silentRenew: false, useRefreshToken: false, usePushedAuthorisationRequests: false, }); }); }); });