1 import { HttpClient, HttpErrorResponse } from '@angular/common/http';
2 import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
3 import { fakeAsync, TestBed, tick } from '@angular/core/testing';
4 import { Router } from '@angular/router';
6 import { ToastrService } from 'ngx-toastr';
8 import { configureTestBed, i18nProviders } from '../../../testing/unit-test-helper';
9 import { AppModule } from '../../app.module';
10 import { CdNotification, CdNotificationConfig } from '../models/cd-notification';
11 import { ApiInterceptorService } from './api-interceptor.service';
12 import { NotificationService } from './notification.service';
14 describe('ApiInterceptorService', () => {
15 let notificationService: NotificationService;
16 let httpTesting: HttpTestingController;
17 let httpClient: HttpClient;
19 const url = 'api/xyz';
21 const httpError = (error, errorOpts, done = (_resp) => {}) => {
22 httpClient.get(url).subscribe(
25 // Error must have been forwarded by the interceptor.
26 expect(resp instanceof HttpErrorResponse).toBeTruthy();
30 httpTesting.expectOne(url).error(error, errorOpts);
33 const runRouterTest = (errorOpts, expectedCallParams) => {
34 httpError(new ErrorEvent('abc'), errorOpts);
36 expect(router.navigate).toHaveBeenCalledWith(...expectedCallParams);
39 const runNotificationTest = (error, errorOpts, expectedCallParams) => {
40 httpError(error, errorOpts);
42 expect(notificationService.show).toHaveBeenCalled();
43 expect(notificationService.save).toHaveBeenCalledWith(expectedCallParams);
46 const createCdNotification = (type, title?, message?, options?, application?) => {
47 return new CdNotification(new CdNotificationConfig(type, title, message, options, application));
51 imports: [AppModule, HttpClientTestingModule],
56 provide: ToastrService,
65 const baseTime = new Date('2022-02-22');
66 spyOn(global, 'Date').and.returnValue(baseTime);
68 httpClient = TestBed.get(HttpClient);
69 httpTesting = TestBed.get(HttpTestingController);
71 notificationService = TestBed.get(NotificationService);
72 spyOn(notificationService, 'show').and.callThrough();
73 spyOn(notificationService, 'save');
75 router = TestBed.get(Router);
76 spyOn(router, 'navigate');
79 it('should be created', () => {
80 const service = TestBed.get(ApiInterceptorService);
81 expect(service).toBeTruthy();
84 describe('test different error behaviours', () => {
86 spyOn(window, 'setTimeout').and.callFake((fn) => fn());
89 it('should redirect 401', () => {
98 it('should redirect 403', () => {
107 it('should show notification (error string)', () => {
112 statusText: 'Foo Bar'
114 createCdNotification(0, '500 - Foo Bar', 'foobar')
118 it('should show notification (error object, triggered from backend)', () => {
123 statusText: 'AAA bbb CCC'
125 createCdNotification(0, '504 - AAA bbb CCC', 'abc')
129 it('should show notification (error object with unknown keys)', () => {
134 statusText: 'Unknown Error',
135 message: 'Http failure response for (unknown url): 0 Unknown Error',
136 name: 'HttpErrorResponse',
140 createCdNotification(
143 'Http failure response for api/xyz: 0 Unknown Error'
148 it('should show notification (undefined error)', () => {
154 createCdNotification(0, '502 - Unknown Error', 'Http failure response for api/xyz: 502 ')
158 it('should show 400 notification', () => {
159 spyOn(notificationService, 'notifyTask');
160 httpError({ task: { name: 'mytask', metadata: { component: 'foobar' } } }, { status: 400 });
161 httpTesting.verify();
162 expect(notificationService.show).toHaveBeenCalledTimes(0);
163 expect(notificationService.notifyTask).toHaveBeenCalledWith({
164 exception: { task: { metadata: { component: 'foobar' }, name: 'mytask' } },
165 metadata: { component: 'foobar' },
172 describe('interceptor error handling', () => {
173 const expectSaveToHaveBeenCalled = (called) => {
176 expect(notificationService.save).toHaveBeenCalled();
178 expect(notificationService.save).not.toHaveBeenCalled();
182 it('should show default behaviour', fakeAsync(() => {
183 httpError(undefined, { status: 500 });
184 expectSaveToHaveBeenCalled(true);
187 it('should prevent the default behaviour with preventDefault', fakeAsync(() => {
188 httpError(undefined, { status: 500 }, (resp) => resp.preventDefault());
189 expectSaveToHaveBeenCalled(false);
192 it('should be able to use preventDefault with 400 errors', fakeAsync(() => {
194 { task: { name: 'someName', metadata: { component: 'someComponent' } } },
196 (resp) => resp.preventDefault()
198 expectSaveToHaveBeenCalled(false);
201 it('should prevent the default behaviour by status code', fakeAsync(() => {
202 httpError(undefined, { status: 500 }, (resp) => resp.ignoreStatusCode(500));
203 expectSaveToHaveBeenCalled(false);
206 it('should use different application icon (default Ceph) in error message', fakeAsync(() => {
207 const msg = 'Cannot connect to Alertmanager';
208 httpError(undefined, { status: 500 }, (resp) => {
209 (resp.application = 'Prometheus'), (resp.message = msg);
211 expectSaveToHaveBeenCalled(true);
212 expect(notificationService.save).toHaveBeenCalledWith(
213 createCdNotification(0, '500 - Unknown Error', msg, undefined, 'Prometheus')