diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-09 15:09:24 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-09 15:09:24 +0300 |
commit | a9ced7da447785c57477b3d8dbccc73a78cface1 (patch) | |
tree | 5179d27ab9d801748ee4ed1c64c985974e799812 /spec | |
parent | ad0265eead72a624ce7a020847db4f0f0c877e57 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
29 files changed, 741 insertions, 473 deletions
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index 7bd0f6bd6b8..93478bbff1d 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -764,6 +764,136 @@ describe GroupsController do end end + describe 'POST #export' do + context 'when the group export feature flag is not enabled' do + before do + sign_in(admin) + stub_feature_flags(group_import_export: false) + end + + it 'returns a not found error' do + post :export, params: { id: group.to_param } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when the user does not have permission to export the group' do + before do + sign_in(guest) + end + + it 'returns an error' do + post :export, params: { id: group.to_param } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when supplied valid params' do + before do + sign_in(admin) + end + + it 'triggers the export job' do + expect(GroupExportWorker).to receive(:perform_async).with(admin.id, group.id, {}) + + post :export, params: { id: group.to_param } + end + + it 'redirects to the edit page' do + post :export, params: { id: group.to_param } + + expect(response).to have_gitlab_http_status(:found) + end + end + + context 'when the endpoint receives requests above the rate limit' do + before do + sign_in(admin) + allow(Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true) + end + + it 'throttles the endpoint' do + post :export, params: { id: group.to_param } + + expect(flash[:alert]).to eq('This endpoint has been requested too many times. Try again later.') + expect(response).to have_gitlab_http_status(:found) + end + end + end + + describe 'GET #download_export' do + context 'when there is a file available to download' do + let(:export_file) { fixture_file_upload('spec/fixtures/group_export.tar.gz') } + + before do + sign_in(admin) + create(:import_export_upload, group: group, export_file: export_file) + end + + it 'sends the file' do + get :download_export, params: { id: group.to_param } + + expect(response.body).to eq export_file.tempfile.read + end + end + + context 'when there is no file available to download' do + before do + sign_in(admin) + end + + it 'returns not found' do + get :download_export, params: { id: group.to_param } + + expect(flash[:alert]) + .to eq 'Group export link has expired. Please generate a new export from your group settings.' + + expect(response).to redirect_to(edit_group_path(group)) + end + end + + context 'when the group export feature flag is not enabled' do + before do + sign_in(admin) + stub_feature_flags(group_import_export: false) + end + + it 'returns a not found error' do + post :export, params: { id: group.to_param } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when the user does not have the required permissions' do + before do + sign_in(guest) + end + + it 'returns not_found' do + get :download_export, params: { id: group.to_param } + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when the endpoint receives requests above the rate limit' do + before do + sign_in(admin) + allow(Gitlab::ApplicationRateLimiter).to receive(:throttled?).and_return(true) + end + + it 'throttles the endpoint' do + get :download_export, params: { id: group.to_param } + + expect(flash[:alert]).to eq('This endpoint has been requested too many times. Try again later.') + expect(response).to have_gitlab_http_status(:found) + end + end + end + context 'token authentication' do it_behaves_like 'authenticates sessionless user', :show, :atom, public: true do before do diff --git a/spec/controllers/projects/import/jira_controller_spec.rb b/spec/controllers/projects/import/jira_controller_spec.rb index 57e0aa098c0..8e0d506e5e4 100644 --- a/spec/controllers/projects/import/jira_controller_spec.rb +++ b/spec/controllers/projects/import/jira_controller_spec.rb @@ -105,16 +105,16 @@ describe Projects::Import::JiraController do context 'when everything is ok' do it 'creates import state' do - expect(project.import_state).to be_nil + expect(project.latest_jira_import).to be_nil post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'Test' } project.reload - jira_project = project.import_data.data.dig('jira', 'projects').first + jira_import = project.latest_jira_import expect(project.import_type).to eq 'jira' - expect(project.import_state.status).to eq 'scheduled' - expect(jira_project['key']).to eq 'Test' + expect(jira_import.status).to eq 'scheduled' + expect(jira_import.jira_project_key).to eq 'Test' expect(response).to redirect_to(project_import_jira_path(project)) end end @@ -122,29 +122,19 @@ describe Projects::Import::JiraController do end context 'when import state is scheduled' do - let_it_be(:import_state) { create(:import_state, project: project, status: :scheduled) } + let_it_be(:jira_import_state) { create(:jira_import_state, :scheduled, project: project) } context 'get show' do it 'renders import status' do get :show, params: { namespace_id: project.namespace.to_param, project_id: project } - expect(project.import_state.status).to eq 'scheduled' + jira_import = project.latest_jira_import + expect(jira_import.status).to eq 'scheduled' expect(flash.now[:notice]).to eq 'Import scheduled' end end context 'post import' do - before do - project.reload - project.create_import_data( - data: { - 'jira': { - 'projects': [{ 'key': 'Test', scheduled_at: 5.days.ago, scheduled_by: { user_id: user.id, name: user.name } }] - } - } - ) - end - it 'uses the existing import data' do post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'New Project' } @@ -155,39 +145,27 @@ describe Projects::Import::JiraController do end context 'when jira import ran before' do - let_it_be(:import_state) { create(:import_state, project: project, status: :finished) } + let_it_be(:jira_import_state) { create(:jira_import_state, :finished, project: project, jira_project_key: 'Test') } context 'get show' do it 'renders import status' do allow(JIRA::Resource::Project).to receive(:all).and_return([]) get :show, params: { namespace_id: project.namespace.to_param, project_id: project } - expect(project.import_state.status).to eq 'finished' + expect(project.latest_jira_import.status).to eq 'finished' expect(flash.now[:notice]).to eq 'Import finished' end end context 'post import' do - before do - project.reload - project.create_import_data( - data: { - 'jira': { - 'projects': [{ 'key': 'Test', scheduled_at: 5.days.ago, scheduled_by: { user_id: user.id, name: user.name } }] - } - } - ) - end - it 'uses the existing import data' do post :import, params: { namespace_id: project.namespace, project_id: project, jira_project_key: 'New Project' } project.reload - expect(project.import_state.status).to eq 'scheduled' - jira_imported_projects = project.import_data.data.dig('jira', 'projects') - expect(jira_imported_projects.size).to eq 2 - expect(jira_imported_projects.first['key']).to eq 'Test' - expect(jira_imported_projects.last['key']).to eq 'New Project' + expect(project.latest_jira_import.status).to eq 'scheduled' + expect(project.jira_imports.size).to eq 2 + expect(project.jira_imports.first.jira_project_key).to eq 'Test' + expect(project.jira_imports.last.jira_project_key).to eq 'New Project' expect(response).to redirect_to(project_import_jira_path(project)) end end diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index cf74b2cc8ce..2cd9cbc4471 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -29,6 +29,10 @@ describe 'Dashboard shortcuts', :js do find('body').send_keys([:shift, 'P']) check_page_title('Projects') + + find('body').send_keys([:shift, 'A']) + + check_page_title('Activity') end end diff --git a/spec/frontend/boards/board_card_spec.js b/spec/frontend/boards/board_card_spec.js index 2524af21826..959c71d05ca 100644 --- a/spec/frontend/boards/board_card_spec.js +++ b/spec/frontend/boards/board_card_spec.js @@ -9,6 +9,7 @@ import axios from '~/lib/utils/axios_utils'; import waitForPromises from 'helpers/wait_for_promises'; import eventHub from '~/boards/eventhub'; +import sidebarEventHub from '~/sidebar/event_hub'; import '~/boards/models/label'; import '~/boards/models/assignee'; import '~/boards/models/list'; @@ -201,7 +202,8 @@ describe('Board card', () => { it('resets detail issue to empty if already set', () => { jest.spyOn(eventHub, '$emit').mockImplementation(() => {}); - [boardsStore.detail.issue] = list.issues; + const [issue] = list.issues; + boardsStore.detail.issue = issue; mountComponent(); wrapper.trigger('mousedown'); @@ -210,4 +212,27 @@ describe('Board card', () => { expect(eventHub.$emit).toHaveBeenCalledWith('clearDetailIssue', undefined); }); }); + + describe('sidebarHub events', () => { + it('closes all sidebars before showing an issue if no issues are opened', () => { + jest.spyOn(sidebarEventHub, '$emit').mockImplementation(() => {}); + boardsStore.detail.issue = {}; + mountComponent(); + + wrapper.trigger('mouseup'); + + expect(sidebarEventHub.$emit).toHaveBeenCalledWith('sidebar.closeAll'); + }); + + it('it does not closes all sidebars before showing an issue if an issue is opened', () => { + jest.spyOn(sidebarEventHub, '$emit').mockImplementation(() => {}); + const [issue] = list.issues; + boardsStore.detail.issue = issue; + mountComponent(); + + wrapper.trigger('mousedown'); + + expect(sidebarEventHub.$emit).not.toHaveBeenCalledWith('sidebar.closeAll'); + }); + }); }); diff --git a/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb b/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb index a5f17acce91..4d060d213ed 100644 --- a/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb +++ b/spec/graphql/resolvers/projects/jira_imports_resolver_spec.rb @@ -7,29 +7,16 @@ describe Resolvers::Projects::JiraImportsResolver do describe '#resolve' do let_it_be(:user) { create(:user) } - let_it_be(:jira_import_data) do - data = JiraImportData.new - data << JiraImportData::JiraProjectDetails.new('AA', 2.days.ago.strftime('%Y-%m-%d %H:%M:%S'), { user_id: user.id, name: user.name }) - data << JiraImportData::JiraProjectDetails.new('BB', 5.days.ago.strftime('%Y-%m-%d %H:%M:%S'), { user_id: user.id, name: user.name }) - data - end - - context 'when feature flag disabled' do - let_it_be(:project) { create(:project, :private, import_data: jira_import_data) } - - before do - stub_feature_flags(jira_issue_import: false) - end + let_it_be(:project, reload: true) { create(:project, :public) } - it_behaves_like 'no jira import access' - end - - context 'when project does not have Jira import data' do - let_it_be(:project) { create(:project, :private, import_data: nil) } + context 'when project does not have Jira imports' do + let(:current_user) { user } - context 'when user cannot read Jira import data' do + context 'when user cannot read Jira imports' do context 'when anonymous user' do - it_behaves_like 'no jira import data present' + let(:current_user) { nil } + + it_behaves_like 'no jira import access' end context 'when user developer' do @@ -37,7 +24,7 @@ describe Resolvers::Projects::JiraImportsResolver do project.add_developer(user) end - it_behaves_like 'no jira import data present' + it_behaves_like 'no jira import access' end end @@ -50,11 +37,25 @@ describe Resolvers::Projects::JiraImportsResolver do end end - context 'when project has Jira import data' do - let_it_be(:project) { create(:project, :private, import_data: jira_import_data) } + context 'when project has Jira imports' do + let_it_be(:current_user) { user } + let_it_be(:jira_import1) { create(:jira_import_state, :finished, project: project, jira_project_key: 'AA', created_at: 2.days.ago) } + let_it_be(:jira_import2) { create(:jira_import_state, :finished, project: project, jira_project_key: 'BB', created_at: 5.days.ago) } + + context 'when feature flag disabled' do + let(:current_user) { user } + + before do + stub_feature_flags(jira_issue_import: false) + end + + it_behaves_like 'no jira import access' + end - context 'when user cannot read Jira import data' do + context 'when user cannot read Jira imports' do context 'when anonymous user' do + let(:current_user) { nil } + it_behaves_like 'no jira import access' end @@ -67,22 +68,22 @@ describe Resolvers::Projects::JiraImportsResolver do end end - context 'when user can access Jira import data' do + context 'when user can access Jira imports' do before do project.add_maintainer(user) end - it 'returns Jira imports sorted ascending by scheduledAt time' do + it 'returns Jira imports sorted ascending by created_at time' do imports = resolve_imports expect(imports.size).to eq 2 - expect(imports.map(&:key)).to eq %w(BB AA) + expect(imports.map(&:jira_project_key)).to eq %w(BB AA) end end end end - def resolve_imports(args = {}, context = { current_user: user }) + def resolve_imports(args = {}, context = { current_user: current_user }) resolve(described_class, obj: project, args: args, ctx: context) end end diff --git a/spec/lib/gitlab/background_migration/populate_canonical_emails_spec.rb b/spec/lib/gitlab/background_migration/populate_canonical_emails_spec.rb new file mode 100644 index 00000000000..37ddb8b569d --- /dev/null +++ b/spec/lib/gitlab/background_migration/populate_canonical_emails_spec.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::BackgroundMigration::PopulateCanonicalEmails, :migration, schema: 20200312053852 do + let(:migration) { described_class.new } + + let_it_be(:users_table) { table(:users) } + let_it_be(:user_canonical_emails_table) { table(:user_canonical_emails) } + + let_it_be(:users) { users_table.all } + let_it_be(:user_canonical_emails) { user_canonical_emails_table.all } + + subject { migration.perform(1, 1) } + + describe 'gmail users' do + using RSpec::Parameterized::TableSyntax + + where(:original_email, :expected_result) do + 'legitimateuser@gmail.com' | 'legitimateuser@gmail.com' + 'userwithplus+somestuff@gmail.com' | 'userwithplus@gmail.com' + 'user.with.periods@gmail.com' | 'userwithperiods@gmail.com' + 'user.with.periods.and.plus+someotherstuff@gmail.com' | 'userwithperiodsandplus@gmail.com' + end + + with_them do + it 'generates the correct canonical email' do + create_user(email: original_email, id: 1) + + subject + + result = canonical_emails + expect(result.count).to eq 1 + expect(result.first).to match({ + 'user_id' => 1, + 'canonical_email' => expected_result + }) + end + end + end + + describe 'non gmail.com domain users' do + %w[ + legitimateuser@somedomain.com + userwithplus+somestuff@other.com + user.with.periods@gmail.org + user.with.periods.and.plus+someotherstuff@orangmail.com + ].each do |non_gmail_address| + it 'does not generate a canonical email' do + create_user(email: non_gmail_address, id: 1) + + subject + + expect(canonical_emails(user_id: 1).count).to eq 0 + end + end + end + + describe 'gracefully handles missing records' do + specify { expect { subject }.not_to raise_error } + end + + describe 'gracefully handles existing records, some of which may have an already-existing identical canonical_email field' do + let_it_be(:user_one) { create_user(email: "example.user@gmail.com", id: 1) } + let_it_be(:user_two) { create_user(email: "exampleuser@gmail.com", id: 2) } + let_it_be(:user_email_one) { user_canonical_emails.create(canonical_email: "exampleuser@gmail.com", user_id: user_one.id) } + + subject { migration.perform(1, 2) } + + it 'only creates one record' do + subject + + expect(canonical_emails.count).not_to be_nil + end + end + + def create_user(attributes) + default_attributes = { + projects_limit: 0 + } + + users.create(default_attributes.merge!(attributes)) + end + + def canonical_emails(user_id: nil) + filter_by_id = user_id ? "WHERE user_id = #{user_id}" : "" + + ApplicationRecord.connection.execute <<~SQL + SELECT canonical_email, user_id + FROM user_canonical_emails + #{filter_by_id}; + SQL + end +end diff --git a/spec/lib/gitlab/grafana_embed_usage_data_spec.rb b/spec/lib/gitlab/grafana_embed_usage_data_spec.rb deleted file mode 100644 index 162db46719b..00000000000 --- a/spec/lib/gitlab/grafana_embed_usage_data_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::GrafanaEmbedUsageData do - describe '#issue_count' do - subject { described_class.issue_count } - - let(:project) { create(:project) } - let(:description_with_embed) { "Some comment\n\nhttps://grafana.example.com/d/xvAk4q0Wk/go-processes?orgId=1&from=1573238522762&to=1573240322762&var-job=prometheus&var-interval=10m&panelId=1&fullscreen" } - let(:description_with_unintegrated_embed) { "Some comment\n\nhttps://grafana.exp.com/d/xvAk4q0Wk/go-processes?orgId=1&from=1573238522762&to=1573240322762&var-job=prometheus&var-interval=10m&panelId=1&fullscreen" } - let(:description_with_non_grafana_inline_metric) { "Some comment\n\n#{Gitlab::Routing.url_helpers.metrics_namespace_project_environment_url(*['foo', 'bar', 12])}" } - - shared_examples "zero count" do - it "does not count the issue" do - expect(subject).to eq(0) - end - end - - context 'with project grafana integration enabled' do - before do - create(:grafana_integration, project: project, enabled: true) - end - - context 'with valid and invalid embeds' do - before do - # Valid - create(:issue, project: project, description: description_with_embed) - create(:issue, project: project, description: description_with_embed) - # In-Valid - create(:issue, project: project, description: description_with_unintegrated_embed) - create(:issue, project: project, description: description_with_non_grafana_inline_metric) - create(:issue, project: project, description: nil) - create(:issue, project: project, description: '') - create(:issue, project: project) - end - - it 'counts only the issues with embeds' do - expect(subject).to eq(2) - end - end - end - - context 'with project grafana integration disabled' do - before do - create(:grafana_integration, project: project, enabled: false) - end - - context 'with one issue having a grafana link in the description and one without' do - before do - create(:issue, project: project, description: description_with_embed) - create(:issue, project: project) - end - - it_behaves_like('zero count') - end - end - - context 'with an un-integrated project' do - context 'with one issue having a grafana link in the description and one without' do - before do - create(:issue, project: project, description: description_with_embed) - create(:issue, project: project) - end - - it_behaves_like('zero count') - end - end - end -end diff --git a/spec/lib/gitlab/jira_import/base_importer_spec.rb b/spec/lib/gitlab/jira_import/base_importer_spec.rb index 8bc43feb356..f22efcb8743 100644 --- a/spec/lib/gitlab/jira_import/base_importer_spec.rb +++ b/spec/lib/gitlab/jira_import/base_importer_spec.rb @@ -37,12 +37,8 @@ describe Gitlab::JiraImport::BaseImporter do end context 'when import data exists' do - let(:jira_import_data) do - data = JiraImportData.new - data << JiraImportData::JiraProjectDetails.new('xx', Time.now.strftime('%Y-%m-%d %H:%M:%S'), { user_id: 1, name: 'root' }) - data - end - let(:project) { create(:project, import_data: jira_import_data) } + let_it_be(:project) { create(:project) } + let_it_be(:jira_import) { create(:jira_import_state, project: project) } let(:subject) { described_class.new(project) } context 'when #imported_items_cache_key is not implemented' do diff --git a/spec/lib/gitlab/jira_import/issues_importer_spec.rb b/spec/lib/gitlab/jira_import/issues_importer_spec.rb index 88e8b195dbe..8e16fd3e978 100644 --- a/spec/lib/gitlab/jira_import/issues_importer_spec.rb +++ b/spec/lib/gitlab/jira_import/issues_importer_spec.rb @@ -3,14 +3,10 @@ require 'spec_helper' describe Gitlab::JiraImport::IssuesImporter do - let(:user) { create(:user) } - let(:jira_import_data) do - data = JiraImportData.new - data << JiraImportData::JiraProjectDetails.new('XX', Time.now.strftime('%Y-%m-%d %H:%M:%S'), { user_id: user.id, name: user.name }) - data - end - let(:project) { create(:project, import_data: jira_import_data) } - let!(:jira_service) { create(:jira_service, project: project) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:jira_import) { create(:jira_import_state, project: project) } + let_it_be(:jira_service) { create(:jira_service, project: project) } subject { described_class.new(project) } diff --git a/spec/lib/gitlab/jira_import/labels_importer_spec.rb b/spec/lib/gitlab/jira_import/labels_importer_spec.rb index eaa13d9ed32..2d0e2bc6b53 100644 --- a/spec/lib/gitlab/jira_import/labels_importer_spec.rb +++ b/spec/lib/gitlab/jira_import/labels_importer_spec.rb @@ -3,14 +3,10 @@ require 'spec_helper' describe Gitlab::JiraImport::LabelsImporter do - let(:user) { create(:user) } - let(:jira_import_data) do - data = JiraImportData.new - data << JiraImportData::JiraProjectDetails.new('XX', Time.now.strftime('%Y-%m-%d %H:%M:%S'), { user_id: user.id, name: user.name }) - data - end - let(:project) { create(:project, import_data: jira_import_data) } - let!(:jira_service) { create(:jira_service, project: project) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:jira_import) { create(:jira_import_state, project: project) } + let_it_be(:jira_service) { create(:jira_service, project: project) } subject { described_class.new(project).execute } diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 37d9c5389dd..12199bc6d5a 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -14,7 +14,7 @@ describe Gitlab::UsageData, :aggregate_failures do let!(:ud) { build(:usage_data) } before do - allow(Gitlab::GrafanaEmbedUsageData).to receive(:issue_count).and_return(2) + allow(described_class).to receive(:grafana_embed_usage_data).and_return(2) end subject { described_class.data } @@ -220,6 +220,71 @@ describe Gitlab::UsageData, :aggregate_failures do end end + describe '#grafana_embed_usage_data' do + subject { described_class.grafana_embed_usage_data } + + let(:project) { create(:project) } + let(:description_with_embed) { "Some comment\n\nhttps://grafana.example.com/d/xvAk4q0Wk/go-processes?orgId=1&from=1573238522762&to=1573240322762&var-job=prometheus&var-interval=10m&panelId=1&fullscreen" } + let(:description_with_unintegrated_embed) { "Some comment\n\nhttps://grafana.exp.com/d/xvAk4q0Wk/go-processes?orgId=1&from=1573238522762&to=1573240322762&var-job=prometheus&var-interval=10m&panelId=1&fullscreen" } + let(:description_with_non_grafana_inline_metric) { "Some comment\n\n#{Gitlab::Routing.url_helpers.metrics_namespace_project_environment_url(*['foo', 'bar', 12])}" } + + shared_examples "zero count" do + it "does not count the issue" do + expect(subject).to eq(0) + end + end + + context 'with project grafana integration enabled' do + before do + create(:grafana_integration, project: project, enabled: true) + end + + context 'with valid and invalid embeds' do + before do + # Valid + create(:issue, project: project, description: description_with_embed) + create(:issue, project: project, description: description_with_embed) + # In-Valid + create(:issue, project: project, description: description_with_unintegrated_embed) + create(:issue, project: project, description: description_with_non_grafana_inline_metric) + create(:issue, project: project, description: nil) + create(:issue, project: project, description: '') + create(:issue, project: project) + end + + it 'counts only the issues with embeds' do + expect(subject).to eq(2) + end + end + end + + context 'with project grafana integration disabled' do + before do + create(:grafana_integration, project: project, enabled: false) + end + + context 'with one issue having a grafana link in the description and one without' do + before do + create(:issue, project: project, description: description_with_embed) + create(:issue, project: project) + end + + it_behaves_like('zero count') + end + end + + context 'with an un-integrated project' do + context 'with one issue having a grafana link in the description and one without' do + before do + create(:issue, project: project, description: description_with_embed) + create(:issue, project: project) + end + + it_behaves_like('zero count') + end + end + end + describe '#count' do let(:relation) { double(:relation) } diff --git a/spec/lib/quality/helm3_client_spec.rb b/spec/lib/quality/helm3_client_spec.rb new file mode 100644 index 00000000000..1144ee9369d --- /dev/null +++ b/spec/lib/quality/helm3_client_spec.rb @@ -0,0 +1,133 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Quality::Helm3Client do + let(:namespace) { 'review-apps-ee' } + let(:release_name) { 'my-release' } + let(:raw_helm_list_page1) do + <<~OUTPUT + [ + {"name":"review-qa-60-reor-1mugd1","namespace":"#{namespace}","revision":1,"updated":"2020-04-03 17:27:10.245952 +0800 +08","status":"failed","chart":"gitlab-1.1.3","app_version":"12.9.2"}, + {"name":"review-7846-fix-s-261vd6","namespace":"#{namespace}","revision":2,"updated":"2020-04-02 17:27:12.245952 +0800 +08","status":"deployed","chart":"gitlab-1.1.3","app_version":"12.9.2"}, + {"name":"review-7867-snowp-lzo3iy","namespace":"#{namespace}","revision":1,"updated":"2020-04-02 15:27:12.245952 +0800 +08","status":"deployed","chart":"gitlab-1.1.3","app_version":"12.9.1"}, + {"name":"review-6709-group-2pzeec","namespace":"#{namespace}","revision":2,"updated":"2020-04-01 21:27:12.245952 +0800 +08","status":"failed","chart":"gitlab-1.1.3","app_version":"12.9.1"} + ] + OUTPUT + end + let(:raw_helm_list_page2) do + <<~OUTPUT + [ + {"name":"review-6709-group-t40qbv","namespace":"#{namespace}","revision":2,"updated":"2020-04-01 11:27:12.245952 +0800 +08","status":"deployed","chart":"gitlab-1.1.3","app_version":"12.9.1"} + ] + OUTPUT + end + let(:raw_helm_list_empty) do + <<~OUTPUT + [] + OUTPUT + end + + subject { described_class.new(namespace: namespace) } + + describe '#releases' do + it 'raises an error if the Helm command fails' do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with([%(helm list --namespace "#{namespace}" --max 256 --offset 0 --output json)]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false))) + + expect { subject.releases.to_a }.to raise_error(described_class::CommandFailedError) + end + + it 'calls helm list with default arguments' do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with([%(helm list --namespace "#{namespace}" --max 256 --offset 0 --output json)]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + + subject.releases.to_a + end + + it 'calls helm list with extra arguments' do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with([%(helm list --namespace "#{namespace}" --max 256 --offset 0 --output json --deployed)]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + + subject.releases(args: ['--deployed']).to_a + end + + it 'returns a list of Release objects' do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with([%(helm list --namespace "#{namespace}" --max 256 --offset 0 --output json --deployed)]) + .and_return(Gitlab::Popen::Result.new([], raw_helm_list_page2, '', double(success?: true))) + expect(Gitlab::Popen).to receive(:popen_with_detail).ordered + .and_return(Gitlab::Popen::Result.new([], raw_helm_list_empty, '', double(success?: true))) + + releases = subject.releases(args: ['--deployed']).to_a + + expect(releases.size).to eq(1) + expect(releases[0]).to have_attributes( + name: 'review-6709-group-t40qbv', + revision: 2, + last_update: Time.parse('2020-04-01 11:27:12.245952 +0800 +08'), + status: 'deployed', + chart: 'gitlab-1.1.3', + app_version: '12.9.1', + namespace: namespace + ) + end + + it 'automatically paginates releases' do + expect(Gitlab::Popen).to receive(:popen_with_detail).ordered + .with([%(helm list --namespace "#{namespace}" --max 256 --offset 0 --output json)]) + .and_return(Gitlab::Popen::Result.new([], raw_helm_list_page1, '', double(success?: true))) + expect(Gitlab::Popen).to receive(:popen_with_detail).ordered + .with([%(helm list --namespace "#{namespace}" --max 256 --offset 256 --output json)]) + .and_return(Gitlab::Popen::Result.new([], raw_helm_list_page2, '', double(success?: true))) + expect(Gitlab::Popen).to receive(:popen_with_detail).ordered + .with([%(helm list --namespace "#{namespace}" --max 256 --offset 512 --output json)]) + .and_return(Gitlab::Popen::Result.new([], raw_helm_list_empty, '', double(success?: true))) + releases = subject.releases.to_a + + expect(releases.size).to eq(5) + expect(releases.last.name).to eq('review-6709-group-t40qbv') + end + end + + describe '#delete' do + it 'raises an error if the Helm command fails' do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with([%(helm uninstall --namespace "#{namespace}" #{release_name})]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false))) + + expect { subject.delete(release_name: release_name) }.to raise_error(described_class::CommandFailedError) + end + + it 'calls helm uninstall with default arguments' do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with([%(helm uninstall --namespace "#{namespace}" #{release_name})]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + + expect(subject.delete(release_name: release_name)).to eq('') + end + + context 'with multiple release names' do + let(:release_name) { %w[my-release my-release-2] } + + it 'raises an error if the Helm command fails' do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with([%(helm uninstall --namespace "#{namespace}" #{release_name.join(' ')})]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: false))) + + expect { subject.delete(release_name: release_name) }.to raise_error(described_class::CommandFailedError) + end + + it 'calls helm uninstall with multiple release names' do + expect(Gitlab::Popen).to receive(:popen_with_detail) + .with([%(helm uninstall --namespace "#{namespace}" #{release_name.join(' ')})]) + .and_return(Gitlab::Popen::Result.new([], '', '', double(success?: true))) + + expect(subject.delete(release_name: release_name)).to eq('') + end + end + end +end diff --git a/spec/models/jira_import_data_spec.rb b/spec/models/jira_import_data_spec.rb deleted file mode 100644 index ad7a704236b..00000000000 --- a/spec/models/jira_import_data_spec.rb +++ /dev/null @@ -1,134 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe JiraImportData do - let(:symbol_keys_project) do - { key: 'AA', scheduled_at: 2.days.ago.strftime('%Y-%m-%d %H:%M:%S'), scheduled_by: { 'user_id' => 1, 'name' => 'tester1' } } - end - - let(:string_keys_project) do - { 'key': 'BB', 'scheduled_at': 1.hour.ago.strftime('%Y-%m-%d %H:%M:%S'), 'scheduled_by': { 'user_id': 2, 'name': 'tester2' } } - end - - let(:jira_project_details) do - JiraImportData::JiraProjectDetails.new('CC', 1.day.ago.strftime('%Y-%m-%d %H:%M:%S'), { user_id: 3, name: 'tester3' }) - end - - describe '#projects' do - it 'returns empty array if no data' do - expect(described_class.new.projects).to eq([]) - end - - it 'returns empty array if no projects' do - import_data = described_class.new(data: { 'some-key' => 10 }) - expect(import_data.projects).to eq([]) - end - - it 'returns JiraProjectDetails sorted by scheduled_at time' do - import_data = described_class.new(data: { jira: { projects: [symbol_keys_project, string_keys_project, jira_project_details] } }) - - expect(import_data.projects.size).to eq 3 - expect(import_data.projects.map(&:key)).to eq(%w(AA CC BB)) - expect(import_data.projects.map(&:scheduled_by).map {|e| e['name']}).to eq %w(tester1 tester3 tester2) - expect(import_data.projects.map(&:scheduled_by).map {|e| e['user_id']}).to eq [1, 3, 2] - end - end - - describe 'add projects' do - it 'adds project when data is nil' do - import_data = described_class.new - expect(import_data.data).to be nil - - import_data << string_keys_project - - expect(import_data.data).to eq({ 'jira' => { 'projects' => [string_keys_project] } }) - end - - it 'adds project when data has some random info' do - import_data = described_class.new(data: { 'one-key': 10 }) - expect(import_data.data).to eq({ 'one-key' => 10 }) - - import_data << string_keys_project - - expect(import_data.data).to eq({ 'one-key' => 10, 'jira' => { 'projects' => [string_keys_project] } }) - end - - it 'adds project when data already has some jira projects' do - import_data = described_class.new(data: { jira: { projects: [symbol_keys_project] } }) - expect(import_data.projects.map(&:to_h)).to eq [symbol_keys_project] - - import_data << string_keys_project - - expect(import_data.data['jira']['projects'].size).to eq 2 - expect(import_data.projects.map(&:key)).to eq(%w(AA BB)) - expect(import_data.projects.map(&:scheduled_by).map {|e| e['name']}).to eq %w(tester1 tester2) - expect(import_data.projects.map(&:scheduled_by).map {|e| e['user_id']}).to eq [1, 2] - end - end - - describe '#force_import!' do - it 'sets force import when data is nil' do - import_data = described_class.new - - import_data.force_import! - - expect(import_data.data['jira'][JiraImportData::FORCE_IMPORT_KEY]).to be true - expect(import_data.force_import?).to be false - end - - it 'sets force import when data is present but no jira key' do - import_data = described_class.new(data: { 'some-key': 'some-data' }) - - import_data.force_import! - - expect(import_data.data['jira'][JiraImportData::FORCE_IMPORT_KEY]).to be true - expect(import_data.data).to eq({ 'some-key' => 'some-data', 'jira' => { JiraImportData::FORCE_IMPORT_KEY => true } }) - expect(import_data.force_import?).to be false - end - - it 'sets force import when data and jira keys exist' do - import_data = described_class.new(data: { 'some-key': 'some-data', 'jira': {} }) - - import_data.force_import! - - expect(import_data.data['jira'][JiraImportData::FORCE_IMPORT_KEY]).to be true - expect(import_data.data).to eq({ 'some-key' => 'some-data', 'jira' => { JiraImportData::FORCE_IMPORT_KEY => true } }) - expect(import_data.force_import?).to be false - end - - it 'sets force import when data and jira project data exist' do - import_data = described_class.new(data: { jira: { projects: [symbol_keys_project], JiraImportData::FORCE_IMPORT_KEY => false }, 'some-key': 'some-data' }) - - import_data.force_import! - - expect(import_data.data['jira'][JiraImportData::FORCE_IMPORT_KEY]).to be true - expect(import_data.data).to eq({ 'some-key' => 'some-data', 'jira' => { 'projects' => [symbol_keys_project.deep_stringify_keys!], JiraImportData::FORCE_IMPORT_KEY => true } }) - expect(import_data.force_import?).to be true - end - end - - describe '#force_import?' do - it 'returns false when data blank' do - expect(described_class.new.force_import?).to be false - end - - it 'returns false if there is no project data present' do - import_data = described_class.new(data: { jira: { JiraImportData::FORCE_IMPORT_KEY => true }, 'one-key': 10 }) - - expect(import_data.force_import?).to be false - end - - it 'returns false when force import set to false' do - import_data = described_class.new(data: { jira: { projects: [symbol_keys_project], JiraImportData::FORCE_IMPORT_KEY => false }, 'one-key': 10 }) - - expect(import_data.force_import?).to be false - end - - it 'returns true when force import set to true' do - import_data = described_class.new(data: { jira: { projects: [symbol_keys_project], JiraImportData::FORCE_IMPORT_KEY => true } }) - - expect(import_data.force_import?).to be true - end - end -end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 21c074cdce2..3c8afee4466 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2281,38 +2281,35 @@ describe Project do end describe '#jira_import_status' do - let(:project) { create(:project, :import_started, import_type: 'jira') } + let(:project) { create(:project, import_type: 'jira') } - context 'when import_data is nil' do + context 'when no jira imports' do it 'returns none' do - expect(project.import_data).to be nil - expect(project.jira_import_status).to eq('none') + expect(project.jira_import_status).to eq('initial') end end - context 'when import_data is set' do - let(:jira_import_data) { JiraImportData.new } - let(:project) { create(:project, :import_started, import_data: jira_import_data, import_type: 'jira') } + context 'when there are jira imports' do + let(:jira_import1) { build(:jira_import_state, :finished, project: project) } + let(:jira_import2) { build(:jira_import_state, project: project) } - it 'returns none' do - expect(project.import_data.becomes(JiraImportData).force_import?).to be false - expect(project.jira_import_status).to eq('none') + before do + expect(project).to receive(:latest_jira_import).and_return(jira_import2) end - context 'when jira_force_import is true' do - let(:imported_jira_project) do - JiraImportData::JiraProjectDetails.new('xx', Time.now.strftime('%Y-%m-%d %H:%M:%S'), { user_id: 1, name: 'root' }) + context 'when latest import status is initial or jira imports are mising' do + it 'returns initial' do + expect(project.jira_import_status).to eq('initial') end + end + context 'when latest import status is scheduled' do before do - jira_import_data = project.import_data.becomes(JiraImportData) - jira_import_data << imported_jira_project - jira_import_data.force_import! + jira_import2.schedule! end - it 'returns started' do - expect(project.import_data.becomes(JiraImportData).force_import?).to be true - expect(project.jira_import_status).to eq('started') + it 'returns scheduled' do + expect(project.jira_import_status).to eq('scheduled') end end end @@ -2375,52 +2372,46 @@ describe Project do context 'jira import' do it 'schedules a jira import job' do project = create(:project, import_type: 'jira') + jira_import = create(:jira_import_state, project: project) expect(Gitlab::JiraImport::Stage::StartImportWorker).to receive(:perform_async).with(project.id).and_return(import_jid) - expect(project.add_import_job).to eq(import_jid) + + jira_import.schedule! + + expect(jira_import.jid).to eq(import_jid) end end end describe '#jira_import?' do - subject(:project) { build(:project, import_type: 'jira') } + let_it_be(:project) { build(:project, import_type: 'jira') } + let_it_be(:jira_import) { build(:jira_import_state, project: project) } - it { expect(project.jira_import?).to be true } - it { expect(project.import?).to be true } - end - - describe '#jira_force_import?' do - let(:imported_jira_project) do - JiraImportData::JiraProjectDetails.new('xx', Time.now.strftime('%Y-%m-%d %H:%M:%S'), { user_id: 1, name: 'root' }) - end - let(:jira_import_data) do - data = JiraImportData.new - data << imported_jira_project - data.force_import! - data + before do + expect(project).to receive(:jira_imports).and_return([jira_import]) end - subject(:project) { build(:project, import_type: 'jira', import_data: jira_import_data) } - - it { expect(project.jira_force_import?).to be true } + it { expect(project.jira_import?).to be true } + it { expect(project.import?).to be true } end describe '#remove_import_data' do - let(:import_data) { ProjectImportData.new(data: { 'test' => 'some data' }) } + let_it_be(:import_data) { ProjectImportData.new(data: { 'test' => 'some data' }) } context 'when jira import' do - let!(:project) { create(:project, import_type: 'jira', import_data: import_data) } + let_it_be(:project, reload: true) { create(:project, import_type: 'jira', import_data: import_data) } + let_it_be(:jira_import) { create(:jira_import_state, project: project) } - it 'does not remove import data' do + it 'does remove import data' do expect(project.mirror?).to be false expect(project.jira_import?).to be true - expect { project.remove_import_data }.not_to change { ProjectImportData.count } + expect { project.remove_import_data }.to change { ProjectImportData.count }.by(-1) end end - context 'when not mirror neither jira import' do - let(:user) { create(:user) } - let!(:project) { create(:project, import_type: 'github', import_data: import_data) } + context 'when neither a mirror nor a jira import' do + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, import_type: 'github', import_data: import_data) } it 'removes import data' do expect(project.mirror?).to be false diff --git a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb index 3eb2ca6909b..7e213d3adb0 100644 --- a/spec/requests/api/graphql/mutations/jira_import/start_spec.rb +++ b/spec/requests/api/graphql/mutations/jira_import/start_spec.rb @@ -118,9 +118,8 @@ describe 'Starting a Jira Import' do expect(jira_import['jiraProjectKey']).to eq 'AA' expect(jira_import['scheduledBy']['username']).to eq current_user.username - expect(project.import_state).not_to be nil - expect(project.import_state.status).to eq 'scheduled' - expect(project.import_data.becomes(JiraImportData).projects.last.scheduled_by['user_id']).to eq current_user.id + expect(project.latest_jira_import).not_to be_nil + expect(project.latest_jira_import).to be_scheduled end end end diff --git a/spec/requests/api/graphql/project/jira_import_spec.rb b/spec/requests/api/graphql/project/jira_import_spec.rb index beebc63a3c6..43e1bb13342 100644 --- a/spec/requests/api/graphql/project/jira_import_spec.rb +++ b/spec/requests/api/graphql/project/jira_import_spec.rb @@ -6,19 +6,10 @@ describe 'query jira import data' do include GraphqlHelpers let_it_be(:current_user) { create(:user) } - let_it_be(:jira_import_data) do - data = JiraImportData.new - data << JiraImportData::JiraProjectDetails.new( - 'AA', 2.days.ago.strftime('%Y-%m-%d %H:%M:%S'), - { user_id: current_user.id, name: current_user.name } - ) - data << JiraImportData::JiraProjectDetails.new( - 'BB', 5.days.ago.strftime('%Y-%m-%d %H:%M:%S'), - { user_id: current_user.id, name: current_user.name } - ) - data - end - let_it_be(:project) { create(:project, :private, :import_started, import_data: jira_import_data, import_type: 'jira') } + let_it_be(:project) { create(:project, :private, :import_started, import_type: 'jira') } + let_it_be(:jira_import1) { create(:jira_import_state, :finished, project: project, jira_project_key: 'AA', user: current_user, created_at: 2.days.ago) } + let_it_be(:jira_import2) { create(:jira_import_state, :finished, project: project, jira_project_key: 'BB', user: current_user, created_at: 5.days.ago) } + let(:query) do %( query { @@ -48,7 +39,7 @@ describe 'query jira import data' do context 'when anonymous user' do let(:current_user) { nil } - it { expect(jira_imports).to be nil } + it { expect(jira_imports).to be_nil } end context 'when user developer' do @@ -56,7 +47,7 @@ describe 'query jira import data' do project.add_developer(current_user) end - it { expect(jira_imports).to be nil } + it { expect(jira_imports).to be_nil } end end @@ -139,7 +130,7 @@ describe 'query jira import data' do it 'does not return import status' do post_graphql(query, current_user: current_user) - expect(graphql_data['project']).to be nil + expect(graphql_data['project']).to be_nil end end @@ -149,12 +140,12 @@ describe 'query jira import data' do end context 'when import never ran' do - let(:project) { create(:project) } + let_it_be(:initial_jira_import) { create(:jira_import_state, project: project, jira_project_key: 'BB', user: current_user) } it 'returns import status' do post_graphql(query, current_user: current_user) - expect(jira_import_status).to eq('none') + expect(jira_import_status).to eq('initial') end end @@ -166,11 +157,8 @@ describe 'query jira import data' do end end - context 'when import running, i.e. force-import: true' do - before do - project.import_data.becomes(JiraImportData).force_import! - project.save! - end + context 'when import running' do + let_it_be(:started_jira_import) { create(:jira_import_state, :started, project: project, jira_project_key: 'BB', user: current_user) } it 'returns import status' do post_graphql(query, current_user: current_user) diff --git a/spec/requests/api/group_export_spec.rb b/spec/requests/api/group_export_spec.rb index 47193591cf1..02ae9d71702 100644 --- a/spec/requests/api/group_export_spec.rb +++ b/spec/requests/api/group_export_spec.rb @@ -102,6 +102,19 @@ describe API::GroupExport do end end + context 'when the export cannot be started' do + before do + group.add_owner(user) + allow(GroupExportWorker).to receive(:perform_async).and_return(nil) + end + + it 'returns an error' do + post api(path, user) + + expect(response).to have_gitlab_http_status(:error) + end + end + context 'when user is not a group owner' do before do group.add_developer(user) diff --git a/spec/services/groups/import_export/export_service_spec.rb b/spec/services/groups/import_export/export_service_spec.rb index 0d7fa98e16b..e026d2166d6 100644 --- a/spec/services/groups/import_export/export_service_spec.rb +++ b/spec/services/groups/import_export/export_service_spec.rb @@ -3,6 +3,37 @@ require 'spec_helper' describe Groups::ImportExport::ExportService do + describe '#async_execute' do + let(:user) { create(:user) } + let(:group) { create(:group) } + + context 'when the job can be successfully scheduled' do + let(:export_service) { described_class.new(group: group, user: user) } + + it 'enqueues an export job' do + expect(GroupExportWorker).to receive(:perform_async).with(user.id, group.id, {}) + + export_service.async_execute + end + + it 'returns truthy' do + expect(export_service.async_execute).to be_present + end + end + + context 'when the job cannot be scheduled' do + let(:export_service) { described_class.new(group: group, user: user) } + + before do + allow(GroupExportWorker).to receive(:perform_async).and_return(nil) + end + + it 'returns falsey' do + expect(export_service.async_execute).to be_falsey + end + end + end + describe '#execute' do let!(:user) { create(:user) } let(:group) { create(:group) } @@ -103,5 +134,23 @@ describe Groups::ImportExport::ExportService do end end end + + context 'when there is an existing export file' do + subject(:export_service) { described_class.new(group: group, user: user) } + + let(:import_export_upload) do + create( + :import_export_upload, + group: group, + export_file: fixture_file_upload('spec/fixtures/group_export.tar.gz') + ) + end + + it 'removes it' do + existing_file = import_export_upload.export_file + + expect { export_service.execute }.to change { existing_file.file }.to(be_nil) + end + end end end diff --git a/spec/services/jira_import/start_import_service_spec.rb b/spec/services/jira_import/start_import_service_spec.rb index 038c53b2b22..8d9ba5ac692 100644 --- a/spec/services/jira_import/start_import_service_spec.rb +++ b/spec/services/jira_import/start_import_service_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' describe JiraImport::StartImportService do let_it_be(:user) { create(:user) } - let(:project) { create(:project) } + let_it_be(:project, reload: true) { create(:project) } subject { described_class.new(user, project, '').execute } @@ -46,10 +46,12 @@ describe JiraImport::StartImportService do end context 'when correct data provided' do - subject { described_class.new(user, project, 'some-key').execute } + let(:fake_key) { 'some-key' } + + subject { described_class.new(user, project, fake_key).execute } context 'when import is already running' do - let!(:import_state) { create(:import_state, project: project, status: :started) } + let_it_be(:jira_import_state) { create(:jira_import_state, :started, project: project) } it_behaves_like 'responds with error', 'Jira import is already running.' end @@ -62,17 +64,16 @@ describe JiraImport::StartImportService do it 'schedules jira import' do subject - expect(project.import_state.status).to eq('scheduled') + expect(project.latest_jira_import).to be_scheduled end it 'creates jira import data' do - subject + jira_import = subject.payload[:import_data] - jira_import_data = project.import_data.becomes(JiraImportData) - expect(jira_import_data.force_import?).to be true - imported_project_data = jira_import_data.projects.last - expect(imported_project_data.key).to eq('some-key') - expect(imported_project_data.scheduled_by['user_id']).to eq(user.id) + expect(jira_import.jira_project_xid).to eq(0) + expect(jira_import.jira_project_name).to eq(fake_key) + expect(jira_import.jira_project_key).to eq(fake_key) + expect(jira_import.user).to eq(user) end end end diff --git a/spec/support/shared_examples/graphql/jira_import/jira_import_resolved_shared_examples.rb b/spec/support/shared_examples/graphql/jira_import/jira_import_resolver_shared_examples.rb index b1d178521bb..3d97fe10a47 100644 --- a/spec/support/shared_examples/graphql/jira_import/jira_import_resolved_shared_examples.rb +++ b/spec/support/shared_examples/graphql/jira_import/jira_import_resolver_shared_examples.rb @@ -2,7 +2,7 @@ shared_examples 'no jira import data present' do it 'returns none' do - expect(resolve_imports).to eq JiraImportData.none + expect(resolve_imports).to eq JiraImportState.none end end diff --git a/spec/support/shared_examples/workers/gitlab/jira_import/jira_import_workers_shared_examples.rb b/spec/support/shared_examples/workers/gitlab/jira_import/jira_import_workers_shared_examples.rb index 71ec1ea6a74..c0d17d6853d 100644 --- a/spec/support/shared_examples/workers/gitlab/jira_import/jira_import_workers_shared_examples.rb +++ b/spec/support/shared_examples/workers/gitlab/jira_import/jira_import_workers_shared_examples.rb @@ -12,14 +12,23 @@ shared_examples 'include import workers modules' do end end -shared_examples 'exit import not started' do - it 'does nothing, and exits' do +shared_examples 'does not advance to next stage' do + it 'does not advance to next stage' do expect(Gitlab::JiraImport::AdvanceStageWorker).not_to receive(:perform_async) described_class.new.perform(project.id) end end +shared_examples 'cannot do jira import' do + it 'does not advance to next stage' do + worker = described_class.new + expect(worker).not_to receive(:import) + + worker.perform(project.id) + end +end + shared_examples 'advance to next stage' do |next_stage| let(:job_waiter) { Gitlab::JobWaiter.new(2, 'some-job-key') } diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb index 2e96f42a251..1d2341bb46f 100644 --- a/spec/tasks/gitlab/db_rake_spec.rb +++ b/spec/tasks/gitlab/db_rake_spec.rb @@ -98,6 +98,39 @@ describe 'gitlab:db namespace rake task' do end end + describe 'clean_structure_sql' do + let_it_be(:clean_rake_task) { 'gitlab:db:clean_structure_sql' } + let_it_be(:test_task_name) { 'gitlab:db:_test_multiple_structure_cleans' } + let_it_be(:structure_file) { 'db/structure.sql' } + let_it_be(:input) { 'this is structure data' } + let(:output) { StringIO.new } + + before do + allow(File).to receive(:read).with(structure_file).and_return(input) + allow(File).to receive(:open).with(structure_file, any_args).and_yield(output) + end + + after do + Rake::Task[test_task_name].clear if Rake::Task.task_defined?(test_task_name) + end + + it 'can be executed multiple times within another rake task' do + Rake::Task.define_task(test_task_name => :environment) do + expect_next_instance_of(Gitlab::Database::SchemaCleaner) do |cleaner| + expect(cleaner).to receive(:clean).with(output) + end + Rake::Task[clean_rake_task].invoke + + expect_next_instance_of(Gitlab::Database::SchemaCleaner) do |cleaner| + expect(cleaner).to receive(:clean).with(output) + end + Rake::Task[clean_rake_task].invoke + end + + run_rake_task(test_task_name) + end + end + def run_rake_task(task_name) Rake::Task[task_name].reenable Rake.application.invoke_task task_name diff --git a/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb b/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb index b6db803ddf5..80629cb875e 100644 --- a/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb +++ b/spec/workers/gitlab/jira_import/import_issue_worker_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' describe Gitlab::JiraImport::ImportIssueWorker do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } + let(:some_key) { 'some-key' } describe 'modules' do it { expect(described_class).to include_module(ApplicationWorker) } @@ -23,7 +24,7 @@ describe Gitlab::JiraImport::ImportIssueWorker do allow(subject).to receive(:insert_and_return_id).and_raise(StandardError) expect(Gitlab::JobWaiter).to receive(:notify) - subject.perform(project.id, 123, issue_attrs, 'some-key') + subject.perform(project.id, 123, issue_attrs, some_key) end it 'record a failed to import issue' do @@ -36,7 +37,7 @@ describe Gitlab::JiraImport::ImportIssueWorker do context 'when import label does not exist' do it 'does not record import failure' do - subject.perform(project.id, 123, issue_attrs, 'some-key') + subject.perform(project.id, 123, issue_attrs, some_key) expect(label.issues.count).to eq(0) expect(Gitlab::Cache::Import::Caching.read(Gitlab::JiraImport.failed_issues_counter_cache_key(project.id)).to_i).to eq(0) @@ -49,7 +50,7 @@ describe Gitlab::JiraImport::ImportIssueWorker do end it 'does not record import failure' do - subject.perform(project.id, 123, issue_attrs, 'some-key') + subject.perform(project.id, 123, issue_attrs, some_key) expect(label.issues.count).to eq(1) expect(Gitlab::Cache::Import::Caching.read(Gitlab::JiraImport.failed_issues_counter_cache_key(project.id)).to_i).to eq(0) diff --git a/spec/workers/gitlab/jira_import/stage/finish_import_worker_spec.rb b/spec/workers/gitlab/jira_import/stage/finish_import_worker_spec.rb index 00505226212..93e2a44223b 100644 --- a/spec/workers/gitlab/jira_import/stage/finish_import_worker_spec.rb +++ b/spec/workers/gitlab/jira_import/stage/finish_import_worker_spec.rb @@ -16,46 +16,29 @@ describe Gitlab::JiraImport::Stage::FinishImportWorker do stub_feature_flags(jira_issue_import: false) end - it_behaves_like 'exit import not started' + it_behaves_like 'cannot do jira import' end context 'when feature flag enabled' do + let_it_be(:jira_import) { create(:jira_import_state, :scheduled, project: project) } + before do stub_feature_flags(jira_issue_import: true) end context 'when import did not start' do - let!(:import_state) { create(:import_state, project: project) } - - it_behaves_like 'exit import not started' + it_behaves_like 'cannot do jira import' end context 'when import started' do - let(:imported_jira_project) do - JiraImportData::JiraProjectDetails.new('xx', Time.now.strftime('%Y-%m-%d %H:%M:%S'), { user_id: 1, name: 'root' }) + before do + jira_import.start! end - let(:jira_import_data) do - data = JiraImportData.new - data << imported_jira_project - data.force_import! - data - end - let(:import_state) { create(:import_state, status: :started) } - let(:project) { create(:project, import_type: 'jira', import_data: jira_import_data, import_state: import_state) } it 'changes import state to finished' do worker.perform(project.id) - expect(project.reload.import_state.status).to eq("finished") - end - - it 'removes force-import flag' do - expect(project.reload.import_data.data['jira'][JiraImportData::FORCE_IMPORT_KEY]).to be true - - worker.perform(project.id) - - expect(project.reload.import_data.data['jira'][JiraImportData::FORCE_IMPORT_KEY]).to be nil - expect(project.reload.import_data.data['jira']).not_to be nil + expect(project.jira_import_status).to eq('finished') end end end diff --git a/spec/workers/gitlab/jira_import/stage/import_attachments_worker_spec.rb b/spec/workers/gitlab/jira_import/stage/import_attachments_worker_spec.rb index 513925507a1..478cb447dc5 100644 --- a/spec/workers/gitlab/jira_import/stage/import_attachments_worker_spec.rb +++ b/spec/workers/gitlab/jira_import/stage/import_attachments_worker_spec.rb @@ -3,34 +3,38 @@ require 'spec_helper' describe Gitlab::JiraImport::Stage::ImportAttachmentsWorker do - let_it_be(:project) { create(:project) } + let_it_be(:project) { create(:project, import_type: 'jira') } describe 'modules' do it_behaves_like 'include import workers modules' end describe '#perform' do - context 'when feature flag enabled' do + context 'when feature flag disabled' do before do stub_feature_flags(jira_issue_import: false) end - it_behaves_like 'exit import not started' + it_behaves_like 'cannot do jira import' + it_behaves_like 'does not advance to next stage' end context 'when feature flag enabled' do + let_it_be(:jira_import) { create(:jira_import_state, :scheduled, project: project) } + before do stub_feature_flags(jira_issue_import: true) end context 'when import did not start' do - let!(:import_state) { create(:import_state, project: project) } - - it_behaves_like 'exit import not started' + it_behaves_like 'cannot do jira import' + it_behaves_like 'does not advance to next stage' end context 'when import started' do - let!(:import_state) { create(:import_state, status: :started, project: project) } + before do + jira_import.start! + end it_behaves_like 'advance to next stage', :notes end diff --git a/spec/workers/gitlab/jira_import/stage/import_issues_worker_spec.rb b/spec/workers/gitlab/jira_import/stage/import_issues_worker_spec.rb index dca748a6ebc..6470a293461 100644 --- a/spec/workers/gitlab/jira_import/stage/import_issues_worker_spec.rb +++ b/spec/workers/gitlab/jira_import/stage/import_issues_worker_spec.rb @@ -4,43 +4,39 @@ require 'spec_helper' describe Gitlab::JiraImport::Stage::ImportIssuesWorker do let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project) } + let_it_be(:project) { create(:project, import_type: 'jira') } describe 'modules' do it_behaves_like 'include import workers modules' end describe '#perform' do - context 'when feature flag enabled' do + context 'when feature flag disabled' do before do stub_feature_flags(jira_issue_import: false) end - it_behaves_like 'exit import not started' + it_behaves_like 'cannot do jira import' + it_behaves_like 'does not advance to next stage' end context 'when feature flag enabled' do + let_it_be(:jira_import, reload: true) { create(:jira_import_state, :scheduled, project: project) } + before do stub_feature_flags(jira_issue_import: true) end context 'when import did not start' do - let!(:import_state) { create(:import_state, project: project) } - - it_behaves_like 'exit import not started' + it_behaves_like 'cannot do jira import' + it_behaves_like 'does not advance to next stage' end context 'when import started', :clean_gitlab_redis_cache do - let(:jira_import_data) do - data = JiraImportData.new - data << JiraImportData::JiraProjectDetails.new('XX', Time.now.strftime('%Y-%m-%d %H:%M:%S'), { user_id: user.id, name: user.name }) - data - end - let(:project) { create(:project, import_data: jira_import_data) } - let!(:jira_service) { create(:jira_service, project: project) } - let!(:import_state) { create(:import_state, status: :started, project: project) } + let_it_be(:jira_service) { create(:jira_service, project: project) } before do + jira_import.start! allow_next_instance_of(Gitlab::JiraImport::IssuesImporter) do |instance| allow(instance).to receive(:fetch_issues).and_return([]) end diff --git a/spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb b/spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb index a3e38cba115..f1562395546 100644 --- a/spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb +++ b/spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb @@ -4,41 +4,40 @@ require 'spec_helper' describe Gitlab::JiraImport::Stage::ImportLabelsWorker do let_it_be(:user) { create(:user) } - let_it_be(:project) { create(:project) } + let_it_be(:project) { create(:project, import_type: 'jira') } describe 'modules' do it_behaves_like 'include import workers modules' end describe '#perform' do - context 'when feature flag enabled' do + context 'when feature flag disabled' do before do stub_feature_flags(jira_issue_import: false) end - it_behaves_like 'exit import not started' + it_behaves_like 'cannot do jira import' + it_behaves_like 'does not advance to next stage' end context 'when feature flag enabled' do + let_it_be(:jira_import, reload: true) { create(:jira_import_state, :scheduled, project: project) } + before do stub_feature_flags(jira_issue_import: true) end context 'when import did not start' do - let!(:import_state) { create(:import_state, project: project) } - - it_behaves_like 'exit import not started' + it_behaves_like 'cannot do jira import' + it_behaves_like 'does not advance to next stage' end context 'when import started' do - let(:jira_import_data) do - data = JiraImportData.new - data << JiraImportData::JiraProjectDetails.new('XX', Time.now.strftime('%Y-%m-%d %H:%M:%S'), { user_id: user.id, name: user.name }) - data - end - let(:project) { create(:project, import_data: jira_import_data) } let!(:jira_service) { create(:jira_service, project: project) } - let!(:import_state) { create(:import_state, status: :started, project: project) } + + before do + jira_import.start! + end it_behaves_like 'advance to next stage', :issues diff --git a/spec/workers/gitlab/jira_import/stage/import_notes_worker_spec.rb b/spec/workers/gitlab/jira_import/stage/import_notes_worker_spec.rb index 7d1c29614e4..956898c1abc 100644 --- a/spec/workers/gitlab/jira_import/stage/import_notes_worker_spec.rb +++ b/spec/workers/gitlab/jira_import/stage/import_notes_worker_spec.rb @@ -3,34 +3,38 @@ require 'spec_helper' describe Gitlab::JiraImport::Stage::ImportNotesWorker do - let_it_be(:project) { create(:project) } + let_it_be(:project) { create(:project, import_type: 'jira') } describe 'modules' do it_behaves_like 'include import workers modules' end describe '#perform' do - context 'when feature flag enabled' do + context 'when feature flag disabled' do before do stub_feature_flags(jira_issue_import: false) end - it_behaves_like 'exit import not started' + it_behaves_like 'cannot do jira import' + it_behaves_like 'does not advance to next stage' end context 'when feature flag enabled' do + let_it_be(:jira_import) { create(:jira_import_state, :scheduled, project: project) } + before do stub_feature_flags(jira_issue_import: true) end context 'when import did not start' do - let!(:import_state) { create(:import_state, project: project) } - - it_behaves_like 'exit import not started' + it_behaves_like 'cannot do jira import' + it_behaves_like 'does not advance to next stage' end context 'when import started' do - let!(:import_state) { create(:import_state, status: :started, project: project) } + before do + jira_import.start! + end it_behaves_like 'advance to next stage', :finish end diff --git a/spec/workers/gitlab/jira_import/stage/start_import_worker_spec.rb b/spec/workers/gitlab/jira_import/stage/start_import_worker_spec.rb index d5e10a950bb..9cffe6e4ff7 100644 --- a/spec/workers/gitlab/jira_import/stage/start_import_worker_spec.rb +++ b/spec/workers/gitlab/jira_import/stage/start_import_worker_spec.rb @@ -3,16 +3,16 @@ require 'spec_helper' describe Gitlab::JiraImport::Stage::StartImportWorker do - let(:project) { create(:project, import_type: 'jira') } + let_it_be(:project) { create(:project, import_type: 'jira') } + let_it_be(:jid) { '12345678' } let(:worker) { described_class.new } - let(:jid) { '12345678' } describe 'modules' do it_behaves_like 'include import workers modules' end describe '#perform' do - context 'when feature flag not enabled' do + context 'when feature flag not disabled' do before do stub_feature_flags(jira_issue_import: false) end @@ -25,19 +25,13 @@ describe Gitlab::JiraImport::Stage::StartImportWorker do end context 'when feature flag enabled' do - let(:symbol_keys_project) do - { key: 'AA', scheduled_at: 2.days.ago.strftime('%Y-%m-%d %H:%M:%S'), scheduled_by: { 'user_id' => 1, 'name' => 'tester1' } } - end - let(:import_data) { JiraImportData.new( data: { 'jira' => { JiraImportData::FORCE_IMPORT_KEY => true, projects: [symbol_keys_project] } }) } + let_it_be(:jira_import, reload: true) { create(:jira_import_state, project: project, jid: jid) } before do stub_feature_flags(jira_issue_import: true) end context 'when import is not scheduled' do - let(:project) { create(:project, import_type: 'jira') } - let(:import_state) { create(:import_state, project: project, status: :none, jid: jid) } - it 'exits because import not started' do expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).not_to receive(:perform_async) @@ -46,32 +40,21 @@ describe Gitlab::JiraImport::Stage::StartImportWorker do end context 'when import is scheduled' do - let(:import_state) { create(:import_state, status: :scheduled, jid: jid) } - let(:project) { create(:project, import_type: 'jira', import_state: import_state) } - - context 'when this is a mirror sync in a jira imported project' do - it 'exits early' do - expect(Gitlab::Import::SetAsyncJid).not_to receive(:set_jid) - expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).not_to receive(:perform_async) - - worker.perform(project.id) - end + before do + jira_import.schedule! end - context 'when scheduled import is a hard triggered jira import and not a mirror' do - let!(:project) { create(:project, import_type: 'jira', import_data: import_data, import_state: import_state) } - - it 'advances to importing labels' do - expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).to receive(:perform_async) + it 'advances to importing labels' do + expect(Gitlab::JiraImport::Stage::ImportLabelsWorker).to receive(:perform_async) - worker.perform(project.id) - end + worker.perform(project.id) end end context 'when import is started' do - let!(:import_state) { create(:import_state, status: :started, jid: jid) } - let!(:project) { create(:project, import_type: 'jira', import_data: import_data, import_state: import_state) } + before do + jira_import.update!(status: :started) + end context 'when this is the same worker that stated import' do it 'advances to importing labels' do @@ -93,8 +76,9 @@ describe Gitlab::JiraImport::Stage::StartImportWorker do end context 'when import is finished' do - let!(:import_state) { create(:import_state, status: :finished, jid: jid) } - let!(:project) { create(:project, import_type: 'jira', import_data: import_data, import_state: import_state) } + before do + jira_import.update!(status: :finished) + end it 'advances to importing labels' do allow(worker).to receive(:jid).and_return(jid) |