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