367 lines
13 KiB
TypeScript
367 lines
13 KiB
TypeScript
import { TestBed, fakeAsync, tick } from '@/testing';
|
|
import { Observable, of, throwError } from 'rxjs';
|
|
import { vi } from 'vitest';
|
|
import { AuthStateService } from '../auth-state/auth-state.service';
|
|
import { ImplicitFlowCallbackService } from '../callback/implicit-flow-callback.service';
|
|
import { IntervalService } from '../callback/interval.service';
|
|
import type { CallbackContext } from '../flows/callback-context';
|
|
import { FlowsDataService } from '../flows/flows-data.service';
|
|
import { FlowsService } from '../flows/flows.service';
|
|
import { ResetAuthDataService } from '../flows/reset-auth-data.service';
|
|
import { LoggerService } from '../logging/logger.service';
|
|
import { mockProvider } from '../testing/mock';
|
|
import { FlowHelper } from '../utils/flowHelper/flow-helper.service';
|
|
import { ValidationResult } from '../validation/validation-result';
|
|
import { IFrameService } from './existing-iframe.service';
|
|
import { SilentRenewService } from './silent-renew.service';
|
|
|
|
describe('SilentRenewService ', () => {
|
|
let silentRenewService: SilentRenewService;
|
|
let flowHelper: FlowHelper;
|
|
let implicitFlowCallbackService: ImplicitFlowCallbackService;
|
|
let iFrameService: IFrameService;
|
|
let flowsDataService: FlowsDataService;
|
|
let loggerService: LoggerService;
|
|
let flowsService: FlowsService;
|
|
let authStateService: AuthStateService;
|
|
let resetAuthDataService: ResetAuthDataService;
|
|
let intervalService: IntervalService;
|
|
|
|
beforeEach(() => {
|
|
TestBed.configureTestingModule({
|
|
providers: [
|
|
SilentRenewService,
|
|
IFrameService,
|
|
mockProvider(FlowsService),
|
|
mockProvider(ResetAuthDataService),
|
|
mockProvider(FlowsDataService),
|
|
mockProvider(AuthStateService),
|
|
mockProvider(LoggerService),
|
|
mockProvider(ImplicitFlowCallbackService),
|
|
mockProvider(IntervalService),
|
|
FlowHelper,
|
|
],
|
|
});
|
|
});
|
|
|
|
beforeEach(() => {
|
|
silentRenewService = TestBed.inject(SilentRenewService);
|
|
iFrameService = TestBed.inject(IFrameService);
|
|
flowHelper = TestBed.inject(FlowHelper);
|
|
implicitFlowCallbackService = TestBed.inject(ImplicitFlowCallbackService);
|
|
flowsDataService = TestBed.inject(FlowsDataService);
|
|
flowsService = TestBed.inject(FlowsService);
|
|
loggerService = TestBed.inject(LoggerService);
|
|
authStateService = TestBed.inject(AuthStateService);
|
|
resetAuthDataService = TestBed.inject(ResetAuthDataService);
|
|
intervalService = TestBed.inject(IntervalService);
|
|
});
|
|
|
|
it('should create', () => {
|
|
expect(silentRenewService).toBeTruthy();
|
|
});
|
|
|
|
describe('refreshSessionWithIFrameCompleted', () => {
|
|
it('is of type observable', () => {
|
|
expect(silentRenewService.refreshSessionWithIFrameCompleted$).toEqual(
|
|
expect.any(Observable)
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('isSilentRenewConfigured', () => {
|
|
it('returns true if refreshToken is configured false and silentRenew is configured true', () => {
|
|
const config = { useRefreshToken: false, silentRenew: true };
|
|
const result = silentRenewService.isSilentRenewConfigured(config);
|
|
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
it('returns false if refreshToken is configured true and silentRenew is configured true', () => {
|
|
const config = { useRefreshToken: true, silentRenew: true };
|
|
|
|
const result = silentRenewService.isSilentRenewConfigured(config);
|
|
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
it('returns false if refreshToken is configured false and silentRenew is configured false', () => {
|
|
const config = { useRefreshToken: false, silentRenew: false };
|
|
|
|
const result = silentRenewService.isSilentRenewConfigured(config);
|
|
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('getOrCreateIframe', () => {
|
|
it('returns iframe if iframe is truthy', () => {
|
|
vi.spyOn(silentRenewService as any, 'getExistingIframe').mockReturnValue({
|
|
name: 'anything',
|
|
});
|
|
|
|
const result = silentRenewService.getOrCreateIframe({
|
|
configId: 'configId1',
|
|
});
|
|
|
|
expect(result).toEqual({ name: 'anything' } as HTMLIFrameElement);
|
|
});
|
|
|
|
it('adds iframe to body if existing iframe is falsy', () => {
|
|
const config = { configId: 'configId1' };
|
|
|
|
vi.spyOn(silentRenewService as any, 'getExistingIframe').mockReturnValue(
|
|
null
|
|
);
|
|
|
|
const spy = vi
|
|
.spyOn(iFrameService, 'addIFrameToWindowBody')
|
|
.mockReturnValue({ name: 'anything' } as HTMLIFrameElement);
|
|
|
|
const result = silentRenewService.getOrCreateIframe(config);
|
|
|
|
expect(result).toEqual({ name: 'anything' } as HTMLIFrameElement);
|
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
expect(spy).toHaveBeenCalledExactlyOnceWith(
|
|
'myiFrameForSilentRenew',
|
|
config
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('codeFlowCallbackSilentRenewIframe', () => {
|
|
it('calls processSilentRenewCodeFlowCallback with correct arguments', async () => {
|
|
const config = { configId: 'configId1' };
|
|
const allConfigs = [config];
|
|
|
|
const spy = vi
|
|
.spyOn(flowsService, 'processSilentRenewCodeFlowCallback')
|
|
.mockReturnValue(of({} as CallbackContext));
|
|
const expectedContext = {
|
|
code: 'some-code',
|
|
refreshToken: '',
|
|
state: 'some-state',
|
|
sessionState: 'some-session-state',
|
|
authResult: null,
|
|
isRenewProcess: true,
|
|
jwtKeys: null,
|
|
validationResult: null,
|
|
existingIdToken: null,
|
|
} as CallbackContext;
|
|
const url = 'url-part-1';
|
|
const urlParts =
|
|
'code=some-code&state=some-state&session_state=some-session-state';
|
|
|
|
await lastValueFrom(silentRenewService
|
|
.codeFlowCallbackSilentRenewIframe([url, urlParts], config, allConfigs));
|
|
expect(spy).toHaveBeenCalledExactlyOnceWith(
|
|
expectedContext,
|
|
config,
|
|
allConfigs
|
|
);
|
|
});
|
|
|
|
it('throws error if url has error param and resets everything on error', async () => {
|
|
const config = { configId: 'configId1' };
|
|
const allConfigs = [config];
|
|
|
|
const spy = vi
|
|
.spyOn(flowsService, 'processSilentRenewCodeFlowCallback')
|
|
.mockReturnValue(of({} as CallbackContext));
|
|
const authStateServiceSpy = vi.spyOn(
|
|
authStateService,
|
|
'updateAndPublishAuthState'
|
|
);
|
|
const resetAuthorizationDataSpy = vi.spyOn(
|
|
resetAuthDataService,
|
|
'resetAuthorizationData'
|
|
);
|
|
const setNonceSpy = vi.spyOn(flowsDataService, 'setNonce');
|
|
const stopPeriodicTokenCheckSpy = vi.spyOn(
|
|
intervalService,
|
|
'stopPeriodicTokenCheck'
|
|
);
|
|
|
|
const url = 'url-part-1';
|
|
const urlParts = 'error=some_error';
|
|
|
|
silentRenewService
|
|
.codeFlowCallbackSilentRenewIframe([url, urlParts], config, allConfigs)
|
|
.subscribe({
|
|
error: (error) => {
|
|
expect(error).toEqual(new Error('some_error'));
|
|
expect(spy).not.toHaveBeenCalled();
|
|
expect(authStateServiceSpy).toHaveBeenCalledExactlyOnceWith({
|
|
isAuthenticated: false,
|
|
validationResult: ValidationResult.LoginRequired,
|
|
isRenewProcess: true,
|
|
});
|
|
expect(resetAuthorizationDataSpy).toHaveBeenCalledExactlyOnceWith(
|
|
config,
|
|
allConfigs
|
|
);
|
|
expect(setNonceSpy).toHaveBeenCalledExactlyOnceWith('', config);
|
|
expect(stopPeriodicTokenCheckSpy).toHaveBeenCalledTimes(1);
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('silentRenewEventHandler', () => {
|
|
it('returns if no details is given', async () => {
|
|
const isCurrentFlowCodeFlowSpy = vi
|
|
.spyOn(flowHelper, 'isCurrentFlowCodeFlow')
|
|
.mockReturnValue(false);
|
|
|
|
vi.spyOn(
|
|
implicitFlowCallbackService,
|
|
'authenticatedImplicitFlowCallback'
|
|
).mockReturnValue(of({} as CallbackContext));
|
|
const eventData = { detail: null } as CustomEvent;
|
|
const allConfigs = [{ configId: 'configId1' }];
|
|
|
|
silentRenewService.silentRenewEventHandler(
|
|
eventData,
|
|
allConfigs[0]!,
|
|
allConfigs
|
|
);
|
|
await vi.advanceTimersByTimeAsync(1000);
|
|
expect(isCurrentFlowCodeFlowSpy).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('calls authorizedImplicitFlowCallback if current flow is not code flow', async () => {
|
|
const isCurrentFlowCodeFlowSpy = vi
|
|
.spyOn(flowHelper, 'isCurrentFlowCodeFlow')
|
|
.mockReturnValue(false);
|
|
const authorizedImplicitFlowCallbackSpy = vi
|
|
.spyOn(implicitFlowCallbackService, 'authenticatedImplicitFlowCallback')
|
|
.mockReturnValue(of({} as CallbackContext));
|
|
const eventData = { detail: 'detail' } as CustomEvent;
|
|
const allConfigs = [{ configId: 'configId1' }];
|
|
|
|
silentRenewService.silentRenewEventHandler(
|
|
eventData,
|
|
allConfigs[0]!,
|
|
allConfigs
|
|
);
|
|
await vi.advanceTimersByTimeAsync(1000);
|
|
expect(isCurrentFlowCodeFlowSpy).toHaveBeenCalled();
|
|
expect(authorizedImplicitFlowCallbackSpy).toHaveBeenCalledExactlyOnceWith(
|
|
allConfigs[0]!,
|
|
allConfigs,
|
|
'detail'
|
|
);
|
|
});
|
|
|
|
it('calls codeFlowCallbackSilentRenewIframe if current flow is code flow', async () => {
|
|
vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(true);
|
|
const codeFlowCallbackSilentRenewIframe = vi
|
|
.spyOn(silentRenewService, 'codeFlowCallbackSilentRenewIframe')
|
|
.mockReturnValue(of({} as CallbackContext));
|
|
const eventData = { detail: 'detail?detail2' } as CustomEvent;
|
|
const allConfigs = [{ configId: 'configId1' }];
|
|
|
|
silentRenewService.silentRenewEventHandler(
|
|
eventData,
|
|
allConfigs[0]!,
|
|
allConfigs
|
|
);
|
|
await vi.advanceTimersByTimeAsync(1000);
|
|
expect(codeFlowCallbackSilentRenewIframe).toHaveBeenCalledExactlyOnceWith(
|
|
['detail', 'detail2'],
|
|
allConfigs[0]!,
|
|
allConfigs
|
|
);
|
|
});
|
|
|
|
it('calls authorizedImplicitFlowCallback if current flow is not code flow', async () => {
|
|
vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(true);
|
|
const codeFlowCallbackSilentRenewIframe = vi
|
|
.spyOn(silentRenewService, 'codeFlowCallbackSilentRenewIframe')
|
|
.mockReturnValue(of({} as CallbackContext));
|
|
const eventData = { detail: 'detail?detail2' } as CustomEvent;
|
|
const allConfigs = [{ configId: 'configId1' }];
|
|
|
|
silentRenewService.silentRenewEventHandler(
|
|
eventData,
|
|
allConfigs[0]!,
|
|
allConfigs
|
|
);
|
|
await vi.advanceTimersByTimeAsync(1000);
|
|
expect(codeFlowCallbackSilentRenewIframe).toHaveBeenCalledExactlyOnceWith(
|
|
['detail', 'detail2'],
|
|
allConfigs[0]!,
|
|
allConfigs
|
|
);
|
|
});
|
|
|
|
it('calls next on refreshSessionWithIFrameCompleted with callbackcontext', async () => {
|
|
vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(true);
|
|
vi.spyOn(
|
|
silentRenewService,
|
|
'codeFlowCallbackSilentRenewIframe'
|
|
).mockReturnValue(
|
|
of({ refreshToken: 'callbackContext' } as CallbackContext)
|
|
);
|
|
const eventData = { detail: 'detail?detail2' } as CustomEvent;
|
|
const allConfigs = [{ configId: 'configId1' }];
|
|
|
|
const result = await lastValueFrom(silentRenewService.refreshSessionWithIFrameCompleted$);
|
|
expect(result).toEqual({
|
|
refreshToken: 'callbackContext',
|
|
} as CallbackContext);
|
|
|
|
silentRenewService.silentRenewEventHandler(
|
|
eventData,
|
|
allConfigs[0]!,
|
|
allConfigs
|
|
);
|
|
await vi.advanceTimersByTimeAsync(1000);
|
|
});
|
|
|
|
it('loggs and calls flowsDataService.resetSilentRenewRunning in case of an error', async () => {
|
|
vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(true);
|
|
vi.spyOn(
|
|
silentRenewService,
|
|
'codeFlowCallbackSilentRenewIframe'
|
|
).mockReturnValue(throwError(() => new Error('ERROR')));
|
|
const resetSilentRenewRunningSpy = vi.spyOn(
|
|
flowsDataService,
|
|
'resetSilentRenewRunning'
|
|
);
|
|
const logErrorSpy = vi.spyOn(loggerService, 'logError');
|
|
const allConfigs = [{ configId: 'configId1' }];
|
|
const eventData = { detail: 'detail?detail2' } as CustomEvent;
|
|
|
|
silentRenewService.silentRenewEventHandler(
|
|
eventData,
|
|
allConfigs[0]!,
|
|
allConfigs
|
|
);
|
|
await vi.advanceTimersByTimeAsync(1000);
|
|
expect(resetSilentRenewRunningSpy).toHaveBeenCalledTimes(1);
|
|
expect(logErrorSpy).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('calls next on refreshSessionWithIFrameCompleted with null in case of error', async () => {
|
|
vi.spyOn(flowHelper, 'isCurrentFlowCodeFlow').mockReturnValue(true);
|
|
vi.spyOn(
|
|
silentRenewService,
|
|
'codeFlowCallbackSilentRenewIframe'
|
|
).mockReturnValue(throwError(() => new Error('ERROR')));
|
|
const eventData = { detail: 'detail?detail2' } as CustomEvent;
|
|
const allConfigs = [{ configId: 'configId1' }];
|
|
|
|
const result = await lastValueFrom(silentRenewService.refreshSessionWithIFrameCompleted$);
|
|
expect(result).toBeNull();
|
|
|
|
silentRenewService.silentRenewEventHandler(
|
|
eventData,
|
|
allConfigs[0]!,
|
|
allConfigs
|
|
);
|
|
await vi.advanceTimersByTimeAsync(1000);
|
|
});
|
|
});
|
|
});
|