Commit a16a4891 authored by Miguel Rincon's avatar Miguel Rincon

Make use of dashboard fixtures to remove mock data

Since the introduction of fixtures its possible to remove some mock data
from the specs. This refactoring MR removes some mock data.
parent 3b5a7799
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { GlModal, GlDeprecatedButton } from '@gitlab/ui';
import Dashboard from 'ee/monitoring/components/dashboard.vue';
import {
mockApiEndpoint,
mockedQueryResultFixture,
environmentData,
} from '../../../../../spec/frontend/monitoring/mock_data';
import { getJSONFixture } from '../../../../../spec/frontend/helpers/fixtures';
import { propsData } from '../../../../../spec/frontend/monitoring/init_utils';
import { mockApiEndpoint, propsData } from 'jest/monitoring/mock_data';
import { metricsDashboardResponse } from 'jest/monitoring/fixture_data';
import { setupStoreWithData } from 'jest/monitoring/store_utils';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
import Tracking from '~/tracking';
import { createStore } from '~/monitoring/stores';
import axios from '~/lib/utils/axios_utils';
import * as types from '~/monitoring/stores/mutation_types';
const localVue = createLocalVue();
const metricsDashboardFixture = getJSONFixture(
'metrics_dashboard/environment_metrics_dashboard.json',
);
const metricsDashboardPayload = metricsDashboardFixture.dashboard;
describe('Dashboard', () => {
let Component;
let mock;
let store;
let wrapper;
......@@ -31,13 +18,12 @@ describe('Dashboard', () => {
const findAddMetricButton = () => wrapper.vm.$refs.addMetricBtn;
const createComponent = (props = {}) => {
wrapper = shallowMount(localVue.extend(Component), {
wrapper = shallowMount(Dashboard, {
propsData: { ...propsData, ...props },
stubs: {
GlDeprecatedButton,
},
store,
localVue,
});
};
......@@ -49,29 +35,13 @@ describe('Dashboard', () => {
window.gon = { ...window.gon, ee: true };
store = createStore();
mock = new MockAdapter(axios);
mock.onGet(mockApiEndpoint).reply(200, metricsDashboardPayload);
Component = localVue.extend(Dashboard);
mock.onGet(mockApiEndpoint).reply(200, metricsDashboardResponse);
});
afterEach(() => {
mock.restore();
});
function setupComponentStore(component) {
component.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`,
metricsDashboardPayload,
);
component.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultFixture,
);
component.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
}
describe('add custom metrics', () => {
describe('when not available', () => {
beforeEach(() => {
......@@ -93,7 +63,7 @@ describe('Dashboard', () => {
customMetricsPath: '/endpoint',
customMetricsAvailable: true,
});
setupComponentStore(wrapper);
setupStoreWithData(wrapper.vm.$store);
origPage = document.body.dataset.page;
document.body.dataset.page = 'projects:environments:metrics';
......@@ -133,6 +103,7 @@ describe('Dashboard', () => {
});
});
});
it('renders custom metrics form fields', () => {
expect(wrapper.find(CustomMetricsFormFields).exists()).toBe(true);
});
......
import { mount } from '@vue/test-utils';
import { setTestTimeout } from 'helpers/timeout';
import { GlLink } from '@gitlab/ui';
import { TEST_HOST } from 'jest/helpers/test_constants';
import {
GlAreaChart,
GlLineChart,
......@@ -12,23 +13,16 @@ import { shallowWrapperContainsSlotText } from 'helpers/vue_test_utils_helper';
import { createStore } from '~/monitoring/stores';
import TimeSeries from '~/monitoring/components/charts/time_series.vue';
import * as types from '~/monitoring/stores/mutation_types';
import { deploymentData, mockProjectDir } from '../../mock_data';
import {
deploymentData,
mockedQueryResultFixture,
metricsDashboardPayload,
metricsDashboardViewModel,
mockProjectDir,
mockHost,
} from '../../mock_data';
metricResultStatus,
} from '../../fixture_data';
import * as iconUtils from '~/lib/utils/icon_utils';
import { getJSONFixture } from '../../../helpers/fixtures';
const mockSvgPathContent = 'mockSvgPathContent';
const metricsDashboardFixture = getJSONFixture(
'metrics_dashboard/environment_metrics_dashboard.json',
);
const metricsDashboardPayload = metricsDashboardFixture.dashboard;
jest.mock('lodash/throttle', () =>
// this throttle mock executes immediately
jest.fn(func => {
......@@ -51,7 +45,7 @@ describe('Time series component', () => {
graphData: { ...graphData, type },
deploymentData: store.state.monitoringDashboard.deploymentData,
annotations: store.state.monitoringDashboard.annotations,
projectPath: `${mockHost}${mockProjectDir}`,
projectPath: `${TEST_HOST}${mockProjectDir}`,
},
store,
stubs: {
......@@ -74,7 +68,7 @@ describe('Time series component', () => {
store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultFixture,
metricResultStatus,
);
// dashboard is a dynamically generated fixture and stored at environment_metrics_dashboard.json
[mockGraphData] = store.state.monitoringDashboard.dashboard.panelGroups[1].panels;
......@@ -606,7 +600,7 @@ describe('Time series component', () => {
store = createStore();
const graphData = cloneDeep(metricsDashboardViewModel.panelGroups[0].panels[3]);
graphData.metrics.forEach(metric =>
Object.assign(metric, { result: mockedQueryResultFixture.result }),
Object.assign(metric, { result: metricResultStatus.result }),
);
timeSeriesChart = makeTimeSeriesChart(graphData, 'area-chart');
......
import { shallowMount, createLocalVue, mount } from '@vue/test-utils';
import { shallowMount, mount } from '@vue/test-utils';
import { GlDropdownItem, GlDeprecatedButton } from '@gitlab/ui';
import VueDraggable from 'vuedraggable';
import MockAdapter from 'axios-mock-adapter';
......@@ -6,7 +6,6 @@ import axios from '~/lib/utils/axios_utils';
import statusCodes from '~/lib/utils/http_status';
import { metricStates } from '~/monitoring/constants';
import Dashboard from '~/monitoring/components/dashboard.vue';
import { getJSONFixture } from '../../../../spec/frontend/helpers/fixtures';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue';
......@@ -14,21 +13,9 @@ import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { createStore } from '~/monitoring/stores';
import * as types from '~/monitoring/stores/mutation_types';
import { setupComponentStore, propsData } from '../init_utils';
import {
metricsDashboardViewModel,
environmentData,
dashboardGitResponse,
mockedQueryResultFixture,
} from '../mock_data';
const localVue = createLocalVue();
const expectedPanelCount = 4;
const metricsDashboardFixture = getJSONFixture(
'metrics_dashboard/environment_metrics_dashboard.json',
);
const metricsDashboardPayload = metricsDashboardFixture.dashboard;
import { setupStoreWithDashboard, setMetricResult, setupStoreWithData } from '../store_utils';
import { environmentData, dashboardGitResponse, propsData } from '../mock_data';
import { metricsDashboardViewModel, metricsDashboardPanelCount } from '../fixture_data';
describe('Dashboard', () => {
let store;
......@@ -43,7 +30,6 @@ describe('Dashboard', () => {
const createShallowWrapper = (props = {}, options = {}) => {
wrapper = shallowMount(Dashboard, {
localVue,
propsData: { ...propsData, ...props },
methods: {
fetchData: jest.fn(),
......@@ -55,7 +41,6 @@ describe('Dashboard', () => {
const createMountedWrapper = (props = {}, options = {}) => {
wrapper = mount(Dashboard, {
localVue,
propsData: { ...propsData, ...props },
methods: {
fetchData: jest.fn(),
......@@ -144,7 +129,7 @@ describe('Dashboard', () => {
{ stubs: ['graph-group', 'panel-type'] },
);
setupComponentStore(wrapper);
setupStoreWithData(wrapper.vm.$store);
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm.showEmptyState).toEqual(false);
......@@ -172,7 +157,7 @@ describe('Dashboard', () => {
beforeEach(() => {
createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] });
setupComponentStore(wrapper);
setupStoreWithData(wrapper.vm.$store);
return wrapper.vm.$nextTick();
});
......@@ -201,14 +186,7 @@ describe('Dashboard', () => {
it('hides the environments dropdown list when there is no environments', () => {
createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] });
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`,
metricsDashboardPayload,
);
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultFixture,
);
setupStoreWithDashboard(wrapper.vm.$store);
return wrapper.vm.$nextTick().then(() => {
expect(findAllEnvironmentsDropdownItems()).toHaveLength(0);
......@@ -218,7 +196,7 @@ describe('Dashboard', () => {
it('renders the datetimepicker dropdown', () => {
createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] });
setupComponentStore(wrapper);
setupStoreWithData(wrapper.vm.$store);
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(DateTimePicker).exists()).toBe(true);
......@@ -228,7 +206,7 @@ describe('Dashboard', () => {
it('renders the refresh dashboard button', () => {
createMountedWrapper({ hasMetrics: true }, { stubs: ['graph-group', 'panel-type'] });
setupComponentStore(wrapper);
setupStoreWithData(wrapper.vm.$store);
return wrapper.vm.$nextTick().then(() => {
const refreshBtn = wrapper.findAll({ ref: 'refreshDashboardBtn' });
......@@ -241,7 +219,11 @@ describe('Dashboard', () => {
describe('when one of the metrics is missing', () => {
beforeEach(() => {
createShallowWrapper({ hasMetrics: true });
setupComponentStore(wrapper);
const { $store } = wrapper.vm;
setupStoreWithDashboard($store);
setMetricResult({ $store, result: [], panel: 2 });
return wrapper.vm.$nextTick();
});
......@@ -273,7 +255,7 @@ describe('Dashboard', () => {
},
);
setupComponentStore(wrapper);
setupStoreWithData(wrapper.vm.$store);
return wrapper.vm.$nextTick();
});
......@@ -348,14 +330,14 @@ describe('Dashboard', () => {
beforeEach(() => {
createShallowWrapper({ hasMetrics: true });
setupComponentStore(wrapper);
setupStoreWithData(wrapper.vm.$store);
return wrapper.vm.$nextTick();
});
it('wraps vuedraggable', () => {
expect(findDraggablePanels().exists()).toBe(true);
expect(findDraggablePanels().length).toEqual(expectedPanelCount);
expect(findDraggablePanels().length).toEqual(metricsDashboardPanelCount);
});
it('is disabled by default', () => {
......@@ -411,11 +393,11 @@ describe('Dashboard', () => {
it('shows a remove button, which removes a panel', () => {
expect(findFirstDraggableRemoveButton().isEmpty()).toBe(false);
expect(findDraggablePanels().length).toEqual(expectedPanelCount);
expect(findDraggablePanels().length).toEqual(metricsDashboardPanelCount);
findFirstDraggableRemoveButton().trigger('click');
return wrapper.vm.$nextTick(() => {
expect(findDraggablePanels().length).toEqual(expectedPanelCount - 1);
expect(findDraggablePanels().length).toEqual(metricsDashboardPanelCount - 1);
});
});
......@@ -534,7 +516,7 @@ describe('Dashboard', () => {
beforeEach(() => {
createShallowWrapper({ hasMetrics: true, currentDashboard });
setupComponentStore(wrapper);
setupStoreWithData(wrapper.vm.$store);
return wrapper.vm.$nextTick();
});
......
......@@ -3,7 +3,7 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import Dashboard from '~/monitoring/components/dashboard.vue';
import { createStore } from '~/monitoring/stores';
import { propsData } from '../init_utils';
import { propsData } from '../mock_data';
jest.mock('~/lib/utils/url_utility');
......
......@@ -9,12 +9,11 @@ import {
updateHistory,
} from '~/lib/utils/url_utility';
import axios from '~/lib/utils/axios_utils';
import { mockProjectDir } from '../mock_data';
import { mockProjectDir, propsData } from '../mock_data';
import Dashboard from '~/monitoring/components/dashboard.vue';
import { createStore } from '~/monitoring/stores';
import { defaultTimeRange } from '~/vue_shared/constants';
import { propsData } from '../init_utils';
jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility');
......
import { mapToDashboardViewModel } from '~/monitoring/stores/utils';
import { metricsResult } from './mock_data';
// Use globally available `getJSONFixture` so this file can be imported by both karma and jest specs
export const metricsDashboardResponse = getJSONFixture(
'metrics_dashboard/environment_metrics_dashboard.json',
);
export const metricsDashboardPayload = metricsDashboardResponse.dashboard;
export const metricsDashboardViewModel = mapToDashboardViewModel(metricsDashboardPayload);
export const metricsDashboardPanelCount = 22;
export const metricResultStatus = {
// First metric in fixture `metrics_dashboard/environment_metrics_dashboard.json`
metricId: 'NO_DB_response_metrics_nginx_ingress_throughput_status_code',
result: metricsResult,
};
export const metricResultPods = {
// Second metric in fixture `metrics_dashboard/environment_metrics_dashboard.json`
metricId: 'NO_DB_response_metrics_nginx_ingress_latency_pod_average',
result: metricsResult,
};
export const metricResultEmpty = {
metricId: 'NO_DB_response_metrics_nginx_ingress_16_throughput_status_code',
result: [],
};
import * as types from '~/monitoring/stores/mutation_types';
import {
metricsDashboardPayload,
mockedEmptyResult,
mockedQueryResultPayload,
mockedQueryResultPayloadCoresTotal,
mockApiEndpoint,
environmentData,
} from './mock_data';
export const propsData = {
hasMetrics: false,
documentationPath: '/path/to/docs',
settingsPath: '/path/to/settings',
clustersPath: '/path/to/clusters',
tagsPath: '/path/to/tags',
projectPath: '/path/to/project',
logsPath: '/path/to/logs',
defaultBranch: 'master',
metricsEndpoint: mockApiEndpoint,
deploymentsEndpoint: null,
emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
emptyLoadingSvgPath: '/path/to/loading.svg',
emptyNoDataSvgPath: '/path/to/no-data.svg',
emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
currentEnvironmentName: 'production',
customMetricsAvailable: false,
customMetricsPath: '',
validateQueryPath: '',
};
export const setupComponentStore = wrapper => {
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`,
metricsDashboardPayload,
);
// Load 3 panels to the dashboard, one with an empty result
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedEmptyResult,
);
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayload,
);
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayloadCoresTotal,
);
wrapper.vm.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
};
import { mapToDashboardViewModel } from '~/monitoring/stores/utils';
// This import path needs to be relative for now because this mock data is used in
// Karma specs too, where the helpers/test_constants alias can not be resolved
import { TEST_HOST } from '../helpers/test_constants';
export const mockHost = 'http://test.host';
export const mockProjectDir = '/frontend-fixtures/environments-project';
export const mockApiEndpoint = `${TEST_HOST}/monitoring/mock`;
export const propsData = {
hasMetrics: false,
documentationPath: '/path/to/docs',
settingsPath: '/path/to/settings',
clustersPath: '/path/to/clusters',
tagsPath: '/path/to/tags',
projectPath: '/path/to/project',
logsPath: '/path/to/logs',
defaultBranch: 'master',
metricsEndpoint: mockApiEndpoint,
deploymentsEndpoint: null,
emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
emptyLoadingSvgPath: '/path/to/loading.svg',
emptyNoDataSvgPath: '/path/to/no-data.svg',
emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
currentEnvironmentName: 'production',
customMetricsAvailable: false,
customMetricsPath: '',
validateQueryPath: '',
};
const customDashboardsData = new Array(30).fill(null).map((_, idx) => ({
default: false,
display_name: `Custom Dashboard ${idx}`,
can_edit: true,
system_dashboard: false,
project_blob_path: `${mockProjectDir}/blob/master/dashboards/.gitlab/dashboards/dashboard_${idx}.yml`,
path: `.gitlab/dashboards/dashboard_${idx}.yml`,
}));
export const mockDashboardsErrorResponse = {
all_dashboards: customDashboardsData,
message: "Each 'panel_group' must define an array :panels",
status: 'error',
};
export const anomalyDeploymentData = [
{
id: 111,
......@@ -266,77 +300,6 @@ export const metricsNewGroupsAPIResponse = [
},
];
const metricsResult = [
{
metric: {},
values: [
[1563272065.589, '10.396484375'],
[1563272125.589, '10.333984375'],
[1563272185.589, '10.333984375'],
[1563272245.589, '10.333984375'],
[1563272305.589, '10.333984375'],
[1563272365.589, '10.333984375'],
[1563272425.589, '10.38671875'],
[1563272485.589, '10.333984375'],
[1563272545.589, '10.333984375'],
[1563272605.589, '10.333984375'],
[1563272665.589, '10.333984375'],
[1563272725.589, '10.333984375'],
[1563272785.589, '10.396484375'],
[1563272845.589, '10.333984375'],
[1563272905.589, '10.333984375'],
[1563272965.589, '10.3984375'],
[1563273025.589, '10.337890625'],
[1563273085.589, '10.34765625'],
[1563273145.589, '10.337890625'],
[1563273205.589, '10.337890625'],
[1563273265.589, '10.337890625'],
[1563273325.589, '10.337890625'],
[1563273385.589, '10.337890625'],
[1563273445.589, '10.337890625'],
[1563273505.589, '10.337890625'],
[1563273565.589, '10.337890625'],
[1563273625.589, '10.337890625'],
[1563273685.589, '10.337890625'],
[1563273745.589, '10.337890625'],
[1563273805.589, '10.337890625'],
[1563273865.589, '10.390625'],
[1563273925.589, '10.390625'],
],
},
];
export const mockedEmptyResult = {
metricId: '1_response_metrics_nginx_ingress_throughput_status_code',
result: [],
};
export const mockedEmptyThroughputResult = {
metricId: 'NO_DB_response_metrics_nginx_ingress_16_throughput_status_code',
result: [],
};
export const mockedQueryResultPayload = {
metricId: '12_system_metrics_kubernetes_container_memory_total',
result: metricsResult,
};
export const mockedQueryResultPayloadCoresTotal = {
metricId: '13_system_metrics_kubernetes_container_cores_total',
result: metricsResult,
};
export const mockedQueryResultFixture = {
// First metric in fixture `metrics_dashboard/environment_metrics_dashboard.json`
metricId: 'NO_DB_response_metrics_nginx_ingress_throughput_status_code',
result: metricsResult,
};
export const mockedQueryResultFixtureStatusCode = {
metricId: 'NO_DB_response_metrics_nginx_ingress_latency_pod_average',
result: metricsResult,
};
const extraEnvironmentData = new Array(15).fill(null).map((_, idx) => ({
id: `gid://gitlab/Environments/${150 + idx}`,
name: `no-deployment/noop-branch-${idx}`,
......@@ -384,158 +347,6 @@ export const environmentData = [
},
].concat(extraEnvironmentData);
export const metricsDashboardPayload = {
dashboard: 'Environment metrics',
priority: 1,
panel_groups: [
{
group: 'System metrics (Kubernetes)',
priority: 5,
panels: [
{
title: 'Memory Usage (Total)',
type: 'area-chart',
y_label: 'Total Memory Used',
weight: 4,
y_axis: {
format: 'megabytes',
},
metrics: [
{
id: 'system_metrics_kubernetes_container_memory_total',
query_range:
'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1000/1000',
label: 'Total',
unit: 'MB',
metric_id: 12,
prometheus_endpoint_path: 'http://test',
},
],
},
{
title: 'Core Usage (Total)',
type: 'area-chart',
y_label: 'Total Cores',
weight: 3,
metrics: [
{
id: 'system_metrics_kubernetes_container_cores_total',
query_range:
'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job)',
label: 'Total',
unit: 'cores',
metric_id: 13,
},
],
},
{
title: 'Memory Usage (Pod average)',
type: 'line-chart',
y_label: 'Memory Used per Pod',
weight: 2,
metrics: [
{
id: 'system_metrics_kubernetes_container_memory_average',
query_range:
'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024',
label: 'Pod average',
unit: 'MB',
metric_id: 14,
},
],
},
{
title: 'memories',
type: 'area-chart',
y_label: 'memories',
metrics: [
{
id: 'metric_of_ages_1000',
label: 'memory_1000',
unit: 'count',
prometheus_endpoint_path: '/root',
metric_id: 20,
},
{
id: 'metric_of_ages_1001',
label: 'memory_1000',
unit: 'count',
prometheus_endpoint_path: '/root',
metric_id: 21,
},
{
id: 'metric_of_ages_1002',
label: 'memory_1000',
unit: 'count',
prometheus_endpoint_path: '/root',
metric_id: 22,
},
{
id: 'metric_of_ages_1003',
label: 'memory_1000',
unit: 'count',
prometheus_endpoint_path: '/root',
metric_id: 23,
},
{
id: 'metric_of_ages_1004',
label: 'memory_1004',
unit: 'count',
prometheus_endpoint_path: '/root',
metric_id: 24,
},
],
},
],
},
{
group: 'Response metrics (NGINX Ingress VTS)',
priority: 10,
panels: [
{
metrics: [
{
id: 'response_metrics_nginx_ingress_throughput_status_code',
label: 'Status Code',
metric_id: 1,
prometheus_endpoint_path:
'/root/autodevops-deploy/environments/32/prometheus/api/v1/query_range?query=sum%28rate%28nginx_upstream_responses_total%7Bupstream%3D~%22%25%7Bkube_namespace%7D-%25%7Bci_environment_slug%7D-.%2A%22%7D%5B2m%5D%29%29+by+%28status_code%29',
query_range:
'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) by (status_code)',
unit: 'req / sec',
},
],
title: 'Throughput',
type: 'area-chart',
weight: 1,
y_label: 'Requests / Sec',
},
],
},
],
};
/**
* Mock of response of metrics_dashboard.json
*/
export const metricsDashboardResponse = {
all_dashboards: [],
dashboard: metricsDashboardPayload,
metrics_data: {},
status: 'success',
};
export const metricsDashboardViewModel = mapToDashboardViewModel(metricsDashboardPayload);
const customDashboardsData = new Array(30).fill(null).map((_, idx) => ({
default: false,
display_name: `Custom Dashboard ${idx}`,
can_edit: true,
system_dashboard: false,
project_blob_path: `${mockProjectDir}/blob/master/dashboards/.gitlab/dashboards/dashboard_${idx}.yml`,
path: `.gitlab/dashboards/dashboard_${idx}.yml`,
}));
export const dashboardGitResponse = [
{
default: true,
......@@ -548,11 +359,47 @@ export const dashboardGitResponse = [
...customDashboardsData,
];
export const mockDashboardsErrorResponse = {
all_dashboards: customDashboardsData,
message: "Each 'panel_group' must define an array :panels",
status: 'error',
};
// Metrics mocks
export const metricsResult = [
{
metric: {},
values: [
[1563272065.589, '10.396484375'],
[1563272125.589, '10.333984375'],
[1563272185.589, '10.333984375'],
[1563272245.589, '10.333984375'],
[1563272305.589, '10.333984375'],
[1563272365.589, '10.333984375'],
[1563272425.589, '10.38671875'],
[1563272485.589, '10.333984375'],
[1563272545.589, '10.333984375'],
[1563272605.589, '10.333984375'],
[1563272665.589, '10.333984375'],
[1563272725.589, '10.333984375'],
[1563272785.589, '10.396484375'],
[1563272845.589, '10.333984375'],
[1563272905.589, '10.333984375'],
[1563272965.589, '10.3984375'],
[1563273025.589, '10.337890625'],
[1563273085.589, '10.34765625'],
[1563273145.589, '10.337890625'],
[1563273205.589, '10.337890625'],
[1563273265.589, '10.337890625'],
[1563273325.589, '10.337890625'],
[1563273385.589, '10.337890625'],
[1563273445.589, '10.337890625'],
[1563273505.589, '10.337890625'],
[1563273565.589, '10.337890625'],
[1563273625.589, '10.337890625'],
[1563273685.589, '10.337890625'],
[1563273745.589, '10.337890625'],
[1563273805.589, '10.337890625'],
[1563273865.589, '10.390625'],
[1563273925.589, '10.390625'],
],
},
];
export const graphDataPrometheusQuery = {
title: 'Super Chart A2',
......
......@@ -31,11 +31,10 @@ import {
deploymentData,
environmentData,
annotationsData,
metricsDashboardResponse,
metricsDashboardViewModel,
dashboardGitResponse,
mockDashboardsErrorResponse,
} from '../mock_data';
import { metricsDashboardResponse, metricsDashboardViewModel, metricsDashboardPanelCount } from '../fixture_data';
jest.mock('~/flash');
......@@ -553,7 +552,7 @@ describe('Monitoring store actions', () => {
fetchDashboardData({ state, commit, dispatch })
.then(() => {
expect(dispatch).toHaveBeenCalledTimes(10); // one per metric plus 1 for deployments
expect(dispatch).toHaveBeenCalledTimes(metricsDashboardPanelCount + 1); // plus 1 for deployments
expect(dispatch).toHaveBeenCalledWith('fetchDeploymentsData');
expect(dispatch).toHaveBeenCalledWith('fetchPrometheusMetric', {
metric,
......@@ -581,11 +580,13 @@ describe('Monitoring store actions', () => {
let metric;
let state;
let data;
let prometheusEndpointPath;
beforeEach(() => {
state = storeState();
[metric] = metricsDashboardResponse.dashboard.panel_groups[0].panels[0].metrics;
metric = convertObjectPropsToCamelCase(metric, { deep: true });
[metric] = metricsDashboardViewModel.panelGroups[0].panels[0].metrics;
prometheusEndpointPath = metric.prometheusEndpointPath;
data = {
metricId: metric.metricId,
......@@ -594,7 +595,7 @@ describe('Monitoring store actions', () => {
});
it('commits result', done => {
mock.onGet('http://test').reply(200, { data }); // One attempt
mock.onGet(prometheusEndpointPath).reply(200, { data }); // One attempt
testAction(
fetchPrometheusMetric,
......@@ -631,7 +632,7 @@ describe('Monitoring store actions', () => {
};
it('uses calculated step', done => {
mock.onGet('http://test').reply(200, { data }); // One attempt
mock.onGet(prometheusEndpointPath).reply(200, { data }); // One attempt
testAction(
fetchPrometheusMetric,
......@@ -673,7 +674,7 @@ describe('Monitoring store actions', () => {
};
it('uses metric step', done => {
mock.onGet('http://test').reply(200, { data }); // One attempt
mock.onGet(prometheusEndpointPath).reply(200, { data }); // One attempt
testAction(
fetchPrometheusMetric,
......@@ -705,10 +706,10 @@ describe('Monitoring store actions', () => {
it('commits result, when waiting for results', done => {
// Mock multiple attempts while the cache is filling up
mock.onGet('http://test').replyOnce(statusCodes.NO_CONTENT);
mock.onGet('http://test').replyOnce(statusCodes.NO_CONTENT);
mock.onGet('http://test').replyOnce(statusCodes.NO_CONTENT);
mock.onGet('http://test').reply(200, { data }); // 4th attempt
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).reply(200, { data }); // 4th attempt
testAction(
fetchPrometheusMetric,
......@@ -739,10 +740,10 @@ describe('Monitoring store actions', () => {
it('commits failure, when waiting for results and getting a server error', done => {
// Mock multiple attempts while the cache is filling up and fails
mock.onGet('http://test').replyOnce(statusCodes.NO_CONTENT);
mock.onGet('http://test').replyOnce(statusCodes.NO_CONTENT);
mock.onGet('http://test').replyOnce(statusCodes.NO_CONTENT);
mock.onGet('http://test').reply(500); // 4th attempt
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).replyOnce(statusCodes.NO_CONTENT);
mock.onGet(prometheusEndpointPath).reply(500); // 4th attempt
const error = new Error('Request failed with status code 500');
......
......@@ -3,18 +3,13 @@ import * as getters from '~/monitoring/stores/getters';
import mutations from '~/monitoring/stores/mutations';
import * as types from '~/monitoring/stores/mutation_types';
import { metricStates } from '~/monitoring/constants';
import { environmentData, metricsResult } from '../mock_data';
import {
environmentData,
mockedEmptyThroughputResult,
mockedQueryResultFixture,
mockedQueryResultFixtureStatusCode,
} from '../mock_data';
import { getJSONFixture } from '../../helpers/fixtures';
const metricsDashboardFixture = getJSONFixture(
'metrics_dashboard/environment_metrics_dashboard.json',
);
const metricsDashboardPayload = metricsDashboardFixture.dashboard;
metricsDashboardPayload,
metricResultStatus,
metricResultPods,
metricResultEmpty,
} from '../fixture_data';
describe('Monitoring store Getters', () => {
describe('getMetricStates', () => {
......@@ -22,6 +17,21 @@ describe('Monitoring store Getters', () => {
let state;
let getMetricStates;
const setMetricSuccess = ({ result = metricsResult, group = 0, panel = 0, metric = 0 }) => {
const { metricId } = state.dashboard.panelGroups[group].panels[panel].metrics[metric];
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, {
metricId,
result,
});
};
const setMetricFailure = ({ group = 0, panel = 0, metric = 0 }) => {
const { metricId } = state.dashboard.panelGroups[group].panels[panel].metrics[metric];
mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
metricId,
});
};
beforeEach(() => {
setupState = (initState = {}) => {
state = initState;
......@@ -61,31 +71,30 @@ describe('Monitoring store Getters', () => {
it('on an empty metric with no result, returns NO_DATA', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedEmptyThroughputResult);
setMetricSuccess({ result: [], group: 2 });
expect(getMetricStates()).toEqual([metricStates.NO_DATA]);
});
it('on a metric with a result, returns OK', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultFixture);
setMetricSuccess({ group: 1 });
expect(getMetricStates()).toEqual([metricStates.OK]);
});
it('on a metric with an error, returns an error', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload);
mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
metricId: groups[0].panels[0].metrics[0].metricId,
});
setMetricFailure({});
expect(getMetricStates()).toEqual([metricStates.UNKNOWN_ERROR]);
});
it('on multiple metrics with results, returns OK', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultFixture);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultFixtureStatusCode);
setMetricSuccess({ group: 1 });
setMetricSuccess({ group: 1, panel: 1 });
expect(getMetricStates()).toEqual([metricStates.OK]);
......@@ -96,15 +105,8 @@ describe('Monitoring store Getters', () => {
it('on multiple metrics errors', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload);
mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
metricId: groups[0].panels[0].metrics[0].metricId,
});
mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
metricId: groups[0].panels[0].metrics[0].metricId,
});
mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
metricId: groups[1].panels[0].metrics[0].metricId,
});
setMetricFailure({});
setMetricFailure({ group: 1 });
// Entire dashboard fails
expect(getMetricStates()).toEqual([metricStates.UNKNOWN_ERROR]);
......@@ -116,14 +118,11 @@ describe('Monitoring store Getters', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload);
// An success in 1 group
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultFixture);
setMetricSuccess({ group: 1 });
// An error in 2 groups
mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
metricId: groups[1].panels[1].metrics[0].metricId,
});
mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, {
metricId: groups[2].panels[0].metrics[0].metricId,
});
setMetricFailure({ group: 1, panel: 1 });
setMetricFailure({ group: 2, panel: 0 });
expect(getMetricStates()).toEqual([metricStates.OK, metricStates.UNKNOWN_ERROR]);
expect(getMetricStates(groups[1].key)).toEqual([
......@@ -182,38 +181,35 @@ describe('Monitoring store Getters', () => {
it('an empty metric, returns empty', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedEmptyThroughputResult);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, metricResultEmpty);
expect(metricsWithData()).toEqual([]);
});
it('a metric with results, it returns a metric', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultFixture);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, metricResultStatus);
expect(metricsWithData()).toEqual([mockedQueryResultFixture.metricId]);
expect(metricsWithData()).toEqual([metricResultStatus.metricId]);
});
it('multiple metrics with results, it return multiple metrics', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultFixture);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultFixtureStatusCode);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, metricResultStatus);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, metricResultPods);
expect(metricsWithData()).toEqual([
mockedQueryResultFixture.metricId,
mockedQueryResultFixtureStatusCode.metricId,
]);
expect(metricsWithData()).toEqual([metricResultStatus.metricId, metricResultPods.metricId]);
});
it('multiple metrics with results, it returns metrics filtered by group', () => {
mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultFixture);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, mockedQueryResultFixtureStatusCode);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, metricResultStatus);
mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, metricResultPods);
// First group has metrics
expect(metricsWithData(state.dashboard.panelGroups[1].key)).toEqual([
mockedQueryResultFixture.metricId,
mockedQueryResultFixtureStatusCode.metricId,
metricResultStatus.metricId,
metricResultPods.metricId,
]);
// Second group has no metrics
......
......@@ -6,12 +6,7 @@ import state from '~/monitoring/stores/state';
import { metricStates } from '~/monitoring/constants';
import { deploymentData, dashboardGitResponse } from '../mock_data';
import { getJSONFixture } from '../../helpers/fixtures';
const metricsDashboardFixture = getJSONFixture(
'metrics_dashboard/environment_metrics_dashboard.json',
);
const metricsDashboardPayload = metricsDashboardFixture.dashboard;
import { metricsDashboardPayload } from '../fixture_data';
describe('Monitoring mutations', () => {
let stateCopy;
......
import * as types from '~/monitoring/stores/mutation_types';
import { metricsResult, environmentData } from './mock_data';
import { metricsDashboardPayload } from './fixture_data';
export const setMetricResult = ({ $store, result, group = 0, panel = 0, metric = 0 }) => {
const { dashboard } = $store.state.monitoringDashboard;
const { metricId } = dashboard.panelGroups[group].panels[panel].metrics[metric];
$store.commit(`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`, {
metricId,
result,
});
};
const setEnvironmentData = $store => {
$store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, environmentData);
};
export const setupStoreWithDashboard = $store => {
$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`,
metricsDashboardPayload,
);
};
export const setupStoreWithData = $store => {
setupStoreWithDashboard($store);
setMetricResult({ $store, result: [], panel: 0 });
setMetricResult({ $store, result: metricsResult, panel: 1 });
setMetricResult({ $store, result: metricsResult, panel: 2 });
setEnvironmentData($store);
};
import * as monitoringUtils from '~/monitoring/utils';
import { queryToObject, mergeUrlParams, removeParams } from '~/lib/utils/url_utility';
import { TEST_HOST } from 'jest/helpers/test_constants';
import {
mockHost,
mockProjectDir,
graphDataPrometheusQuery,
graphDataPrometheusQueryRange,
......@@ -11,7 +11,7 @@ import {
jest.mock('~/lib/utils/url_utility');
const mockPath = `${mockHost}${mockProjectDir}/-/environments/29/metrics`;
const mockPath = `${TEST_HOST}${mockProjectDir}/-/environments/29/metrics`;
const generatedLink = 'http://chart.link.com';
......
......@@ -2,66 +2,13 @@ import Vue from 'vue';
import { createLocalVue } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue';
import * as types from '~/monitoring/stores/mutation_types';
import { createStore } from '~/monitoring/stores';
import axios from '~/lib/utils/axios_utils';
import {
metricsDashboardPayload,
mockedEmptyResult,
mockedQueryResultPayload,
mockedQueryResultPayloadCoresTotal,
mockApiEndpoint,
environmentData,
} from '../mock_data';
import { mockApiEndpoint, propsData } from '../mock_data';
import { metricsDashboardPayload } from '../fixture_data';
import { setupStoreWithData } from '../store_utils';
const localVue = createLocalVue();
const propsData = {
hasMetrics: false,
documentationPath: '/path/to/docs',
settingsPath: '/path/to/settings',
clustersPath: '/path/to/clusters',
tagsPath: '/path/to/tags',
projectPath: '/path/to/project',
defaultBranch: 'master',
metricsEndpoint: mockApiEndpoint,
deploymentsEndpoint: null,
emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
emptyLoadingSvgPath: '/path/to/loading.svg',
emptyNoDataSvgPath: '/path/to/no-data.svg',
emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
currentEnvironmentName: 'production',
customMetricsAvailable: false,
customMetricsPath: '',
validateQueryPath: '',
};
function setupComponentStore(component) {
// Load 2 panel groups
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`,
metricsDashboardPayload,
);
// Load 3 panels to the dashboard, one with an empty result
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedEmptyResult,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayload,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
mockedQueryResultPayloadCoresTotal,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
}
describe('Dashboard', () => {
let DashboardComponent;
......@@ -109,7 +56,7 @@ describe('Dashboard', () => {
store,
});
setupComponentStore(component);
setupStoreWithData(component.$store);
return Vue.nextTick().then(() => {
[promPanel] = component.$el.querySelectorAll('.prometheus-panel');
......
export * from '../../frontend/monitoring/fixture_data';
export * from '../../frontend/monitoring/store_utils';
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