summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/environments/environment_item_spec.js4
-rw-r--r--spec/frontend/lib/utils/datetime_utility_spec.js14
-rw-r--r--spec/frontend/releases/components/app_index_spec.js109
-rw-r--r--spec/frontend/snippets/components/snippet_header_spec.js3
-rw-r--r--spec/lib/gitlab/http_spec.rb11
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb18
-rw-r--r--spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb160
-rw-r--r--spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb26
8 files changed, 263 insertions, 82 deletions
diff --git a/spec/frontend/environments/environment_item_spec.js b/spec/frontend/environments/environment_item_spec.js
index 5d374a162ab..1b429783821 100644
--- a/spec/frontend/environments/environment_item_spec.js
+++ b/spec/frontend/environments/environment_item_spec.js
@@ -3,7 +3,7 @@ import { format } from 'timeago.js';
import EnvironmentItem from '~/environments/components/environment_item.vue';
import PinComponent from '~/environments/components/environment_pin.vue';
import DeleteComponent from '~/environments/components/environment_delete.vue';
-
+import { differenceInMilliseconds } from '~/lib/utils/datetime_utility';
import { environment, folder, tableData } from './mock_data';
describe('Environment item', () => {
@@ -135,7 +135,7 @@ describe('Environment item', () => {
});
describe('in the past', () => {
- const pastDate = new Date(Date.now() - 100000);
+ const pastDate = new Date(differenceInMilliseconds(100000));
beforeEach(() => {
factory({
propsData: {
diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js
index 9eb5587e83c..5b1fdea058b 100644
--- a/spec/frontend/lib/utils/datetime_utility_spec.js
+++ b/spec/frontend/lib/utils/datetime_utility_spec.js
@@ -653,3 +653,17 @@ describe('differenceInSeconds', () => {
expect(datetimeUtility.differenceInSeconds(startDate, endDate)).toBe(expected);
});
});
+
+describe('differenceInMilliseconds', () => {
+ const startDateTime = new Date('2019-07-17T00:00:00.000Z');
+
+ it.each`
+ startDate | endDate | expected
+ ${startDateTime.getTime()} | ${new Date('2019-07-17T00:00:00.000Z')} | ${0}
+ ${startDateTime} | ${new Date('2019-07-17T12:00:00.000Z').getTime()} | ${43200000}
+ ${startDateTime} | ${new Date('2019-07-18T00:00:00.000Z').getTime()} | ${86400000}
+ ${new Date('2019-07-18T00:00:00.000Z')} | ${startDateTime.getTime()} | ${-86400000}
+ `('returns $expected for $endDate - $startDate', ({ startDate, endDate, expected }) => {
+ expect(datetimeUtility.differenceInMilliseconds(startDate, endDate)).toBe(expected);
+ });
+});
diff --git a/spec/frontend/releases/components/app_index_spec.js b/spec/frontend/releases/components/app_index_spec.js
index 8eafe07cb2f..0ffece36d90 100644
--- a/spec/frontend/releases/components/app_index_spec.js
+++ b/spec/frontend/releases/components/app_index_spec.js
@@ -1,12 +1,11 @@
import { range as rge } from 'lodash';
-import Vue from 'vue';
-import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
+import Vuex from 'vuex';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
-import app from '~/releases/components/app_index.vue';
+import ReleasesApp from '~/releases/components/app_index.vue';
import createStore from '~/releases/stores';
import listModule from '~/releases/stores/modules/list';
import api from '~/api';
-import { resetStore } from '../stores/modules/list/helpers';
import {
pageInfoHeadersWithoutPagination,
pageInfoHeadersWithPagination,
@@ -14,30 +13,37 @@ import {
releases,
} from '../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
describe('Releases App ', () => {
- const Component = Vue.extend(app);
- let store;
- let vm;
- let releasesPagination;
+ let wrapper;
+
+ const releasesPagination = rge(21).map(index => ({
+ ...convertObjectPropsToCamelCase(release, { deep: true }),
+ tagName: `${index}.00`,
+ }));
- const props = {
+ const defaultProps = {
projectId: 'gitlab-ce',
documentationPath: 'help/releases',
illustrationPath: 'illustration/path',
};
- beforeEach(() => {
- store = createStore({ modules: { list: listModule } });
- releasesPagination = rge(21).map(index => ({
- ...convertObjectPropsToCamelCase(release, { deep: true }),
- tagName: `${index}.00`,
- }));
- });
+ const createComponent = (propsData = defaultProps) => {
+ const store = createStore({ modules: { list: listModule } });
+
+ wrapper = shallowMount(ReleasesApp, {
+ store,
+ localVue,
+ propsData,
+ });
+ };
afterEach(() => {
- resetStore(store);
- vm.$destroy();
+ wrapper.destroy();
});
describe('while loading', () => {
@@ -47,16 +53,15 @@ describe('Releases App ', () => {
// Need to defer the return value here to the next stack,
// otherwise the loading state disappears before our test even starts.
.mockImplementation(() => waitForPromises().then(() => ({ data: [], headers: {} })));
- vm = mountComponentWithStore(Component, { props, store });
+
+ createComponent();
});
it('renders loading icon', () => {
- expect(vm.$el.querySelector('.js-loading')).not.toBeNull();
- expect(vm.$el.querySelector('.js-empty-state')).toBeNull();
- expect(vm.$el.querySelector('.js-success-state')).toBeNull();
- expect(vm.$el.querySelector('.gl-pagination')).toBeNull();
-
- return waitForPromises();
+ expect(wrapper.contains('.js-loading')).toBe(true);
+ expect(wrapper.contains('.js-empty-state')).toBe(false);
+ expect(wrapper.contains('.js-success-state')).toBe(false);
+ expect(wrapper.contains(TablePagination)).toBe(false);
});
});
@@ -65,14 +70,15 @@ describe('Releases App ', () => {
jest
.spyOn(api, 'releases')
.mockResolvedValue({ data: releases, headers: pageInfoHeadersWithoutPagination });
- vm = mountComponentWithStore(Component, { props, store });
+
+ createComponent();
});
it('renders success state', () => {
- expect(vm.$el.querySelector('.js-loading')).toBeNull();
- expect(vm.$el.querySelector('.js-empty-state')).toBeNull();
- expect(vm.$el.querySelector('.js-success-state')).not.toBeNull();
- expect(vm.$el.querySelector('.gl-pagination')).toBeNull();
+ expect(wrapper.contains('.js-loading')).toBe(false);
+ expect(wrapper.contains('.js-empty-state')).toBe(false);
+ expect(wrapper.contains('.js-success-state')).toBe(true);
+ expect(wrapper.contains(TablePagination)).toBe(true);
});
});
@@ -81,69 +87,60 @@ describe('Releases App ', () => {
jest
.spyOn(api, 'releases')
.mockResolvedValue({ data: releasesPagination, headers: pageInfoHeadersWithPagination });
- vm = mountComponentWithStore(Component, { props, store });
+
+ createComponent();
});
it('renders success state', () => {
- expect(vm.$el.querySelector('.js-loading')).toBeNull();
- expect(vm.$el.querySelector('.js-empty-state')).toBeNull();
- expect(vm.$el.querySelector('.js-success-state')).not.toBeNull();
- expect(vm.$el.querySelector('.gl-pagination')).not.toBeNull();
+ expect(wrapper.contains('.js-loading')).toBe(false);
+ expect(wrapper.contains('.js-empty-state')).toBe(false);
+ expect(wrapper.contains('.js-success-state')).toBe(true);
+ expect(wrapper.contains(TablePagination)).toBe(true);
});
});
describe('with empty request', () => {
beforeEach(() => {
jest.spyOn(api, 'releases').mockResolvedValue({ data: [], headers: {} });
- vm = mountComponentWithStore(Component, { props, store });
+
+ createComponent();
});
it('renders empty state', () => {
- expect(vm.$el.querySelector('.js-loading')).toBeNull();
- expect(vm.$el.querySelector('.js-empty-state')).not.toBeNull();
- expect(vm.$el.querySelector('.js-success-state')).toBeNull();
- expect(vm.$el.querySelector('.gl-pagination')).toBeNull();
+ expect(wrapper.contains('.js-loading')).toBe(false);
+ expect(wrapper.contains('.js-empty-state')).toBe(true);
+ expect(wrapper.contains('.js-success-state')).toBe(false);
});
});
describe('"New release" button', () => {
- const findNewReleaseButton = () => vm.$el.querySelector('.js-new-release-btn');
+ const findNewReleaseButton = () => wrapper.find('.js-new-release-btn');
beforeEach(() => {
jest.spyOn(api, 'releases').mockResolvedValue({ data: [], headers: {} });
});
- const factory = additionalProps => {
- vm = mountComponentWithStore(Component, {
- props: {
- ...props,
- ...additionalProps,
- },
- store,
- });
- };
-
describe('when the user is allowed to create a new Release', () => {
const newReleasePath = 'path/to/new/release';
beforeEach(() => {
- factory({ newReleasePath });
+ createComponent({ ...defaultProps, newReleasePath });
});
it('renders the "New release" button', () => {
- expect(findNewReleaseButton()).not.toBeNull();
+ expect(findNewReleaseButton().exists()).toBe(true);
});
it('renders the "New release" button with the correct href', () => {
- expect(findNewReleaseButton().getAttribute('href')).toBe(newReleasePath);
+ expect(findNewReleaseButton().attributes('href')).toBe(newReleasePath);
});
});
describe('when the user is not allowed to create a new Release', () => {
- beforeEach(() => factory());
+ beforeEach(() => createComponent());
it('does not render the "New release" button', () => {
- expect(findNewReleaseButton()).toBeNull();
+ expect(findNewReleaseButton().exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/snippets/components/snippet_header_spec.js b/spec/frontend/snippets/components/snippet_header_spec.js
index a997b337047..5836de1fdbe 100644
--- a/spec/frontend/snippets/components/snippet_header_spec.js
+++ b/spec/frontend/snippets/components/snippet_header_spec.js
@@ -5,6 +5,7 @@ import { Blob, BinaryBlob } from 'jest/blob/components/mock_data';
import waitForPromises from 'helpers/wait_for_promises';
import DeleteSnippetMutation from '~/snippets/mutations/deleteSnippet.mutation.graphql';
import SnippetHeader from '~/snippets/components/snippet_header.vue';
+import { differenceInMilliseconds } from '~/lib/utils/datetime_utility';
describe('Snippet header component', () => {
let wrapper;
@@ -67,7 +68,7 @@ describe('Snippet header component', () => {
name: 'Thor Odinson',
},
blobs: [Blob],
- createdAt: new Date(Date.now() - 32 * 24 * 3600 * 1000).toISOString(),
+ createdAt: new Date(differenceInMilliseconds(32 * 24 * 3600 * 1000)).toISOString(),
};
mutationVariables = {
diff --git a/spec/lib/gitlab/http_spec.rb b/spec/lib/gitlab/http_spec.rb
index 5c990eb3248..308f7f46251 100644
--- a/spec/lib/gitlab/http_spec.rb
+++ b/spec/lib/gitlab/http_spec.rb
@@ -157,17 +157,6 @@ RSpec.describe Gitlab::HTTP do
described_class.put('http://example.org', write_timeout: 1)
end
end
-
- context 'when default timeouts feature is disabled' do
- it 'does not apply any defaults' do
- stub_feature_flags(http_default_timeouts: false)
- expect(described_class).to receive(:httparty_perform_request).with(
- Net::HTTP::Get, 'http://example.org', open_timeout: 1
- ).and_call_original
-
- described_class.get('http://example.org', open_timeout: 1)
- end
- end
end
describe '.try_get' do
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 18eaafd9f89..52a8548dea2 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -194,13 +194,16 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
)
end
- it 'includes project imports usage data' do
+ it 'includes imports usage data' do
for_defined_days_back do
user = create(:user)
- %w(gitlab_project gitlab github bitbucket bitbucket_server gitea git manifest).each do |type|
+ %w(gitlab_project gitlab github bitbucket bitbucket_server gitea git manifest fogbugz phabricator).each do |type|
create(:project, import_type: type, creator_id: user.id)
end
+
+ jira_project = create(:project, creator_id: user.id)
+ create(:jira_import_state, :finished, project: jira_project)
end
expect(described_class.usage_activity_by_stage_manage({})).to include(
@@ -214,6 +217,11 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
gitea: 2,
git: 2,
manifest: 2
+ },
+ issues_imported: {
+ jira: 2,
+ fogbugz: 2,
+ phabricator: 2
}
}
)
@@ -228,6 +236,11 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
gitea: 1,
git: 1,
manifest: 1
+ },
+ issues_imported: {
+ jira: 1,
+ fogbugz: 1,
+ phabricator: 1
}
}
)
@@ -1054,6 +1067,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
'g_compliance_audit_events' => 123,
'i_compliance_credential_inventory' => 123,
'i_compliance_audit_events' => 123,
+ 'i_compliance_audit_events_api' => 123,
'compliance_unique_visits_for_any_target' => 543,
'compliance_unique_visits_for_any_target_monthly' => 987
}
diff --git a/spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb b/spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb
new file mode 100644
index 00000000000..a821e4a43df
--- /dev/null
+++ b/spec/migrations/20200811130433_create_missing_vulnerabilities_issue_links_spec.rb
@@ -0,0 +1,160 @@
+# frozen_string_literal: true
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20200811130433_create_missing_vulnerabilities_issue_links.rb')
+
+RSpec.describe CreateMissingVulnerabilitiesIssueLinks, :migration do
+ let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
+ let(:users) { table(:users) }
+ let(:user) { create_user! }
+ let(:project) { table(:projects).create!(id: 123, namespace_id: namespace.id) }
+ let(:scanners) { table(:vulnerability_scanners) }
+ let(:scanner) { scanners.create!(project_id: project.id, external_id: 'test 1', name: 'test scanner 1') }
+ let(:different_scanner) { scanners.create!(project_id: project.id, external_id: 'test 2', name: 'test scanner 2') }
+ let(:issues) { table(:issues) }
+ let(:issue1) { issues.create!(id: 123, project_id: project.id) }
+ let(:issue2) { issues.create!(id: 124, project_id: project.id) }
+ let(:issue3) { issues.create!(id: 125, project_id: project.id) }
+ let(:vulnerabilities) { table(:vulnerabilities) }
+ let(:vulnerabilities_findings) { table(:vulnerability_occurrences) }
+ let(:vulnerability_feedback) { table(:vulnerability_feedback) }
+ let(:vulnerability_issue_links) { table(:vulnerability_issue_links) }
+ let(:vulnerability_identifiers) { table(:vulnerability_identifiers) }
+ let(:vulnerability_identifier) { vulnerability_identifiers.create!(project_id: project.id, external_type: 'test 1', external_id: 'test 1', fingerprint: 'test 1', name: 'test 1') }
+ let(:different_vulnerability_identifier) { vulnerability_identifiers.create!(project_id: project.id, external_type: 'test 2', external_id: 'test 2', fingerprint: 'test 2', name: 'test 2') }
+
+ let!(:vulnerability) do
+ create_vulnerability!(
+ project_id: project.id,
+ author_id: user.id
+ )
+ end
+
+ before do
+ create_finding!(
+ vulnerability_id: vulnerability.id,
+ project_id: project.id,
+ scanner_id: scanner.id,
+ primary_identifier_id: vulnerability_identifier.id
+ )
+ create_feedback!(
+ issue_id: issue1.id,
+ project_id: project.id,
+ author_id: user.id
+ )
+
+ # Create a finding with no vulnerability_id
+ # https://gitlab.com/gitlab-com/gl-infra/production/-/issues/2539
+ create_finding!(
+ vulnerability_id: nil,
+ project_id: project.id,
+ scanner_id: different_scanner.id,
+ primary_identifier_id: different_vulnerability_identifier.id,
+ location_fingerprint: 'somewhereinspace',
+ uuid: 'test2'
+ )
+ create_feedback!(
+ category: 2,
+ issue_id: issue2.id,
+ project_id: project.id,
+ author_id: user.id
+ )
+ end
+
+ context 'with no Vulnerabilities::IssueLinks present' do
+ it 'creates missing Vulnerabilities::IssueLinks' do
+ expect(vulnerability_issue_links.count).to eq(0)
+
+ migrate!
+
+ expect(vulnerability_issue_links.count).to eq(1)
+ end
+ end
+
+ context 'when an Vulnerabilities::IssueLink already exists' do
+ before do
+ vulnerability_issue_links.create!(vulnerability_id: vulnerability.id, issue_id: issue1.id)
+ end
+
+ it 'creates no duplicates' do
+ expect(vulnerability_issue_links.count).to eq(1)
+
+ migrate!
+
+ expect(vulnerability_issue_links.count).to eq(1)
+ end
+ end
+
+ context 'when an Vulnerabilities::IssueLink of type created already exists' do
+ before do
+ vulnerability_issue_links.create!(vulnerability_id: vulnerability.id, issue_id: issue3.id, link_type: 2)
+ end
+
+ it 'creates no duplicates' do
+ expect(vulnerability_issue_links.count).to eq(1)
+
+ migrate!
+
+ expect(vulnerability_issue_links.count).to eq(1)
+ end
+ end
+
+ private
+
+ def create_vulnerability!(project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0)
+ vulnerabilities.create!(
+ project_id: project_id,
+ author_id: author_id,
+ title: title,
+ severity: severity,
+ confidence: confidence,
+ report_type: report_type
+ )
+ end
+
+ # rubocop:disable Metrics/ParameterLists
+ def create_finding!(
+ vulnerability_id:, project_id:, scanner_id:, primary_identifier_id:,
+ name: "test", severity: 7, confidence: 7, report_type: 0,
+ project_fingerprint: '123qweasdzxc', location_fingerprint: 'test',
+ metadata_version: 'test', raw_metadata: 'test', uuid: 'test')
+ vulnerabilities_findings.create!(
+ vulnerability_id: vulnerability_id,
+ project_id: project_id,
+ name: name,
+ severity: severity,
+ confidence: confidence,
+ report_type: report_type,
+ project_fingerprint: project_fingerprint,
+ scanner_id: scanner.id,
+ primary_identifier_id: vulnerability_identifier.id,
+ location_fingerprint: location_fingerprint,
+ metadata_version: metadata_version,
+ raw_metadata: raw_metadata,
+ uuid: uuid
+ )
+ end
+ # rubocop:enable Metrics/ParameterLists
+
+ # project_fingerprint on Vulnerabilities::Finding is a bytea and we need to match this
+ def create_feedback!(issue_id:, project_id:, author_id:, feedback_type: 1, category: 0, project_fingerprint: '3132337177656173647a7863')
+ vulnerability_feedback.create!(
+ feedback_type: feedback_type,
+ issue_id: issue_id,
+ category: category,
+ project_fingerprint: project_fingerprint,
+ project_id: project_id,
+ author_id: author_id
+ )
+ end
+
+ def create_user!(name: "Example User", email: "user@example.com", user_type: nil, created_at: Time.now, confirmed_at: Time.now)
+ users.create!(
+ name: name,
+ email: email,
+ username: name,
+ projects_limit: 0,
+ user_type: user_type,
+ confirmed_at: confirmed_at
+ )
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb b/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb
index 5a4dc3ce187..a20ac823550 100644
--- a/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb
+++ b/spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb
@@ -7,9 +7,14 @@ RSpec.describe 'PipelineCancel' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
- let_it_be(:pipeline) { create(:ci_pipeline, :running, project: project, user: user) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project, user: user) }
- let(:mutation) { graphql_mutation(:pipeline_cancel, {}, 'errors') }
+ let(:mutation) do
+ variables = {
+ id: pipeline.to_global_id.to_s
+ }
+ graphql_mutation(:pipeline_cancel, variables, 'errors')
+ end
let(:mutation_response) { graphql_mutation_response(:pipeline_cancel) }
@@ -17,24 +22,25 @@ RSpec.describe 'PipelineCancel' do
project.add_maintainer(user)
end
- it 'reports the service-level error' do
- service = double(execute: ServiceResponse.error(message: 'Error canceling pipeline'))
- allow(::Ci::CancelUserPipelinesService).to receive(:new).and_return(service)
+ it 'does not cancel any pipelines not owned by the current user' do
+ build = create(:ci_build, :running, pipeline: pipeline)
post_graphql_mutation(mutation, current_user: create(:user))
- expect(mutation_response).to include('errors' => ['Error canceling pipeline'])
+ expect(graphql_errors).not_to be_empty
+ expect(build).not_to be_canceled
end
- it 'does not change any pipelines not owned by the current user' do
- build = create(:ci_build, :running, pipeline: pipeline)
+ it 'returns a error if the pipline cannot be be canceled' do
+ build = create(:ci_build, :success, pipeline: pipeline)
- post_graphql_mutation(mutation, current_user: create(:user))
+ post_graphql_mutation(mutation, current_user: user)
+ expect(mutation_response).to include('errors' => include(eq 'Pipeline is not cancelable'))
expect(build).not_to be_canceled
end
- it "cancels all of the current user's cancelable pipelines" do
+ it "cancels all cancelable builds from a pipeline" do
build = create(:ci_build, :running, pipeline: pipeline)
post_graphql_mutation(mutation, current_user: user)