Commit ea3117fc authored by Paul Slaughter's avatar Paul Slaughter

Merge branch 'cleanup-mock-anomaly-data' into 'master'

Use graph data generation for anomaly chart tests

See merge request gitlab-org/gitlab!36297
parents d2440650 d8e95309
......@@ -3,28 +3,14 @@ import { TEST_HOST } from 'helpers/test_constants';
import Anomaly from '~/monitoring/components/charts/anomaly.vue';
import { colorValues } from '~/monitoring/constants';
import {
anomalyDeploymentData,
mockProjectDir,
anomalyMockGraphData,
anomalyMockResultValues,
} from '../../mock_data';
import { anomalyDeploymentData, mockProjectDir } from '../../mock_data';
import { anomalyGraphData } from '../../graph_data';
import MonitorTimeSeriesChart from '~/monitoring/components/charts/time_series.vue';
const mockProjectPath = `${TEST_HOST}${mockProjectDir}`;
const makeAnomalyGraphData = (datasetName, template = anomalyMockGraphData) => {
const metrics = anomalyMockResultValues[datasetName].map((values, index) => ({
...template.metrics[index],
result: [
{
metrics: {},
values,
},
],
}));
return { ...template, metrics };
};
const TEST_UPPER = 11;
const TEST_LOWER = 9;
describe('Anomaly chart component', () => {
let wrapper;
......@@ -38,13 +24,22 @@ describe('Anomaly chart component', () => {
const getTimeSeriesProps = () => findTimeSeries().props();
describe('wrapped monitor-time-series-chart component', () => {
const dataSetName = 'noAnomaly';
const dataSet = anomalyMockResultValues[dataSetName];
const mockValues = ['10', '10', '10'];
const mockGraphData = anomalyGraphData(
{},
{
upper: mockValues.map(() => String(TEST_UPPER)),
values: mockValues,
lower: mockValues.map(() => String(TEST_LOWER)),
},
);
const inputThresholds = ['some threshold'];
beforeEach(() => {
setupAnomalyChart({
graphData: makeAnomalyGraphData(dataSetName),
graphData: mockGraphData,
deploymentData: anomalyDeploymentData,
thresholds: inputThresholds,
projectPath: mockProjectPath,
......@@ -65,21 +60,21 @@ describe('Anomaly chart component', () => {
it('receives "metric" with all data', () => {
const { graphData } = getTimeSeriesProps();
const query = graphData.metrics[0];
const expectedQuery = makeAnomalyGraphData(dataSetName).metrics[0];
expect(query).toEqual(expectedQuery);
const metric = graphData.metrics[0];
const expectedMetric = mockGraphData.metrics[0];
expect(metric).toEqual(expectedMetric);
});
it('receives the "metric" results', () => {
const { graphData } = getTimeSeriesProps();
const { result } = graphData.metrics[0];
const { values } = result[0];
const [metricDataset] = dataSet;
expect(values).toEqual(expect.any(Array));
values.forEach(([, y], index) => {
expect(y).toBeCloseTo(metricDataset[index][1]);
});
expect(values).toEqual([
[expect.any(String), 10],
[expect.any(String), 10],
[expect.any(String), 10],
]);
});
});
......@@ -108,14 +103,13 @@ describe('Anomaly chart component', () => {
it('upper boundary values are stacked on top of lower boundary', () => {
const [lowerSeries, upperSeries] = series;
const [, upperDataset, lowerDataset] = dataSet;
lowerSeries.data.forEach(([, y], i) => {
expect(y).toBeCloseTo(lowerDataset[i][1]);
lowerSeries.data.forEach(([, y]) => {
expect(y).toBeCloseTo(TEST_LOWER);
});
upperSeries.data.forEach(([, y], i) => {
expect(y).toBeCloseTo(upperDataset[i][1] - lowerDataset[i][1]);
upperSeries.data.forEach(([, y]) => {
expect(y).toBeCloseTo(TEST_UPPER - TEST_LOWER);
});
});
});
......@@ -140,11 +134,10 @@ describe('Anomaly chart component', () => {
}),
);
});
it('does not display anomalies', () => {
const { symbolSize, itemStyle } = seriesConfig;
const [metricDataset] = dataSet;
metricDataset.forEach((v, dataIndex) => {
mockValues.forEach((v, dataIndex) => {
const size = symbolSize(null, { dataIndex });
const color = itemStyle.color({ dataIndex });
......@@ -155,9 +148,10 @@ describe('Anomaly chart component', () => {
});
it('can format y values (to use in tooltips)', () => {
expect(parseFloat(wrapper.vm.yValueFormatted(0, 0))).toEqual(dataSet[0][0][1]);
expect(parseFloat(wrapper.vm.yValueFormatted(1, 0))).toEqual(dataSet[1][0][1]);
expect(parseFloat(wrapper.vm.yValueFormatted(2, 0))).toEqual(dataSet[2][0][1]);
mockValues.forEach((v, dataIndex) => {
const formatted = wrapper.vm.yValueFormatted(0, dataIndex);
expect(parseFloat(formatted)).toEqual(parseFloat(v));
});
});
});
......@@ -179,12 +173,18 @@ describe('Anomaly chart component', () => {
});
describe('with no boundary data', () => {
const dataSetName = 'noBoundary';
const dataSet = anomalyMockResultValues[dataSetName];
const noBoundaryData = anomalyGraphData(
{},
{
upper: [],
values: ['10', '10', '10'],
lower: [],
},
);
beforeEach(() => {
setupAnomalyChart({
graphData: makeAnomalyGraphData(dataSetName),
graphData: noBoundaryData,
deploymentData: anomalyDeploymentData,
});
});
......@@ -204,7 +204,7 @@ describe('Anomaly chart component', () => {
});
it('can format y values (to use in tooltips)', () => {
expect(parseFloat(wrapper.vm.yValueFormatted(0, 0))).toEqual(dataSet[0][0][1]);
expect(parseFloat(wrapper.vm.yValueFormatted(0, 0))).toEqual(10);
expect(wrapper.vm.yValueFormatted(1, 0)).toBe(''); // missing boundary
expect(wrapper.vm.yValueFormatted(2, 0)).toBe(''); // missing boundary
});
......@@ -212,12 +212,20 @@ describe('Anomaly chart component', () => {
});
describe('with one anomaly', () => {
const dataSetName = 'oneAnomaly';
const dataSet = anomalyMockResultValues[dataSetName];
const mockValues = ['10', '20', '10'];
const oneAnomalyData = anomalyGraphData(
{},
{
upper: mockValues.map(() => TEST_UPPER),
values: mockValues,
lower: mockValues.map(() => TEST_LOWER),
},
);
beforeEach(() => {
setupAnomalyChart({
graphData: makeAnomalyGraphData(dataSetName),
graphData: oneAnomalyData,
deploymentData: anomalyDeploymentData,
});
});
......@@ -226,13 +234,12 @@ describe('Anomaly chart component', () => {
it('displays one anomaly', () => {
const { seriesConfig } = getTimeSeriesProps();
const { symbolSize, itemStyle } = seriesConfig;
const [metricDataset] = dataSet;
const bigDots = metricDataset.filter((v, dataIndex) => {
const bigDots = mockValues.filter((v, dataIndex) => {
const size = symbolSize(null, { dataIndex });
return size > 0.1;
});
const redDots = metricDataset.filter((v, dataIndex) => {
const redDots = mockValues.filter((v, dataIndex) => {
const color = itemStyle.color({ dataIndex });
return color === colorValues.anomalySymbol;
});
......@@ -244,13 +251,21 @@ describe('Anomaly chart component', () => {
});
describe('with offset', () => {
const dataSetName = 'negativeBoundary';
const dataSet = anomalyMockResultValues[dataSetName];
const expectedOffset = 4; // Lowst point in mock data is -3.70, it gets rounded
const mockValues = ['10', '11', '12'];
const mockUpper = ['20', '20', '20'];
const mockLower = ['-1', '-2', '-3.70'];
const expectedOffset = 4; // Lowest point in mock data is -3.70, it gets rounded
beforeEach(() => {
setupAnomalyChart({
graphData: makeAnomalyGraphData(dataSetName),
graphData: anomalyGraphData(
{},
{
upper: mockUpper,
values: mockValues,
lower: mockLower,
},
),
deploymentData: anomalyDeploymentData,
});
});
......@@ -266,11 +281,11 @@ describe('Anomaly chart component', () => {
const { graphData } = getTimeSeriesProps();
const { result } = graphData.metrics[0];
const { values } = result[0];
const [metricDataset] = dataSet;
expect(values).toEqual(expect.any(Array));
values.forEach(([, y], index) => {
expect(y).toBeCloseTo(metricDataset[index][1] + expectedOffset);
expect(y).toBeCloseTo(parseFloat(mockValues[index]) + expectedOffset);
});
});
});
......@@ -281,14 +296,12 @@ describe('Anomaly chart component', () => {
const { option } = getTimeSeriesProps();
const { series } = option;
const [lowerSeries, upperSeries] = series;
const [, upperDataset, lowerDataset] = dataSet;
lowerSeries.data.forEach(([, y], i) => {
expect(y).toBeCloseTo(lowerDataset[i][1] + expectedOffset);
expect(y).toBeCloseTo(parseFloat(mockLower[i]) + expectedOffset);
});
upperSeries.data.forEach(([, y], i) => {
expect(y).toBeCloseTo(upperDataset[i][1] - lowerDataset[i][1]);
expect(y).toBeCloseTo(parseFloat(mockUpper[i] - mockLower[i]));
});
});
});
......
......@@ -9,7 +9,6 @@ import AlertWidget from '~/monitoring/components/alert_widget.vue';
import DashboardPanel from '~/monitoring/components/dashboard_panel.vue';
import {
anomalyMockGraphData,
mockLogsHref,
mockLogsPath,
mockNamespace,
......@@ -19,7 +18,7 @@ import {
barMockData,
} from '../mock_data';
import { dashboardProps, graphData, graphDataEmpty } from '../fixture_data';
import { singleStatGraphData } from '../graph_data';
import { anomalyGraphData, singleStatGraphData } from '../graph_data';
import { panelTypes } from '~/monitoring/constants';
......@@ -233,7 +232,7 @@ describe('Dashboard Panel', () => {
${dataWithType(panelTypes.AREA_CHART)} | ${MonitorTimeSeriesChart} | ${true}
${dataWithType(panelTypes.LINE_CHART)} | ${MonitorTimeSeriesChart} | ${true}
${singleStatGraphData()} | ${MonitorSingleStatChart} | ${true}
${anomalyMockGraphData} | ${MonitorAnomalyChart} | ${false}
${anomalyGraphData()} | ${MonitorAnomalyChart} | ${false}
${dataWithType(panelTypes.COLUMN)} | ${MonitorColumnChart} | ${false}
${dataWithType(panelTypes.STACKED_COLUMN)} | ${MonitorStackedColumnChart} | ${false}
${graphDataPrometheusQueryRangeMultiTrack} | ${MonitorHeatmapChart} | ${false}
......
......@@ -124,3 +124,41 @@ export const singleStatGraphData = (panelOptions = {}, dataOptions = {}) => {
...panelOptions,
});
};
/**
* Generate mock graph data according to options
*
* @param {Object} panelOptions - Panel options as in YML.
* @param {Object} dataOptions
* @param {Array} dataOptions.values - Metric values
* @param {Array} dataOptions.upper - Upper boundary values
* @param {Array} dataOptions.lower - Lower boundary values
*/
export const anomalyGraphData = (panelOptions = {}, dataOptions = {}) => {
const { values, upper, lower } = dataOptions;
return mapPanelToViewModel({
title: 'Anomaly Panel',
type: panelTypes.ANOMALY_CHART,
x_label: 'X Axis',
y_label: 'Y Axis',
metrics: [
{
label: `Metric`,
state: metricStates.OK,
result: matrixSingleResult({ values }),
},
{
label: `Upper boundary`,
state: metricStates.OK,
result: matrixSingleResult({ values: upper }),
},
{
label: `Lower boundary`,
state: metricStates.OK,
result: matrixSingleResult({ values: lower }),
},
],
...panelOptions,
});
};
......@@ -51,136 +51,6 @@ export const anomalyDeploymentData = [
},
];
export const anomalyMockResultValues = {
noAnomaly: [
[
['2019-08-19T19:00:00.000Z', 1.25],
['2019-08-19T20:00:00.000Z', 1.45],
['2019-08-19T21:00:00.000Z', 1.55],
['2019-08-19T22:00:00.000Z', 1.48],
],
[
// upper boundary
['2019-08-19T19:00:00.000Z', 2],
['2019-08-19T20:00:00.000Z', 2.55],
['2019-08-19T21:00:00.000Z', 2.65],
['2019-08-19T22:00:00.000Z', 3.0],
],
[
// lower boundary
['2019-08-19T19:00:00.000Z', 0.45],
['2019-08-19T20:00:00.000Z', 0.65],
['2019-08-19T21:00:00.000Z', 0.7],
['2019-08-19T22:00:00.000Z', 0.8],
],
],
noBoundary: [
[
['2019-08-19T19:00:00.000Z', 1.25],
['2019-08-19T20:00:00.000Z', 1.45],
['2019-08-19T21:00:00.000Z', 1.55],
['2019-08-19T22:00:00.000Z', 1.48],
],
[
// empty upper boundary
],
[
// empty lower boundary
],
],
oneAnomaly: [
[
['2019-08-19T19:00:00.000Z', 1.25],
['2019-08-19T20:00:00.000Z', 3.45], // anomaly
['2019-08-19T21:00:00.000Z', 1.55],
],
[
// upper boundary
['2019-08-19T19:00:00.000Z', 2],
['2019-08-19T20:00:00.000Z', 2.55],
['2019-08-19T21:00:00.000Z', 2.65],
],
[
// lower boundary
['2019-08-19T19:00:00.000Z', 0.45],
['2019-08-19T20:00:00.000Z', 0.65],
['2019-08-19T21:00:00.000Z', 0.7],
],
],
negativeBoundary: [
[
['2019-08-19T19:00:00.000Z', 1.25],
['2019-08-19T20:00:00.000Z', 3.45], // anomaly
['2019-08-19T21:00:00.000Z', 1.55],
],
[
// upper boundary
['2019-08-19T19:00:00.000Z', 2],
['2019-08-19T20:00:00.000Z', 2.55],
['2019-08-19T21:00:00.000Z', 2.65],
],
[
// lower boundary
['2019-08-19T19:00:00.000Z', -1.25],
['2019-08-19T20:00:00.000Z', -2.65],
['2019-08-19T21:00:00.000Z', -3.7], // lowest point
],
],
};
export const anomalyMockGraphData = {
title: 'Requests Per Second Mock Data',
type: 'anomaly-chart',
weight: 3,
metrics: [
{
metricId: '90',
id: 'metric',
query_range: 'MOCK_PROMETHEUS_METRIC_QUERY_RANGE',
unit: 'RPS',
label: 'Metrics RPS',
metric_id: 90,
prometheus_endpoint_path: 'MOCK_METRIC_PEP',
result: [
{
metric: {},
values: [['2019-08-19T19:00:00.000Z', 0]],
},
],
},
{
metricId: '91',
id: 'upper',
query_range: '...',
unit: 'RPS',
label: 'Upper Limit Metrics RPS',
metric_id: 91,
prometheus_endpoint_path: 'MOCK_UPPER_PEP',
result: [
{
metric: {},
values: [['2019-08-19T19:00:00.000Z', 0]],
},
],
},
{
metricId: '92',
id: 'lower',
query_range: '...',
unit: 'RPS',
label: 'Lower Limit Metrics RPS',
metric_id: 92,
prometheus_endpoint_path: 'MOCK_LOWER_PEP',
result: [
{
metric: {},
values: [['2019-08-19T19:00:00.000Z', 0]],
},
],
},
],
};
export const deploymentData = [
{
id: 111,
......
import * as monitoringUtils from '~/monitoring/utils';
import * as urlUtils from '~/lib/utils/url_utility';
import { TEST_HOST } from 'jest/helpers/test_constants';
import { mockProjectDir, anomalyMockGraphData, barMockData } from './mock_data';
import { mockProjectDir, barMockData } from './mock_data';
import { singleStatGraphData, anomalyGraphData } from './graph_data';
import { metricsDashboardViewModel, graphData } from './fixture_data';
import { singleStatGraphData } from './graph_data';
const mockPath = `${TEST_HOST}${mockProjectDir}/-/environments/29/metrics`;
......@@ -102,12 +102,12 @@ describe('monitoring/utils', () => {
let fourMetrics;
beforeEach(() => {
oneMetric = singleStatGraphData();
threeMetrics = anomalyMockGraphData;
threeMetrics = anomalyGraphData();
const metrics = [...threeMetrics.metrics];
metrics.push(threeMetrics.metrics[0]);
fourMetrics = {
...anomalyMockGraphData,
...anomalyGraphData(),
metrics,
};
});
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment