]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add frontend unit tests for orchestrator components 29127/head
authorKiefer Chang <kiefer.chang@suse.com>
Tue, 13 Aug 2019 09:58:15 +0000 (17:58 +0800)
committerKiefer Chang <kiefer.chang@suse.com>
Fri, 30 Aug 2019 05:36:21 +0000 (13:36 +0800)
- Add unit tests for hosts, inventory, and services
- Refine permission of host-details component

Signed-off-by: Kiefer Chang <kiefer.chang@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/host-details/host-details.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/hosts/hosts.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory.model.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.model.ts

index 9cd20068f8bde2304f55ec028585282a9fa6b2eb..d5a22d1ed9ad38fbe119fcfcf11b5933a17b2abd 100644 (file)
@@ -1,22 +1,22 @@
 <tabset *ngIf="selection.hasSingleSelection">
   <tab i18n-heading
        heading="Inventory"
-       *ngIf="hostsPermission.read">
+       *ngIf="permissions.hosts.read">
     <cd-inventory
-      [hostname]="host['hostname']">
+      [hostname]="selection.first()['hostname']">
     </cd-inventory>
   </tab>
   <tab i18n-heading
        heading="Services"
-       *ngIf="hostsPermission.read">
+       *ngIf="permissions.hosts.read">
     <cd-services
-      [hostname]="host['hostname']">
+      [hostname]="selection.first()['hostname']">
     </cd-services>
   </tab>
   <tab i18n-heading
        heading="Performance Details"
-       *ngIf="grafanaPermission.read">
-    <cd-grafana [grafanaPath]="'host-details?var-ceph_hosts=' + host['hostname']"
+       *ngIf="permissions.grafana.read">
+    <cd-grafana [grafanaPath]="'host-details?var-ceph_hosts=' + selection.first()['hostname']"
                 uid="rtOg0AiWz"
                 grafanaStyle="three">
     </cd-grafana>
index 99b396c55d38bc42c2e1964b5f3ab92d71b6c90f..e89d6c869f2a72f40cbc714809f8c954c4399ea6 100644 (file)
@@ -1,11 +1,15 @@
 import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { of } from 'rxjs';
 
 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
-import { TabsModule } from 'ngx-bootstrap/tabs';
+import { TabsetComponent, TabsModule } from 'ngx-bootstrap/tabs';
 
-import { configureTestBed } from '../../../../../testing/unit-test-helper';
+import { RouterTestingModule } from '@angular/router/testing';
+import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
+import { OrchestratorService } from '../../../../shared/api/orchestrator.service';
 import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
+import { Permissions } from '../../../../shared/models/permissions';
 import { SharedModule } from '../../../../shared/shared.module';
 import { InventoryComponent } from '../../inventory/inventory.component';
 import { ServicesComponent } from '../../services/services.component';
@@ -20,21 +24,55 @@ describe('HostDetailsComponent', () => {
       HttpClientTestingModule,
       TabsModule.forRoot(),
       BsDropdownModule.forRoot(),
+      RouterTestingModule,
       SharedModule
     ],
-    declarations: [HostDetailsComponent, InventoryComponent, ServicesComponent]
+    declarations: [HostDetailsComponent, InventoryComponent, ServicesComponent],
+    providers: [i18nProviders]
   });
 
   beforeEach(() => {
     fixture = TestBed.createComponent(HostDetailsComponent);
     component = fixture.componentInstance;
-
     component.selection = new CdTableSelection();
-
+    component.permissions = new Permissions({
+      hosts: ['read'],
+      grafana: ['read']
+    });
+    const orchService = TestBed.get(OrchestratorService);
+    spyOn(orchService, 'status').and.returnValue(of({ available: true }));
+    spyOn(orchService, 'inventoryList').and.returnValue(of([]));
+    spyOn(orchService, 'serviceList').and.returnValue(of([]));
     fixture.detectChanges();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
+
+  describe('Host details tabset', () => {
+    beforeEach(() => {
+      component.selection.selected = [
+        {
+          hostname: 'localhost'
+        }
+      ];
+      component.selection.update();
+    });
+
+    it('should recognize a tabset child', () => {
+      fixture.detectChanges();
+      const tabsetChild: TabsetComponent = component.tabsetChild;
+      expect(tabsetChild).toBeDefined();
+    });
+
+    it('should show tabs', () => {
+      fixture.detectChanges();
+      const tabs = component.tabsetChild.tabs;
+      expect(tabs.length).toBe(3);
+      expect(tabs[0].heading).toBe('Inventory');
+      expect(tabs[1].heading).toBe('Services');
+      expect(tabs[2].heading).toBe('Performance Details');
+    });
+  });
 });
index 1c4bf2c0b6d34d3dd293d826e2713fe2eb6a915d..d66a7f87fd359f8475dc5d6c414902648cd86480 100644 (file)
@@ -1,30 +1,24 @@
-import { Component, Input, OnChanges } from '@angular/core';
+import { Component, Input, ViewChild } from '@angular/core';
+
+import { TabsetComponent } from 'ngx-bootstrap/tabs';
 
 import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
-import { Permission } from '../../../../shared/models/permissions';
-import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
+import { Permissions } from '../../../../shared/models/permissions';
 
 @Component({
   selector: 'cd-host-details',
   templateUrl: './host-details.component.html',
   styleUrls: ['./host-details.component.scss']
 })
-export class HostDetailsComponent implements OnChanges {
-  grafanaPermission: Permission;
-  hostsPermission: Permission;
+export class HostDetailsComponent {
+  @Input()
+  permissions: Permissions;
 
   @Input()
   selection: CdTableSelection;
-  host: any;
 
-  constructor(private authStorageService: AuthStorageService) {
-    this.grafanaPermission = this.authStorageService.getPermissions().grafana;
-    this.hostsPermission = this.authStorageService.getPermissions().hosts;
-  }
+  @ViewChild(TabsetComponent, { static: false })
+  tabsetChild: TabsetComponent;
 
-  ngOnChanges() {
-    if (this.selection.hasSelection) {
-      this.host = this.selection.first();
-    }
-  }
+  constructor() {}
 }
index 1cd4ff468252e2e6a21e653ca5f1257bd2bfa282..f4ecf6477af1bdc1660b88c7b9a9cb1625988144 100644 (file)
@@ -29,6 +29,7 @@
         </span>
       </ng-template>
       <cd-host-details cdTableDetail
+                       [permissions]="permissions"
                        [selection]="selection">
       </cd-host-details>
     </cd-table>
index 9336ff8c3794ae8a475e27def301ce1889cfe10f..9e3fb85fc0b24e9c3fb72cc837e9ed637967ee0c 100644 (file)
@@ -2,13 +2,42 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { RouterTestingModule } from '@angular/router/testing';
+import { of } from 'rxjs';
 import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
+import { OrchestratorService } from '../../../shared/api/orchestrator.service';
+import { CdTableFetchDataContext } from '../../../shared/models/cd-table-fetch-data-context';
 import { SharedModule } from '../../../shared/shared.module';
 import { InventoryComponent } from './inventory.component';
 
 describe('InventoryComponent', () => {
   let component: InventoryComponent;
   let fixture: ComponentFixture<InventoryComponent>;
+  let reqHostname: string;
+
+  const inventoryNodes = [
+    {
+      name: 'host0',
+      devices: [
+        {
+          type: 'hdd',
+          id: '/dev/sda'
+        }
+      ]
+    },
+    {
+      name: 'host1',
+      devices: [
+        {
+          type: 'hdd',
+          id: '/dev/sda'
+        }
+      ]
+    }
+  ];
+
+  const getIventoryList = (hostname: String) => {
+    return hostname ? inventoryNodes.filter((node) => node.name === hostname) : inventoryNodes;
+  };
 
   configureTestBed({
     imports: [SharedModule, HttpClientTestingModule, RouterTestingModule],
@@ -19,10 +48,30 @@ describe('InventoryComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(InventoryComponent);
     component = fixture.componentInstance;
+    const orchService = TestBed.get(OrchestratorService);
+    spyOn(orchService, 'status').and.returnValue(of({ available: true }));
+    reqHostname = '';
+    spyOn(orchService, 'inventoryList').and.callFake(() => of(getIventoryList(reqHostname)));
     fixture.detectChanges();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
+
+  it('should have columns that are sortable', () => {
+    expect(component.columns.every((column) => Boolean(column.prop))).toBeTruthy();
+  });
+
+  it('should return all devices', () => {
+    component.getInventory(new CdTableFetchDataContext(() => {}));
+    expect(component.devices.length).toBe(2);
+  });
+
+  it('should return devices on a host', () => {
+    reqHostname = 'host0';
+    component.getInventory(new CdTableFetchDataContext(() => {}));
+    expect(component.devices.length).toBe(1);
+    expect(component.devices[0].hostname).toBe(reqHostname);
+  });
 });
index b598b08505a2fce51888b2ce5820aa29c3481ee9..8435d268ff0b0736ea5e5b2241af1bf301fff8e9 100644 (file)
@@ -16,7 +16,7 @@ import { Device, InventoryNode } from './inventory.model';
   styleUrls: ['./inventory.component.scss']
 })
 export class InventoryComponent implements OnChanges, OnInit {
-  @ViewChild(TableComponent)
+  @ViewChild(TableComponent, { static: false })
   table: TableComponent;
 
   @Input() hostname = '';
index 50b833fe38b46953b98d85e81f1fff8897cb4202..c2eb0924eb1b95e199f4eaf0e7ffe17e06b28026 100644 (file)
@@ -1,10 +1,18 @@
-export interface Device {
-  id: string;
+export class Device {
   hostname: string;
   uid: string;
+
+  blank: boolean;
+  type: string;
+  id: string;
+  size: number;
+  rotates: boolean;
+  available: boolean;
+  dev_id: string;
+  extended: any;
 }
 
-export interface InventoryNode {
+export class InventoryNode {
   name: string;
   devices: Device[];
 }
index 4faddc317b65e2fc86878a94e31005403f570bf5..d74b8a41caaceea1c3df456bf64f1a623b270127 100644 (file)
@@ -2,13 +2,48 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { HttpClientTestingModule } from '@angular/common/http/testing';
 import { RouterTestingModule } from '@angular/router/testing';
+import { of } from 'rxjs';
 import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
+import { OrchestratorService } from '../../../shared/api/orchestrator.service';
+import { CdTableFetchDataContext } from '../../../shared/models/cd-table-fetch-data-context';
 import { SharedModule } from '../../../shared/shared.module';
 import { ServicesComponent } from './services.component';
 
 describe('ServicesComponent', () => {
   let component: ServicesComponent;
   let fixture: ComponentFixture<ServicesComponent>;
+  let reqHostname: string;
+
+  const services = [
+    {
+      nodename: 'host0',
+      service: '',
+      service_instance: 'x',
+      service_type: 'mon'
+    },
+    {
+      nodename: 'host0',
+      service: '',
+      service_instance: '0',
+      service_type: 'osd'
+    },
+    {
+      nodename: 'host1',
+      service: '',
+      service_instance: 'y',
+      service_type: 'mon'
+    },
+    {
+      nodename: 'host1',
+      service: '',
+      service_instance: '1',
+      service_type: 'osd'
+    }
+  ];
+
+  const getServiceList = (hostname: String) => {
+    return hostname ? services.filter((service) => service.nodename === hostname) : services;
+  };
 
   configureTestBed({
     imports: [SharedModule, HttpClientTestingModule, RouterTestingModule],
@@ -19,10 +54,31 @@ describe('ServicesComponent', () => {
   beforeEach(() => {
     fixture = TestBed.createComponent(ServicesComponent);
     component = fixture.componentInstance;
+    const orchService = TestBed.get(OrchestratorService);
+    spyOn(orchService, 'status').and.returnValue(of({ available: true }));
+    reqHostname = '';
+    spyOn(orchService, 'serviceList').and.callFake(() => of(getServiceList(reqHostname)));
     fixture.detectChanges();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
+
+  it('should have columns that are sortable', () => {
+    expect(component.columns.every((column) => Boolean(column.prop))).toBeTruthy();
+  });
+
+  it('should return all services', () => {
+    component.getServices(new CdTableFetchDataContext(() => {}));
+    expect(component.services.length).toBe(4);
+  });
+
+  it('should return services on a host', () => {
+    reqHostname = 'host0';
+    component.getServices(new CdTableFetchDataContext(() => {}));
+    expect(component.services.length).toBe(2);
+    expect(component.services[0].nodename).toBe(reqHostname);
+    expect(component.services[1].nodename).toBe(reqHostname);
+  });
 });
index 3e0849dd5b64c4eeea34ea6ff20c4e0d333972fc..692b28b5839b1cb1010904f4d8ad785d4fab3f32 100644 (file)
@@ -15,7 +15,7 @@ import { Service } from './services.model';
   styleUrls: ['./services.component.scss']
 })
 export class ServicesComponent implements OnChanges, OnInit {
-  @ViewChild(TableComponent)
+  @ViewChild(TableComponent, { static: false })
   table: TableComponent;
 
   @Input() hostname = '';
@@ -25,7 +25,7 @@ export class ServicesComponent implements OnChanges, OnInit {
   docsUrl: string;
 
   columns: Array<CdTableColumn> = [];
-  services: Array<object> = [];
+  services: Array<Service> = [];
   isLoadingServices = false;
 
   constructor(
@@ -129,9 +129,7 @@ export class ServicesComponent implements OnChanges, OnInit {
       (data: Service[]) => {
         const services: Service[] = [];
         data.forEach((service: Service) => {
-          service.uid = `${service.nodename}-${service.service_type}-${service.service}-${
-            service.service_instance
-          }`;
+          service.uid = `${service.nodename}-${service.service_type}-${service.service}-${service.service_instance}`;
           services.push(service);
         });
         this.services = services;
index 72b8b7592e878b7bdace0de4428a4651f2e86009..61c1b0d11a2a62826b145c7ca71e08098afadd63 100644 (file)
@@ -1,7 +1,14 @@
-export interface Service {
+export class Service {
   uid: string;
+
   nodename: string;
-  service_type: string;
+  container_id: string;
   service: string;
   service_instance: string;
+  service_type: string;
+  version: string;
+  rados_config_location: string;
+  service_url: string;
+  status: string;
+  status_desc: string;
 }