]>
Commit | Line | Data |
---|---|---|
11fdf7f2 | 1 | import { HttpClientTestingModule } from '@angular/common/http/testing'; |
f67539c2 | 2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; |
e306af50 | 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; |
11fdf7f2 TL |
4 | import { RouterTestingModule } from '@angular/router/testing'; |
5 | ||
9f95a23c TL |
6 | import { ToastrModule } from 'ngx-toastr'; |
7 | import { of } from 'rxjs'; | |
11fdf7f2 | 8 | |
f67539c2 TL |
9 | import { CephModule } from '~/app/ceph/ceph.module'; |
10 | import { CephSharedModule } from '~/app/ceph/shared/ceph-shared.module'; | |
11 | import { CoreModule } from '~/app/core/core.module'; | |
12 | import { HostService } from '~/app/shared/api/host.service'; | |
13 | import { OrchestratorService } from '~/app/shared/api/orchestrator.service'; | |
14 | import { TableActionsComponent } from '~/app/shared/datatable/table-actions/table-actions.component'; | |
a4b75251 | 15 | import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context'; |
f67539c2 TL |
16 | import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; |
17 | import { OrchestratorFeature } from '~/app/shared/models/orchestrator.enum'; | |
18 | import { Permissions } from '~/app/shared/models/permissions'; | |
19 | import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; | |
20 | import { SharedModule } from '~/app/shared/shared.module'; | |
21 | import { | |
22 | configureTestBed, | |
23 | OrchestratorHelper, | |
24 | TableActionHelper | |
25 | } from '~/testing/unit-test-helper'; | |
11fdf7f2 TL |
26 | import { HostsComponent } from './hosts.component'; |
27 | ||
b3b6e05e TL |
28 | class MockShowForceMaintenanceModal { |
29 | showModal = false; | |
30 | showModalDialog(msg: string) { | |
31 | if ( | |
32 | msg.includes('WARNING') && | |
33 | !msg.includes('It is NOT safe to stop') && | |
34 | !msg.includes('ALERT') && | |
522d829b | 35 | !msg.includes('unsafe to stop') |
b3b6e05e TL |
36 | ) { |
37 | this.showModal = true; | |
38 | } | |
39 | } | |
40 | } | |
41 | ||
11fdf7f2 TL |
42 | describe('HostsComponent', () => { |
43 | let component: HostsComponent; | |
44 | let fixture: ComponentFixture<HostsComponent>; | |
9f95a23c | 45 | let hostListSpy: jasmine.Spy; |
f67539c2 | 46 | let orchService: OrchestratorService; |
b3b6e05e | 47 | let showForceMaintenanceModal: MockShowForceMaintenanceModal; |
11fdf7f2 TL |
48 | |
49 | const fakeAuthStorageService = { | |
50 | getPermissions: () => { | |
51 | return new Permissions({ hosts: ['read', 'update', 'create', 'delete'] }); | |
52 | } | |
53 | }; | |
54 | ||
55 | configureTestBed({ | |
56 | imports: [ | |
e306af50 | 57 | BrowserAnimationsModule, |
9f95a23c | 58 | CephSharedModule, |
11fdf7f2 TL |
59 | SharedModule, |
60 | HttpClientTestingModule, | |
9f95a23c TL |
61 | RouterTestingModule, |
62 | ToastrModule.forRoot(), | |
63 | CephModule, | |
64 | CoreModule | |
11fdf7f2 | 65 | ], |
f67539c2 TL |
66 | providers: [ |
67 | { provide: AuthStorageService, useValue: fakeAuthStorageService }, | |
68 | TableActionsComponent | |
69 | ] | |
11fdf7f2 TL |
70 | }); |
71 | ||
72 | beforeEach(() => { | |
b3b6e05e | 73 | showForceMaintenanceModal = new MockShowForceMaintenanceModal(); |
11fdf7f2 TL |
74 | fixture = TestBed.createComponent(HostsComponent); |
75 | component = fixture.componentInstance; | |
f67539c2 TL |
76 | hostListSpy = spyOn(TestBed.inject(HostService), 'list'); |
77 | orchService = TestBed.inject(OrchestratorService); | |
11fdf7f2 TL |
78 | }); |
79 | ||
80 | it('should create', () => { | |
81 | expect(component).toBeTruthy(); | |
82 | }); | |
83 | ||
f67539c2 | 84 | it('should render hosts list even with not permission mapped services', () => { |
11fdf7f2 TL |
85 | const hostname = 'ceph.dev'; |
86 | const payload = [ | |
87 | { | |
88 | services: [ | |
89 | { | |
90 | type: 'osd', | |
91 | id: '0' | |
92 | }, | |
93 | { | |
94 | type: 'rgw', | |
95 | id: 'rgw' | |
96 | }, | |
97 | { | |
98 | type: 'notPermissionMappedService', | |
99 | id: '1' | |
100 | } | |
101 | ], | |
102 | hostname: hostname, | |
e306af50 | 103 | labels: ['foo', 'bar'] |
11fdf7f2 TL |
104 | } |
105 | ]; | |
106 | ||
a4b75251 TL |
107 | OrchestratorHelper.mockStatus(false); |
108 | hostListSpy.and.callFake(() => of(payload)); | |
109 | fixture.detectChanges(); | |
110 | ||
111 | component.getHosts(new CdTableFetchDataContext(() => undefined)); | |
112 | fixture.detectChanges(); | |
113 | ||
114 | const spans = fixture.debugElement.nativeElement.querySelectorAll( | |
115 | '.datatable-body-cell-label span' | |
116 | ); | |
117 | expect(spans[0].textContent).toBe(hostname); | |
118 | }); | |
119 | ||
120 | it('should test if host facts are tranformed correctly if orch available', () => { | |
121 | const features = [OrchestratorFeature.HOST_FACTS]; | |
122 | const payload = [ | |
123 | { | |
124 | hostname: 'host_test', | |
125 | services: [ | |
126 | { | |
127 | type: 'osd', | |
128 | id: '0' | |
129 | } | |
130 | ], | |
131 | cpu_count: 2, | |
132 | cpu_cores: 1, | |
133 | memory_total_kb: 1024, | |
134 | hdd_count: 4, | |
135 | hdd_capacity_bytes: 1024, | |
136 | flash_count: 4, | |
137 | flash_capacity_bytes: 1024, | |
138 | nic_count: 1 | |
139 | } | |
140 | ]; | |
141 | OrchestratorHelper.mockStatus(true, features); | |
142 | hostListSpy.and.callFake(() => of(payload)); | |
143 | fixture.detectChanges(); | |
144 | ||
145 | component.getHosts(new CdTableFetchDataContext(() => undefined)); | |
146 | expect(hostListSpy).toHaveBeenCalled(); | |
147 | expect(component.hosts[0]['cpu_count']).toEqual(2); | |
148 | expect(component.hosts[0]['memory_total_bytes']).toEqual(1048576); | |
149 | expect(component.hosts[0]['raw_capacity']).toEqual(2048); | |
150 | expect(component.hosts[0]['hdd_count']).toEqual(4); | |
151 | expect(component.hosts[0]['flash_count']).toEqual(4); | |
152 | expect(component.hosts[0]['cpu_cores']).toEqual(1); | |
153 | expect(component.hosts[0]['nic_count']).toEqual(1); | |
154 | }); | |
155 | ||
156 | it('should test if host facts are unavailable if no orch available', () => { | |
157 | const payload = [ | |
158 | { | |
159 | hostname: 'host_test', | |
160 | services: [ | |
161 | { | |
162 | type: 'osd', | |
163 | id: '0' | |
164 | } | |
165 | ] | |
166 | } | |
167 | ]; | |
168 | OrchestratorHelper.mockStatus(false); | |
169 | hostListSpy.and.callFake(() => of(payload)); | |
170 | fixture.detectChanges(); | |
171 | ||
172 | component.getHosts(new CdTableFetchDataContext(() => undefined)); | |
173 | fixture.detectChanges(); | |
174 | ||
175 | const spans = fixture.debugElement.nativeElement.querySelectorAll( | |
176 | '.datatable-body-cell-label span' | |
177 | ); | |
178 | expect(spans[7].textContent).toBe('Unavailable'); | |
179 | }); | |
180 | ||
181 | it('should test if host facts are unavailable if get_fatcs orch feature is not available', () => { | |
182 | const payload = [ | |
183 | { | |
184 | hostname: 'host_test', | |
185 | services: [ | |
186 | { | |
187 | type: 'osd', | |
188 | id: '0' | |
189 | } | |
190 | ] | |
191 | } | |
192 | ]; | |
f67539c2 | 193 | OrchestratorHelper.mockStatus(true); |
9f95a23c | 194 | hostListSpy.and.callFake(() => of(payload)); |
f67539c2 | 195 | fixture.detectChanges(); |
11fdf7f2 | 196 | |
a4b75251 TL |
197 | component.getHosts(new CdTableFetchDataContext(() => undefined)); |
198 | fixture.detectChanges(); | |
11fdf7f2 | 199 | |
a4b75251 TL |
200 | const spans = fixture.debugElement.nativeElement.querySelectorAll( |
201 | '.datatable-body-cell-label span' | |
202 | ); | |
203 | expect(spans[7].textContent).toBe('Unavailable'); | |
f67539c2 | 204 | }); |
f6b5b4d7 | 205 | |
b3b6e05e TL |
206 | it('should show force maintenance modal when it is safe to stop host', () => { |
207 | const errorMsg = `WARNING: Stopping 1 out of 1 daemons in Grafana service. | |
208 | Service will not be operational with no daemons left. At | |
209 | least 1 daemon must be running to guarantee service.`; | |
210 | showForceMaintenanceModal.showModalDialog(errorMsg); | |
211 | expect(showForceMaintenanceModal.showModal).toBeTruthy(); | |
212 | }); | |
213 | ||
214 | it('should not show force maintenance modal when error is an ALERT', () => { | |
215 | const errorMsg = `ALERT: Cannot stop active Mgr daemon, Please switch active Mgrs | |
216 | with 'ceph mgr fail ceph-node-00'`; | |
217 | showForceMaintenanceModal.showModalDialog(errorMsg); | |
218 | expect(showForceMaintenanceModal.showModal).toBeFalsy(); | |
219 | }); | |
220 | ||
221 | it('should not show force maintenance modal when it is not safe to stop host', () => { | |
222 | const errorMsg = `WARNING: Stopping 1 out of 1 daemons in Grafana service. | |
223 | Service will not be operational with no daemons left. At | |
224 | least 1 daemon must be running to guarantee service. | |
225 | It is NOT safe to stop ['mon.ceph-node-00']: not enough | |
226 | monitors would be available (ceph-node-02) after stopping mons`; | |
227 | showForceMaintenanceModal.showModalDialog(errorMsg); | |
228 | expect(showForceMaintenanceModal.showModal).toBeFalsy(); | |
229 | }); | |
230 | ||
522d829b TL |
231 | it('should not show force maintenance modal when it is unsafe to stop host', () => { |
232 | const errorMsg = 'unsafe to stop osd.0 because of some unknown reason'; | |
b3b6e05e TL |
233 | showForceMaintenanceModal.showModalDialog(errorMsg); |
234 | expect(showForceMaintenanceModal.showModal).toBeFalsy(); | |
235 | }); | |
236 | ||
f67539c2 TL |
237 | describe('table actions', () => { |
238 | const fakeHosts = require('./fixtures/host_list_response.json'); | |
f6b5b4d7 TL |
239 | |
240 | beforeEach(() => { | |
f67539c2 | 241 | hostListSpy.and.callFake(() => of(fakeHosts)); |
f6b5b4d7 TL |
242 | }); |
243 | ||
f67539c2 TL |
244 | const testTableActions = async ( |
245 | orch: boolean, | |
246 | features: OrchestratorFeature[], | |
247 | tests: { selectRow?: number; expectResults: any }[] | |
248 | ) => { | |
249 | OrchestratorHelper.mockStatus(orch, features); | |
250 | fixture.detectChanges(); | |
251 | await fixture.whenStable(); | |
f91f0fd5 | 252 | |
f67539c2 TL |
253 | for (const test of tests) { |
254 | if (test.selectRow) { | |
255 | component.selection = new CdTableSelection(); | |
256 | component.selection.selected = [test.selectRow]; | |
f91f0fd5 | 257 | } |
f67539c2 TL |
258 | await TableActionHelper.verifyTableActions( |
259 | fixture, | |
260 | component.tableActions, | |
261 | test.expectResults | |
262 | ); | |
263 | } | |
264 | }; | |
265 | ||
266 | it('should have correct states when Orchestrator is enabled', async () => { | |
267 | const tests = [ | |
268 | { | |
269 | expectResults: { | |
a4b75251 | 270 | Add: { disabled: false, disableDesc: '' }, |
f67539c2 | 271 | Edit: { disabled: true, disableDesc: '' }, |
a4b75251 | 272 | Remove: { disabled: true, disableDesc: '' } |
f67539c2 TL |
273 | } |
274 | }, | |
275 | { | |
276 | selectRow: fakeHosts[0], // non-orchestrator host | |
277 | expectResults: { | |
a4b75251 | 278 | Add: { disabled: false, disableDesc: '' }, |
f67539c2 | 279 | Edit: { disabled: true, disableDesc: component.messages.nonOrchHost }, |
a4b75251 | 280 | Remove: { disabled: true, disableDesc: component.messages.nonOrchHost } |
f67539c2 TL |
281 | } |
282 | }, | |
283 | { | |
284 | selectRow: fakeHosts[1], // orchestrator host | |
285 | expectResults: { | |
a4b75251 | 286 | Add: { disabled: false, disableDesc: '' }, |
f67539c2 | 287 | Edit: { disabled: false, disableDesc: '' }, |
a4b75251 | 288 | Remove: { disabled: false, disableDesc: '' } |
f67539c2 | 289 | } |
f91f0fd5 | 290 | } |
f67539c2 TL |
291 | ]; |
292 | ||
293 | const features = [ | |
a4b75251 | 294 | OrchestratorFeature.HOST_ADD, |
f67539c2 | 295 | OrchestratorFeature.HOST_LABEL_ADD, |
a4b75251 | 296 | OrchestratorFeature.HOST_REMOVE, |
f67539c2 TL |
297 | OrchestratorFeature.HOST_LABEL_REMOVE |
298 | ]; | |
299 | await testTableActions(true, features, tests); | |
f91f0fd5 TL |
300 | }); |
301 | ||
f67539c2 TL |
302 | it('should have correct states when Orchestrator is disabled', async () => { |
303 | const resultNoOrchestrator = { | |
304 | disabled: true, | |
305 | disableDesc: orchService.disableMessages.noOrchestrator | |
306 | }; | |
307 | const tests = [ | |
308 | { | |
309 | expectResults: { | |
a4b75251 | 310 | Add: resultNoOrchestrator, |
f67539c2 | 311 | Edit: { disabled: true, disableDesc: '' }, |
a4b75251 | 312 | Remove: { disabled: true, disableDesc: '' } |
f67539c2 TL |
313 | } |
314 | }, | |
315 | { | |
316 | selectRow: fakeHosts[0], // non-orchestrator host | |
317 | expectResults: { | |
a4b75251 | 318 | Add: resultNoOrchestrator, |
f67539c2 | 319 | Edit: { disabled: true, disableDesc: component.messages.nonOrchHost }, |
a4b75251 | 320 | Remove: { disabled: true, disableDesc: component.messages.nonOrchHost } |
f67539c2 TL |
321 | } |
322 | }, | |
323 | { | |
324 | selectRow: fakeHosts[1], // orchestrator host | |
325 | expectResults: { | |
a4b75251 | 326 | Add: resultNoOrchestrator, |
f67539c2 | 327 | Edit: resultNoOrchestrator, |
a4b75251 | 328 | Remove: resultNoOrchestrator |
f67539c2 TL |
329 | } |
330 | } | |
331 | ]; | |
332 | await testTableActions(false, [], tests); | |
f91f0fd5 TL |
333 | }); |
334 | ||
f67539c2 TL |
335 | it('should have correct states when Orchestrator features are missing', async () => { |
336 | const resultMissingFeatures = { | |
337 | disabled: true, | |
338 | disableDesc: orchService.disableMessages.missingFeature | |
339 | }; | |
340 | const tests = [ | |
341 | { | |
342 | expectResults: { | |
a4b75251 | 343 | Add: resultMissingFeatures, |
f67539c2 | 344 | Edit: { disabled: true, disableDesc: '' }, |
a4b75251 | 345 | Remove: { disabled: true, disableDesc: '' } |
f67539c2 TL |
346 | } |
347 | }, | |
348 | { | |
349 | selectRow: fakeHosts[0], // non-orchestrator host | |
350 | expectResults: { | |
a4b75251 | 351 | Add: resultMissingFeatures, |
f67539c2 | 352 | Edit: { disabled: true, disableDesc: component.messages.nonOrchHost }, |
a4b75251 | 353 | Remove: { disabled: true, disableDesc: component.messages.nonOrchHost } |
f67539c2 TL |
354 | } |
355 | }, | |
356 | { | |
357 | selectRow: fakeHosts[1], // orchestrator host | |
358 | expectResults: { | |
a4b75251 | 359 | Add: resultMissingFeatures, |
f67539c2 | 360 | Edit: resultMissingFeatures, |
a4b75251 | 361 | Remove: resultMissingFeatures |
f67539c2 | 362 | } |
f91f0fd5 | 363 | } |
f67539c2 TL |
364 | ]; |
365 | await testTableActions(true, [], tests); | |
f6b5b4d7 TL |
366 | }); |
367 | }); | |
11fdf7f2 | 368 | }); |