[CST-5535] test cases added.

This commit is contained in:
Pratik Rajkotiya
2022-04-05 18:46:58 +05:30
parent 108f6e60f9
commit 392d0e366d
11 changed files with 334 additions and 21 deletions

View File

@@ -57,6 +57,7 @@
"@angular/core": "~11.2.14",
"@angular/forms": "~11.2.14",
"@angular/localize": "11.2.14",
"@angular/material": "9.2.0",
"@angular/platform-browser": "~11.2.14",
"@angular/platform-browser-dynamic": "~11.2.14",
"@angular/platform-server": "~11.2.14",

View File

@@ -210,8 +210,8 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard';
},
{
path: 'health',
loadChildren: () => import('./health-page/health.routing.module')
.then((m) => m.HealthPageRoutingModule)
loadChildren: () => import('./health-page/health.module')
.then((m) => m.HealthModule)
},
{
path: ACCESS_CONTROL_MODULE_PATH,

View File

@@ -0,0 +1,32 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { DspaceRestService } from '../core/dspace-rest/dspace-rest.service';
import { RawRestResponse } from '../core/dspace-rest/raw-rest-response.model';
import { HALEndpointService } from '../core/shared/hal-endpoint.service';
@Injectable({
providedIn: 'root'
})
export class HealthDataService {
constructor(protected halService: HALEndpointService,
protected restService: DspaceRestService) {
}
/**
* @returns health data
*/
getHealth(): Observable<RawRestResponse> {
return this.halService.getEndpoint('/actuator').pipe(
map((restURL: string) => restURL + '/health'),
switchMap((endpoint: string) => this.restService.get(endpoint)));
}
/**
* @returns information of server
*/
getInfo(): Observable<RawRestResponse> {
return this.halService.getEndpoint('/actuator').pipe(
map((restURL: string) => restURL + '/info'),
switchMap((endpoint: string) => this.restService.get(endpoint)));
}
}

View File

@@ -1,12 +1,23 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { HealthPageRoutingModule } from './health.routing.module';
import { HealthComponent } from './health/health.component';
import { MatExpansionModule } from '@angular/material/expansion';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
@NgModule({
imports: [
CommonModule,
HealthPageRoutingModule,
MatExpansionModule,
NgbModule,
TranslateModule
],
declarations: [
HealthComponent
]
})
export class HealthModule {
}
}

View File

@@ -2,12 +2,15 @@ import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
import { HealthComponent } from './health/health.component';
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
resolve: { breadcrumb: I18nBreadcrumbResolver },
data: { breadcrumbKey: 'health' },
canActivate: [AuthenticatedGuard],
children: [
{

View File

@@ -1 +1,39 @@
<p>health works!</p>
<div class="container">
<ul ngbNav #nav="ngbNav" [(activeId)]="activeId" class="nav-tabs">
<li [ngbNavItem]="'Health'">
<a ngbNavLink>{{'health-page.health' | translate}}</a>
<ng-template ngbNavContent>
<div id="health">
{{'health-page.status' | translate}} : <i [ngClass]="getHealthIconClass(healthGlobalStatus)"> </i>
<mat-accordion multi="true">
<mat-expansion-panel *ngFor="let health of healthArr">
<mat-expansion-panel-header>
<mat-panel-title> {{ health.components }} <i [ngClass]="getHealthIconClass(health.status)"></i></mat-panel-title>
</mat-expansion-panel-header>
<ng-template matExpansionPanelContent>
<div *ngFor="let item of health.details | keyvalue">
{{ item.key }} : {{item.value}}
</div>
</ng-template>
</mat-expansion-panel>
</mat-accordion>
</div>
</ng-template>
</li>
<li [ngbNavItem]="'Info'">
<a ngbNavLink>{{'health-page.info' | translate}}</a>
<ng-template ngbNavContent>
<div id="info">
<small [ngStyle]="serverInfo.style" *ngFor="let serverInfo of serverInfoArr; let index = index">
{{ serverInfo.value }} <br>
</small>
</div>
</ng-template>
</li>
</ul>
<div [ngbNavOutlet]="nav" class="mt-2"></div>
</div>

View File

@@ -0,0 +1,8 @@
.mat-expansion-panel-header {
padding-left: 0px;
}
.circle-red {
color:red;
align-items: center;
}

View File

@@ -1,14 +1,138 @@
import { CommonModule } from '@angular/common';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatExpansionModule } from '@angular/material/expansion';
import { By } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs';
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
import { HealthDataService } from '../health-data.service';
import { HealthPageRoutingModule } from '../health.routing.module';
import { HealthComponent } from './health.component';
describe('HealthComponent', () => {
function getHealth() {
return of({
'payload':{
'status':'UP_WITH_ISSUES',
'components':{
'db':{
'status':'UP',
'components':{
'dataSource':{
'status':'UP',
'details':{
'database':'PostgreSQL',
'result':1,
'validationQuery':'SELECT 1'
}
},
'dspaceDataSource':{
'status':'UP',
'details':{
'database':'PostgreSQL',
'result':1,
'validationQuery':'SELECT 1'
}
}
}
},
'geoIp':{
'status':'UP_WITH_ISSUES',
'details':{
'reason':'The GeoLite Database file is missing (/var/lib/GeoIP/GeoLite2-City.mmdb)! Solr Statistics cannot generate location based reports! Please see the DSpace installation instructions for instructions to install this file.'
}
},
'solrOaiCore':{
'status':'UP',
'details':{
'status':0,
'detectedPathType':'particular core'
}
},
'solrSearchCore':{
'status':'UP',
'details':{
'status':0,
'detectedPathType':'particular core'
}
},
'solrStatisticsCore':{
'status':'UP',
'details':{
'status':0,
'detectedPathType':'particular core'
}
}
}
},
'statusCode':200,
'statusText':'OK'
});
}
function getInfo() {
return of({
'payload':{
'app':{
'name':'DSpace at My University',
'version':'7.3',
'dir':'/Users/pratikrajkotiya/Documents/Project/FrontEnd/dspace-cris-install',
'url':'http://localhost:8080/server',
'db':'jdbc:postgresql://localhost:5432/4science',
'solr':{
'server':'http://localhost:8983/solr',
'prefix':''
},
'mail':{
'server':'smtp.example.com',
'from-address':'dspace-noreply@myu.edu',
'feedback-recipient':'dspace-help@myu.edu',
'mail-admin':'dspace-help@myu.edu',
'mail-helpdesk':'dspace-help@myu.edu',
'alert-recipient':'dspace-help@myu.edu'
},
'cors':{
'allowed-origins':'http://localhost:4000'
},
'ui':{
'url':'http://localhost:4000'
}
}
},
'statusCode':200,
'statusText':'OK'
});
}
function getMockHealthDataService() {
return jasmine.createSpyObj('healthDataService', {
getHealth: getHealth(),
getInfo: getInfo()
});
}
fdescribe('HealthComponent', () => {
let component: HealthComponent;
let fixture: ComponentFixture<HealthComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HealthComponent ]
imports: [
NgbNavModule,
CommonModule,
HealthPageRoutingModule,
MatExpansionModule,
BrowserAnimationsModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderMock
}
}),
],
declarations: [ HealthComponent ],
providers:[{ provide: HealthDataService, useValue: getMockHealthDataService() }]
})
.compileComponents();
});
@@ -22,4 +146,15 @@ describe('HealthComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render health tab.', () => {
const healthTab = fixture.debugElement.query(By.css('#health'));
expect(healthTab).toBeTruthy();
});
it('should render info tab.', () => {
const infoTab = fixture.debugElement.query(By.css('#info'));
expect(infoTab).toBeFalsy();
});
});

View File

@@ -1,26 +1,100 @@
import { Component, OnInit } from '@angular/core';
import { DspaceRestService } from '../../core/dspace-rest/dspace-rest.service';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { HealthDataService } from '../health-data.service';
enum HealthStatus {
UP = 'UP',
UP_WITH_ISSUES = 'UP_WITH_ISSUES',
DOWN = 'DOWN'
}
@Component({
selector: 'ds-health',
templateUrl: './health.component.html',
styleUrls: ['./health.component.scss']
})
export class HealthComponent implements OnInit {
constructor(protected halService: HALEndpointService,
protected restService: DspaceRestService) {
}
healthArr: string[];
serverInfoArr: string[];
healthGlobalStatus: string;
activeId ='Health';
constructor(private healthDataService: HealthDataService) { }
ngOnInit(): void {
this.halService.getRootHref();
console.log('this.halService.getRootHref()',);
this.restService.get(this.halService.getRootHref() + '/actuator' + '/health').subscribe((data)=>{
console.log(data);
})
this.healthDataService.getHealth().subscribe((data) => {
this.healthArr = this.getHealth(data.payload.components);
this.healthGlobalStatus = data.payload.status;
});
this.healthDataService.getInfo().subscribe((data) => {
this.serverInfoArr = this.getInfo(data.payload, null, []);
});
}
/**
* @param obj represents a info
* @param key represents a nested key of info
* @param arr represents a key value pair or only key
* @returns {{arr}} of key value pair or only key
*/
getInfo(obj, key, arr) {
if (typeof obj === 'object' && key !== null) {
arr.push({style: {'font-weight': 'bold' ,'font-size.px': key === 'app' ? '30' : '20' }, value: key});
}
if (typeof obj !== 'object') {
arr.push({style: {'font-size.px': '15'}, value: `${key} = ${obj}`});
return obj;
}
// tslint:disable-next-line: forin
for (const objKey in obj) {
this.getInfo(obj[objKey], objKey, arr);
}
return arr;
}
/**
* @param subCompObj represent nested sub component
* @param superCompkey represents a key of super component
* @returns linear components array
*/
getHealthSubComponents(subCompObj, superCompkey) {
const subCompArr = [];
// tslint:disable-next-line: forin
for (const key in subCompObj) {
subCompArr.push({ ...subCompObj[key], components: superCompkey + '.' + key });
}
return subCompArr;
}
/**
* @param componentsObj represent health data
* @returns linear components array
*/
getHealth(componentsObj) {
let componentsArr = [];
for (const key in componentsObj) {
if (componentsObj[key].hasOwnProperty('components')) {
componentsArr.push({ ...componentsObj[key], components: key });
// tslint:disable-next-line: no-string-literal
componentsArr = [...componentsArr, ...this.getHealthSubComponents(componentsObj[key]['components'], key)];
} else {
componentsArr.push({ ...componentsObj[key], components: key });
}
}
return componentsArr;
}
/**
* @param status of perticular block
* @returns {{ string }} class respective status
*/
getHealthIconClass(status: string): string {
if (status === HealthStatus.UP) {
return 'fa fa-check-circle text-success ml-2 mt-1';
} else if (status === HealthStatus.UP_WITH_ISSUES) {
return 'fa fa-exclamation-triangle text-warning ml-2 mt-1';
} else {
return 'fa fa-times-circle circle-red ml-2 mt-1';
}
}
}

View File

@@ -2909,7 +2909,13 @@
"profile.title": "Update Profile",
"health.breadcrumbs": "Health",
"health-page.health" : "Health",
"health-page.info" : "Info",
"health-page.status" : "Status",
"project.listelement.badge": "Research Project",

View File

@@ -397,6 +397,11 @@
glob "7.1.2"
yargs "^16.2.0"
"@angular/material@9.2.0":
version "9.2.0"
resolved "https://registry.yarnpkg.com/@angular/material/-/material-9.2.0.tgz#1b6f0a2e115f93885d7fc2dc4b258d8c9cf6821f"
integrity sha512-KKzEIVh6/m56m+Ao8p4PK0SyEr0574l3VP2swj1qPag3u+FYgemmXCGTaChrKdDsez+zeTCPXImBGXzE6NQ80Q==
"@angular/platform-browser-dynamic@~11.2.14":
version "11.2.14"
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.2.14.tgz#3c7fff1a1daacba5390acf033d28c377ec281166"