diff options
Diffstat (limited to 'spec/frontend/monitoring/store')
-rw-r--r-- | spec/frontend/monitoring/store/actions_spec.js | 125 | ||||
-rw-r--r-- | spec/frontend/monitoring/store/getters_spec.js | 119 | ||||
-rw-r--r-- | spec/frontend/monitoring/store/mutations_spec.js | 149 |
3 files changed, 259 insertions, 134 deletions
diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js index 22f2b2e3c77..5c7ab4e6a1f 100644 --- a/spec/frontend/monitoring/store/actions_spec.js +++ b/spec/frontend/monitoring/store/actions_spec.js @@ -1,10 +1,11 @@ import MockAdapter from 'axios-mock-adapter'; import testAction from 'helpers/vuex_action_helper'; +import { backoffMockImplementation } from 'jest/helpers/backoff_helper'; import Tracking from '~/tracking'; import axios from '~/lib/utils/axios_utils'; import statusCodes from '~/lib/utils/http_status'; import * as commonUtils from '~/lib/utils/common_utils'; -import createFlash from '~/flash'; +import { deprecatedCreateFlash as createFlash } from '~/flash'; import { defaultTimeRange } from '~/vue_shared/constants'; import * as getters from '~/monitoring/stores/getters'; import { ENVIRONMENT_AVAILABLE_STATE } from '~/monitoring/constants'; @@ -30,6 +31,7 @@ import { duplicateSystemDashboard, updateVariablesAndFetchData, fetchVariableMetricLabelValues, + fetchPanelPreview, } from '~/monitoring/stores/actions'; import { gqClient, @@ -73,19 +75,7 @@ describe('Monitoring store actions', () => { commit = jest.fn(); dispatch = jest.fn(); - jest.spyOn(commonUtils, 'backOff').mockImplementation(callback => { - const q = new Promise((resolve, reject) => { - const stop = arg => (arg instanceof Error ? reject(arg) : resolve(arg)); - const next = () => callback(next, stop); - // Define a timeout based on a mock timer - setTimeout(() => { - callback(next, stop); - }); - }); - // Run all resolved promises in chain - jest.runOnlyPendingTimers(); - return q; - }); + jest.spyOn(commonUtils, 'backOff').mockImplementation(backoffMockImplementation); }); afterEach(() => { @@ -483,7 +473,6 @@ describe('Monitoring store actions', () => { ], [], () => { - expect(mock.history.get).toHaveLength(1); done(); }, ).catch(done.fail); @@ -569,46 +558,8 @@ describe('Monitoring store actions', () => { }); }); - it('commits result, when waiting for results', done => { - // Mock multiple attempts while the cache is filling up - 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, - { metric, defaultQueryParams }, - state, - [ - { - type: types.REQUEST_METRIC_RESULT, - payload: { - metricId: metric.metricId, - }, - }, - { - type: types.RECEIVE_METRIC_RESULT_SUCCESS, - payload: { - metricId: metric.metricId, - data, - }, - }, - ], - [], - () => { - expect(mock.history.get).toHaveLength(4); - done(); - }, - ).catch(done.fail); - }); - 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(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 + mock.onGet(prometheusEndpointPath).reply(500); const error = new Error('Request failed with status code 500'); @@ -633,7 +584,6 @@ describe('Monitoring store actions', () => { ], [], ).catch(e => { - expect(mock.history.get).toHaveLength(4); expect(e).toEqual(error); done(); }); @@ -1205,4 +1155,69 @@ describe('Monitoring store actions', () => { ); }); }); + + describe('fetchPanelPreview', () => { + const panelPreviewEndpoint = '/builder.json'; + const mockYmlContent = 'mock yml content'; + + beforeEach(() => { + state.panelPreviewEndpoint = panelPreviewEndpoint; + }); + + it('should not commit or dispatch if payload is empty', () => { + testAction(fetchPanelPreview, '', state, [], []); + }); + + it('should store the panel and fetch metric results', () => { + const mockPanel = { + title: 'Go heap size', + type: 'area-chart', + }; + + mock + .onPost(panelPreviewEndpoint, { panel_yaml: mockYmlContent }) + .reply(statusCodes.OK, mockPanel); + + testAction( + fetchPanelPreview, + mockYmlContent, + state, + [ + { type: types.SET_PANEL_PREVIEW_IS_SHOWN, payload: true }, + { type: types.REQUEST_PANEL_PREVIEW, payload: mockYmlContent }, + { type: types.RECEIVE_PANEL_PREVIEW_SUCCESS, payload: mockPanel }, + ], + [{ type: 'fetchPanelPreviewMetrics' }], + ); + }); + + it('should display a validation error when the backend cannot process the yml', () => { + const mockErrorMsg = 'Each "metric" must define one of :query or :query_range'; + + mock + .onPost(panelPreviewEndpoint, { panel_yaml: mockYmlContent }) + .reply(statusCodes.UNPROCESSABLE_ENTITY, { + message: mockErrorMsg, + }); + + testAction(fetchPanelPreview, mockYmlContent, state, [ + { type: types.SET_PANEL_PREVIEW_IS_SHOWN, payload: true }, + { type: types.REQUEST_PANEL_PREVIEW, payload: mockYmlContent }, + { type: types.RECEIVE_PANEL_PREVIEW_FAILURE, payload: mockErrorMsg }, + ]); + }); + + it('should display a generic error when the backend fails', () => { + mock.onPost(panelPreviewEndpoint, { panel_yaml: mockYmlContent }).reply(500); + + testAction(fetchPanelPreview, mockYmlContent, state, [ + { type: types.SET_PANEL_PREVIEW_IS_SHOWN, payload: true }, + { type: types.REQUEST_PANEL_PREVIEW, payload: mockYmlContent }, + { + type: types.RECEIVE_PANEL_PREVIEW_FAILURE, + payload: 'Request failed with status code 500', + }, + ]); + }); + }); }); diff --git a/spec/frontend/monitoring/store/getters_spec.js b/spec/frontend/monitoring/store/getters_spec.js index a69f5265ea7..509de8a4596 100644 --- a/spec/frontend/monitoring/store/getters_spec.js +++ b/spec/frontend/monitoring/store/getters_spec.js @@ -11,37 +11,36 @@ import { storeVariables, mockLinks, } from '../mock_data'; -import { - metricsDashboardPayload, - metricResultStatus, - metricResultPods, - metricResultEmpty, -} from '../fixture_data'; +import { metricsDashboardPayload } from '../fixture_data'; describe('Monitoring store Getters', () => { + let state; + + const getMetric = ({ group = 0, panel = 0, metric = 0 } = {}) => + state.dashboard.panelGroups[group].panels[panel].metrics[metric]; + + const setMetricSuccess = ({ group, panel, metric, result = metricsResult } = {}) => { + const { metricId } = getMetric({ group, panel, metric }); + mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, { + metricId, + data: { + resultType: 'matrix', + result, + }, + }); + }; + + const setMetricFailure = ({ group, panel, metric } = {}) => { + const { metricId } = getMetric({ group, panel, metric }); + mutations[types.RECEIVE_METRIC_RESULT_FAILURE](state, { + metricId, + }); + }; + describe('getMetricStates', () => { let setupState; - 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, - data: { - resultType: 'matrix', - 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; @@ -81,7 +80,7 @@ describe('Monitoring store Getters', () => { it('on an empty metric with no result, returns NO_DATA', () => { mutations[types.RECEIVE_METRICS_DASHBOARD_SUCCESS](state, metricsDashboardPayload); - setMetricSuccess({ result: [], group: 2 }); + setMetricSuccess({ group: 2, result: [] }); expect(getMetricStates()).toEqual([metricStates.NO_DATA]); }); @@ -147,7 +146,6 @@ describe('Monitoring store Getters', () => { describe('metricsWithData', () => { let metricsWithData; let setupState; - let state; beforeEach(() => { setupState = (initState = {}) => { @@ -191,35 +189,39 @@ 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, metricResultEmpty); + setMetricSuccess({ result: [] }); 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, metricResultStatus); + setMetricSuccess(); - expect(metricsWithData()).toEqual([metricResultStatus.metricId]); + expect(metricsWithData()).toEqual([getMetric().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, metricResultStatus); - mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, metricResultPods); + setMetricSuccess({ panel: 0 }); + setMetricSuccess({ panel: 1 }); - expect(metricsWithData()).toEqual([metricResultStatus.metricId, metricResultPods.metricId]); + expect(metricsWithData()).toEqual([ + getMetric({ panel: 0 }).metricId, + getMetric({ panel: 1 }).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, metricResultStatus); - mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](state, metricResultPods); + + setMetricSuccess({ group: 1 }); + setMetricSuccess({ group: 1, panel: 1 }); // First group has metrics expect(metricsWithData(state.dashboard.panelGroups[1].key)).toEqual([ - metricResultStatus.metricId, - metricResultPods.metricId, + getMetric({ group: 1 }).metricId, + getMetric({ group: 1, panel: 1 }).metricId, ]); // Second group has no metrics @@ -229,7 +231,6 @@ describe('Monitoring store Getters', () => { }); describe('filteredEnvironments', () => { - let state; const setupState = (initState = {}) => { state = { ...state, @@ -284,7 +285,6 @@ describe('Monitoring store Getters', () => { describe('metricsSavedToDb', () => { let metricsSavedToDb; - let state; let mockData; beforeEach(() => { @@ -335,8 +335,6 @@ describe('Monitoring store Getters', () => { }); describe('getCustomVariablesParams', () => { - let state; - beforeEach(() => { state = { variables: {}, @@ -367,58 +365,65 @@ describe('Monitoring store Getters', () => { describe('selectedDashboard', () => { const { selectedDashboard } = getters; - const localGetters = state => ({ - fullDashboardPath: getters.fullDashboardPath(state), + const localGetters = localState => ({ + fullDashboardPath: getters.fullDashboardPath(localState), }); it('returns a dashboard', () => { - const state = { + const localState = { allDashboards: dashboardGitResponse, currentDashboard: dashboardGitResponse[0].path, customDashboardBasePath, }; - expect(selectedDashboard(state, localGetters(state))).toEqual(dashboardGitResponse[0]); + expect(selectedDashboard(localState, localGetters(localState))).toEqual( + dashboardGitResponse[0], + ); }); - it('returns a non-default dashboard', () => { - const state = { + it('returns a dashboard different from the overview dashboard', () => { + const localState = { allDashboards: dashboardGitResponse, currentDashboard: dashboardGitResponse[1].path, customDashboardBasePath, }; - expect(selectedDashboard(state, localGetters(state))).toEqual(dashboardGitResponse[1]); + expect(selectedDashboard(localState, localGetters(localState))).toEqual( + dashboardGitResponse[1], + ); }); - it('returns a default dashboard when no dashboard is selected', () => { - const state = { + it('returns the overview dashboard when no dashboard is selected', () => { + const localState = { allDashboards: dashboardGitResponse, currentDashboard: null, customDashboardBasePath, }; - expect(selectedDashboard(state, localGetters(state))).toEqual(dashboardGitResponse[0]); + expect(selectedDashboard(localState, localGetters(localState))).toEqual( + dashboardGitResponse[0], + ); }); - it('returns a default dashboard when dashboard cannot be found', () => { - const state = { + it('returns the overview dashboard when dashboard cannot be found', () => { + const localState = { allDashboards: dashboardGitResponse, currentDashboard: 'wrong_path', customDashboardBasePath, }; - expect(selectedDashboard(state, localGetters(state))).toEqual(dashboardGitResponse[0]); + expect(selectedDashboard(localState, localGetters(localState))).toEqual( + dashboardGitResponse[0], + ); }); it('returns null when no dashboards are present', () => { - const state = { + const localState = { allDashboards: [], currentDashboard: dashboardGitResponse[0].path, customDashboardBasePath, }; - expect(selectedDashboard(state, localGetters(state))).toEqual(null); + expect(selectedDashboard(localState, localGetters(localState))).toEqual(null); }); }); describe('linksWithMetadata', () => { - let state; const setupState = (initState = {}) => { state = { ...state, diff --git a/spec/frontend/monitoring/store/mutations_spec.js b/spec/frontend/monitoring/store/mutations_spec.js index 14b38d79aa2..8d1351fc909 100644 --- a/spec/frontend/monitoring/store/mutations_spec.js +++ b/spec/frontend/monitoring/store/mutations_spec.js @@ -4,8 +4,8 @@ import mutations from '~/monitoring/stores/mutations'; import * as types from '~/monitoring/stores/mutation_types'; import state from '~/monitoring/stores/state'; import { dashboardEmptyStates, metricStates } from '~/monitoring/constants'; - import { deploymentData, dashboardGitResponse, storeTextVariables } from '../mock_data'; +import { prometheusMatrixMultiResult } from '../graph_data'; import { metricsDashboardPayload } from '../fixture_data'; describe('Monitoring mutations', () => { @@ -259,27 +259,6 @@ describe('Monitoring mutations', () => { describe('Individual panel/metric results', () => { const metricId = 'NO_DB_response_metrics_nginx_ingress_throughput_status_code'; - const data = { - resultType: 'matrix', - result: [ - { - metric: { - __name__: 'up', - job: 'prometheus', - instance: 'localhost:9090', - }, - values: [[1435781430.781, '1'], [1435781445.781, '1'], [1435781460.781, '1']], - }, - { - metric: { - __name__: 'up', - job: 'node', - instance: 'localhost:9091', - }, - values: [[1435781430.781, '0'], [1435781445.781, '0'], [1435781460.781, '1']], - }, - ], - }; const dashboard = metricsDashboardPayload; const getMetric = () => stateCopy.dashboard.panelGroups[1].panels[0].metrics[0]; @@ -307,6 +286,8 @@ describe('Monitoring mutations', () => { }); it('adds results to the store', () => { + const data = prometheusMatrixMultiResult(); + expect(getMetric().result).toBe(null); mutations[types.RECEIVE_METRIC_RESULT_SUCCESS](stateCopy, { @@ -488,4 +469,128 @@ describe('Monitoring mutations', () => { }); }); }); + + describe('REQUEST_PANEL_PREVIEW', () => { + it('saves yml content and resets other preview data', () => { + const mockYmlContent = 'mock yml content'; + mutations[types.REQUEST_PANEL_PREVIEW](stateCopy, mockYmlContent); + + expect(stateCopy.panelPreviewIsLoading).toBe(true); + expect(stateCopy.panelPreviewYml).toBe(mockYmlContent); + expect(stateCopy.panelPreviewGraphData).toBe(null); + expect(stateCopy.panelPreviewError).toBe(null); + }); + }); + + describe('RECEIVE_PANEL_PREVIEW_SUCCESS', () => { + it('saves graph data', () => { + mutations[types.RECEIVE_PANEL_PREVIEW_SUCCESS](stateCopy, { + title: 'My Title', + type: 'area-chart', + }); + + expect(stateCopy.panelPreviewIsLoading).toBe(false); + expect(stateCopy.panelPreviewGraphData).toMatchObject({ + title: 'My Title', + type: 'area-chart', + }); + expect(stateCopy.panelPreviewError).toBe(null); + }); + }); + + describe('RECEIVE_PANEL_PREVIEW_FAILURE', () => { + it('saves graph data', () => { + mutations[types.RECEIVE_PANEL_PREVIEW_FAILURE](stateCopy, 'Error!'); + + expect(stateCopy.panelPreviewIsLoading).toBe(false); + expect(stateCopy.panelPreviewGraphData).toBe(null); + expect(stateCopy.panelPreviewError).toBe('Error!'); + }); + }); + + describe('panel preview metric', () => { + const getPreviewMetricAt = i => stateCopy.panelPreviewGraphData.metrics[i]; + + beforeEach(() => { + stateCopy.panelPreviewGraphData = { + title: 'Preview panel title', + metrics: [ + { + query: 'query', + }, + ], + }; + }); + + describe('REQUEST_PANEL_PREVIEW_METRIC_RESULT', () => { + it('sets the metric to loading for the first time', () => { + mutations[types.REQUEST_PANEL_PREVIEW_METRIC_RESULT](stateCopy, { index: 0 }); + + expect(getPreviewMetricAt(0).loading).toBe(true); + expect(getPreviewMetricAt(0).state).toBe(metricStates.LOADING); + }); + + it('sets the metric to loading and keeps the result', () => { + getPreviewMetricAt(0).result = [[0, 1]]; + getPreviewMetricAt(0).state = metricStates.OK; + + mutations[types.REQUEST_PANEL_PREVIEW_METRIC_RESULT](stateCopy, { index: 0 }); + + expect(getPreviewMetricAt(0)).toMatchObject({ + loading: true, + result: [[0, 1]], + state: metricStates.OK, + }); + }); + }); + + describe('RECEIVE_PANEL_PREVIEW_METRIC_RESULT_SUCCESS', () => { + it('saves the result in the metric', () => { + const data = prometheusMatrixMultiResult(); + + mutations[types.RECEIVE_PANEL_PREVIEW_METRIC_RESULT_SUCCESS](stateCopy, { + index: 0, + data, + }); + + expect(getPreviewMetricAt(0)).toMatchObject({ + loading: false, + state: metricStates.OK, + result: expect.any(Array), + }); + expect(getPreviewMetricAt(0).result).toHaveLength(data.result.length); + }); + }); + + describe('RECEIVE_PANEL_PREVIEW_METRIC_RESULT_FAILURE', () => { + it('stores an error in the metric', () => { + mutations[types.RECEIVE_PANEL_PREVIEW_METRIC_RESULT_FAILURE](stateCopy, { + index: 0, + }); + + expect(getPreviewMetricAt(0).loading).toBe(false); + expect(getPreviewMetricAt(0).state).toBe(metricStates.UNKNOWN_ERROR); + expect(getPreviewMetricAt(0).result).toBe(null); + + expect(getPreviewMetricAt(0)).toMatchObject({ + loading: false, + result: null, + state: metricStates.UNKNOWN_ERROR, + }); + }); + + it('stores a timeout error in a metric', () => { + mutations[types.RECEIVE_PANEL_PREVIEW_METRIC_RESULT_FAILURE](stateCopy, { + index: 0, + error: { message: 'BACKOFF_TIMEOUT' }, + }); + + expect(getPreviewMetricAt(0)).toMatchObject({ + loading: false, + result: null, + state: metricStates.TIMEOUT, + }); + }); + }); + }); }); |