]>
Commit | Line | Data |
---|---|---|
1 | import { HttpClientTestingModule } from '@angular/common/http/testing'; | |
2 | import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; | |
3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | |
4 | import { RouterTestingModule } from '@angular/router/testing'; | |
5 | ||
6 | import { NgbModalModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; | |
7 | import { MockComponent } from 'ng-mocks'; | |
8 | import { ToastrModule } from 'ngx-toastr'; | |
9 | import { Subject, throwError as observableThrowError } from 'rxjs'; | |
10 | ||
11 | import { RbdService } from '~/app/shared/api/rbd.service'; | |
12 | import { ComponentsModule } from '~/app/shared/components/components.module'; | |
13 | import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; | |
14 | import { ActionLabelsI18n } from '~/app/shared/constants/app.constants'; | |
15 | import { DataTableModule } from '~/app/shared/datatable/datatable.module'; | |
16 | import { TableActionsComponent } from '~/app/shared/datatable/table-actions/table-actions.component'; | |
17 | import { CdTableSelection } from '~/app/shared/models/cd-table-selection'; | |
18 | import { ExecutingTask } from '~/app/shared/models/executing-task'; | |
19 | import { Permissions } from '~/app/shared/models/permissions'; | |
20 | import { PipesModule } from '~/app/shared/pipes/pipes.module'; | |
21 | import { AuthStorageService } from '~/app/shared/services/auth-storage.service'; | |
22 | import { ModalService } from '~/app/shared/services/modal.service'; | |
23 | import { NotificationService } from '~/app/shared/services/notification.service'; | |
24 | import { SummaryService } from '~/app/shared/services/summary.service'; | |
25 | import { TaskListService } from '~/app/shared/services/task-list.service'; | |
26 | import { configureTestBed, expectItemTasks, PermissionHelper } from '~/testing/unit-test-helper'; | |
27 | import { RbdSnapshotFormModalComponent } from '../rbd-snapshot-form/rbd-snapshot-form-modal.component'; | |
28 | import { RbdTabsComponent } from '../rbd-tabs/rbd-tabs.component'; | |
29 | import { RbdSnapshotActionsModel } from './rbd-snapshot-actions.model'; | |
30 | import { RbdSnapshotListComponent } from './rbd-snapshot-list.component'; | |
31 | import { RbdSnapshotModel } from './rbd-snapshot.model'; | |
32 | ||
33 | describe('RbdSnapshotListComponent', () => { | |
34 | let component: RbdSnapshotListComponent; | |
35 | let fixture: ComponentFixture<RbdSnapshotListComponent>; | |
36 | let summaryService: SummaryService; | |
37 | ||
38 | const fakeAuthStorageService = { | |
39 | isLoggedIn: () => { | |
40 | return true; | |
41 | }, | |
42 | getPermissions: () => { | |
43 | return new Permissions({ 'rbd-image': ['read', 'update', 'create', 'delete'] }); | |
44 | } | |
45 | }; | |
46 | ||
47 | configureTestBed( | |
48 | { | |
49 | declarations: [ | |
50 | RbdSnapshotListComponent, | |
51 | RbdTabsComponent, | |
52 | MockComponent(RbdSnapshotFormModalComponent) | |
53 | ], | |
54 | imports: [ | |
55 | BrowserAnimationsModule, | |
56 | ComponentsModule, | |
57 | DataTableModule, | |
58 | HttpClientTestingModule, | |
59 | PipesModule, | |
60 | RouterTestingModule, | |
61 | NgbNavModule, | |
62 | ToastrModule.forRoot(), | |
63 | NgbModalModule | |
64 | ], | |
65 | providers: [ | |
66 | { provide: AuthStorageService, useValue: fakeAuthStorageService }, | |
67 | TaskListService | |
68 | ] | |
69 | }, | |
70 | [CriticalConfirmationModalComponent] | |
71 | ); | |
72 | ||
73 | beforeEach(() => { | |
74 | fixture = TestBed.createComponent(RbdSnapshotListComponent); | |
75 | component = fixture.componentInstance; | |
76 | component.ngOnChanges(); | |
77 | summaryService = TestBed.inject(SummaryService); | |
78 | }); | |
79 | ||
80 | it('should create', () => { | |
81 | fixture.detectChanges(); | |
82 | expect(component).toBeTruthy(); | |
83 | }); | |
84 | ||
85 | describe('api delete request', () => { | |
86 | let called: boolean; | |
87 | let rbdService: RbdService; | |
88 | let notificationService: NotificationService; | |
89 | let authStorageService: AuthStorageService; | |
90 | ||
91 | beforeEach(() => { | |
92 | fixture.detectChanges(); | |
93 | const modalService = TestBed.inject(ModalService); | |
94 | const actionLabelsI18n = TestBed.inject(ActionLabelsI18n); | |
95 | called = false; | |
96 | rbdService = new RbdService(null, null); | |
97 | notificationService = new NotificationService(null, null, null); | |
98 | authStorageService = new AuthStorageService(); | |
99 | authStorageService.set('user', { 'rbd-image': ['create', 'read', 'update', 'delete'] }); | |
100 | component = new RbdSnapshotListComponent( | |
101 | authStorageService, | |
102 | modalService, | |
103 | null, | |
104 | null, | |
105 | rbdService, | |
106 | null, | |
107 | notificationService, | |
108 | null, | |
109 | null, | |
110 | actionLabelsI18n, | |
111 | null | |
112 | ); | |
113 | spyOn(rbdService, 'deleteSnapshot').and.returnValue(observableThrowError({ status: 500 })); | |
114 | spyOn(notificationService, 'notifyTask').and.stub(); | |
115 | }); | |
116 | ||
117 | it('should call stopLoadingSpinner if the request fails', fakeAsync(() => { | |
118 | component.updateSelection(new CdTableSelection([{ name: 'someName' }])); | |
119 | expect(called).toBe(false); | |
120 | component.deleteSnapshotModal(); | |
121 | spyOn(component.modalRef.componentInstance, 'stopLoadingSpinner').and.callFake(() => { | |
122 | called = true; | |
123 | }); | |
124 | component.modalRef.componentInstance.submitAction(); | |
125 | tick(500); | |
126 | expect(called).toBe(true); | |
127 | })); | |
128 | }); | |
129 | ||
130 | describe('handling of executing tasks', () => { | |
131 | let snapshots: RbdSnapshotModel[]; | |
132 | ||
133 | const addSnapshot = (name: string) => { | |
134 | const model = new RbdSnapshotModel(); | |
135 | model.id = 1; | |
136 | model.name = name; | |
137 | snapshots.push(model); | |
138 | }; | |
139 | ||
140 | const addTask = (task_name: string, snapshot_name: string) => { | |
141 | const task = new ExecutingTask(); | |
142 | task.name = task_name; | |
143 | task.metadata = { | |
144 | image_spec: 'rbd/foo', | |
145 | snapshot_name: snapshot_name | |
146 | }; | |
147 | summaryService.addRunningTask(task); | |
148 | }; | |
149 | ||
150 | const refresh = (data: any) => { | |
151 | summaryService['summaryDataSource'].next(data); | |
152 | }; | |
153 | ||
154 | beforeEach(() => { | |
155 | fixture.detectChanges(); | |
156 | snapshots = []; | |
157 | addSnapshot('a'); | |
158 | addSnapshot('b'); | |
159 | addSnapshot('c'); | |
160 | component.snapshots = snapshots; | |
161 | component.poolName = 'rbd'; | |
162 | component.rbdName = 'foo'; | |
163 | refresh({ executing_tasks: [], finished_tasks: [] }); | |
164 | component.ngOnChanges(); | |
165 | fixture.detectChanges(); | |
166 | }); | |
167 | ||
168 | it('should gets all snapshots without tasks', () => { | |
169 | expect(component.snapshots.length).toBe(3); | |
170 | expect(component.snapshots.every((image) => !image.cdExecuting)).toBeTruthy(); | |
171 | }); | |
172 | ||
173 | it('should add a new image from a task', () => { | |
174 | addTask('rbd/snap/create', 'd'); | |
175 | expect(component.snapshots.length).toBe(4); | |
176 | expectItemTasks(component.snapshots[0], undefined); | |
177 | expectItemTasks(component.snapshots[1], undefined); | |
178 | expectItemTasks(component.snapshots[2], undefined); | |
179 | expectItemTasks(component.snapshots[3], 'Creating'); | |
180 | }); | |
181 | ||
182 | it('should show when an existing image is being modified', () => { | |
183 | addTask('rbd/snap/edit', 'a'); | |
184 | addTask('rbd/snap/delete', 'b'); | |
185 | addTask('rbd/snap/rollback', 'c'); | |
186 | expect(component.snapshots.length).toBe(3); | |
187 | expectItemTasks(component.snapshots[0], 'Updating'); | |
188 | expectItemTasks(component.snapshots[1], 'Deleting'); | |
189 | expectItemTasks(component.snapshots[2], 'Rolling back'); | |
190 | }); | |
191 | }); | |
192 | ||
193 | describe('snapshot modal dialog', () => { | |
194 | beforeEach(() => { | |
195 | component.poolName = 'pool01'; | |
196 | component.rbdName = 'image01'; | |
197 | spyOn(TestBed.inject(ModalService), 'show').and.callFake(() => { | |
198 | const ref: any = {}; | |
199 | ref.componentInstance = new RbdSnapshotFormModalComponent( | |
200 | null, | |
201 | null, | |
202 | null, | |
203 | null, | |
204 | TestBed.inject(ActionLabelsI18n), | |
205 | null | |
206 | ); | |
207 | ref.componentInstance.onSubmit = new Subject(); | |
208 | return ref; | |
209 | }); | |
210 | }); | |
211 | ||
212 | it('should display old snapshot name', () => { | |
213 | component.selection.selected = [{ name: 'oldname' }]; | |
214 | component.openEditSnapshotModal(); | |
215 | expect(component.modalRef.componentInstance.snapName).toBe('oldname'); | |
216 | expect(component.modalRef.componentInstance.editing).toBeTruthy(); | |
217 | }); | |
218 | ||
219 | it('should display suggested snapshot name', () => { | |
220 | component.openCreateSnapshotModal(); | |
221 | expect(component.modalRef.componentInstance.snapName).toMatch( | |
222 | RegExp(`^${component.rbdName}_[\\d-]+T[\\d.:]+[\\+-][\\d:]+$`) | |
223 | ); | |
224 | }); | |
225 | }); | |
226 | ||
227 | it('should test all TableActions combinations', () => { | |
228 | component.ngOnInit(); | |
229 | const permissionHelper: PermissionHelper = new PermissionHelper(component.permission); | |
230 | const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions( | |
231 | component.tableActions | |
232 | ); | |
233 | ||
234 | expect(tableActions).toEqual({ | |
235 | 'create,update,delete': { | |
236 | actions: [ | |
237 | 'Create', | |
238 | 'Rename', | |
239 | 'Protect', | |
240 | 'Unprotect', | |
241 | 'Clone', | |
242 | 'Copy', | |
243 | 'Rollback', | |
244 | 'Delete' | |
245 | ], | |
246 | primary: { multiple: 'Create', executing: 'Rename', single: 'Rename', no: 'Create' } | |
247 | }, | |
248 | 'create,update': { | |
249 | actions: ['Create', 'Rename', 'Protect', 'Unprotect', 'Clone', 'Copy', 'Rollback'], | |
250 | primary: { multiple: 'Create', executing: 'Rename', single: 'Rename', no: 'Create' } | |
251 | }, | |
252 | 'create,delete': { | |
253 | actions: ['Create', 'Clone', 'Copy', 'Delete'], | |
254 | primary: { multiple: 'Create', executing: 'Clone', single: 'Clone', no: 'Create' } | |
255 | }, | |
256 | create: { | |
257 | actions: ['Create', 'Clone', 'Copy'], | |
258 | primary: { multiple: 'Create', executing: 'Clone', single: 'Clone', no: 'Create' } | |
259 | }, | |
260 | 'update,delete': { | |
261 | actions: ['Rename', 'Protect', 'Unprotect', 'Rollback', 'Delete'], | |
262 | primary: { multiple: 'Rename', executing: 'Rename', single: 'Rename', no: 'Rename' } | |
263 | }, | |
264 | update: { | |
265 | actions: ['Rename', 'Protect', 'Unprotect', 'Rollback'], | |
266 | primary: { multiple: 'Rename', executing: 'Rename', single: 'Rename', no: 'Rename' } | |
267 | }, | |
268 | delete: { | |
269 | actions: ['Delete'], | |
270 | primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Delete' } | |
271 | }, | |
272 | 'no-permissions': { | |
273 | actions: [], | |
274 | primary: { multiple: '', executing: '', single: '', no: '' } | |
275 | } | |
276 | }); | |
277 | }); | |
278 | ||
279 | describe('clone button disable state', () => { | |
280 | let actions: RbdSnapshotActionsModel; | |
281 | ||
282 | beforeEach(() => { | |
283 | fixture.detectChanges(); | |
284 | const rbdService = TestBed.inject(RbdService); | |
285 | const actionLabelsI18n = TestBed.inject(ActionLabelsI18n); | |
286 | actions = new RbdSnapshotActionsModel(actionLabelsI18n, [], rbdService); | |
287 | }); | |
288 | ||
289 | it('should be disabled with version 1 and protected false', () => { | |
290 | const selection = new CdTableSelection([{ name: 'someName', is_protected: false }]); | |
291 | const disableDesc = actions.getCloneDisableDesc(selection); | |
292 | expect(disableDesc).toBe('Snapshot must be protected in order to clone.'); | |
293 | }); | |
294 | ||
295 | it.each([ | |
296 | [1, true], | |
297 | [2, true], | |
298 | [2, false] | |
299 | ])('should be enabled with version %d and protected %s', (version, is_protected) => { | |
300 | actions.cloneFormatVersion = version; | |
301 | const selection = new CdTableSelection([{ name: 'someName', is_protected: is_protected }]); | |
302 | const disableDesc = actions.getCloneDisableDesc(selection); | |
303 | expect(disableDesc).toBe(false); | |
304 | }); | |
305 | }); | |
306 | ||
307 | describe('protect button disable state', () => { | |
308 | let actions: RbdSnapshotActionsModel; | |
309 | ||
310 | beforeEach(() => { | |
311 | fixture.detectChanges(); | |
312 | const rbdService = TestBed.inject(RbdService); | |
313 | const actionLabelsI18n = TestBed.inject(ActionLabelsI18n); | |
314 | actions = new RbdSnapshotActionsModel(actionLabelsI18n, [], rbdService); | |
315 | }); | |
316 | ||
317 | it('should be disabled if layering not supported', () => { | |
318 | const selection = new CdTableSelection([{ name: 'someName', is_protected: false }]); | |
319 | const disableDesc = actions.getProtectDisableDesc(selection, ['deep-flatten', 'fast-diff']); | |
320 | expect(disableDesc).toBe('The layering feature needs to be enabled on parent image'); | |
321 | }); | |
322 | }); | |
323 | }); |