]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: Add unit test to the frontend services
authorTiago Melo <tmelo@suse.com>
Thu, 24 May 2018 10:41:45 +0000 (11:41 +0100)
committerTiago Melo <tmelo@suse.com>
Fri, 25 May 2018 12:41:56 +0000 (13:41 +0100)
Signed-off-by: Tiago Melo <tmelo@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-guard.service.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/services/formatter.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/module-status-guard.service.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/services/notification.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/notification.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/summary.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/summary.service.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-manager-message.service.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-manager.service.spec.ts [new file with mode: 0644]

index fb8a6e4f59eaf90209bf0b821d2d233b982acefd..7b967cf95561ae8ebd35e8c6c5ac5e3eae6ac4a7 100644 (file)
@@ -116,7 +116,7 @@ export class RbdListComponent implements OnInit, OnDestroy {
       }
     ];
 
-    this.summaryService.get().then(resp => {
+    this.summaryService.get().subscribe((resp: any) => {
       this.loadImages(resp.executing_tasks);
       this.summaryDataSubscription = this.summaryService.summaryData$.subscribe((data: any) => {
         this.loadImages(data.executing_tasks);
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-guard.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-guard.service.spec.ts
new file mode 100644 (file)
index 0000000..6be3273
--- /dev/null
@@ -0,0 +1,51 @@
+import { Component } from '@angular/core';
+import { fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { Router, Routes } from '@angular/router';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { AuthGuardService } from './auth-guard.service';
+import { AuthStorageService } from './auth-storage.service';
+
+describe('AuthGuardService', () => {
+  let service: AuthGuardService;
+
+  @Component({selector: 'cd-login', template: ''})
+  class LoginComponent { }
+
+  const routes: Routes = [{ path: 'login', component: LoginComponent }];
+
+  const fakeService = {
+    isLoggedIn: () => true
+  };
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [RouterTestingModule.withRoutes(routes)],
+      providers: [AuthGuardService, { provide: AuthStorageService, useValue: fakeService }],
+      declarations: [LoginComponent]
+    });
+
+    service = TestBed.get(AuthGuardService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+
+  it('should allow the user if loggedIn', () => {
+    expect(service.canActivate(null, null)).toBe(true);
+  });
+
+  it(
+    'should prevent user if not loggedIn and redirect to login page',
+    fakeAsync(() => {
+      const router = TestBed.get(Router);
+      const authStorageService = TestBed.get(AuthStorageService);
+      spyOn(authStorageService, 'isLoggedIn').and.returnValue(false);
+
+      expect(service.canActivate(null, null)).toBe(false);
+      tick();
+      expect(router.url).toBe('/login');
+    })
+  );
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/auth-storage.service.spec.ts
new file mode 100644 (file)
index 0000000..068a1e1
--- /dev/null
@@ -0,0 +1,35 @@
+import { AuthStorageService } from './auth-storage.service';
+
+describe('AuthStorageService', () => {
+  let service: AuthStorageService;
+  const username = 'foobar';
+
+  beforeEach(() => {
+    service = new AuthStorageService();
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+
+  it('should store username', () => {
+    service.set(username);
+    expect(localStorage.getItem('dashboard_username')).toBe(username);
+  });
+
+  it('should remove username', () => {
+    service.set(username);
+    service.remove();
+    expect(localStorage.getItem('dashboard_username')).toBe(null);
+  });
+
+  it('should be loggedIn', () => {
+    service.set(username);
+    expect(service.isLoggedIn()).toBe(true);
+  });
+
+  it('should not be loggedIn', () => {
+    service.remove();
+    expect(service.isLoggedIn()).toBe(false);
+  });
+});
index 42f64e9b20d10cd5d339bb8f2edcaa6e6756d950..3cac2b4aa6cbb597779df8a31527f0e77c05a334 100644 (file)
@@ -39,7 +39,7 @@ describe('FormatterService', () => {
       expect(service.truncate(value, 6)).toBe('1234.567899');
       expect(service.truncate(value, 7)).toBe('1234.567899');
       expect(service.truncate(value, 10)).toBe('1234.567899');
-      expect(service.truncate(100.00, 4)).toBe('100');
+      expect(service.truncate(100.0, 4)).toBe('100');
     });
   });
 
@@ -57,6 +57,9 @@ describe('FormatterService', () => {
       expect(service.format_number('1', 1024, formats)).toBe('1B');
       expect(service.format_number('1024', 1024, formats)).toBe('1KiB');
       expect(service.format_number(23.45678 * Math.pow(1024, 3), 1024, formats)).toBe('23.4568GiB');
+      expect(service.format_number(23.45678 * Math.pow(1024, 3), 1024, formats, 2)).toBe(
+        '23.46GiB'
+      );
     });
   });
 
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/module-status-guard.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/module-status-guard.service.spec.ts
new file mode 100644 (file)
index 0000000..6ffe064
--- /dev/null
@@ -0,0 +1,107 @@
+import { HttpClient } from '@angular/common/http';
+import { Component } from '@angular/core';
+import { fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { ActivatedRouteSnapshot, Router, Routes } from '@angular/router';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import 'rxjs/add/observable/of';
+import { Observable } from 'rxjs/Observable';
+
+import { ModuleStatusGuardService } from './module-status-guard.service';
+
+describe('ModuleStatusGuardService', () => {
+  let service: ModuleStatusGuardService;
+
+  @Component({ selector: 'cd-foo', template: '' })
+  class FooComponent {}
+
+  const fakeService = {
+    get: () => true
+  };
+
+  const routes: Routes = [{ path: '**', component: FooComponent }];
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      imports: [RouterTestingModule.withRoutes(routes)],
+      providers: [ModuleStatusGuardService, { provide: HttpClient, useValue: fakeService }],
+      declarations: [FooComponent]
+    });
+
+    service = TestBed.get(ModuleStatusGuardService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+
+  it(
+    'should test canActivate with status available',
+    fakeAsync(() => {
+      let result = false;
+      const route = new ActivatedRouteSnapshot();
+      route.data = {
+        moduleStatusGuardConfig: {
+          apiPath: 'bar',
+          redirectTo: 'foo'
+        }
+      };
+      const httpClient = TestBed.get(HttpClient);
+      spyOn(httpClient, 'get').and.returnValue(Observable.of({ available: true, message: 'foo' }));
+      service.canActivate(route, null).subscribe((resp) => {
+        result = resp;
+      });
+
+      tick();
+      expect(result).toBe(true);
+    })
+  );
+
+  it(
+    'should test canActivateChild with status unavailable',
+    fakeAsync(() => {
+      let result = true;
+      const route = new ActivatedRouteSnapshot();
+      route.data = {
+        moduleStatusGuardConfig: {
+          apiPath: 'bar',
+          redirectTo: '/foo'
+        }
+      };
+      const httpClient = TestBed.get(HttpClient);
+      const router = TestBed.get(Router);
+      spyOn(httpClient, 'get').and.returnValue(Observable.of({ available: false, message: null }));
+      service.canActivateChild(route, null).subscribe((resp) => {
+        result = resp;
+      });
+
+      tick();
+      expect(result).toBe(false);
+      expect(router.url).toBe('/foo/');
+    })
+  );
+
+  it(
+    'should test canActivateChild with status unavailable',
+    fakeAsync(() => {
+      let result = true;
+      const route = new ActivatedRouteSnapshot();
+      route.data = {
+        moduleStatusGuardConfig: {
+          apiPath: 'bar',
+          redirectTo: '/foo'
+        }
+      };
+      const httpClient = TestBed.get(HttpClient);
+      const router = TestBed.get(Router);
+      spyOn(httpClient, 'get').and.returnValue(Observable.of(null));
+      service.canActivateChild(route, null).subscribe((resp) => {
+        result = resp;
+      });
+
+      tick();
+      expect(result).toBe(false);
+      expect(router.url).toBe('/foo');
+    })
+  );
+});
index e7f5e99b68e53882c14bb51cb91c63793528ac17..8a5676aff470179ba03bbbeeaf33f2c5d0a89474 100644 (file)
-import { inject, TestBed } from '@angular/core/testing';
+import { fakeAsync, TestBed, tick } from '@angular/core/testing';
 
-import { ToastOptions, ToastsManager } from 'ng2-toastr';
+import * as _ from 'lodash';
+import { ToastsManager } from 'ng2-toastr';
 
 import { NotificationType } from '../enum/notification-type.enum';
+import { FinishedTask } from '../models/finished-task';
 import { NotificationService } from './notification.service';
 import { TaskManagerMessageService } from './task-manager-message.service';
-import { TaskManagerService } from './task-manager.service';
 
 describe('NotificationService', () => {
+  let notificationService: NotificationService;
+  const fakeService = {
+    // ToastsManager
+    error: () => true,
+    info: () => true,
+    success: () => true,
+    // TaskManagerMessageService
+    getDescription: () => true,
+    getErrorMessage: () => true,
+    getSuccessMessage: () => true
+  };
+
   beforeEach(() => {
     TestBed.configureTestingModule({
       providers: [
         NotificationService,
-        ToastsManager,
-        ToastOptions,
-        TaskManagerService,
-        TaskManagerMessageService
+        { provide: TaskManagerMessageService, useValue: fakeService },
+        { provide: ToastsManager, useValue: fakeService }
       ]
     });
+
+    notificationService = TestBed.get(NotificationService);
+    notificationService.removeAll();
+  });
+
+  it('should be created', () => {
+    expect(notificationService).toBeTruthy();
   });
 
-  it('should be created',
-    inject([NotificationService], (service: NotificationService) => {
-      expect(service).toBeTruthy();
-    }));
-
-  it('should not create a notification',
-    inject([NotificationService], (service: NotificationService) => {
-      expect(service).toBeTruthy();
-      service.removeAll();
-      const timeoutId = service.show(NotificationType.error, 'Simple test');
-      service.cancel(timeoutId);
-      expect(service['dataSource'].getValue().length).toBe(0);
-    }));
+  it('should read empty notification list', () => {
+    localStorage.setItem('cdNotifications', '[]');
+    expect(notificationService['dataSource'].getValue()).toEqual([]);
+  });
+
+  it(
+    'should read old notifications',
+    fakeAsync(() => {
+      localStorage.setItem(
+        'cdNotifications',
+        '[{"type":2,"message":"foobar","timestamp":"2018-05-24T09:41:32.726Z"}]'
+      );
+      const service = new NotificationService(null, null);
+      expect(service['dataSource'].getValue().length).toBe(1);
+    })
+  );
+
+  it(
+    'should cancel a notification',
+    fakeAsync(() => {
+      const timeoutId = notificationService.show(NotificationType.error, 'Simple test');
+      notificationService.cancel(timeoutId);
+      tick(5000);
+      expect(notificationService['dataSource'].getValue().length).toBe(0);
+    })
+  );
+
+  it(
+    'should create a success notification and save it',
+    fakeAsync(() => {
+      notificationService.show(NotificationType.success, 'Simple test');
+      tick(100);
+      expect(notificationService['dataSource'].getValue().length).toBe(1);
+      expect(notificationService['dataSource'].getValue()[0].type).toBe(NotificationType.success);
+    })
+  );
+
+  it(
+    'should create an error notification and save it',
+    fakeAsync(() => {
+      notificationService.show(NotificationType.error, 'Simple test');
+      tick(100);
+      expect(notificationService['dataSource'].getValue().length).toBe(1);
+      expect(notificationService['dataSource'].getValue()[0].type).toBe(NotificationType.error);
+    })
+  );
+
+  it(
+    'should create an info notification and save it',
+    fakeAsync(() => {
+      notificationService.show(NotificationType.info, 'Simple test');
+      tick(100);
+      expect(notificationService['dataSource'].getValue().length).toBe(1);
+      expect(notificationService['dataSource'].getValue()[0].type).toBe(NotificationType.info);
+    })
+  );
+
+  it(
+    'should never have more then 10 notifications',
+    fakeAsync(() => {
+      for (let index = 0; index < 15; index++) {
+        notificationService.show(NotificationType.info, 'Simple test');
+        tick(100);
+      }
+      expect(notificationService['dataSource'].getValue().length).toBe(10);
+    })
+  );
+
+  it(
+    'should show a success task notification',
+    fakeAsync(() => {
+      const task = _.assign(new FinishedTask(), {
+        success: true
+      });
+      notificationService.notifyTask(task, true);
+      tick(100);
+      expect(notificationService['dataSource'].getValue().length).toBe(1);
+      expect(notificationService['dataSource'].getValue()[0].type).toBe(NotificationType.success);
+    })
+  );
+
+  it(
+    'should show a error task notification',
+    fakeAsync(() => {
+      const task = _.assign(new FinishedTask(), {
+        success: false
+      });
+      notificationService.notifyTask(task);
+      tick(100);
+      expect(notificationService['dataSource'].getValue().length).toBe(1);
+      expect(notificationService['dataSource'].getValue()[0].type).toBe(NotificationType.error);
+    })
+  );
 });
index 5057e9af722ea27b4e9582980ec2029fa2d002b1..a80d259303134f5be0bd49c4e496bd8324ba3c86 100644 (file)
@@ -19,8 +19,10 @@ export class NotificationService {
 
   KEY = 'cdNotifications';
 
-  constructor(public toastr: ToastsManager,
-              private taskManagerMessageService: TaskManagerMessageService) {
+  constructor(
+    public toastr: ToastsManager,
+    private taskManagerMessageService: TaskManagerMessageService
+  ) {
     const stringNotifications = localStorage.getItem(this.KEY);
     let notifications: CdNotification[] = [];
 
@@ -89,12 +91,16 @@ export class NotificationService {
 
   notifyTask(finishedTask: FinishedTask, success: boolean = true) {
     if (finishedTask.success && success) {
-      this.show(NotificationType.success,
-        this.taskManagerMessageService.getSuccessMessage(finishedTask));
+      this.show(
+        NotificationType.success,
+        this.taskManagerMessageService.getSuccessMessage(finishedTask)
+      );
     } else {
-      this.show(NotificationType.error,
+      this.show(
+        NotificationType.error,
         this.taskManagerMessageService.getErrorMessage(finishedTask),
-        this.taskManagerMessageService.getDescription(finishedTask));
+        this.taskManagerMessageService.getDescription(finishedTask)
+      );
     }
   }
 
@@ -103,8 +109,6 @@ export class NotificationService {
    * @param {number} timeoutId A number representing the ID of the timeout to be canceled.
    */
   cancel(timeoutId) {
-    if (timeoutId) {
-      clearTimeout(timeoutId);
-    }
+    clearTimeout(timeoutId);
   }
 }
index 2f0c4f9cc33c675d9a8fe82d6d5ad9e26623cd04..697e9401a084bdae62a218493db153a56a9b89ee 100644 (file)
@@ -1,21 +1,61 @@
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-import { inject, TestBed } from '@angular/core/testing';
+import { HttpClient } from '@angular/common/http';
+import { fakeAsync, TestBed, tick } from '@angular/core/testing';
+
+import 'rxjs/add/observable/of';
+import { Observable } from 'rxjs/Observable';
 
 import { AuthStorageService } from './auth-storage.service';
 import { SummaryService } from './summary.service';
 
 describe('SummaryService', () => {
+  let summaryService: SummaryService;
+  let authStorageService: AuthStorageService;
+
+  const httpClientSpy = {
+    get: () =>
+      Observable.of({
+        executing_tasks: [],
+        health_status: 'HEALTH_OK',
+        mgr_id: 'x',
+        rbd_mirroring: { errors: 0, warnings: 0 },
+        rbd_pools: [],
+        have_mon_connection: true,
+        finished_tasks: [],
+        filesystems: [{ id: 1, name: 'cephfs_a' }]
+      })
+  };
+
   beforeEach(() => {
     TestBed.configureTestingModule({
-      providers: [SummaryService, AuthStorageService],
-      imports: [HttpClientTestingModule]
+      providers: [
+        SummaryService,
+        AuthStorageService,
+        { provide: HttpClient, useValue: httpClientSpy }
+      ]
     });
+
+    summaryService = TestBed.get(SummaryService);
+    authStorageService = TestBed.get(AuthStorageService);
   });
 
-  it(
-    'should be created',
-    inject([SummaryService], (service: SummaryService) => {
-      expect(service).toBeTruthy();
-    })
-  );
+  it('should be created', () => {
+    expect(summaryService).toBeTruthy();
+  });
+
+  it('should call refresh', fakeAsync(() => {
+    authStorageService.set('foobar');
+    let result = false;
+    summaryService.refresh();
+    summaryService.summaryData$.subscribe((res) => {
+      result = true;
+    });
+    tick(5000);
+    spyOn(summaryService, 'refresh').and.callFake(() => true);
+    tick(5000);
+    expect(result).toEqual(true);
+  }));
+
+  it('should get summary', () => {
+    expect(summaryService.get()).toEqual(jasmine.any(Object));
+  });
 });
index 2701f741c6338ae563d45f98a05ed1fa684a9db1..a6fb493bcc6d3abda2bb75757db46b6470d96ef4 100644 (file)
@@ -38,8 +38,6 @@ export class SummaryService {
   }
 
   get() {
-    return this.http.get('api/summary').toPromise().then((resp: any) => {
-      return resp;
-    });
+    return this.http.get('api/summary');
   }
 }
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-manager-message.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-manager-message.service.spec.ts
new file mode 100644 (file)
index 0000000..5dade8f
--- /dev/null
@@ -0,0 +1,51 @@
+import * as _ from 'lodash';
+
+import { FinishedTask } from '../models/finished-task';
+import { TaskException } from '../models/task-exception';
+import { TaskManagerMessageService } from './task-manager-message.service';
+
+describe('TaskManagerMessageService', () => {
+  let service: TaskManagerMessageService;
+  let finishedTask: FinishedTask;
+
+  beforeEach(() => {
+    service = new TaskManagerMessageService();
+    finishedTask = new FinishedTask();
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+
+  it('should getDescription', () => {
+    finishedTask.name = 'foo';
+    finishedTask.exception = _.assign(new TaskException(), {
+      code: 1
+    });
+    finishedTask.metadata = {};
+
+    const message = service.getDescription(finishedTask);
+    expect(message).toBe('Unknown Task');
+  });
+
+  it('should getErrorMessage', () => {
+    finishedTask.exception = _.assign(new TaskException(), {
+      code: 1
+    });
+    const message = service.getErrorMessage(finishedTask);
+    expect(message).toBe(undefined);
+  });
+
+  it('should getSuccessMessage', () => {
+    const message = service.getSuccessMessage(finishedTask);
+    expect(message).toBe('Task executed successfully');
+  });
+
+  it('should test if all messages methods are defined', () => {
+    _.forIn(service.messages, (value, key) => {
+      expect(value.descr({})).toBeTruthy();
+      expect(value.success({})).toBeTruthy();
+      expect(value.error({})).toBeTruthy();
+    });
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-manager.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/task-manager.service.spec.ts
new file mode 100644 (file)
index 0000000..1daa304
--- /dev/null
@@ -0,0 +1,71 @@
+import { fakeAsync, TestBed, tick } from '@angular/core/testing';
+
+import * as _ from 'lodash';
+import 'rxjs/add/observable/of';
+import { Subject } from 'rxjs/Subject';
+
+import { SummaryService } from './summary.service';
+import { TaskManagerService } from './task-manager.service';
+
+describe('TaskManagerService', () => {
+  let taskManagerService: TaskManagerService;
+
+  const summaryDataSource = new Subject();
+  const fakeService = {
+    summaryData$: summaryDataSource.asObservable()
+  };
+
+  const summary = {
+    executing_tasks: [],
+    health_status: 'HEALTH_OK',
+    mgr_id: 'x',
+    rbd_mirroring: { errors: 0, warnings: 0 },
+    rbd_pools: [],
+    have_mon_connection: true,
+    finished_tasks: [{ name: 'foo', metadata: {} }],
+    filesystems: [{ id: 1, name: 'cephfs_a' }]
+  };
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [TaskManagerService, { provide: SummaryService, useValue: fakeService }]
+    });
+
+    taskManagerService = TestBed.get(TaskManagerService);
+  });
+
+  it('should be created', () => {
+    expect(taskManagerService).toBeTruthy();
+  });
+
+  it(
+    'should subscribe and be notified when task is finished',
+    fakeAsync(() => {
+      let called = false;
+      taskManagerService.subscribe('foo', {}, () => (called = true));
+      expect(taskManagerService.subscriptions.length).toBe(1);
+
+      summaryDataSource.next(summary);
+      tick();
+
+      expect(called).toEqual(true);
+      expect(taskManagerService.subscriptions).toEqual([]);
+    })
+  );
+
+  it(
+    'should subscribe and process executing taks',
+    fakeAsync(() => {
+      let called = false;
+      taskManagerService.subscribe('foo', {}, () => (called = true));
+      const original_subscriptions = _.cloneDeep(taskManagerService.subscriptions);
+      const new_summary = _.assign(summary, {
+        executing_tasks: [{ name: 'foo', metadata: {} }],
+        finished_tasks: []
+      });
+      summaryDataSource.next(summary);
+      tick();
+      expect(taskManagerService.subscriptions).toEqual(original_subscriptions);
+    })
+  );
+});