Skip to content

Commit

Permalink
NAS-131912 / 25.04 / Adapt to back-end changes in disk stats (#11234)
Browse files Browse the repository at this point in the history
  • Loading branch information
bvasilenko authored Jan 1, 2025
1 parent 493273c commit 56d6ba9
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 13 deletions.
8 changes: 8 additions & 0 deletions src/app/admin.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { marker as T } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslationsLoadedGuard } from 'app/core/guards/translations-loaded.guard';
import { WebSocketConnectionGuard } from 'app/core/guards/websocket-connection.guard';
import { AdminLayoutComponent } from 'app/modules/layout/admin-layout/admin-layout.component';
import { PlotterService } from 'app/pages/reports-dashboard/services/plotter.service';
import { SmoothPlotterService } from 'app/pages/reports-dashboard/services/smooth-plotter.service';
import { AuthGuardService } from 'app/services/auth/auth-guard.service';
import { TwoFactorGuardService } from 'app/services/auth/two-factor-guard.service';

Expand Down Expand Up @@ -72,6 +74,12 @@ export const adminRoutes: Routes = [
path: 'reportsdashboard',
loadChildren: () => import('app/pages/reports-dashboard/reports-dashboard.routes').then((module) => module.reportsDashboardRoutes),
data: { title: T('Reporting'), breadcrumb: T('Reporting') },
providers: [
{
provide: PlotterService,
useClass: SmoothPlotterService,
},
],
},
{
path: 'shell',
Expand Down
102 changes: 102 additions & 0 deletions src/app/pages/reports-dashboard/reports-dashboard.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Spectator, createComponentFactory, mockProvider } from '@ngneat/spectator/jest';
import { MockComponent } from 'ng-mocks';
import { of } from 'rxjs';
import { mockApi } from 'app/core/testing/utils/mock-api.utils';
import { mockAuth } from 'app/core/testing/utils/mock-auth.utils';
import { ReportingGraphName } from 'app/enums/reporting.enum';
import { ReportingGraph } from 'app/interfaces/reporting-graph.interface';
import { PageHeaderComponent } from 'app/modules/page-header/page-title-header/page-header.component';
import { ReportsGlobalControlsComponent } from 'app/pages/reports-dashboard/components/reports-global-controls/reports-global-controls.component';
import { ReportTab, ReportType } from 'app/pages/reports-dashboard/interfaces/report-tab.interface';
import { Report } from 'app/pages/reports-dashboard/interfaces/report.interface';
import { ReportsDashboardComponent } from 'app/pages/reports-dashboard/reports-dashboard.component';
import { ReportsService } from 'app/pages/reports-dashboard/reports.service';
import { LayoutService } from 'app/services/layout.service';

const fakeTabs: ReportTab[] = [
{ label: 'CPU', value: ReportType.Cpu },
{ label: 'Memory', value: ReportType.Memory },
{ label: 'Disk', value: ReportType.Disk },
];

describe('ReportsDashboardComponent', () => {
let spectator: Spectator<ReportsDashboardComponent>;

const createComponent = createComponentFactory({
component: ReportsDashboardComponent,
imports: [
MockComponent(PageHeaderComponent),
MockComponent(ReportsGlobalControlsComponent),
],
providers: [
mockProvider(LayoutService, {
getContentContainer: jest.fn(() => document.createElement('div')),
}),

mockProvider(ReportsService, {
getReportGraphs: jest.fn(() => of([
{ name: ReportingGraphName.Cpu, title: 'CPU', identifiers: [] },
{ name: ReportingGraphName.Memory, title: 'Memory', identifiers: [] },
{
name: ReportingGraphName.Disk,
title: 'Disks',
identifiers: ['HDD | Model | test-sda-uuid', 'HDD | Model | test-sdb-uuid'],
},
] as ReportingGraph[])),
getReportTabs: jest.fn(() => fakeTabs),
}),
mockApi([]),
mockAuth(),
],
});

beforeEach(() => {
spectator = createComponent();
});

it('separates disk and other reports', () => {
const fakeReports = [
{
identifiers: [],
name: ReportingGraphName.Cpu,
title: 'CPU',
isRendered: [true],
},
{
identifiers: [],
name: ReportingGraphName.Memory,
title: 'Memory',
isRendered: [true],
},
{
identifiers: ['HDD | Model | test-sda-uuid', 'HDD | Model | test-sdb-uuid'],
name: ReportingGraphName.Disk,
title: 'Disks',
isRendered: [true, true],
},
] as Report[];

expect(spectator.component.allReports).toEqual(fakeReports);
expect(spectator.component.diskReports).toEqual([fakeReports[2]]);
expect(spectator.component.otherReports).toEqual([fakeReports[0], fakeReports[1]]);
});

describe('buildDiskReport', () => {
it('rebuilds disk reports', () => {
spectator.component.updateActiveTab(fakeTabs[2]);
expect(spectator.component.activeReports).toHaveLength(2);

spectator.component.buildDiskReport({
devices: ['test-sdb-uuid'],
metrics: [ReportingGraphName.Disk],
});
expect(spectator.component.visibleReports).toEqual([1]);

spectator.component.buildDiskReport({
devices: ['test-sda-uuid'],
metrics: [ReportingGraphName.Disk],
});
expect(spectator.component.visibleReports).toEqual([0]);
});
});
});
15 changes: 4 additions & 11 deletions src/app/pages/reports-dashboard/reports-dashboard.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import { PageHeaderComponent } from 'app/modules/page-header/page-title-header/p
import { ReportTab, ReportType } from 'app/pages/reports-dashboard/interfaces/report-tab.interface';
import { Report } from 'app/pages/reports-dashboard/interfaces/report.interface';
import { reportingElements } from 'app/pages/reports-dashboard/reports-dashboard.elements';
import { PlotterService } from 'app/pages/reports-dashboard/services/plotter.service';
import { SmoothPlotterService } from 'app/pages/reports-dashboard/services/smooth-plotter.service';
import { LayoutService } from 'app/services/layout.service';
import { ReportComponent } from './components/report/report.component';
import { ReportsGlobalControlsComponent } from './components/reports-global-controls/reports-global-controls.component';
Expand All @@ -41,12 +39,6 @@ import { ReportsService } from './reports.service';
ReportComponent,
MatCard,
],
providers: [
{
provide: PlotterService,
useClass: SmoothPlotterService,
},
],
})
export class ReportsDashboardComponent implements OnInit, OnDestroy {
readonly searchableElements = reportingElements;
Expand Down Expand Up @@ -90,8 +82,8 @@ export class ReportsDashboardComponent implements OnInit, OnDestroy {
};
});

this.diskReports = this.allReports.filter((report) => report.name.startsWith('disk'));
this.otherReports = this.allReports.filter((report) => !report.name.startsWith('disk'));
this.diskReports = this.allReports.filter((report) => report.name === ReportingGraphName.Disk);
this.otherReports = this.allReports.filter((report) => report.name !== ReportingGraphName.Disk);

this.activateTabFromUrl();
this.cdr.markForCheck();
Expand Down Expand Up @@ -240,7 +232,8 @@ export class ReportsDashboardComponent implements OnInit, OnDestroy {

const visible: number[] = [];
this.activeReports.forEach((item, index) => {
const deviceMatch = devices.includes(item.identifiers[0]);
const [,, identifier] = item.identifiers[0].split(' | ');
const deviceMatch = devices.includes(identifier);
const metricMatch = metrics.includes(item.name);
const condition = deviceMatch && metricMatch;
if (condition) {
Expand Down
117 changes: 117 additions & 0 deletions src/app/pages/reports-dashboard/reports.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest';
import { firstValueFrom } from 'rxjs';
import { MockApiService } from 'app/core/testing/classes/mock-api.service';
import { mockCall, mockApi } from 'app/core/testing/utils/mock-api.utils';
import { ReportingGraphName } from 'app/enums/reporting.enum';
import { Disk } from 'app/interfaces/disk.interface';
import { ReportingGraph } from 'app/interfaces/reporting-graph.interface';
import { ReportType } from 'app/pages/reports-dashboard/interfaces/report-tab.interface';
import { Report } from 'app/pages/reports-dashboard/interfaces/report.interface';
import { ReportsService } from 'app/pages/reports-dashboard/reports.service';

describe('ReportsService', () => {
let spectator: SpectatorService<ReportsService>;
let api: MockApiService;

const createService = createServiceFactory({
service: ReportsService,
providers: [
mockApi([
mockCall('disk.query', [
{ devname: 'sda', identifier: '{uuid}test-sda-uuid' },
{ devname: 'sdb', identifier: '{uuid}test-sdb-uuid' },
] as Disk[]),
mockCall('reporting.netdata_graphs', [
{ name: ReportingGraphName.Cpu },
{ name: ReportingGraphName.Ups },
] as ReportingGraph[]),
mockCall('reporting.netdata_get_data', [{
name: 'cpu',
identifier: 'cpu',
legend: ['time', 'active'],
start: 1735281261,
end: 1735281265,
data: [
[1735281261, 382],
[1735281262, 381],
[1735281263, 380],
[1735281264, 384],
],
aggregations: { min: [0], mean: [5], max: [10] },
}]),
mockCall('disk.temperatures', {}),
]),
],
});

beforeEach(() => {
spectator = createService();
api = spectator.inject(MockApiService);
});

describe('getDiskDevices', () => {
it('returns disk options', async () => {
const options = await firstValueFrom(spectator.service.getDiskDevices());
expect(api.call).toHaveBeenCalledWith('disk.query');
expect(options).toEqual([
{ label: 'sda', value: '{uuid}test-sda-uuid' },
{ label: 'sdb', value: '{uuid}test-sdb-uuid' },
]);
});
});

describe('getNetData', () => {
it('returns report data', async () => {
const data = await firstValueFrom(spectator.service.getNetData({
params: { name: 'cpu' },
truncate: true,
timeFrame: { start: 1735284410000, end: 1735284411000 },
report: { vertical_label: '%' } as Report,
}));
expect(api.call).toHaveBeenCalledWith(
'reporting.netdata_get_data',
[[{ name: 'cpu' }], { end: 1735284411000, start: 1735284410000 }],
);
expect(data).toEqual({
name: 'cpu',
identifier: 'cpu',
legend: ['active'],
start: 1735281261,
end: 1735281265,
aggregations: { max: ['10'], mean: ['5'], min: ['0'] },
data: [[1735281261, 382], [1735281262, 381], [1735281263, 380], [1735281264, 384]],
});
});
});

describe('getReportTabs', () => {
it('returns report tabs', () => {
const tabs = spectator.service.getReportTabs();
expect(tabs).toEqual([
{ label: 'CPU', value: ReportType.Cpu },
{ label: 'Disk', value: ReportType.Disk },
{ label: 'Memory', value: ReportType.Memory },
{ label: 'Network', value: ReportType.Network },
{ label: 'NFS', value: ReportType.Nfs },
{ label: 'Partition', value: ReportType.Partition },
{ label: 'System', value: ReportType.System },
{ label: 'UPS', value: ReportType.Ups },
{ label: 'Target', value: ReportType.Target },
{ label: 'ZFS', value: ReportType.Zfs },
]);
});
});

describe('getDiskMetrics', () => {
it('returns disk metrics', async () => {
const fakeMetrics = [
{ label: 'sda', value: 'uuid_sda' },
{ label: 'sdb', value: 'uuid_sdb' },
];
spectator.service.setDiskMetrics(fakeMetrics);

const metrics = await firstValueFrom(spectator.service.getDiskMetrics());
expect(metrics).toEqual(fakeMetrics);
});
});
});
3 changes: 1 addition & 2 deletions src/app/pages/reports-dashboard/reports.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ export class ReportsService {
return disks
.filter((disk) => !disk.devname.includes('multipath'))
.map((disk) => {
const [value] = disk.devname.split(' ');
return { label: disk.devname, value };
return { label: disk.devname, value: disk.identifier };
})
.sort((a, b) => a.label.localeCompare(b.label));
}),
Expand Down

0 comments on commit 56d6ba9

Please sign in to comment.