summaryrefslogtreecommitdiff
path: root/spec/frontend/vue_shared/security_reports
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-11-19 08:27:35 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-11-19 08:27:35 +0000
commit7e9c479f7de77702622631cff2628a9c8dcbc627 (patch)
treec8f718a08e110ad7e1894510980d2155a6549197 /spec/frontend/vue_shared/security_reports
parente852b0ae16db4052c1c567d9efa4facc81146e88 (diff)
downloadgitlab-ce-7e9c479f7de77702622631cff2628a9c8dcbc627.tar.gz
Add latest changes from gitlab-org/gitlab@13-6-stable-eev13.6.0-rc42
Diffstat (limited to 'spec/frontend/vue_shared/security_reports')
-rw-r--r--spec/frontend/vue_shared/security_reports/security_reports_app_spec.js52
-rw-r--r--spec/frontend/vue_shared/security_reports/store/modules/sast/actions_spec.js203
-rw-r--r--spec/frontend/vue_shared/security_reports/store/modules/sast/mutations_spec.js84
-rw-r--r--spec/frontend/vue_shared/security_reports/store/modules/secret_detection/actions_spec.js203
-rw-r--r--spec/frontend/vue_shared/security_reports/store/modules/secret_detection/mutations_spec.js84
5 files changed, 622 insertions, 4 deletions
diff --git a/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js b/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
index 31bdfc931ac..ab87d80b291 100644
--- a/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
+++ b/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
@@ -5,7 +5,7 @@ import SecurityReportsApp from '~/vue_shared/security_reports/security_reports_a
jest.mock('~/flash');
-describe('Grouped security reports app', () => {
+describe('Security reports app', () => {
let wrapper;
let mrTabsMock;
@@ -21,6 +21,8 @@ describe('Grouped security reports app', () => {
});
};
+ const anyParams = expect.any(Object);
+
const findPipelinesTabAnchor = () => wrapper.find('[data-testid="show-pipelines"]');
const findHelpLink = () => wrapper.find('[data-testid="help"]');
const setupMrTabsMock = () => {
@@ -43,10 +45,12 @@ describe('Grouped security reports app', () => {
window.mrTabs = { tabShown: jest.fn() };
setupMockJobArtifact(reportType);
createComponent();
+ return wrapper.vm.$nextTick();
});
it('calls the pipelineJobs API correctly', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledWith(props.projectId, props.pipelineId);
+ expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
+ expect(Api.pipelineJobs).toHaveBeenCalledWith(props.projectId, props.pipelineId, anyParams);
});
it('renders the expected message', () => {
@@ -75,10 +79,12 @@ describe('Grouped security reports app', () => {
beforeEach(() => {
setupMockJobArtifact('foo');
createComponent();
+ return wrapper.vm.$nextTick();
});
it('calls the pipelineJobs API correctly', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledWith(props.projectId, props.pipelineId);
+ expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
+ expect(Api.pipelineJobs).toHaveBeenCalledWith(props.projectId, props.pipelineId, anyParams);
});
it('renders nothing', () => {
@@ -86,6 +92,42 @@ describe('Grouped security reports app', () => {
});
});
+ describe('security artifacts on last page of multi-page response', () => {
+ const numPages = 3;
+
+ beforeEach(() => {
+ jest
+ .spyOn(Api, 'pipelineJobs')
+ .mockImplementation(async (projectId, pipelineId, { page }) => {
+ const requestedPage = parseInt(page, 10);
+ if (requestedPage < numPages) {
+ return {
+ // Some jobs with no relevant artifacts
+ data: [{}, {}],
+ headers: { 'x-next-page': String(requestedPage + 1) },
+ };
+ } else if (requestedPage === numPages) {
+ return {
+ data: [{ artifacts: [{ file_type: SecurityReportsApp.reportTypes[0] }] }],
+ };
+ }
+
+ throw new Error('Test failed due to request of non-existent jobs page');
+ });
+
+ createComponent();
+ return wrapper.vm.$nextTick();
+ });
+
+ it('fetches all pages', () => {
+ expect(Api.pipelineJobs).toHaveBeenCalledTimes(numPages);
+ });
+
+ it('renders the expected message', () => {
+ expect(wrapper.text()).toMatchInterpolatedText(SecurityReportsApp.i18n.scansHaveRun);
+ });
+ });
+
describe('given an error from the API', () => {
let error;
@@ -93,10 +135,12 @@ describe('Grouped security reports app', () => {
error = new Error('an error');
jest.spyOn(Api, 'pipelineJobs').mockRejectedValue(error);
createComponent();
+ return wrapper.vm.$nextTick();
});
it('calls the pipelineJobs API correctly', () => {
- expect(Api.pipelineJobs).toHaveBeenCalledWith(props.projectId, props.pipelineId);
+ expect(Api.pipelineJobs).toHaveBeenCalledTimes(1);
+ expect(Api.pipelineJobs).toHaveBeenCalledWith(props.projectId, props.pipelineId, anyParams);
});
it('renders nothing', () => {
diff --git a/spec/frontend/vue_shared/security_reports/store/modules/sast/actions_spec.js b/spec/frontend/vue_shared/security_reports/store/modules/sast/actions_spec.js
new file mode 100644
index 00000000000..a11f4e05913
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/store/modules/sast/actions_spec.js
@@ -0,0 +1,203 @@
+import MockAdapter from 'axios-mock-adapter';
+import testAction from 'helpers/vuex_action_helper';
+
+import createState from '~/vue_shared/security_reports/store/modules/sast/state';
+import * as types from '~/vue_shared/security_reports/store/modules/sast/mutation_types';
+import * as actions from '~/vue_shared/security_reports/store/modules/sast/actions';
+import axios from '~/lib/utils/axios_utils';
+
+const diffEndpoint = 'diff-endpoint.json';
+const blobPath = 'blob-path.json';
+const reports = {
+ base: 'base',
+ head: 'head',
+ enrichData: 'enrichData',
+ diff: 'diff',
+};
+const error = 'Something went wrong';
+const vulnerabilityFeedbackPath = 'vulnerability-feedback-path';
+const rootState = { vulnerabilityFeedbackPath, blobPath };
+
+let state;
+
+describe('sast report actions', () => {
+ beforeEach(() => {
+ state = createState();
+ });
+
+ describe('setDiffEndpoint', () => {
+ it(`should commit ${types.SET_DIFF_ENDPOINT} with the correct path`, done => {
+ testAction(
+ actions.setDiffEndpoint,
+ diffEndpoint,
+ state,
+ [
+ {
+ type: types.SET_DIFF_ENDPOINT,
+ payload: diffEndpoint,
+ },
+ ],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestDiff', () => {
+ it(`should commit ${types.REQUEST_DIFF}`, done => {
+ testAction(actions.requestDiff, {}, state, [{ type: types.REQUEST_DIFF }], [], done);
+ });
+ });
+
+ describe('receiveDiffSuccess', () => {
+ it(`should commit ${types.RECEIVE_DIFF_SUCCESS} with the correct response`, done => {
+ testAction(
+ actions.receiveDiffSuccess,
+ reports,
+ state,
+ [
+ {
+ type: types.RECEIVE_DIFF_SUCCESS,
+ payload: reports,
+ },
+ ],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveDiffError', () => {
+ it(`should commit ${types.RECEIVE_DIFF_ERROR} with the correct response`, done => {
+ testAction(
+ actions.receiveDiffError,
+ error,
+ state,
+ [
+ {
+ type: types.RECEIVE_DIFF_ERROR,
+ payload: error,
+ },
+ ],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchDiff', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ state.paths.diffEndpoint = diffEndpoint;
+ rootState.canReadVulnerabilityFeedback = true;
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('when diff and vulnerability feedback endpoints respond successfully', () => {
+ beforeEach(() => {
+ mock
+ .onGet(diffEndpoint)
+ .replyOnce(200, reports.diff)
+ .onGet(vulnerabilityFeedbackPath)
+ .replyOnce(200, reports.enrichData);
+ });
+
+ it('should dispatch the `receiveDiffSuccess` action', done => {
+ const { diff, enrichData } = reports;
+ testAction(
+ actions.fetchDiff,
+ {},
+ { ...rootState, ...state },
+ [],
+ [
+ { type: 'requestDiff' },
+ {
+ type: 'receiveDiffSuccess',
+ payload: {
+ diff,
+ enrichData,
+ },
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('when diff endpoint responds successfully and fetching vulnerability feedback is not authorized', () => {
+ beforeEach(() => {
+ rootState.canReadVulnerabilityFeedback = false;
+ mock.onGet(diffEndpoint).replyOnce(200, reports.diff);
+ });
+
+ it('should dispatch the `receiveDiffSuccess` action with empty enrich data', done => {
+ const { diff } = reports;
+ const enrichData = [];
+ testAction(
+ actions.fetchDiff,
+ {},
+ { ...rootState, ...state },
+ [],
+ [
+ { type: 'requestDiff' },
+ {
+ type: 'receiveDiffSuccess',
+ payload: {
+ diff,
+ enrichData,
+ },
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('when the vulnerability feedback endpoint fails', () => {
+ beforeEach(() => {
+ mock
+ .onGet(diffEndpoint)
+ .replyOnce(200, reports.diff)
+ .onGet(vulnerabilityFeedbackPath)
+ .replyOnce(404);
+ });
+
+ it('should dispatch the `receiveError` action', done => {
+ testAction(
+ actions.fetchDiff,
+ {},
+ { ...rootState, ...state },
+ [],
+ [{ type: 'requestDiff' }, { type: 'receiveDiffError' }],
+ done,
+ );
+ });
+ });
+
+ describe('when the diff endpoint fails', () => {
+ beforeEach(() => {
+ mock
+ .onGet(diffEndpoint)
+ .replyOnce(404)
+ .onGet(vulnerabilityFeedbackPath)
+ .replyOnce(200, reports.enrichData);
+ });
+
+ it('should dispatch the `receiveDiffError` action', done => {
+ testAction(
+ actions.fetchDiff,
+ {},
+ { ...rootState, ...state },
+ [],
+ [{ type: 'requestDiff' }, { type: 'receiveDiffError' }],
+ done,
+ );
+ });
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/security_reports/store/modules/sast/mutations_spec.js b/spec/frontend/vue_shared/security_reports/store/modules/sast/mutations_spec.js
new file mode 100644
index 00000000000..fd611f38a34
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/store/modules/sast/mutations_spec.js
@@ -0,0 +1,84 @@
+import * as types from '~/vue_shared/security_reports/store/modules/sast/mutation_types';
+import createState from '~/vue_shared/security_reports/store/modules/sast/state';
+import mutations from '~/vue_shared/security_reports/store/modules/sast/mutations';
+
+const createIssue = ({ ...config }) => ({ changed: false, ...config });
+
+describe('sast module mutations', () => {
+ const path = 'path';
+ let state;
+
+ beforeEach(() => {
+ state = createState();
+ });
+
+ describe(types.SET_DIFF_ENDPOINT, () => {
+ it('should set the SAST diff endpoint', () => {
+ mutations[types.SET_DIFF_ENDPOINT](state, path);
+
+ expect(state.paths.diffEndpoint).toBe(path);
+ });
+ });
+
+ describe(types.REQUEST_DIFF, () => {
+ it('should set the `isLoading` status to `true`', () => {
+ mutations[types.REQUEST_DIFF](state);
+
+ expect(state.isLoading).toBe(true);
+ });
+ });
+
+ describe(types.RECEIVE_DIFF_SUCCESS, () => {
+ beforeEach(() => {
+ const reports = {
+ diff: {
+ added: [
+ createIssue({ cve: 'CVE-1' }),
+ createIssue({ cve: 'CVE-2' }),
+ createIssue({ cve: 'CVE-3' }),
+ ],
+ fixed: [createIssue({ cve: 'CVE-4' }), createIssue({ cve: 'CVE-5' })],
+ existing: [createIssue({ cve: 'CVE-6' })],
+ base_report_out_of_date: true,
+ },
+ };
+ state.isLoading = true;
+ mutations[types.RECEIVE_DIFF_SUCCESS](state, reports);
+ });
+
+ it('should set the `isLoading` status to `false`', () => {
+ expect(state.isLoading).toBe(false);
+ });
+
+ it('should set the `baseReportOutofDate` status to `false`', () => {
+ expect(state.baseReportOutofDate).toBe(true);
+ });
+
+ it('should have the relevant `new` issues', () => {
+ expect(state.newIssues).toHaveLength(3);
+ });
+
+ it('should have the relevant `resolved` issues', () => {
+ expect(state.resolvedIssues).toHaveLength(2);
+ });
+
+ it('should have the relevant `all` issues', () => {
+ expect(state.allIssues).toHaveLength(1);
+ });
+ });
+
+ describe(types.RECEIVE_DIFF_ERROR, () => {
+ beforeEach(() => {
+ state.isLoading = true;
+ mutations[types.RECEIVE_DIFF_ERROR](state);
+ });
+
+ it('should set the `isLoading` status to `false`', () => {
+ expect(state.isLoading).toBe(false);
+ });
+
+ it('should set the `hasError` status to `true`', () => {
+ expect(state.hasError).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/security_reports/store/modules/secret_detection/actions_spec.js b/spec/frontend/vue_shared/security_reports/store/modules/secret_detection/actions_spec.js
new file mode 100644
index 00000000000..bbcdfb5cd99
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/store/modules/secret_detection/actions_spec.js
@@ -0,0 +1,203 @@
+import MockAdapter from 'axios-mock-adapter';
+import testAction from 'helpers/vuex_action_helper';
+
+import createState from '~/vue_shared/security_reports/store/modules/secret_detection/state';
+import * as types from '~/vue_shared/security_reports/store/modules/secret_detection/mutation_types';
+import * as actions from '~/vue_shared/security_reports/store/modules/secret_detection/actions';
+import axios from '~/lib/utils/axios_utils';
+
+const diffEndpoint = 'diff-endpoint.json';
+const blobPath = 'blob-path.json';
+const reports = {
+ base: 'base',
+ head: 'head',
+ enrichData: 'enrichData',
+ diff: 'diff',
+};
+const error = 'Something went wrong';
+const vulnerabilityFeedbackPath = 'vulnerability-feedback-path';
+const rootState = { vulnerabilityFeedbackPath, blobPath };
+
+let state;
+
+describe('secret detection report actions', () => {
+ beforeEach(() => {
+ state = createState();
+ });
+
+ describe('setDiffEndpoint', () => {
+ it(`should commit ${types.SET_DIFF_ENDPOINT} with the correct path`, done => {
+ testAction(
+ actions.setDiffEndpoint,
+ diffEndpoint,
+ state,
+ [
+ {
+ type: types.SET_DIFF_ENDPOINT,
+ payload: diffEndpoint,
+ },
+ ],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestDiff', () => {
+ it(`should commit ${types.REQUEST_DIFF}`, done => {
+ testAction(actions.requestDiff, {}, state, [{ type: types.REQUEST_DIFF }], [], done);
+ });
+ });
+
+ describe('receiveDiffSuccess', () => {
+ it(`should commit ${types.RECEIVE_DIFF_SUCCESS} with the correct response`, done => {
+ testAction(
+ actions.receiveDiffSuccess,
+ reports,
+ state,
+ [
+ {
+ type: types.RECEIVE_DIFF_SUCCESS,
+ payload: reports,
+ },
+ ],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveDiffError', () => {
+ it(`should commit ${types.RECEIVE_DIFF_ERROR} with the correct response`, done => {
+ testAction(
+ actions.receiveDiffError,
+ error,
+ state,
+ [
+ {
+ type: types.RECEIVE_DIFF_ERROR,
+ payload: error,
+ },
+ ],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchDiff', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ state.paths.diffEndpoint = diffEndpoint;
+ rootState.canReadVulnerabilityFeedback = true;
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('when diff and vulnerability feedback endpoints respond successfully', () => {
+ beforeEach(() => {
+ mock
+ .onGet(diffEndpoint)
+ .replyOnce(200, reports.diff)
+ .onGet(vulnerabilityFeedbackPath)
+ .replyOnce(200, reports.enrichData);
+ });
+
+ it('should dispatch the `receiveDiffSuccess` action', done => {
+ const { diff, enrichData } = reports;
+ testAction(
+ actions.fetchDiff,
+ {},
+ { ...rootState, ...state },
+ [],
+ [
+ { type: 'requestDiff' },
+ {
+ type: 'receiveDiffSuccess',
+ payload: {
+ diff,
+ enrichData,
+ },
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('when diff endpoint responds successfully and fetching vulnerability feedback is not authorized', () => {
+ beforeEach(() => {
+ rootState.canReadVulnerabilityFeedback = false;
+ mock.onGet(diffEndpoint).replyOnce(200, reports.diff);
+ });
+
+ it('should dispatch the `receiveDiffSuccess` action with empty enrich data', done => {
+ const { diff } = reports;
+ const enrichData = [];
+ testAction(
+ actions.fetchDiff,
+ {},
+ { ...rootState, ...state },
+ [],
+ [
+ { type: 'requestDiff' },
+ {
+ type: 'receiveDiffSuccess',
+ payload: {
+ diff,
+ enrichData,
+ },
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('when the vulnerability feedback endpoint fails', () => {
+ beforeEach(() => {
+ mock
+ .onGet(diffEndpoint)
+ .replyOnce(200, reports.diff)
+ .onGet(vulnerabilityFeedbackPath)
+ .replyOnce(404);
+ });
+
+ it('should dispatch the `receiveDiffError` action', done => {
+ testAction(
+ actions.fetchDiff,
+ {},
+ { ...rootState, ...state },
+ [],
+ [{ type: 'requestDiff' }, { type: 'receiveDiffError' }],
+ done,
+ );
+ });
+ });
+
+ describe('when the diff endpoint fails', () => {
+ beforeEach(() => {
+ mock
+ .onGet(diffEndpoint)
+ .replyOnce(404)
+ .onGet(vulnerabilityFeedbackPath)
+ .replyOnce(200, reports.enrichData);
+ });
+
+ it('should dispatch the `receiveDiffError` action', done => {
+ testAction(
+ actions.fetchDiff,
+ {},
+ { ...rootState, ...state },
+ [],
+ [{ type: 'requestDiff' }, { type: 'receiveDiffError' }],
+ done,
+ );
+ });
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/security_reports/store/modules/secret_detection/mutations_spec.js b/spec/frontend/vue_shared/security_reports/store/modules/secret_detection/mutations_spec.js
new file mode 100644
index 00000000000..13fcc0f47a3
--- /dev/null
+++ b/spec/frontend/vue_shared/security_reports/store/modules/secret_detection/mutations_spec.js
@@ -0,0 +1,84 @@
+import * as types from '~/vue_shared/security_reports/store/modules/secret_detection/mutation_types';
+import createState from '~/vue_shared/security_reports/store/modules/secret_detection/state';
+import mutations from '~/vue_shared/security_reports/store/modules/secret_detection/mutations';
+
+const createIssue = ({ ...config }) => ({ changed: false, ...config });
+
+describe('secret detection module mutations', () => {
+ const path = 'path';
+ let state;
+
+ beforeEach(() => {
+ state = createState();
+ });
+
+ describe(types.SET_DIFF_ENDPOINT, () => {
+ it('should set the secret detection diff endpoint', () => {
+ mutations[types.SET_DIFF_ENDPOINT](state, path);
+
+ expect(state.paths.diffEndpoint).toBe(path);
+ });
+ });
+
+ describe(types.REQUEST_DIFF, () => {
+ it('should set the `isLoading` status to `true`', () => {
+ mutations[types.REQUEST_DIFF](state);
+
+ expect(state.isLoading).toBe(true);
+ });
+ });
+
+ describe(types.RECEIVE_DIFF_SUCCESS, () => {
+ beforeEach(() => {
+ const reports = {
+ diff: {
+ added: [
+ createIssue({ cve: 'CVE-1' }),
+ createIssue({ cve: 'CVE-2' }),
+ createIssue({ cve: 'CVE-3' }),
+ ],
+ fixed: [createIssue({ cve: 'CVE-4' }), createIssue({ cve: 'CVE-5' })],
+ existing: [createIssue({ cve: 'CVE-6' })],
+ base_report_out_of_date: true,
+ },
+ };
+ state.isLoading = true;
+ mutations[types.RECEIVE_DIFF_SUCCESS](state, reports);
+ });
+
+ it('should set the `isLoading` status to `false`', () => {
+ expect(state.isLoading).toBe(false);
+ });
+
+ it('should set the `baseReportOutofDate` status to `true`', () => {
+ expect(state.baseReportOutofDate).toBe(true);
+ });
+
+ it('should have the relevant `new` issues', () => {
+ expect(state.newIssues).toHaveLength(3);
+ });
+
+ it('should have the relevant `resolved` issues', () => {
+ expect(state.resolvedIssues).toHaveLength(2);
+ });
+
+ it('should have the relevant `all` issues', () => {
+ expect(state.allIssues).toHaveLength(1);
+ });
+ });
+
+ describe(types.RECEIVE_DIFF_ERROR, () => {
+ beforeEach(() => {
+ state.isLoading = true;
+ mutations[types.RECEIVE_DIFF_ERROR](state);
+ });
+
+ it('should set the `isLoading` status to `false`', () => {
+ expect(state.isLoading).toBe(false);
+ });
+
+ it('should set the `hasError` status to `true`', () => {
+ expect(state.hasError).toBe(true);
+ });
+ });
+});