555 lines
16 KiB
JavaScript
555 lines
16 KiB
JavaScript
import MockAdapter from 'axios-mock-adapter';
|
|
import testAction from 'helpers/vuex_action_helper';
|
|
import Tracking from '~/tracking';
|
|
import * as types from '~/logs/stores/mutation_types';
|
|
import { convertToFixedRange } from '~/lib/utils/datetime_range';
|
|
import logsPageState from '~/logs/stores/state';
|
|
import {
|
|
setInitData,
|
|
showFilteredLogs,
|
|
showPodLogs,
|
|
fetchEnvironments,
|
|
fetchLogs,
|
|
fetchMoreLogsPrepend,
|
|
fetchManagedApps,
|
|
} from '~/logs/stores/actions';
|
|
|
|
import { defaultTimeRange } from '~/vue_shared/constants';
|
|
|
|
import axios from '~/lib/utils/axios_utils';
|
|
import { deprecatedCreateFlash as flash } from '~/flash';
|
|
|
|
import {
|
|
mockPodName,
|
|
mockEnvironmentsEndpoint,
|
|
mockEnvironments,
|
|
mockPods,
|
|
mockLogsResult,
|
|
mockEnvName,
|
|
mockSearch,
|
|
mockLogsEndpoint,
|
|
mockResponse,
|
|
mockCursor,
|
|
mockNextCursor,
|
|
mockManagedApps,
|
|
mockManagedAppsEndpoint,
|
|
} from '../mock_data';
|
|
import { TOKEN_TYPE_POD_NAME } from '~/logs/constants';
|
|
|
|
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();
|
|
});
|
|
});
|
|
});
|
|
});
|