gitlab-org--gitlab-foss/spec/frontend/logs/stores/actions_spec.js

554 lines
16 KiB
JavaScript

import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import { deprecatedCreateFlash as flash } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import { TOKEN_TYPE_POD_NAME } from '~/logs/constants';
import {
setInitData,
showFilteredLogs,
showPodLogs,
fetchEnvironments,
fetchLogs,
fetchMoreLogsPrepend,
fetchManagedApps,
} from '~/logs/stores/actions';
import * as types from '~/logs/stores/mutation_types';
import logsPageState from '~/logs/stores/state';
import Tracking from '~/tracking';
import { defaultTimeRange } from '~/vue_shared/constants';
import {
mockPodName,
mockEnvironmentsEndpoint,
mockEnvironments,
mockPods,
mockLogsResult,
mockEnvName,
mockSearch,
mockLogsEndpoint,
mockResponse,
mockCursor,
mockNextCursor,
mockManagedApps,
mockManagedAppsEndpoint,
} from '../mock_data';
jest.mock('~/flash');
jest.mock('~/lib/utils/datetime_range');
jest.mock('~/logs/utils');
const mockDefaultRange = {
start: '2020-01-10T18:00:00.000Z',
end: '2020-01-10T19:00:00.000Z',
};
const mockFixedRange = {
start: '2020-01-09T18:06:20.000Z',
end: '2020-01-09T18:36:20.000Z',
};
const mockRollingRange = {
duration: 120,
};
const mockRollingRangeAsFixed = {
start: '2020-01-10T18:00:00.000Z',
end: '2020-01-10T17:58:00.000Z',
};
describe('Logs Store actions', () => {
let state;
let mock;
const latestGetParams = () => mock.history.get[mock.history.get.length - 1].params;
convertToFixedRange.mockImplementation((range) => {
if (range === defaultTimeRange) {
return { ...mockDefaultRange };
}
if (range === mockFixedRange) {
return { ...mockFixedRange };
}
if (range === mockRollingRange) {
return { ...mockRollingRangeAsFixed };
}
throw new Error('Invalid time range');
});
beforeEach(() => {
state = logsPageState();
});
afterEach(() => {
flash.mockClear();
});
describe('setInitData', () => {
it('should commit environment and pod name mutation', () =>
testAction(
setInitData,
{ timeRange: mockFixedRange, environmentName: mockEnvName, podName: mockPodName },
state,
[
{ type: types.SET_TIME_RANGE, payload: mockFixedRange },
{ type: types.SET_PROJECT_ENVIRONMENT, payload: mockEnvName },
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
],
));
});
describe('showFilteredLogs', () => {
it('empty search should filter with defaults', () =>
testAction(
showFilteredLogs,
undefined,
state,
[
{ type: types.SET_CURRENT_POD_NAME, payload: null },
{ type: types.SET_SEARCH, payload: '' },
],
[{ type: 'fetchLogs', payload: 'used_search_bar' }],
));
it('text search should filter with a search term', () =>
testAction(
showFilteredLogs,
[mockSearch],
state,
[
{ type: types.SET_CURRENT_POD_NAME, payload: null },
{ type: types.SET_SEARCH, payload: mockSearch },
],
[{ type: 'fetchLogs', payload: 'used_search_bar' }],
));
it('pod search should filter with a search term', () =>
testAction(
showFilteredLogs,
[{ type: TOKEN_TYPE_POD_NAME, value: { data: mockPodName, operator: '=' } }],
state,
[
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.SET_SEARCH, payload: '' },
],
[{ type: 'fetchLogs', payload: 'used_search_bar' }],
));
it('pod search should filter with a pod selection and a search term', () =>
testAction(
showFilteredLogs,
[{ type: TOKEN_TYPE_POD_NAME, value: { data: mockPodName, operator: '=' } }, mockSearch],
state,
[
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.SET_SEARCH, payload: mockSearch },
],
[{ type: 'fetchLogs', payload: 'used_search_bar' }],
));
it('pod search should filter with a pod selection and two search terms', () =>
testAction(
showFilteredLogs,
['term1', 'term2'],
state,
[
{ type: types.SET_CURRENT_POD_NAME, payload: null },
{ type: types.SET_SEARCH, payload: `term1 term2` },
],
[{ type: 'fetchLogs', payload: 'used_search_bar' }],
));
it('pod search should filter with a pod selection and a search terms before and after', () =>
testAction(
showFilteredLogs,
[
'term1',
{ type: TOKEN_TYPE_POD_NAME, value: { data: mockPodName, operator: '=' } },
'term2',
],
state,
[
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.SET_SEARCH, payload: `term1 term2` },
],
[{ type: 'fetchLogs', payload: 'used_search_bar' }],
));
});
describe('showPodLogs', () => {
it('should commit pod name', () =>
testAction(
showPodLogs,
mockPodName,
state,
[{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName }],
[{ type: 'fetchLogs', payload: 'pod_log_changed' }],
));
});
describe('fetchEnvironments', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
});
it('should commit RECEIVE_ENVIRONMENTS_DATA_SUCCESS mutation on correct data', () => {
mock.onGet(mockEnvironmentsEndpoint).replyOnce(200, { environments: mockEnvironments });
return testAction(
fetchEnvironments,
mockEnvironmentsEndpoint,
state,
[
{ type: types.REQUEST_ENVIRONMENTS_DATA },
{ type: types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS, payload: mockEnvironments },
],
[{ type: 'fetchLogs', payload: 'environment_selected' }],
);
});
it('should commit RECEIVE_ENVIRONMENTS_DATA_ERROR on wrong data', () => {
mock.onGet(mockEnvironmentsEndpoint).replyOnce(500);
return testAction(
fetchEnvironments,
mockEnvironmentsEndpoint,
state,
[
{ type: types.REQUEST_ENVIRONMENTS_DATA },
{ type: types.RECEIVE_ENVIRONMENTS_DATA_ERROR },
],
[],
);
});
});
describe('fetchManagedApps', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
});
it('should commit RECEIVE_MANAGED_APPS_DATA_SUCCESS mutation on succesful fetch', () => {
mock.onGet(mockManagedAppsEndpoint).replyOnce(200, { clusters: mockManagedApps });
return testAction(fetchManagedApps, mockManagedAppsEndpoint, state, [
{ type: types.RECEIVE_MANAGED_APPS_DATA_SUCCESS, payload: mockManagedApps },
]);
});
it('should commit RECEIVE_MANAGED_APPS_DATA_ERROR on wrong data', () => {
mock.onGet(mockManagedAppsEndpoint).replyOnce(500);
return testAction(
fetchManagedApps,
mockManagedAppsEndpoint,
state,
[{ type: types.RECEIVE_MANAGED_APPS_DATA_ERROR }],
[],
);
});
});
describe('when the backend responds succesfully', () => {
let expectedMutations;
let expectedActions;
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(mockLogsEndpoint).reply(200, mockResponse);
mock.onGet(mockLogsEndpoint).replyOnce(202); // mock reactive cache
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
});
afterEach(() => {
mock.reset();
});
describe('fetchLogs', () => {
beforeEach(() => {
expectedMutations = [
{ type: types.REQUEST_LOGS_DATA },
{
type: types.RECEIVE_LOGS_DATA_SUCCESS,
payload: { logs: mockLogsResult, cursor: mockNextCursor },
},
{ type: types.SET_CURRENT_POD_NAME, payload: mockPodName },
{ type: types.RECEIVE_PODS_DATA_SUCCESS, payload: mockPods },
];
expectedActions = [];
});
it('should commit logs and pod data when there is pod name defined', () => {
state.pods.current = mockPodName;
state.timeRange.current = mockFixedRange;
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toMatchObject({
pod_name: mockPodName,
});
});
});
it('should commit logs and pod data when there is pod name defined and a non-default date range', () => {
state.pods.current = mockPodName;
state.timeRange.current = mockFixedRange;
state.logs.cursor = mockCursor;
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toEqual({
pod_name: mockPodName,
start_time: mockFixedRange.start,
end_time: mockFixedRange.end,
cursor: mockCursor,
});
});
});
it('should commit logs and pod data when there is pod name and search and a faulty date range', () => {
state.pods.current = mockPodName;
state.search = mockSearch;
state.timeRange.current = 'INVALID_TIME_RANGE';
expectedMutations.splice(1, 0, {
type: types.SHOW_TIME_RANGE_INVALID_WARNING,
});
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toEqual({
pod_name: mockPodName,
search: mockSearch,
});
});
});
it('should commit logs and pod data when no pod name defined', () => {
state.timeRange.current = defaultTimeRange;
return testAction(fetchLogs, null, state, expectedMutations, expectedActions, () => {
expect(latestGetParams()).toEqual({
start_time: expect.any(String),
end_time: expect.any(String),
});
});
});
});
describe('fetchMoreLogsPrepend', () => {
beforeEach(() => {
expectedMutations = [
{ type: types.REQUEST_LOGS_DATA_PREPEND },
{
type: types.RECEIVE_LOGS_DATA_PREPEND_SUCCESS,
payload: { logs: mockLogsResult, cursor: mockNextCursor },
},
];
expectedActions = [];
});
it('should commit logs and pod data when there is pod name defined', () => {
state.pods.current = mockPodName;
state.timeRange.current = mockFixedRange;
expectedActions = [];
return testAction(
fetchMoreLogsPrepend,
null,
state,
expectedMutations,
expectedActions,
() => {
expect(latestGetParams()).toMatchObject({
pod_name: mockPodName,
});
},
);
});
it('should commit logs and pod data when there is pod name defined and a non-default date range', () => {
state.pods.current = mockPodName;
state.timeRange.current = mockFixedRange;
state.logs.cursor = mockCursor;
return testAction(
fetchMoreLogsPrepend,
null,
state,
expectedMutations,
expectedActions,
() => {
expect(latestGetParams()).toEqual({
pod_name: mockPodName,
start_time: mockFixedRange.start,
end_time: mockFixedRange.end,
cursor: mockCursor,
});
},
);
});
it('should commit logs and pod data when there is pod name and search and a faulty date range', () => {
state.pods.current = mockPodName;
state.search = mockSearch;
state.timeRange.current = 'INVALID_TIME_RANGE';
expectedMutations.splice(1, 0, {
type: types.SHOW_TIME_RANGE_INVALID_WARNING,
});
return testAction(
fetchMoreLogsPrepend,
null,
state,
expectedMutations,
expectedActions,
() => {
expect(latestGetParams()).toEqual({
pod_name: mockPodName,
search: mockSearch,
});
},
);
});
it('should commit logs and pod data when no pod name defined', () => {
state.timeRange.current = defaultTimeRange;
return testAction(
fetchMoreLogsPrepend,
null,
state,
expectedMutations,
expectedActions,
() => {
expect(latestGetParams()).toEqual({
start_time: expect.any(String),
end_time: expect.any(String),
});
},
);
});
it('should not commit logs or pod data when it has reached the end', () => {
state.logs.isComplete = true;
state.logs.cursor = null;
return testAction(
fetchMoreLogsPrepend,
null,
state,
[], // no mutations done
[], // no actions dispatched
() => {
expect(mock.history.get).toHaveLength(0);
},
);
});
});
});
describe('when the backend responds with an error', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(mockLogsEndpoint).reply(500);
});
afterEach(() => {
mock.reset();
});
it('fetchLogs should commit logs and pod errors', () => {
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
state.timeRange.current = defaultTimeRange;
return testAction(
fetchLogs,
null,
state,
[
{ type: types.REQUEST_LOGS_DATA },
{ type: types.RECEIVE_PODS_DATA_ERROR },
{ type: types.RECEIVE_LOGS_DATA_ERROR },
],
[],
() => {
expect(mock.history.get[0].url).toBe(mockLogsEndpoint);
},
);
});
it('fetchMoreLogsPrepend should commit logs and pod errors', () => {
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
state.timeRange.current = defaultTimeRange;
return testAction(
fetchMoreLogsPrepend,
null,
state,
[
{ type: types.REQUEST_LOGS_DATA_PREPEND },
{ type: types.RECEIVE_LOGS_DATA_PREPEND_ERROR },
],
[],
() => {
expect(mock.history.get[0].url).toBe(mockLogsEndpoint);
},
);
});
});
});
describe('Tracking user interaction', () => {
let commit;
let dispatch;
let state;
let mock;
beforeEach(() => {
jest.spyOn(Tracking, 'event');
commit = jest.fn();
dispatch = jest.fn();
state = logsPageState();
state.environments.options = mockEnvironments;
state.environments.current = mockEnvName;
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.reset();
});
describe('Logs with data', () => {
beforeEach(() => {
mock.onGet(mockLogsEndpoint).reply(200, mockResponse);
mock.onGet(mockLogsEndpoint).replyOnce(202); // mock reactive cache
});
it('tracks fetched logs with data', () => {
return fetchLogs({ state, commit, dispatch }, 'environment_selected').then(() => {
expect(Tracking.event).toHaveBeenCalledWith(document.body.dataset.page, 'logs_view', {
label: 'environment_selected',
property: 'count',
value: 1,
});
});
});
});
describe('Logs without data', () => {
beforeEach(() => {
mock.onGet(mockLogsEndpoint).reply(200, {
...mockResponse,
logs: [],
});
mock.onGet(mockLogsEndpoint).replyOnce(202); // mock reactive cache
});
it('does not track empty log responses', () => {
return fetchLogs({ state, commit, dispatch }).then(() => {
expect(Tracking.event).not.toHaveBeenCalled();
});
});
});
});