]>
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'; | |
15 | import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; | |
16 | import { OrchestratorFeature } from '~/app/shared/models/orchestrator.enum'; | |
17 | import { Permissions } from '~/app/shared/models/permissions'; | |
18 | import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; | |
19 | import { SharedModule } from '~/app/shared/shared.module'; | |
20 | import { | |
21 | configureTestBed, | |
22 | OrchestratorHelper, | |
23 | TableActionHelper | |
24 | } from '~/testing/unit-test-helper'; | |
11fdf7f2 TL |
25 | import { HostsComponent } from './hosts.component'; |
26 | ||
b3b6e05e TL |
27 | class MockShowForceMaintenanceModal { |
28 | showModal = false; | |
29 | showModalDialog(msg: string) { | |
30 | if ( | |
31 | msg.includes('WARNING') && | |
32 | !msg.includes('It is NOT safe to stop') && | |
33 | !msg.includes('ALERT') && | |
34 | !msg.includes('unable to stop') | |
35 | ) { | |
36 | this.showModal = true; | |
37 | } | |
38 | } | |
39 | } | |
40 | ||
11fdf7f2 TL |
41 | describe('HostsComponent', () => { |
42 | let component: HostsComponent; | |
43 | let fixture: ComponentFixture<HostsComponent>; | |
9f95a23c | 44 | let hostListSpy: jasmine.Spy; |
f67539c2 | 45 | let orchService: OrchestratorService; |
b3b6e05e | 46 | let showForceMaintenanceModal: MockShowForceMaintenanceModal; |
11fdf7f2 TL |
47 | |
48 | const fakeAuthStorageService = { | |
49 | getPermissions: () => { | |
50 | return new Permissions({ hosts: ['read', 'update', 'create', 'delete'] }); | |
51 | } | |
52 | }; | |
53 | ||
54 | configureTestBed({ | |
55 | imports: [ | |
e306af50 | 56 | BrowserAnimationsModule, |
9f95a23c | 57 | CephSharedModule, |
11fdf7f2 TL |
58 | SharedModule, |
59 | HttpClientTestingModule, | |
9f95a23c TL |
60 | RouterTestingModule, |
61 | ToastrModule.forRoot(), | |
62 | CephModule, | |
63 | CoreModule | |
11fdf7f2 | 64 | ], |
f67539c2 TL |
65 | providers: [ |
66 | { provide: AuthStorageService, useValue: fakeAuthStorageService }, | |
67 | TableActionsComponent | |
68 | ] | |
11fdf7f2 TL |
69 | }); |
70 | ||
71 | beforeEach(() => { | |
b3b6e05e | 72 | showForceMaintenanceModal = new MockShowForceMaintenanceModal(); |
11fdf7f2 TL |
73 | fixture = TestBed.createComponent(HostsComponent); |
74 | component = fixture.componentInstance; | |
f67539c2 TL |
75 | hostListSpy = spyOn(TestBed.inject(HostService), 'list'); |
76 | orchService = TestBed.inject(OrchestratorService); | |
11fdf7f2 TL |
77 | }); |
78 | ||
79 | it('should create', () => { | |
80 | expect(component).toBeTruthy(); | |
81 | }); | |
82 | ||
f67539c2 | 83 | it('should render hosts list even with not permission mapped services', () => { |
11fdf7f2 TL |
84 | const hostname = 'ceph.dev'; |
85 | const payload = [ | |
86 | { | |
87 | services: [ | |
88 | { | |
89 | type: 'osd', | |
90 | id: '0' | |
91 | }, | |
92 | { | |
93 | type: 'rgw', | |
94 | id: 'rgw' | |
95 | }, | |
96 | { | |
97 | type: 'notPermissionMappedService', | |
98 | id: '1' | |
99 | } | |
100 | ], | |
101 | hostname: hostname, | |
e306af50 TL |
102 | ceph_version: 'ceph version Development', |
103 | labels: ['foo', 'bar'] | |
11fdf7f2 TL |
104 | } |
105 | ]; | |
106 | ||
f67539c2 | 107 | OrchestratorHelper.mockStatus(true); |
9f95a23c | 108 | hostListSpy.and.callFake(() => of(payload)); |
f67539c2 | 109 | fixture.detectChanges(); |
11fdf7f2 | 110 | |
f67539c2 | 111 | return fixture.whenStable().then(() => { |
11fdf7f2 TL |
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 | }); | |
f67539c2 | 119 | }); |
f6b5b4d7 | 120 | |
b3b6e05e TL |
121 | it('should show force maintenance modal when it is safe to stop host', () => { |
122 | const errorMsg = `WARNING: Stopping 1 out of 1 daemons in Grafana service. | |
123 | Service will not be operational with no daemons left. At | |
124 | least 1 daemon must be running to guarantee service.`; | |
125 | showForceMaintenanceModal.showModalDialog(errorMsg); | |
126 | expect(showForceMaintenanceModal.showModal).toBeTruthy(); | |
127 | }); | |
128 | ||
129 | it('should not show force maintenance modal when error is an ALERT', () => { | |
130 | const errorMsg = `ALERT: Cannot stop active Mgr daemon, Please switch active Mgrs | |
131 | with 'ceph mgr fail ceph-node-00'`; | |
132 | showForceMaintenanceModal.showModalDialog(errorMsg); | |
133 | expect(showForceMaintenanceModal.showModal).toBeFalsy(); | |
134 | }); | |
135 | ||
136 | it('should not show force maintenance modal when it is not safe to stop host', () => { | |
137 | const errorMsg = `WARNING: Stopping 1 out of 1 daemons in Grafana service. | |
138 | Service will not be operational with no daemons left. At | |
139 | least 1 daemon must be running to guarantee service. | |
140 | It is NOT safe to stop ['mon.ceph-node-00']: not enough | |
141 | monitors would be available (ceph-node-02) after stopping mons`; | |
142 | showForceMaintenanceModal.showModalDialog(errorMsg); | |
143 | expect(showForceMaintenanceModal.showModal).toBeFalsy(); | |
144 | }); | |
145 | ||
146 | it('should not show force maintenance modal when it is unable to stop host', () => { | |
147 | const errorMsg = 'unable to stop osd.0 because of some unknown reason'; | |
148 | showForceMaintenanceModal.showModalDialog(errorMsg); | |
149 | expect(showForceMaintenanceModal.showModal).toBeFalsy(); | |
150 | }); | |
151 | ||
f67539c2 TL |
152 | describe('table actions', () => { |
153 | const fakeHosts = require('./fixtures/host_list_response.json'); | |
f6b5b4d7 TL |
154 | |
155 | beforeEach(() => { | |
f67539c2 | 156 | hostListSpy.and.callFake(() => of(fakeHosts)); |
f6b5b4d7 TL |
157 | }); |
158 | ||
f67539c2 TL |
159 | const testTableActions = async ( |
160 | orch: boolean, | |
161 | features: OrchestratorFeature[], | |
162 | tests: { selectRow?: number; expectResults: any }[] | |
163 | ) => { | |
164 | OrchestratorHelper.mockStatus(orch, features); | |
165 | fixture.detectChanges(); | |
166 | await fixture.whenStable(); | |
f91f0fd5 | 167 | |
f67539c2 TL |
168 | for (const test of tests) { |
169 | if (test.selectRow) { | |
170 | component.selection = new CdTableSelection(); | |
171 | component.selection.selected = [test.selectRow]; | |
f91f0fd5 | 172 | } |
f67539c2 TL |
173 | await TableActionHelper.verifyTableActions( |
174 | fixture, | |
175 | component.tableActions, | |
176 | test.expectResults | |
177 | ); | |
178 | } | |
179 | }; | |
180 | ||
181 | it('should have correct states when Orchestrator is enabled', async () => { | |
182 | const tests = [ | |
183 | { | |
184 | expectResults: { | |
185 | Create: { disabled: false, disableDesc: '' }, | |
186 | Edit: { disabled: true, disableDesc: '' }, | |
187 | Delete: { disabled: true, disableDesc: '' } | |
188 | } | |
189 | }, | |
190 | { | |
191 | selectRow: fakeHosts[0], // non-orchestrator host | |
192 | expectResults: { | |
193 | Create: { disabled: false, disableDesc: '' }, | |
194 | Edit: { disabled: true, disableDesc: component.messages.nonOrchHost }, | |
195 | Delete: { disabled: true, disableDesc: component.messages.nonOrchHost } | |
196 | } | |
197 | }, | |
198 | { | |
199 | selectRow: fakeHosts[1], // orchestrator host | |
200 | expectResults: { | |
201 | Create: { disabled: false, disableDesc: '' }, | |
202 | Edit: { disabled: false, disableDesc: '' }, | |
203 | Delete: { disabled: false, disableDesc: '' } | |
204 | } | |
f91f0fd5 | 205 | } |
f67539c2 TL |
206 | ]; |
207 | ||
208 | const features = [ | |
209 | OrchestratorFeature.HOST_CREATE, | |
210 | OrchestratorFeature.HOST_LABEL_ADD, | |
211 | OrchestratorFeature.HOST_DELETE, | |
212 | OrchestratorFeature.HOST_LABEL_REMOVE | |
213 | ]; | |
214 | await testTableActions(true, features, tests); | |
f91f0fd5 TL |
215 | }); |
216 | ||
f67539c2 TL |
217 | it('should have correct states when Orchestrator is disabled', async () => { |
218 | const resultNoOrchestrator = { | |
219 | disabled: true, | |
220 | disableDesc: orchService.disableMessages.noOrchestrator | |
221 | }; | |
222 | const tests = [ | |
223 | { | |
224 | expectResults: { | |
225 | Create: resultNoOrchestrator, | |
226 | Edit: { disabled: true, disableDesc: '' }, | |
227 | Delete: { disabled: true, disableDesc: '' } | |
228 | } | |
229 | }, | |
230 | { | |
231 | selectRow: fakeHosts[0], // non-orchestrator host | |
232 | expectResults: { | |
233 | Create: resultNoOrchestrator, | |
234 | Edit: { disabled: true, disableDesc: component.messages.nonOrchHost }, | |
235 | Delete: { disabled: true, disableDesc: component.messages.nonOrchHost } | |
236 | } | |
237 | }, | |
238 | { | |
239 | selectRow: fakeHosts[1], // orchestrator host | |
240 | expectResults: { | |
241 | Create: resultNoOrchestrator, | |
242 | Edit: resultNoOrchestrator, | |
243 | Delete: resultNoOrchestrator | |
244 | } | |
245 | } | |
246 | ]; | |
247 | await testTableActions(false, [], tests); | |
f91f0fd5 TL |
248 | }); |
249 | ||
f67539c2 TL |
250 | it('should have correct states when Orchestrator features are missing', async () => { |
251 | const resultMissingFeatures = { | |
252 | disabled: true, | |
253 | disableDesc: orchService.disableMessages.missingFeature | |
254 | }; | |
255 | const tests = [ | |
256 | { | |
257 | expectResults: { | |
258 | Create: resultMissingFeatures, | |
259 | Edit: { disabled: true, disableDesc: '' }, | |
260 | Delete: { disabled: true, disableDesc: '' } | |
261 | } | |
262 | }, | |
263 | { | |
264 | selectRow: fakeHosts[0], // non-orchestrator host | |
265 | expectResults: { | |
266 | Create: resultMissingFeatures, | |
267 | Edit: { disabled: true, disableDesc: component.messages.nonOrchHost }, | |
268 | Delete: { disabled: true, disableDesc: component.messages.nonOrchHost } | |
269 | } | |
270 | }, | |
271 | { | |
272 | selectRow: fakeHosts[1], // orchestrator host | |
273 | expectResults: { | |
274 | Create: resultMissingFeatures, | |
275 | Edit: resultMissingFeatures, | |
276 | Delete: resultMissingFeatures | |
277 | } | |
f91f0fd5 | 278 | } |
f67539c2 TL |
279 | ]; |
280 | await testTableActions(true, [], tests); | |
f6b5b4d7 TL |
281 | }); |
282 | }); | |
11fdf7f2 | 283 | }); |