From 983a0bba5d2a042c4a3bbb22432ec192c7501d82 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 20 Apr 2020 18:38:24 +0000 Subject: Add latest changes from gitlab-org/gitlab@12-10-stable-ee --- spec/lib/gitlab/application_context_spec.rb | 12 ++ .../create_resource_user_mention_spec.rb | 2 +- spec/lib/gitlab/ci/jwt_spec.rb | 124 +++++++++++++++++ spec/lib/gitlab/ci/status/bridge/factory_spec.rb | 72 ++++++++++ spec/lib/gitlab/current_settings_spec.rb | 27 +--- .../cycle_analytics/group_stage_summary_spec.rb | 66 +++++++-- .../gitlab/cycle_analytics/stage_summary_spec.rb | 90 ++++++++++-- spec/lib/gitlab/data_builder/pipeline_spec.rb | 2 +- spec/lib/gitlab/database/migration_helpers_spec.rb | 6 + .../gitlab/diff/formatters/text_formatter_spec.rb | 3 +- spec/lib/gitlab/diff/highlight_cache_spec.rb | 52 ++++++- spec/lib/gitlab/diff/position_spec.rb | 1 + spec/lib/gitlab/elasticsearch/logs/lines_spec.rb | 89 ++++++++++++ spec/lib/gitlab/elasticsearch/logs/pods_spec.rb | 35 +++++ spec/lib/gitlab/elasticsearch/logs_spec.rb | 89 ------------ spec/lib/gitlab/file_hook_spec.rb | 2 +- spec/lib/gitlab/gitaly_client_spec.rb | 9 ++ .../grape_logging/loggers/perf_logger_spec.rb | 2 +- .../loggers/queue_duration_logger_spec.rb | 4 +- .../group/legacy_tree_restorer_spec.rb | 153 +++++++++++++++++++++ .../import_export/group/tree_restorer_spec.rb | 153 --------------------- .../import_export/project/import_task_spec.rb | 2 +- .../import_export/project/tree_restorer_spec.rb | 2 +- .../gitlab/import_export/safe_model_attributes.yml | 2 + spec/lib/gitlab/instrumentation_helper_spec.rb | 10 +- spec/lib/gitlab/json_spec.rb | 91 ++++++++++++ .../gitlab/kubernetes/helm/base_command_spec.rb | 52 +++++++ .../gitlab/kubernetes/helm/init_command_spec.rb | 52 ------- .../gitlab/kubernetes/helm/install_command_spec.rb | 16 --- .../gitlab/kubernetes/helm/patch_command_spec.rb | 16 --- spec/lib/gitlab/project_template_spec.rb | 1 + spec/lib/gitlab/prometheus/adapter_spec.rb | 8 ++ .../sidekiq_logging/structured_logger_spec.rb | 18 ++- .../duplicate_jobs/server_spec.rb | 13 +- .../worker_context/server_spec.rb | 13 +- spec/lib/gitlab/sidekiq_middleware_spec.rb | 47 +++---- .../slash_commands/presenters/issue_show_spec.rb | 4 +- spec/lib/gitlab/utils_spec.rb | 18 ++- 38 files changed, 913 insertions(+), 445 deletions(-) create mode 100644 spec/lib/gitlab/ci/jwt_spec.rb create mode 100644 spec/lib/gitlab/ci/status/bridge/factory_spec.rb create mode 100644 spec/lib/gitlab/elasticsearch/logs/lines_spec.rb create mode 100644 spec/lib/gitlab/elasticsearch/logs/pods_spec.rb delete mode 100644 spec/lib/gitlab/elasticsearch/logs_spec.rb create mode 100644 spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb delete mode 100644 spec/lib/gitlab/import_export/group/tree_restorer_spec.rb create mode 100644 spec/lib/gitlab/json_spec.rb (limited to 'spec/lib/gitlab') diff --git a/spec/lib/gitlab/application_context_spec.rb b/spec/lib/gitlab/application_context_spec.rb index 0903ca6f9e8..6674ea059a0 100644 --- a/spec/lib/gitlab/application_context_spec.rb +++ b/spec/lib/gitlab/application_context_spec.rb @@ -42,6 +42,18 @@ describe Gitlab::ApplicationContext do end end + describe '.current_context_include?' do + it 'returns true if the key was present in the context' do + described_class.with_context(caller_id: "Hello") do + expect(described_class.current_context_include?(:caller_id)).to be(true) + end + end + + it 'returns false if the key was not present in the current context' do + expect(described_class.current_context_include?(:caller_id)).to be(false) + end + end + describe '#to_lazy_hash' do let(:user) { build(:user) } let(:project) { build(:project) } diff --git a/spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb b/spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb index ff8b9dd1005..d4f52a11ce7 100644 --- a/spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb +++ b/spec/lib/gitlab/background_migration/user_mentions/create_resource_user_mention_spec.rb @@ -79,7 +79,7 @@ describe Gitlab::BackgroundMigration::UserMentions::CreateResourceUserMention, s context 'migrate commit mentions' do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') } - let(:commit) { Commit.new(RepoHelpers.sample_commit, project.becomes(Project)) } + let(:commit) { Commit.new(RepoHelpers.sample_commit, project) } let(:commit_user_mentions) { table(:commit_user_mentions) } let!(:note1) { notes.create!(commit_id: commit.id, noteable_type: 'Commit', project_id: project.id, author_id: author.id, note: description_mentions) } diff --git a/spec/lib/gitlab/ci/jwt_spec.rb b/spec/lib/gitlab/ci/jwt_spec.rb new file mode 100644 index 00000000000..f2897708b08 --- /dev/null +++ b/spec/lib/gitlab/ci/jwt_spec.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Ci::Jwt do + let(:namespace) { build_stubbed(:namespace) } + let(:project) { build_stubbed(:project, namespace: namespace) } + let(:user) { build_stubbed(:user) } + let(:pipeline) { build_stubbed(:ci_pipeline, ref: 'auto-deploy-2020-03-19') } + let(:build) do + build_stubbed( + :ci_build, + project: project, + user: user, + pipeline: pipeline + ) + end + + describe '#payload' do + subject(:payload) { described_class.new(build, ttl: 30).payload } + + it 'has correct values for the standard JWT attributes' do + Timecop.freeze do + now = Time.now.to_i + + aggregate_failures do + expect(payload[:iss]).to eq(Settings.gitlab.host) + expect(payload[:iat]).to eq(now) + expect(payload[:exp]).to eq(now + 30) + expect(payload[:sub]).to eq("job_#{build.id}") + end + end + end + + it 'has correct values for the custom attributes' do + aggregate_failures do + expect(payload[:namespace_id]).to eq(namespace.id.to_s) + expect(payload[:namespace_path]).to eq(namespace.full_path) + expect(payload[:project_id]).to eq(project.id.to_s) + expect(payload[:project_path]).to eq(project.full_path) + expect(payload[:user_id]).to eq(user.id.to_s) + expect(payload[:user_email]).to eq(user.email) + expect(payload[:user_login]).to eq(user.username) + expect(payload[:pipeline_id]).to eq(pipeline.id.to_s) + expect(payload[:job_id]).to eq(build.id.to_s) + expect(payload[:ref]).to eq(pipeline.source_ref) + end + end + + it 'skips user related custom attributes if build has no user assigned' do + allow(build).to receive(:user).and_return(nil) + + expect { payload }.not_to raise_error + end + + describe 'ref type' do + context 'branches' do + it 'is "branch"' do + expect(payload[:ref_type]).to eq('branch') + end + end + + context 'tags' do + let(:build) { build_stubbed(:ci_build, :on_tag, project: project) } + + it 'is "tag"' do + expect(payload[:ref_type]).to eq('tag') + end + end + + context 'merge requests' do + let(:pipeline) { build_stubbed(:ci_pipeline, :detached_merge_request_pipeline) } + + it 'is "branch"' do + expect(payload[:ref_type]).to eq('branch') + end + end + end + + describe 'ref_protected' do + it 'is false when ref is not protected' do + expect(build).to receive(:protected).and_return(false) + + expect(payload[:ref_protected]).to eq('false') + end + + it 'is true when ref is protected' do + expect(build).to receive(:protected).and_return(true) + + expect(payload[:ref_protected]).to eq('true') + end + end + end + + describe '.for_build' do + let(:rsa_key) { OpenSSL::PKey::RSA.new(Rails.application.secrets.openid_connect_signing_key) } + + subject(:jwt) { described_class.for_build(build) } + + it 'generates JWT with key id' do + _payload, headers = JWT.decode(jwt, rsa_key.public_key, true, { algorithm: 'RS256' }) + + expect(headers['kid']).to eq(rsa_key.public_key.to_jwk['kid']) + end + + it 'generates JWT for the given job with ttl equal to build timeout' do + expect(build).to receive(:metadata_timeout).and_return(3_600) + + payload, _headers = JWT.decode(jwt, rsa_key.public_key, true, { algorithm: 'RS256' }) + ttl = payload["exp"] - payload["iat"] + + expect(ttl).to eq(3_600) + end + + it 'generates JWT for the given job with default ttl if build timeout is not set' do + expect(build).to receive(:metadata_timeout).and_return(nil) + + payload, _headers = JWT.decode(jwt, rsa_key.public_key, true, { algorithm: 'RS256' }) + ttl = payload["exp"] - payload["iat"] + + expect(ttl).to eq(5.minutes.to_i) + end + end +end diff --git a/spec/lib/gitlab/ci/status/bridge/factory_spec.rb b/spec/lib/gitlab/ci/status/bridge/factory_spec.rb new file mode 100644 index 00000000000..1f417781988 --- /dev/null +++ b/spec/lib/gitlab/ci/status/bridge/factory_spec.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Ci::Status::Bridge::Factory do + let(:user) { create(:user) } + let(:project) { bridge.project } + let(:status) { factory.fabricate! } + let(:factory) { described_class.new(bridge, user) } + + before do + stub_not_protect_default_branch + + project.add_developer(user) + end + + context 'when bridge is created' do + let(:bridge) { create(:ci_bridge) } + + it 'matches correct core status' do + expect(factory.core_status).to be_a Gitlab::Ci::Status::Created + end + + it 'fabricates status with correct details' do + expect(status.text).to eq s_('CiStatusText|created') + expect(status.icon).to eq 'status_created' + expect(status.favicon).to eq 'favicon_status_created' + expect(status.label).to be_nil + expect(status).not_to have_details + expect(status).not_to have_action + end + end + + context 'when bridge is failed' do + let(:bridge) { create(:ci_bridge, :failed) } + + it 'matches correct core status' do + expect(factory.core_status).to be_a Gitlab::Ci::Status::Failed + end + + it 'matches correct extended statuses' do + expect(factory.extended_statuses) + .to eq [Gitlab::Ci::Status::Bridge::Failed] + end + + it 'fabricates a failed bridge status' do + expect(status).to be_a Gitlab::Ci::Status::Bridge::Failed + end + + it 'fabricates status with correct details' do + expect(status.text).to eq s_('CiStatusText|failed') + expect(status.icon).to eq 'status_failed' + expect(status.favicon).to eq 'favicon_status_failed' + expect(status.label).to be_nil + expect(status.status_tooltip).to eq "#{s_('CiStatusText|failed')} - (unknown failure)" + expect(status).not_to have_details + expect(status).not_to have_action + end + + context 'failed with downstream_pipeline_creation_failed' do + before do + bridge.failure_reason = 'downstream_pipeline_creation_failed' + end + + it 'fabricates correct status_tooltip' do + expect(status.status_tooltip).to eq( + "#{s_('CiStatusText|failed')} - (downstream pipeline can not be created)" + ) + end + end + end +end diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb index adbd7eabd18..bfd9980ee9c 100644 --- a/spec/lib/gitlab/current_settings_spec.rb +++ b/spec/lib/gitlab/current_settings_spec.rb @@ -49,20 +49,16 @@ describe Gitlab::CurrentSettings do end end - context 'with DB unavailable' do - context 'and settings in cache' do - include_context 'with settings in cache' - - it 'fetches the settings from cache without issuing any query' do - expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0) - end + context 'in a Rake task with DB unavailable' do + before do + allow(Gitlab::Runtime).to receive(:rake?).and_return(true) + # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues + # during the initialization phase of the test suite, so instead let's mock the internals of it + allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false) end context 'and no settings in cache' do before do - # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues - # during the initialization phase of the test suite, so instead let's mock the internals of it - allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false) expect(ApplicationSetting).not_to receive(:current) end @@ -185,17 +181,6 @@ describe Gitlab::CurrentSettings do expect(described_class.current_application_settings).to eq(:current_settings) end end - - context 'when the application_settings table does not exist' do - it 'returns a FakeApplicationSettings object' do - expect(Gitlab::Database) - .to receive(:cached_table_exists?) - .with('application_settings') - .and_return(false) - - expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings) - end - end end end end diff --git a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb index 664009f140f..2242895f8ea 100644 --- a/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/group_stage_summary_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do end it "finds the number of issues created after it" do - expect(subject.first[:value]).to eq(2) + expect(subject.first[:value]).to eq('2') end context 'with subgroups' do @@ -29,7 +29,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do end it "finds issues from them" do - expect(subject.first[:value]).to eq(3) + expect(subject.first[:value]).to eq('3') end end @@ -41,7 +41,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do subject { described_class.new(group, options: { from: Time.now, current_user: user, projects: [project.id, project_2.id] }).data } it 'finds issues from those projects' do - expect(subject.first[:value]).to eq(2) + expect(subject.first[:value]).to eq('2') end end @@ -49,7 +49,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do subject { described_class.new(group, options: { from: 10.days.ago, to: Time.now, current_user: user }).data } it 'finds issues from 5 days ago' do - expect(subject.first[:value]).to eq(2) + expect(subject.first[:value]).to eq('2') end end end @@ -62,7 +62,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do end it "doesn't find issues from them" do - expect(subject.first[:value]).to eq(2) + expect(subject.first[:value]).to eq('2') end end end @@ -77,7 +77,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do end it "finds the number of deploys made created after it" do - expect(subject.second[:value]).to eq(2) + expect(subject.second[:value]).to eq('2') end context 'with subgroups' do @@ -88,7 +88,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do end it "finds deploys from them" do - expect(subject.second[:value]).to eq(3) + expect(subject.second[:value]).to eq('3') end end @@ -102,7 +102,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do subject { described_class.new(group, options: { from: Time.now, current_user: user, projects: [project.id, project_2.id] }).data } it 'shows deploys from those projects' do - expect(subject.second[:value]).to eq(2) + expect(subject.second[:value]).to eq('2') end end @@ -110,7 +110,7 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do subject { described_class.new(group, options: { from: 10.days.ago, to: Time.now, current_user: user }).data } it 'finds deployments from 5 days ago' do - expect(subject.second[:value]).to eq(2) + expect(subject.second[:value]).to eq('2') end end end @@ -123,7 +123,53 @@ describe Gitlab::CycleAnalytics::GroupStageSummary do end it "doesn't find deploys from them" do - expect(subject.second[:value]).to eq(0) + expect(subject.second[:value]).to eq('-') + end + end + end + + describe '#deployment_frequency' do + let(:from) { 6.days.ago } + let(:to) { nil } + + subject do + described_class.new(group, options: { + from: from, + to: to, + current_user: user + }).data.third + end + + it 'includes the unit: `per day`' do + expect(subject[:unit]).to eq(_('per day')) + end + + before do + Timecop.freeze(5.days.ago) do + create(:deployment, :success, project: project) + end + end + + context 'when `to` is nil' do + it 'includes range until now' do + # 1 deployment over 7 days + expect(subject[:value]).to eq('0.1') + end + end + + context 'when `to` is given' do + let(:from) { 10.days.ago } + let(:to) { 10.days.from_now } + + before do + Timecop.freeze(5.days.from_now) do + create(:deployment, :success, project: project) + end + end + + it 'returns deployment frequency within `from` and `to` range' do + # 2 deployments over 20 days + expect(subject[:value]).to eq('0.1') end end end diff --git a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb index 94edef20296..a86278871ff 100644 --- a/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb @@ -20,13 +20,13 @@ describe Gitlab::CycleAnalytics::StageSummary do Timecop.freeze(5.days.ago) { create(:issue, project: project) } Timecop.freeze(5.days.from_now) { create(:issue, project: project) } - expect(subject).to eq(1) + expect(subject).to eq('1') end it "doesn't find issues from other projects" do Timecop.freeze(5.days.from_now) { create(:issue, project: create(:project)) } - expect(subject).to eq(0) + expect(subject).to eq('-') end context 'when `to` parameter is given' do @@ -38,14 +38,14 @@ describe Gitlab::CycleAnalytics::StageSummary do it "doesn't find any record" do options[:to] = Time.now - expect(subject).to eq(0) + expect(subject).to eq('-') end it "finds records created between `from` and `to` range" do options[:from] = 10.days.ago options[:to] = 10.days.from_now - expect(subject).to eq(2) + expect(subject).to eq('2') end end end @@ -57,19 +57,19 @@ describe Gitlab::CycleAnalytics::StageSummary do Timecop.freeze(5.days.ago) { create_commit("Test message", project, user, 'master') } Timecop.freeze(5.days.from_now) { create_commit("Test message", project, user, 'master') } - expect(subject).to eq(1) + expect(subject).to eq('1') end it "doesn't find commits from other projects" do Timecop.freeze(5.days.from_now) { create_commit("Test message", create(:project, :repository), user, 'master') } - expect(subject).to eq(0) + expect(subject).to eq('-') end - it "finds a large (> 100) snumber of commits if present" do + it "finds a large (> 100) number of commits if present" do Timecop.freeze(5.days.from_now) { create_commit("Test message", project, user, 'master', count: 100) } - expect(subject).to eq(100) + expect(subject).to eq('100') end context 'when `to` parameter is given' do @@ -81,14 +81,14 @@ describe Gitlab::CycleAnalytics::StageSummary do it "doesn't find any record" do options[:to] = Time.now - expect(subject).to eq(0) + expect(subject).to eq('-') end it "finds records created between `from` and `to` range" do options[:from] = 10.days.ago options[:to] = 10.days.from_now - expect(subject).to eq(2) + expect(subject).to eq('2') end end @@ -118,7 +118,7 @@ describe Gitlab::CycleAnalytics::StageSummary do Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project) } Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project) } - expect(subject).to eq(1) + expect(subject).to eq('1') end it "doesn't find commits from other projects" do @@ -126,7 +126,7 @@ describe Gitlab::CycleAnalytics::StageSummary do create(:deployment, :success, project: create(:project, :repository)) end - expect(subject).to eq(0) + expect(subject).to eq('-') end context 'when `to` parameter is given' do @@ -138,14 +138,76 @@ describe Gitlab::CycleAnalytics::StageSummary do it "doesn't find any record" do options[:to] = Time.now - expect(subject).to eq(0) + expect(subject).to eq('-') end it "finds records created between `from` and `to` range" do options[:from] = 10.days.ago options[:to] = 10.days.from_now - expect(subject).to eq(2) + expect(subject).to eq('2') + end + end + end + + describe '#deployment_frequency' do + subject { stage_summary.fourth[:value] } + + it 'includes the unit: `per day`' do + expect(stage_summary.fourth[:unit]).to eq _('per day') + end + + before do + Timecop.freeze(5.days.ago) { create(:deployment, :success, project: project) } + end + + it 'returns 0.0 when there were deploys but the frequency was too low' do + options[:from] = 30.days.ago + + # 1 deployment over 30 days + # frequency of 0.03, rounded off to 0.0 + expect(subject).to eq('0') + end + + it 'returns `-` when there were no deploys' do + options[:from] = 4.days.ago + + # 0 deployment in the last 4 days + expect(subject).to eq('-') + end + + context 'when `to` is nil' do + it 'includes range until now' do + options[:from] = 6.days.ago + options[:to] = nil + + # 1 deployment over 7 days + expect(subject).to eq('0.1') + end + end + + context 'when `to` is given' do + before do + Timecop.freeze(5.days.from_now) { create(:deployment, :success, project: project) } + end + + it 'finds records created between `from` and `to` range' do + options[:from] = 10.days.ago + options[:to] = 10.days.from_now + + # 2 deployments over 20 days + expect(subject).to eq('0.1') + end + + context 'when `from` and `to` are within a day' do + it 'returns the number of deployments made on that day' do + Timecop.freeze(Time.now) do + create(:deployment, :success, project: project) + options[:from] = options[:to] = Time.now + + expect(subject).to eq('1') + end + end end end end diff --git a/spec/lib/gitlab/data_builder/pipeline_spec.rb b/spec/lib/gitlab/data_builder/pipeline_spec.rb index da22da8de0f..519f5873d75 100644 --- a/spec/lib/gitlab/data_builder/pipeline_spec.rb +++ b/spec/lib/gitlab/data_builder/pipeline_spec.rb @@ -83,7 +83,7 @@ describe Gitlab::DataBuilder::Pipeline do expect(merge_request_attrs[:target_branch]).to eq(merge_request.target_branch) expect(merge_request_attrs[:target_project_id]).to eq(merge_request.target_project_id) expect(merge_request_attrs[:state]).to eq(merge_request.state) - expect(merge_request_attrs[:merge_status]).to eq(merge_request.merge_status) + expect(merge_request_attrs[:merge_status]).to eq(merge_request.public_merge_status) expect(merge_request_attrs[:url]).to eq("http://localhost/#{merge_request.target_project.full_path}/-/merge_requests/#{merge_request.iid}") end end diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index 3db9320c021..3a0148615b9 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -215,6 +215,7 @@ describe Gitlab::Database::MigrationHelpers do context 'ON DELETE statements' do context 'on_delete: :nullify' do it 'appends ON DELETE SET NULL statement' do + expect(model).to receive(:with_lock_retries).and_call_original expect(model).to receive(:disable_statement_timeout).and_call_original expect(model).to receive(:execute).with(/statement_timeout/) expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/) @@ -230,6 +231,7 @@ describe Gitlab::Database::MigrationHelpers do context 'on_delete: :cascade' do it 'appends ON DELETE CASCADE statement' do + expect(model).to receive(:with_lock_retries).and_call_original expect(model).to receive(:disable_statement_timeout).and_call_original expect(model).to receive(:execute).with(/statement_timeout/) expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/) @@ -245,6 +247,7 @@ describe Gitlab::Database::MigrationHelpers do context 'on_delete: nil' do it 'appends no ON DELETE statement' do + expect(model).to receive(:with_lock_retries).and_call_original expect(model).to receive(:disable_statement_timeout).and_call_original expect(model).to receive(:execute).with(/statement_timeout/) expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT/) @@ -261,6 +264,7 @@ describe Gitlab::Database::MigrationHelpers do context 'when no custom key name is supplied' do it 'creates a concurrent foreign key and validates it' do + expect(model).to receive(:with_lock_retries).and_call_original expect(model).to receive(:disable_statement_timeout).and_call_original expect(model).to receive(:execute).with(/statement_timeout/) expect(model).to receive(:execute).ordered.with(/NOT VALID/) @@ -287,6 +291,7 @@ describe Gitlab::Database::MigrationHelpers do context 'when a custom key name is supplied' do context 'for creating a new foreign key for a column that does not presently exist' do it 'creates a new foreign key' do + expect(model).to receive(:with_lock_retries).and_call_original expect(model).to receive(:disable_statement_timeout).and_call_original expect(model).to receive(:execute).with(/statement_timeout/) expect(model).to receive(:execute).ordered.with(/NOT VALID/) @@ -314,6 +319,7 @@ describe Gitlab::Database::MigrationHelpers do context 'when the supplied key name is different from the existing foreign key name' do it 'creates a new foreign key' do + expect(model).to receive(:with_lock_retries).and_call_original expect(model).to receive(:disable_statement_timeout).and_call_original expect(model).to receive(:execute).with(/statement_timeout/) expect(model).to receive(:execute).ordered.with(/NOT VALID/) diff --git a/spec/lib/gitlab/diff/formatters/text_formatter_spec.rb b/spec/lib/gitlab/diff/formatters/text_formatter_spec.rb index 33d4994f5db..e275ebef2c9 100644 --- a/spec/lib/gitlab/diff/formatters/text_formatter_spec.rb +++ b/spec/lib/gitlab/diff/formatters/text_formatter_spec.rb @@ -9,7 +9,8 @@ describe Gitlab::Diff::Formatters::TextFormatter do start_sha: 456, head_sha: 789, old_path: 'old_path.txt', - new_path: 'new_path.txt' + new_path: 'new_path.txt', + line_range: nil } end diff --git a/spec/lib/gitlab/diff/highlight_cache_spec.rb b/spec/lib/gitlab/diff/highlight_cache_spec.rb index a16e5e185bb..3c128aad976 100644 --- a/spec/lib/gitlab/diff/highlight_cache_spec.rb +++ b/spec/lib/gitlab/diff/highlight_cache_spec.rb @@ -113,7 +113,7 @@ describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do allow(redis).to receive(:info).and_return({ "redis_version" => "3.0.0" }) expect(described_class.gitlab_redis_diff_caching_memory_usage_bytes) - .not_to receive(:observe).and_call_original + .not_to receive(:observe) cache.send(:write_to_redis_hash, diff_hash) end @@ -163,6 +163,56 @@ describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do end end + describe "GZip usage" do + let(:diff_file) do + diffs = merge_request.diffs + raw_diff = diffs.diffable.raw_diffs(diffs.diff_options.merge(paths: ['CHANGELOG'])).first + Gitlab::Diff::File.new(raw_diff, + repository: diffs.project.repository, + diff_refs: diffs.diff_refs, + fallback_diff_refs: diffs.fallback_diff_refs) + end + + context "feature flag :gzip_diff_cache disabled" do + before do + stub_feature_flags(gzip_diff_cache: true) + end + + it "uses ActiveSupport::Gzip when reading from the cache" do + expect(ActiveSupport::Gzip).to receive(:decompress).at_least(:once).and_call_original + + cache.write_if_empty + cache.decorate(diff_file) + end + + it "uses ActiveSupport::Gzip to compress data when writing to cache" do + expect(ActiveSupport::Gzip).to receive(:compress).and_call_original + + cache.send(:write_to_redis_hash, diff_hash) + end + end + + context "feature flag :gzip_diff_cache disabled" do + before do + stub_feature_flags(gzip_diff_cache: false) + end + + it "doesn't use ActiveSupport::Gzip when reading from the cache" do + expect(ActiveSupport::Gzip).not_to receive(:decompress) + + cache.write_if_empty + cache.decorate(diff_file) + end + + it "doesn't use ActiveSupport::Gzip to compress data when writing to cache" do + expect(ActiveSupport::Gzip).not_to receive(:compress) + + expect { cache.send(:write_to_redis_hash, diff_hash) } + .to change { Gitlab::Redis::Cache.with { |r| r.hgetall(cache_key) } } + end + end + end + describe 'metrics' do it 'defines :gitlab_redis_diff_caching_memory_usage_bytes histogram' do expect(described_class).to respond_to(:gitlab_redis_diff_caching_memory_usage_bytes) diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb index 4b11ff16c38..a83c0f35d92 100644 --- a/spec/lib/gitlab/diff/position_spec.rb +++ b/spec/lib/gitlab/diff/position_spec.rb @@ -28,6 +28,7 @@ describe Gitlab::Diff::Position do new_path: "files/ruby/popen.rb", old_line: nil, new_line: 14, + line_range: nil, base_sha: nil, head_sha: nil, start_sha: nil, diff --git a/spec/lib/gitlab/elasticsearch/logs/lines_spec.rb b/spec/lib/gitlab/elasticsearch/logs/lines_spec.rb new file mode 100644 index 00000000000..8b6a19fa2c5 --- /dev/null +++ b/spec/lib/gitlab/elasticsearch/logs/lines_spec.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Elasticsearch::Logs::Lines do + let(:client) { Elasticsearch::Transport::Client } + + let(:es_message_1) { { timestamp: "2019-12-13T14:35:34.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [25/Oct/2019:08:03:22 UTC] \"GET / HTTP/1.1\" 200 13" } } + let(:es_message_2) { { timestamp: "2019-12-13T14:35:35.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [27/Oct/2019:23:49:54 UTC] \"GET / HTTP/1.1\" 200 13" } } + let(:es_message_3) { { timestamp: "2019-12-13T14:35:36.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [04/Nov/2019:23:09:24 UTC] \"GET / HTTP/1.1\" 200 13" } } + let(:es_message_4) { { timestamp: "2019-12-13T14:35:37.034Z", pod: "production-6866bc8974-m4sk4", message: "- -\u003e /" } } + + let(:es_response) { JSON.parse(fixture_file('lib/elasticsearch/logs_response.json')) } + + subject { described_class.new(client) } + + let(:namespace) { "autodevops-deploy-9-production" } + let(:pod_name) { "production-6866bc8974-m4sk4" } + let(:container_name) { "auto-deploy-app" } + let(:search) { "foo +bar "} + let(:start_time) { "2019-12-13T14:35:34.034Z" } + let(:end_time) { "2019-12-13T14:35:34.034Z" } + let(:cursor) { "9999934,1572449784442" } + + let(:body) { JSON.parse(fixture_file('lib/elasticsearch/query.json')) } + let(:body_with_container) { JSON.parse(fixture_file('lib/elasticsearch/query_with_container.json')) } + let(:body_with_search) { JSON.parse(fixture_file('lib/elasticsearch/query_with_search.json')) } + let(:body_with_times) { JSON.parse(fixture_file('lib/elasticsearch/query_with_times.json')) } + let(:body_with_start_time) { JSON.parse(fixture_file('lib/elasticsearch/query_with_start_time.json')) } + let(:body_with_end_time) { JSON.parse(fixture_file('lib/elasticsearch/query_with_end_time.json')) } + let(:body_with_cursor) { JSON.parse(fixture_file('lib/elasticsearch/query_with_cursor.json')) } + + RSpec::Matchers.define :a_hash_equal_to_json do |expected| + match do |actual| + actual.as_json == expected + end + end + + describe '#pod_logs' do + it 'returns the logs as an array' do + expect(client).to receive(:search).with(body: a_hash_equal_to_json(body)).and_return(es_response) + + result = subject.pod_logs(namespace, pod_name: pod_name) + expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) + end + + it 'can further filter the logs by container name' do + expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_container)).and_return(es_response) + + result = subject.pod_logs(namespace, pod_name: pod_name, container_name: container_name) + expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) + end + + it 'can further filter the logs by search' do + expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_search)).and_return(es_response) + + result = subject.pod_logs(namespace, pod_name: pod_name, search: search) + expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) + end + + it 'can further filter the logs by start_time and end_time' do + expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_times)).and_return(es_response) + + result = subject.pod_logs(namespace, pod_name: pod_name, start_time: start_time, end_time: end_time) + expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) + end + + it 'can further filter the logs by only start_time' do + expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_start_time)).and_return(es_response) + + result = subject.pod_logs(namespace, pod_name: pod_name, start_time: start_time) + expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) + end + + it 'can further filter the logs by only end_time' do + expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_end_time)).and_return(es_response) + + result = subject.pod_logs(namespace, pod_name: pod_name, end_time: end_time) + expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) + end + + it 'can search after a cursor' do + expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_cursor)).and_return(es_response) + + result = subject.pod_logs(namespace, pod_name: pod_name, cursor: cursor) + expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) + end + end +end diff --git a/spec/lib/gitlab/elasticsearch/logs/pods_spec.rb b/spec/lib/gitlab/elasticsearch/logs/pods_spec.rb new file mode 100644 index 00000000000..0a4ab0780c5 --- /dev/null +++ b/spec/lib/gitlab/elasticsearch/logs/pods_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Elasticsearch::Logs::Pods do + let(:client) { Elasticsearch::Transport::Client } + + let(:es_query) { JSON.parse(fixture_file('lib/elasticsearch/pods_query.json'), symbolize_names: true) } + let(:es_response) { JSON.parse(fixture_file('lib/elasticsearch/pods_response.json')) } + let(:namespace) { "autodevops-deploy-9-production" } + + subject { described_class.new(client) } + + describe '#pods' do + it 'returns the pods' do + expect(client).to receive(:search).with(body: es_query).and_return(es_response) + + result = subject.pods(namespace) + expect(result).to eq([ + { + name: "runner-gitlab-runner-7bbfb5dcb5-p6smb", + container_names: %w[runner-gitlab-runner] + }, + { + name: "elastic-stack-elasticsearch-master-1", + container_names: %w[elasticsearch chown sysctl] + }, + { + name: "ingress-nginx-ingress-controller-76449bcc8d-8qgl6", + container_names: %w[nginx-ingress-controller] + } + ]) + end + end +end diff --git a/spec/lib/gitlab/elasticsearch/logs_spec.rb b/spec/lib/gitlab/elasticsearch/logs_spec.rb deleted file mode 100644 index 6b9d1dbef99..00000000000 --- a/spec/lib/gitlab/elasticsearch/logs_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::Elasticsearch::Logs do - let(:client) { Elasticsearch::Transport::Client } - - let(:es_message_1) { { timestamp: "2019-12-13T14:35:34.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [25/Oct/2019:08:03:22 UTC] \"GET / HTTP/1.1\" 200 13" } } - let(:es_message_2) { { timestamp: "2019-12-13T14:35:35.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [27/Oct/2019:23:49:54 UTC] \"GET / HTTP/1.1\" 200 13" } } - let(:es_message_3) { { timestamp: "2019-12-13T14:35:36.034Z", pod: "production-6866bc8974-m4sk4", message: "10.8.2.1 - - [04/Nov/2019:23:09:24 UTC] \"GET / HTTP/1.1\" 200 13" } } - let(:es_message_4) { { timestamp: "2019-12-13T14:35:37.034Z", pod: "production-6866bc8974-m4sk4", message: "- -\u003e /" } } - - let(:es_response) { JSON.parse(fixture_file('lib/elasticsearch/logs_response.json')) } - - subject { described_class.new(client) } - - let(:namespace) { "autodevops-deploy-9-production" } - let(:pod_name) { "production-6866bc8974-m4sk4" } - let(:container_name) { "auto-deploy-app" } - let(:search) { "foo +bar "} - let(:start_time) { "2019-12-13T14:35:34.034Z" } - let(:end_time) { "2019-12-13T14:35:34.034Z" } - let(:cursor) { "9999934,1572449784442" } - - let(:body) { JSON.parse(fixture_file('lib/elasticsearch/query.json')) } - let(:body_with_container) { JSON.parse(fixture_file('lib/elasticsearch/query_with_container.json')) } - let(:body_with_search) { JSON.parse(fixture_file('lib/elasticsearch/query_with_search.json')) } - let(:body_with_times) { JSON.parse(fixture_file('lib/elasticsearch/query_with_times.json')) } - let(:body_with_start_time) { JSON.parse(fixture_file('lib/elasticsearch/query_with_start_time.json')) } - let(:body_with_end_time) { JSON.parse(fixture_file('lib/elasticsearch/query_with_end_time.json')) } - let(:body_with_cursor) { JSON.parse(fixture_file('lib/elasticsearch/query_with_cursor.json')) } - - RSpec::Matchers.define :a_hash_equal_to_json do |expected| - match do |actual| - actual.as_json == expected - end - end - - describe '#pod_logs' do - it 'returns the logs as an array' do - expect(client).to receive(:search).with(body: a_hash_equal_to_json(body)).and_return(es_response) - - result = subject.pod_logs(namespace, pod_name: pod_name) - expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) - end - - it 'can further filter the logs by container name' do - expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_container)).and_return(es_response) - - result = subject.pod_logs(namespace, pod_name: pod_name, container_name: container_name) - expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) - end - - it 'can further filter the logs by search' do - expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_search)).and_return(es_response) - - result = subject.pod_logs(namespace, pod_name: pod_name, search: search) - expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) - end - - it 'can further filter the logs by start_time and end_time' do - expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_times)).and_return(es_response) - - result = subject.pod_logs(namespace, pod_name: pod_name, start_time: start_time, end_time: end_time) - expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) - end - - it 'can further filter the logs by only start_time' do - expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_start_time)).and_return(es_response) - - result = subject.pod_logs(namespace, pod_name: pod_name, start_time: start_time) - expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) - end - - it 'can further filter the logs by only end_time' do - expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_end_time)).and_return(es_response) - - result = subject.pod_logs(namespace, pod_name: pod_name, end_time: end_time) - expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) - end - - it 'can search after a cursor' do - expect(client).to receive(:search).with(body: a_hash_equal_to_json(body_with_cursor)).and_return(es_response) - - result = subject.pod_logs(namespace, pod_name: pod_name, cursor: cursor) - expect(result).to eq(logs: [es_message_4, es_message_3, es_message_2, es_message_1], cursor: cursor) - end - end -end diff --git a/spec/lib/gitlab/file_hook_spec.rb b/spec/lib/gitlab/file_hook_spec.rb index d184eb483d4..fda3583289b 100644 --- a/spec/lib/gitlab/file_hook_spec.rb +++ b/spec/lib/gitlab/file_hook_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::FileHook do - let(:file_hook) { Rails.root.join('plugins', 'test.rb') } + let(:file_hook) { Rails.root.join('file_hooks', 'test.rb') } let(:tmp_file) { Tempfile.new('file_hook-dump') } let(:file_hook_source) do diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index b03c1feb429..2c6aee58326 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -19,6 +19,15 @@ describe Gitlab::GitalyClient do }) end + describe '.query_time', :request_store do + it 'increments query times' do + subject.query_time += 0.451 + subject.query_time += 0.322 + + expect(subject.query_time).to eq(0.773) + end + end + describe '.long_timeout' do context 'default case' do it { expect(subject.long_timeout).to eq(6.hours) } diff --git a/spec/lib/gitlab/grape_logging/loggers/perf_logger_spec.rb b/spec/lib/gitlab/grape_logging/loggers/perf_logger_spec.rb index 6f20b8877e0..09ba4b89a1a 100644 --- a/spec/lib/gitlab/grape_logging/loggers/perf_logger_spec.rb +++ b/spec/lib/gitlab/grape_logging/loggers/perf_logger_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::GrapeLogging::Loggers::PerfLogger do payload = subject.parameters(mock_request, nil) expect(payload[:redis_calls]).to eq(1) - expect(payload[:redis_duration_ms]).to be >= 0 + expect(payload[:redis_duration_s]).to be >= 0 end end end diff --git a/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb b/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb index c0762e9892b..17c0659327d 100644 --- a/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb +++ b/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb @@ -25,11 +25,11 @@ describe Gitlab::GrapeLogging::Loggers::QueueDurationLogger do ) end - it 'returns the correct duration in ms' do + it 'returns the correct duration in seconds' do Timecop.freeze(start_time) do subject.before - expect(subject.parameters(mock_request, nil)).to eq( { 'queue_duration': 1.hour.to_f * 1000 }) + expect(subject.parameters(mock_request, nil)).to eq( { 'queue_duration_s': 1.hour.to_f }) end end end diff --git a/spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb new file mode 100644 index 00000000000..3030cdf4cf8 --- /dev/null +++ b/spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb @@ -0,0 +1,153 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::ImportExport::Group::LegacyTreeRestorer do + include ImportExport::CommonUtil + + let(:shared) { Gitlab::ImportExport::Shared.new(group) } + + describe 'restore group tree' do + before_all do + # Using an admin for import, so we can check assignment of existing members + user = create(:admin, email: 'root@gitlabexample.com') + create(:user, email: 'adriene.mcclure@gitlabexample.com') + create(:user, email: 'gwendolyn_robel@gitlabexample.com') + + RSpec::Mocks.with_temporary_scope do + @group = create(:group, name: 'group', path: 'group') + @shared = Gitlab::ImportExport::Shared.new(@group) + + setup_import_export_config('group_exports/complex') + + group_tree_restorer = described_class.new(user: user, shared: @shared, group: @group, group_hash: nil) + + @restored_group_json = group_tree_restorer.restore + end + end + + context 'JSON' do + it 'restores models based on JSON' do + expect(@restored_group_json).to be_truthy + end + + it 'has the group description' do + expect(Group.find_by_path('group').description).to eq('Group Description') + end + + it 'has group labels' do + expect(@group.labels.count).to eq(10) + end + + context 'issue boards' do + it 'has issue boards' do + expect(@group.boards.count).to eq(1) + end + + it 'has board label lists' do + lists = @group.boards.find_by(name: 'first board').lists + + expect(lists.count).to eq(3) + expect(lists.first.label.title).to eq('TSL') + expect(lists.second.label.title).to eq('Sosync') + end + end + + it 'has badges' do + expect(@group.badges.count).to eq(1) + end + + it 'has milestones' do + expect(@group.milestones.count).to eq(5) + end + + it 'has group children' do + expect(@group.children.count).to eq(2) + end + + it 'has group members' do + expect(@group.members.map(&:user).map(&:email)).to contain_exactly('root@gitlabexample.com', 'adriene.mcclure@gitlabexample.com', 'gwendolyn_robel@gitlabexample.com') + end + end + end + + context 'excluded attributes' do + let!(:source_user) { create(:user, id: 123) } + let!(:importer_user) { create(:user) } + let(:group) { create(:group) } + let(:shared) { Gitlab::ImportExport::Shared.new(group) } + let(:group_tree_restorer) { described_class.new(user: importer_user, shared: shared, group: group, group_hash: nil) } + let(:group_json) { ActiveSupport::JSON.decode(IO.read(File.join(shared.export_path, 'group.json'))) } + + shared_examples 'excluded attributes' do + excluded_attributes = %w[ + id + owner_id + parent_id + created_at + updated_at + runners_token + runners_token_encrypted + saml_discovery_token + ] + + before do + group.add_owner(importer_user) + + setup_import_export_config('group_exports/complex') + end + + excluded_attributes.each do |excluded_attribute| + it 'does not allow override of excluded attributes' do + expect(group_json[excluded_attribute]).not_to eq(group.public_send(excluded_attribute)) + end + end + end + + include_examples 'excluded attributes' + end + + context 'group.json file access check' do + let(:user) { create(:user) } + let!(:group) { create(:group, name: 'group2', path: 'group2') } + let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group, group_hash: nil) } + let(:restored_group_json) { group_tree_restorer.restore } + + it 'does not read a symlink' do + Dir.mktmpdir do |tmpdir| + setup_symlink(tmpdir, 'group.json') + allow(shared).to receive(:export_path).and_call_original + + expect(group_tree_restorer.restore).to eq(false) + expect(shared.errors).to include('Incorrect JSON format') + end + end + end + + context 'group visibility levels' do + let(:user) { create(:user) } + let(:shared) { Gitlab::ImportExport::Shared.new(group) } + let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group, group_hash: nil) } + + before do + setup_import_export_config(filepath) + + group_tree_restorer.restore + end + + shared_examples 'with visibility level' do |visibility_level, expected_visibilities| + context "when visibility level is #{visibility_level}" do + let(:group) { create(:group, visibility_level) } + let(:filepath) { "group_exports/visibility_levels/#{visibility_level}" } + + it "imports all subgroups as #{visibility_level}" do + expect(group.children.map(&:visibility_level)).to eq(expected_visibilities) + end + end + end + + include_examples 'with visibility level', :public, [20, 10, 0] + include_examples 'with visibility level', :private, [0, 0, 0] + include_examples 'with visibility level', :internal, [10, 10, 0] + end +end diff --git a/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb deleted file mode 100644 index fc7e4737d13..00000000000 --- a/spec/lib/gitlab/import_export/group/tree_restorer_spec.rb +++ /dev/null @@ -1,153 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe Gitlab::ImportExport::Group::TreeRestorer do - include ImportExport::CommonUtil - - let(:shared) { Gitlab::ImportExport::Shared.new(group) } - - describe 'restore group tree' do - before_all do - # Using an admin for import, so we can check assignment of existing members - user = create(:admin, email: 'root@gitlabexample.com') - create(:user, email: 'adriene.mcclure@gitlabexample.com') - create(:user, email: 'gwendolyn_robel@gitlabexample.com') - - RSpec::Mocks.with_temporary_scope do - @group = create(:group, name: 'group', path: 'group') - @shared = Gitlab::ImportExport::Shared.new(@group) - - setup_import_export_config('group_exports/complex') - - group_tree_restorer = described_class.new(user: user, shared: @shared, group: @group, group_hash: nil) - - @restored_group_json = group_tree_restorer.restore - end - end - - context 'JSON' do - it 'restores models based on JSON' do - expect(@restored_group_json).to be_truthy - end - - it 'has the group description' do - expect(Group.find_by_path('group').description).to eq('Group Description') - end - - it 'has group labels' do - expect(@group.labels.count).to eq(10) - end - - context 'issue boards' do - it 'has issue boards' do - expect(@group.boards.count).to eq(1) - end - - it 'has board label lists' do - lists = @group.boards.find_by(name: 'first board').lists - - expect(lists.count).to eq(3) - expect(lists.first.label.title).to eq('TSL') - expect(lists.second.label.title).to eq('Sosync') - end - end - - it 'has badges' do - expect(@group.badges.count).to eq(1) - end - - it 'has milestones' do - expect(@group.milestones.count).to eq(5) - end - - it 'has group children' do - expect(@group.children.count).to eq(2) - end - - it 'has group members' do - expect(@group.members.map(&:user).map(&:email)).to contain_exactly('root@gitlabexample.com', 'adriene.mcclure@gitlabexample.com', 'gwendolyn_robel@gitlabexample.com') - end - end - end - - context 'excluded attributes' do - let!(:source_user) { create(:user, id: 123) } - let!(:importer_user) { create(:user) } - let(:group) { create(:group) } - let(:shared) { Gitlab::ImportExport::Shared.new(group) } - let(:group_tree_restorer) { described_class.new(user: importer_user, shared: shared, group: group, group_hash: nil) } - let(:group_json) { ActiveSupport::JSON.decode(IO.read(File.join(shared.export_path, 'group.json'))) } - - shared_examples 'excluded attributes' do - excluded_attributes = %w[ - id - owner_id - parent_id - created_at - updated_at - runners_token - runners_token_encrypted - saml_discovery_token - ] - - before do - group.add_owner(importer_user) - - setup_import_export_config('group_exports/complex') - end - - excluded_attributes.each do |excluded_attribute| - it 'does not allow override of excluded attributes' do - expect(group_json[excluded_attribute]).not_to eq(group.public_send(excluded_attribute)) - end - end - end - - include_examples 'excluded attributes' - end - - context 'group.json file access check' do - let(:user) { create(:user) } - let!(:group) { create(:group, name: 'group2', path: 'group2') } - let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group, group_hash: nil) } - let(:restored_group_json) { group_tree_restorer.restore } - - it 'does not read a symlink' do - Dir.mktmpdir do |tmpdir| - setup_symlink(tmpdir, 'group.json') - allow(shared).to receive(:export_path).and_call_original - - expect(group_tree_restorer.restore).to eq(false) - expect(shared.errors).to include('Incorrect JSON format') - end - end - end - - context 'group visibility levels' do - let(:user) { create(:user) } - let(:shared) { Gitlab::ImportExport::Shared.new(group) } - let(:group_tree_restorer) { described_class.new(user: user, shared: shared, group: group, group_hash: nil) } - - before do - setup_import_export_config(filepath) - - group_tree_restorer.restore - end - - shared_examples 'with visibility level' do |visibility_level, expected_visibilities| - context "when visibility level is #{visibility_level}" do - let(:group) { create(:group, visibility_level) } - let(:filepath) { "group_exports/visibility_levels/#{visibility_level}" } - - it "imports all subgroups as #{visibility_level}" do - expect(group.children.map(&:visibility_level)).to eq(expected_visibilities) - end - end - end - - include_examples 'with visibility level', :public, [20, 10, 0] - include_examples 'with visibility level', :private, [0, 0, 0] - include_examples 'with visibility level', :internal, [10, 10, 0] - end -end diff --git a/spec/lib/gitlab/import_export/project/import_task_spec.rb b/spec/lib/gitlab/import_export/project/import_task_spec.rb index f7b9cbaa095..4f4fcd3ad8a 100644 --- a/spec/lib/gitlab/import_export/project/import_task_spec.rb +++ b/spec/lib/gitlab/import_export/project/import_task_spec.rb @@ -2,7 +2,7 @@ require 'rake_helper' -describe Gitlab::ImportExport::Project::ImportTask do +describe Gitlab::ImportExport::Project::ImportTask, :request_store do let(:username) { 'root' } let(:namespace_path) { username } let!(:user) { create(:user, username: username) } diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb index 80ae9a08257..04e8bd05666 100644 --- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb @@ -6,7 +6,7 @@ def match_mr1_note(content_regex) MergeRequest.find_by(title: 'MR1').notes.select { |n| n.note.match(/#{content_regex}/)}.first end -describe Gitlab::ImportExport::Project::TreeRestorer, quarantine: { flaky: 'https://gitlab.com/gitlab-org/gitlab/-/issues/213793' } do +describe Gitlab::ImportExport::Project::TreeRestorer do include ImportExport::CommonUtil let(:shared) { project.import_export_shared } diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 55b907fff7c..88d7fdaef36 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -595,6 +595,7 @@ ProjectFeature: - builds_access_level - repository_access_level - pages_access_level +- metrics_dashboard_access_level - created_at - updated_at ProtectedBranch::MergeAccessLevel: @@ -811,6 +812,7 @@ ContainerExpirationPolicy: - next_run_at - project_id - name_regex +- name_regex_keep - cadence - older_than - keep_n diff --git a/spec/lib/gitlab/instrumentation_helper_spec.rb b/spec/lib/gitlab/instrumentation_helper_spec.rb index 9788c9f4a3c..858fa044a52 100644 --- a/spec/lib/gitlab/instrumentation_helper_spec.rb +++ b/spec/lib/gitlab/instrumentation_helper_spec.rb @@ -26,7 +26,7 @@ describe Gitlab::InstrumentationHelper do subject expect(payload[:gitaly_calls]).to eq(1) - expect(payload[:gitaly_duration]).to be >= 0 + expect(payload[:gitaly_duration_s]).to be >= 0 expect(payload[:redis_calls]).to be_nil expect(payload[:redis_duration_ms]).to be_nil end @@ -39,7 +39,7 @@ describe Gitlab::InstrumentationHelper do subject expect(payload[:redis_calls]).to eq(1) - expect(payload[:redis_duration_ms]).to be >= 0 + expect(payload[:redis_duration_s]).to be >= 0 expect(payload[:gitaly_calls]).to be_nil expect(payload[:gitaly_duration]).to be_nil end @@ -49,12 +49,12 @@ describe Gitlab::InstrumentationHelper do describe '.queue_duration_for_job' do where(:enqueued_at, :created_at, :time_now, :expected_duration) do "2019-06-01T00:00:00.000+0000" | nil | "2019-06-01T02:00:00.000+0000" | 2.hours.to_f - "2019-06-01T02:00:00.000+0000" | nil | "2019-06-01T02:00:00.001+0000" | 0.001 + "2019-06-01T02:00:00.000+0000" | nil | "2019-06-01T02:00:00.001+0000" | 0.0 "2019-06-01T02:00:00.000+0000" | "2019-05-01T02:00:00.000+0000" | "2019-06-01T02:00:01.000+0000" | 1 - nil | "2019-06-01T02:00:00.000+0000" | "2019-06-01T02:00:00.001+0000" | 0.001 + nil | "2019-06-01T02:00:00.000+0000" | "2019-06-01T02:00:00.001+0000" | 0.0 nil | nil | "2019-06-01T02:00:00.001+0000" | nil "2019-06-01T02:00:00.000+0200" | nil | "2019-06-01T02:00:00.000-0200" | 4.hours.to_f - 1571825569.998168 | nil | "2019-10-23T12:13:16.000+0200" | 26.001832 + 1571825569.998168 | nil | "2019-10-23T12:13:16.000+0200" | 26.00 1571825569 | nil | "2019-10-23T12:13:16.000+0200" | 27 "invalid_date" | nil | "2019-10-23T12:13:16.000+0200" | nil "" | nil | "2019-10-23T12:13:16.000+0200" | nil diff --git a/spec/lib/gitlab/json_spec.rb b/spec/lib/gitlab/json_spec.rb new file mode 100644 index 00000000000..5186ab041da --- /dev/null +++ b/spec/lib/gitlab/json_spec.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Gitlab::Json do + describe ".parse" do + it "parses an object" do + expect(subject.parse('{ "foo": "bar" }')).to eq({ "foo" => "bar" }) + end + + it "parses an array" do + expect(subject.parse('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }]) + end + + it "raises an error on a string" do + expect { subject.parse('"foo"') }.to raise_error(JSON::ParserError) + end + + it "raises an error on a true bool" do + expect { subject.parse("true") }.to raise_error(JSON::ParserError) + end + + it "raises an error on a false bool" do + expect { subject.parse("false") }.to raise_error(JSON::ParserError) + end + end + + describe ".parse!" do + it "parses an object" do + expect(subject.parse!('{ "foo": "bar" }')).to eq({ "foo" => "bar" }) + end + + it "parses an array" do + expect(subject.parse!('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }]) + end + + it "raises an error on a string" do + expect { subject.parse!('"foo"') }.to raise_error(JSON::ParserError) + end + + it "raises an error on a true bool" do + expect { subject.parse!("true") }.to raise_error(JSON::ParserError) + end + + it "raises an error on a false bool" do + expect { subject.parse!("false") }.to raise_error(JSON::ParserError) + end + end + + describe ".dump" do + it "dumps an object" do + expect(subject.dump({ "foo" => "bar" })).to eq('{"foo":"bar"}') + end + + it "dumps an array" do + expect(subject.dump([{ "foo" => "bar" }])).to eq('[{"foo":"bar"}]') + end + + it "dumps a string" do + expect(subject.dump("foo")).to eq('"foo"') + end + + it "dumps a true bool" do + expect(subject.dump(true)).to eq("true") + end + + it "dumps a false bool" do + expect(subject.dump(false)).to eq("false") + end + end + + describe ".generate" do + it "delegates to the adapter" do + args = [{ foo: "bar" }] + + expect(JSON).to receive(:generate).with(*args) + + subject.generate(*args) + end + end + + describe ".pretty_generate" do + it "delegates to the adapter" do + args = [{ foo: "bar" }] + + expect(JSON).to receive(:pretty_generate).with(*args) + + subject.pretty_generate(*args) + end + end +end diff --git a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb index c59078449b8..a11a9d08503 100644 --- a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb @@ -61,4 +61,56 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do it { is_expected.to eq('install-test-class-name') } end + + describe '#service_account_resource' do + let(:resource) do + Kubeclient::Resource.new(metadata: { name: 'tiller', namespace: 'gitlab-managed-apps' }) + end + + subject { base_command.service_account_resource } + + context 'rbac is enabled' do + let(:rbac) { true } + + it 'generates a Kubeclient resource for the tiller ServiceAccount' do + is_expected.to eq(resource) + end + end + + context 'rbac is not enabled' do + let(:rbac) { false } + + it 'generates nothing' do + is_expected.to be_nil + end + end + end + + describe '#cluster_role_binding_resource' do + let(:resource) do + Kubeclient::Resource.new( + metadata: { name: 'tiller-admin' }, + roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'ClusterRole', name: 'cluster-admin' }, + subjects: [{ kind: 'ServiceAccount', name: 'tiller', namespace: 'gitlab-managed-apps' }] + ) + end + + subject { base_command.cluster_role_binding_resource } + + context 'rbac is enabled' do + let(:rbac) { true } + + it 'generates a Kubeclient resource for the ClusterRoleBinding for tiller' do + is_expected.to eq(resource) + end + end + + context 'rbac is not enabled' do + let(:rbac) { false } + + it 'generates nothing' do + is_expected.to be_nil + end + end + end end diff --git a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb index f87ceb45766..13021a08f9f 100644 --- a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb @@ -83,56 +83,4 @@ describe Gitlab::Kubernetes::Helm::InitCommand do end end end - - describe '#service_account_resource' do - let(:resource) do - Kubeclient::Resource.new(metadata: { name: 'tiller', namespace: 'gitlab-managed-apps' }) - end - - subject { init_command.service_account_resource } - - context 'rbac is enabled' do - let(:rbac) { true } - - it 'generates a Kubeclient resource for the tiller ServiceAccount' do - is_expected.to eq(resource) - end - end - - context 'rbac is not enabled' do - let(:rbac) { false } - - it 'generates nothing' do - is_expected.to be_nil - end - end - end - - describe '#cluster_role_binding_resource' do - let(:resource) do - Kubeclient::Resource.new( - metadata: { name: 'tiller-admin' }, - roleRef: { apiGroup: 'rbac.authorization.k8s.io', kind: 'ClusterRole', name: 'cluster-admin' }, - subjects: [{ kind: 'ServiceAccount', name: 'tiller', namespace: 'gitlab-managed-apps' }] - ) - end - - subject { init_command.cluster_role_binding_resource } - - context 'rbac is enabled' do - let(:rbac) { true } - - it 'generates a Kubeclient resource for the ClusterRoleBinding for tiller' do - is_expected.to eq(resource) - end - end - - context 'rbac is not enabled' do - let(:rbac) { false } - - it 'generates nothing' do - is_expected.to be_nil - end - end - end end diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb index f94ceae362a..a5ed8f57bf3 100644 --- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb @@ -305,20 +305,4 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do is_expected.to eq(resource) end end - - describe '#service_account_resource' do - subject { install_command.service_account_resource } - - it 'returns nothing' do - is_expected.to be_nil - end - end - - describe '#cluster_role_binding_resource' do - subject { install_command.cluster_role_binding_resource } - - it 'returns nothing' do - is_expected.to be_nil - end - end end diff --git a/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb index 064efebdb96..e69570f5371 100644 --- a/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/patch_command_spec.rb @@ -199,20 +199,4 @@ describe Gitlab::Kubernetes::Helm::PatchCommand do is_expected.to eq(resource) end end - - describe '#service_account_resource' do - subject { patch_command.service_account_resource } - - it 'returns nothing' do - is_expected.to be_nil - end - end - - describe '#cluster_role_binding_resource' do - subject { patch_command.cluster_role_binding_resource } - - it 'returns nothing' do - is_expected.to be_nil - end - end end diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb index ddc41e64147..aa18a1a843c 100644 --- a/spec/lib/gitlab/project_template_spec.rb +++ b/spec/lib/gitlab/project_template_spec.rb @@ -19,6 +19,7 @@ describe Gitlab::ProjectTemplate do described_class.new('plainhtml', 'Pages/Plain HTML', 'Everything you need to get started using a plain HTML Pages site.', 'https://gitlab.com/pages/plain-html'), described_class.new('gitbook', 'Pages/GitBook', 'Everything you need to get started using a GitBook Pages site.', 'https://gitlab.com/pages/gitbook'), described_class.new('hexo', 'Pages/Hexo', 'Everything you need to get started using a Hexo Pages site.', 'https://gitlab.com/pages/hexo'), + described_class.new('sse_middleman', 'Static Site Editor/Middleman', _('Middleman project with Static Site Editor support'), 'https://gitlab.com/gitlab-org/project-templates/static-site-editor-middleman'), described_class.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhugo'), described_class.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll'), described_class.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html'), diff --git a/spec/lib/gitlab/prometheus/adapter_spec.rb b/spec/lib/gitlab/prometheus/adapter_spec.rb index 202bf65f92b..afee95467fa 100644 --- a/spec/lib/gitlab/prometheus/adapter_spec.rb +++ b/spec/lib/gitlab/prometheus/adapter_spec.rb @@ -19,6 +19,14 @@ describe Gitlab::Prometheus::Adapter do it 'return prometheus service as prometheus adapter' do expect(subject.prometheus_adapter).to eq(prometheus_service) end + + context 'with cluster with prometheus available' do + let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) } + + it 'returns prometheus service' do + expect(subject.prometheus_adapter).to eq(prometheus_service) + end + end end context "prometheus service can't execute queries" do diff --git a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb index db7c5f771b7..f4b939c3013 100644 --- a/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb +++ b/spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb @@ -42,11 +42,10 @@ describe Gitlab::SidekiqLogging::StructuredLogger do start_payload.merge( 'message' => 'TestWorker JID-da883554ee4fe414012f5f42: done: 0.0 sec', 'job_status' => 'done', - 'duration' => 0.0, + 'duration_s' => 0.0, 'completed_at' => timestamp.to_f, - 'cpu_s' => 1.111112, - 'db_duration' => 0, - 'db_duration_s' => 0 + 'cpu_s' => 1.11, + 'db_duration_s' => 0.0 ) end let(:exception_payload) do @@ -160,11 +159,11 @@ describe Gitlab::SidekiqLogging::StructuredLogger do let(:timing_data) do { gitaly_calls: 10, - gitaly_duration: 10000, + gitaly_duration_s: 10000, rugged_calls: 1, - rugged_duration_ms: 5000, + rugged_duration_s: 5000, redis_calls: 3, - redis_duration_ms: 1234 + redis_duration_s: 1234 } end @@ -193,12 +192,11 @@ describe Gitlab::SidekiqLogging::StructuredLogger do let(:expected_start_payload) { start_payload.except('args') } let(:expected_end_payload) do - end_payload.except('args').merge('cpu_s' => a_value > 0) + end_payload.except('args').merge('cpu_s' => a_value >= 0) end let(:expected_end_payload_with_db) do expected_end_payload.merge( - 'db_duration' => a_value >= 100, 'db_duration_s' => a_value >= 0.1 ) end @@ -226,7 +224,7 @@ describe Gitlab::SidekiqLogging::StructuredLogger do let(:time) { { duration: 0.1231234, cputime: 1.2342345 } } let(:payload) { { 'class' => 'my-class', 'message' => 'my-message', 'job_status' => 'my-job-status' } } let(:current_utc_time) { Time.now.utc } - let(:payload_with_time_keys) { { 'class' => 'my-class', 'message' => 'my-message', 'job_status' => 'my-job-status', 'duration' => 0.123123, 'cpu_s' => 1.234235, 'completed_at' => current_utc_time.to_f } } + let(:payload_with_time_keys) { { 'class' => 'my-class', 'message' => 'my-message', 'job_status' => 'my-job-status', 'duration_s' => 0.12, 'cpu_s' => 1.23, 'completed_at' => current_utc_time.to_f } } subject { described_class.new } diff --git a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb index 0ea248fbcf1..312ebd30a76 100644 --- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb @@ -21,18 +21,9 @@ describe Gitlab::SidekiqMiddleware::DuplicateJobs::Server, :clean_gitlab_redis_q end around do |example| - Sidekiq::Testing.inline! { example.run } - end - - before(:context) do - Sidekiq::Testing.server_middleware do |chain| + with_sidekiq_server_middleware do |chain| chain.add described_class - end - end - - after(:context) do - Sidekiq::Testing.server_middleware do |chain| - chain.remove described_class + Sidekiq::Testing.inline! { example.run } end end diff --git a/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb index f64ebece930..fdf643a8ad1 100644 --- a/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb @@ -41,18 +41,9 @@ describe Gitlab::SidekiqMiddleware::WorkerContext::Server do end around do |example| - Sidekiq::Testing.inline! { example.run } - end - - before(:context) do - Sidekiq::Testing.server_middleware do |chain| + with_sidekiq_server_middleware do |chain| chain.add described_class - end - end - - after(:context) do - Sidekiq::Testing.server_middleware do |chain| - chain.remove described_class + Sidekiq::Testing.inline! { example.run } end end diff --git a/spec/lib/gitlab/sidekiq_middleware_spec.rb b/spec/lib/gitlab/sidekiq_middleware_spec.rb index 32c1807ba6e..752ec6a0a3f 100644 --- a/spec/lib/gitlab/sidekiq_middleware_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware_spec.rb @@ -28,11 +28,16 @@ describe Gitlab::SidekiqMiddleware do # 2) yielding exactly once describe '.server_configurator' do around do |example| - original = Sidekiq::Testing.server_middleware.dup - - example.run - - Sidekiq::Testing.instance_variable_set :@server_chain, original + with_sidekiq_server_middleware do |chain| + described_class.server_configurator( + metrics: metrics, + arguments_logger: arguments_logger, + memory_killer: memory_killer, + request_store: request_store + ).call(chain) + + example.run + end end let(:middleware_expected_args) { [a_kind_of(worker_class), hash_including({ 'args' => job_args }), anything] } @@ -54,21 +59,17 @@ describe Gitlab::SidekiqMiddleware do end let(:enabled_sidekiq_middlewares) { all_sidekiq_middlewares - disabled_sidekiq_middlewares } - before do - Sidekiq::Testing.server_middleware.clear - Sidekiq::Testing.server_middleware(&described_class.server_configurator( - metrics: metrics, - arguments_logger: arguments_logger, - memory_killer: memory_killer, - request_store: request_store - )) - - enabled_sidekiq_middlewares.each do |middleware| - expect_any_instance_of(middleware).to receive(:call).with(*middleware_expected_args).once.and_call_original - end + shared_examples "a server middleware chain" do + it "passes through the right server middlewares" do + enabled_sidekiq_middlewares.each do |middleware| + expect_any_instance_of(middleware).to receive(:call).with(*middleware_expected_args).once.and_call_original + end - disabled_sidekiq_middlewares.each do |middleware| - expect_any_instance_of(Gitlab::SidekiqMiddleware::ArgumentsLogger).not_to receive(:call) + disabled_sidekiq_middlewares.each do |middleware| + expect_any_instance_of(middleware).not_to receive(:call) + end + + worker_class.perform_async(*job_args) end end @@ -86,9 +87,7 @@ describe Gitlab::SidekiqMiddleware do ] end - it "passes through server middlewares" do - worker_class.perform_async(*job_args) - end + it_behaves_like "a server middleware chain" end context "all optional middlewares on" do @@ -98,9 +97,7 @@ describe Gitlab::SidekiqMiddleware do let(:request_store) { true } let(:disabled_sidekiq_middlewares) { [] } - it "passes through server middlewares" do - worker_class.perform_async(*job_args) - end + it_behaves_like "a server middleware chain" context "server metrics" do let(:gitaly_histogram) { double(:gitaly_histogram) } diff --git a/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb index 56d6bf1c788..47b9a67f54f 100644 --- a/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb +++ b/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb @@ -3,7 +3,8 @@ require 'spec_helper' describe Gitlab::SlashCommands::Presenters::IssueShow do - let(:project) { create(:project) } + let(:user) { create(:user, :with_avatar) } + let(:project) { create(:project, creator: user) } let(:issue) { create(:issue, project: project) } let(:attachment) { subject[:attachments].first } @@ -15,6 +16,7 @@ describe Gitlab::SlashCommands::Presenters::IssueShow do expect(subject[:response_type]).to be(:in_channel) expect(subject).to have_key(:attachments) expect(attachment[:title]).to start_with(issue.title) + expect(attachment[:author_icon]).to eq(user.avatar_url(only_path: false)) end context 'with upvotes' do diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb index d3780d22241..e34367cbbf9 100644 --- a/spec/lib/gitlab/utils_spec.rb +++ b/spec/lib/gitlab/utils_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' describe Gitlab::Utils do delegate :to_boolean, :boolean_to_yes_no, :slugify, :random_string, :which, :ensure_array_from_string, :to_exclusive_sentence, :bytes_to_megabytes, - :append_path, :check_path_traversal!, to: :described_class + :append_path, :check_path_traversal!, :ms_to_round_sec, to: :described_class describe '.check_path_traversal!' do it 'detects path traversal at the start of the string' do @@ -55,6 +55,22 @@ describe Gitlab::Utils do end end + describe '.ms_to_round_sec' do + using RSpec::Parameterized::TableSyntax + + where(:original, :expected) do + 1999.8999 | 2 + 12384 | 12.38 + 333 | 0.33 + end + + with_them do + it "returns rounded seconds" do + expect(ms_to_round_sec(original)).to eq(expected) + end + end + end + describe '.to_exclusive_sentence' do it 'calls #to_sentence on the array' do array = double -- cgit v1.2.3