]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/services/api-interceptor.service.spec.ts
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / shared / services / api-interceptor.service.spec.ts
CommitLineData
11fdf7f2
TL
1import { HttpClient, HttpErrorResponse } from '@angular/common/http';
2import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
3import { fakeAsync, TestBed, tick } from '@angular/core/testing';
4import { Router } from '@angular/router';
5
494da23a 6import { ToastrService } from 'ngx-toastr';
11fdf7f2
TL
7
8import { configureTestBed, i18nProviders } from '../../../testing/unit-test-helper';
9import { AppModule } from '../../app.module';
10import { CdNotification, CdNotificationConfig } from '../models/cd-notification';
11import { ApiInterceptorService } from './api-interceptor.service';
12import { NotificationService } from './notification.service';
13
14describe('ApiInterceptorService', () => {
15 let notificationService: NotificationService;
16 let httpTesting: HttpTestingController;
17 let httpClient: HttpClient;
18 let router: Router;
19 const url = 'api/xyz';
20
21 const httpError = (error, errorOpts, done = (_resp) => {}) => {
22 httpClient.get(url).subscribe(
23 () => {},
24 (resp) => {
25 // Error must have been forwarded by the interceptor.
26 expect(resp instanceof HttpErrorResponse).toBeTruthy();
27 done(resp);
28 }
29 );
30 httpTesting.expectOne(url).error(error, errorOpts);
31 };
32
33 const runRouterTest = (errorOpts, expectedCallParams) => {
34 httpError(new ErrorEvent('abc'), errorOpts);
35 httpTesting.verify();
36 expect(router.navigate).toHaveBeenCalledWith(...expectedCallParams);
37 };
38
39 const runNotificationTest = (error, errorOpts, expectedCallParams) => {
40 httpError(error, errorOpts);
41 httpTesting.verify();
42 expect(notificationService.show).toHaveBeenCalled();
43 expect(notificationService.save).toHaveBeenCalledWith(expectedCallParams);
44 };
45
46 const createCdNotification = (type, title?, message?, options?, application?) => {
47 return new CdNotification(new CdNotificationConfig(type, title, message, options, application));
48 };
49
50 configureTestBed({
51 imports: [AppModule, HttpClientTestingModule],
52 providers: [
53 NotificationService,
54 i18nProviders,
55 {
494da23a 56 provide: ToastrService,
11fdf7f2
TL
57 useValue: {
58 error: () => true
59 }
60 }
61 ]
62 });
63
64 beforeEach(() => {
65 const baseTime = new Date('2022-02-22');
66 spyOn(global, 'Date').and.returnValue(baseTime);
67
68 httpClient = TestBed.get(HttpClient);
69 httpTesting = TestBed.get(HttpTestingController);
70
71 notificationService = TestBed.get(NotificationService);
72 spyOn(notificationService, 'show').and.callThrough();
73 spyOn(notificationService, 'save');
74
75 router = TestBed.get(Router);
76 spyOn(router, 'navigate');
77 });
78
79 it('should be created', () => {
80 const service = TestBed.get(ApiInterceptorService);
81 expect(service).toBeTruthy();
82 });
83
84 describe('test different error behaviours', () => {
85 beforeEach(() => {
86 spyOn(window, 'setTimeout').and.callFake((fn) => fn());
87 });
88
89 it('should redirect 401', () => {
90 runRouterTest(
91 {
92 status: 401
93 },
94 [['/login']]
95 );
96 });
97
98 it('should redirect 403', () => {
99 runRouterTest(
100 {
101 status: 403
102 },
103 [['/403']]
104 );
105 });
106
107 it('should show notification (error string)', () => {
108 runNotificationTest(
109 'foobar',
110 {
111 status: 500,
112 statusText: 'Foo Bar'
113 },
114 createCdNotification(0, '500 - Foo Bar', 'foobar')
115 );
116 });
117
118 it('should show notification (error object, triggered from backend)', () => {
119 runNotificationTest(
120 { detail: 'abc' },
121 {
122 status: 504,
123 statusText: 'AAA bbb CCC'
124 },
125 createCdNotification(0, '504 - AAA bbb CCC', 'abc')
126 );
127 });
128
129 it('should show notification (error object with unknown keys)', () => {
130 runNotificationTest(
131 { type: 'error' },
132 {
133 status: 0,
134 statusText: 'Unknown Error',
135 message: 'Http failure response for (unknown url): 0 Unknown Error',
136 name: 'HttpErrorResponse',
137 ok: false,
138 url: null
139 },
140 createCdNotification(
141 0,
142 '0 - Unknown Error',
143 'Http failure response for api/xyz: 0 Unknown Error'
144 )
145 );
146 });
147
148 it('should show notification (undefined error)', () => {
149 runNotificationTest(
150 undefined,
151 {
152 status: 502
153 },
154 createCdNotification(0, '502 - Unknown Error', 'Http failure response for api/xyz: 502 ')
155 );
156 });
157
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' },
166 name: 'mytask',
167 success: false
168 });
169 });
170 });
171
172 describe('interceptor error handling', () => {
81eedcae
TL
173 const expectSaveToHaveBeenCalled = (called) => {
174 tick(510);
175 if (called) {
176 expect(notificationService.save).toHaveBeenCalled();
177 } else {
178 expect(notificationService.save).not.toHaveBeenCalled();
179 }
180 };
181
11fdf7f2
TL
182 it('should show default behaviour', fakeAsync(() => {
183 httpError(undefined, { status: 500 });
81eedcae 184 expectSaveToHaveBeenCalled(true);
11fdf7f2
TL
185 }));
186
187 it('should prevent the default behaviour with preventDefault', fakeAsync(() => {
188 httpError(undefined, { status: 500 }, (resp) => resp.preventDefault());
81eedcae 189 expectSaveToHaveBeenCalled(false);
11fdf7f2
TL
190 }));
191
192 it('should be able to use preventDefault with 400 errors', fakeAsync(() => {
193 httpError(
194 { task: { name: 'someName', metadata: { component: 'someComponent' } } },
195 { status: 400 },
196 (resp) => resp.preventDefault()
197 );
81eedcae 198 expectSaveToHaveBeenCalled(false);
11fdf7f2
TL
199 }));
200
201 it('should prevent the default behaviour by status code', fakeAsync(() => {
202 httpError(undefined, { status: 500 }, (resp) => resp.ignoreStatusCode(500));
81eedcae 203 expectSaveToHaveBeenCalled(false);
11fdf7f2
TL
204 }));
205
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);
210 });
81eedcae 211 expectSaveToHaveBeenCalled(true);
11fdf7f2
TL
212 expect(notificationService.save).toHaveBeenCalledWith(
213 createCdNotification(0, '500 - Unknown Error', msg, undefined, 'Prometheus')
214 );
215 }));
216 });
217});