Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-25 21:08:10 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-25 21:08:10 +0300
commit5d75b2b9a9d11c20667895e6aa68ea4e76658c5d (patch)
tree2aa529b0a153c805f5f4ecb357321a4e4f4c59cb /spec
parent6f2065c468b05658125b746169c56764a8ccddb1 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/lib/gitlab/database/postgresql_adapter/schema_versions_copy_mixin_spec.rb42
-rw-r--r--spec/lib/uploaded_file_spec.rb10
-rw-r--r--spec/models/concerns/bulk_insert_safe_spec.rb31
-rw-r--r--spec/requests/api/releases_spec.rb20
-rw-r--r--spec/services/projects/prometheus/alerts/notify_service_spec.rb343
-rw-r--r--spec/support/helpers/test_env.rb53
-rw-r--r--spec/support/praefect.rb11
-rw-r--r--spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb6
8 files changed, 489 insertions, 27 deletions
diff --git a/spec/lib/gitlab/database/postgresql_adapter/schema_versions_copy_mixin_spec.rb b/spec/lib/gitlab/database/postgresql_adapter/schema_versions_copy_mixin_spec.rb
new file mode 100644
index 00000000000..968dfc1ea43
--- /dev/null
+++ b/spec/lib/gitlab/database/postgresql_adapter/schema_versions_copy_mixin_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Database::PostgresqlAdapter::SchemaVersionsCopyMixin do
+ let(:schema_migration) { double('schem_migration', table_name: table_name, all_versions: versions) }
+ let(:versions) { %w(5 2 1000 200 4 93 2) }
+ let(:table_name) { "schema_migrations" }
+
+ let(:instance) do
+ Object.new.extend(described_class)
+ end
+
+ before do
+ allow(instance).to receive(:schema_migration).and_return(schema_migration)
+ allow(instance).to receive(:quote_table_name).with(table_name).and_return("\"#{table_name}\"")
+ end
+
+ subject { instance.dump_schema_information }
+
+ it 'uses COPY FROM STDIN' do
+ expect(subject.split("\n").first).to match(/COPY "schema_migrations" \(version\) FROM STDIN;/)
+ end
+
+ it 'contains a sorted list of versions by their numeric value' do
+ version_lines = subject.split("\n")[1..-2].map(&:to_i)
+
+ expect(version_lines).to eq(versions.map(&:to_i).sort)
+ end
+
+ it 'contains a end-of-data marker' do
+ expect(subject).to end_with("\\.\n")
+ end
+
+ context 'with non-Integer versions' do
+ let(:versions) { %w(5 2 4 abc) }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(/invalid value for Integer/)
+ end
+ end
+end
diff --git a/spec/lib/uploaded_file_spec.rb b/spec/lib/uploaded_file_spec.rb
index 2bbbd67b13c..25536c07dd9 100644
--- a/spec/lib/uploaded_file_spec.rb
+++ b/spec/lib/uploaded_file_spec.rb
@@ -59,6 +59,16 @@ describe UploadedFile do
expect(subject.sha256).to eq('sha256')
expect(subject.remote_id).to eq('remote_id')
end
+
+ it 'handles a blank path' do
+ params['file.path'] = ''
+
+ # Not a real file, so can't determine size itself
+ params['file.size'] = 1.byte
+
+ expect { described_class.from_params(params, :file, upload_path) }
+ .not_to raise_error
+ end
end
end
diff --git a/spec/models/concerns/bulk_insert_safe_spec.rb b/spec/models/concerns/bulk_insert_safe_spec.rb
index a8e56cb8bdd..5ed1d6b9967 100644
--- a/spec/models/concerns/bulk_insert_safe_spec.rb
+++ b/spec/models/concerns/bulk_insert_safe_spec.rb
@@ -129,10 +129,37 @@ describe BulkInsertSafe do
end.not_to change { described_class.count }
end
- it 'does nothing and returns true when items are empty' do
- expect(described_class.bulk_insert!([])).to be(true)
+ it 'does nothing and returns an empty array when items are empty' do
+ expect(described_class.bulk_insert!([])).to eq([])
expect(described_class.count).to eq(0)
end
+
+ context 'with returns option set' do
+ context 'when is set to :ids' do
+ it 'return an array with the primary key values for all inserted records' do
+ items = described_class.valid_list(1)
+
+ expect(described_class.bulk_insert!(items, returns: :ids)).to contain_exactly(a_kind_of(Integer))
+ end
+ end
+
+ context 'when is set to nil' do
+ it 'returns an empty array' do
+ items = described_class.valid_list(1)
+
+ expect(described_class.bulk_insert!(items, returns: nil)).to eq([])
+ end
+ end
+
+ context 'when is set to anything else' do
+ it 'raises an error' do
+ items = described_class.valid_list(1)
+
+ expect { described_class.bulk_insert!([items], returns: [:id, :name]) }
+ .to raise_error(ArgumentError, "returns needs to be :ids or nil")
+ end
+ end
+ end
end
context 'when duplicate items are to be inserted' do
diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb
index e66e999dc27..0589554bf44 100644
--- a/spec/requests/api/releases_spec.rb
+++ b/spec/requests/api/releases_spec.rb
@@ -6,6 +6,7 @@ describe API::Releases do
let(:project) { create(:project, :repository, :private) }
let(:maintainer) { create(:user) }
let(:reporter) { create(:user) }
+ let(:developer) { create(:user) }
let(:guest) { create(:user) }
let(:non_project_member) { create(:user) }
let(:commit) { create(:commit, project: project) }
@@ -15,6 +16,7 @@ describe API::Releases do
project.add_maintainer(maintainer)
project.add_reporter(reporter)
project.add_guest(guest)
+ project.add_developer(developer)
project.repository.add_tag(maintainer, 'v0.1', commit.id)
project.repository.add_tag(maintainer, 'v0.2', commit.id)
@@ -248,6 +250,24 @@ describe API::Releases do
.to match_array(release.sources.map(&:url))
end
+ context 'with evidence' do
+ let!(:evidence) { create(:evidence, release: release) }
+
+ it 'returns the evidence' do
+ get api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(json_response['evidences'].count).to eq(1)
+ end
+
+ it '#collected_at' do
+ Timecop.freeze(Time.now.round) do
+ get api("/projects/#{project.id}/releases/v0.1", maintainer)
+
+ expect(json_response['evidences'].first['collected_at'].to_datetime.to_i).to be_within(1.minute).of(release.evidences.first.created_at.to_i)
+ end
+ end
+ end
+
context 'when release has link asset' do
let!(:link) do
create(:release_link,
diff --git a/spec/services/projects/prometheus/alerts/notify_service_spec.rb b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
new file mode 100644
index 00000000000..ce850e65329
--- /dev/null
+++ b/spec/services/projects/prometheus/alerts/notify_service_spec.rb
@@ -0,0 +1,343 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::Prometheus::Alerts::NotifyService do
+ let_it_be(:project, reload: true) { create(:project) }
+
+ let(:service) { described_class.new(project, nil, payload) }
+ let(:token_input) { 'token' }
+
+ let!(:setting) do
+ create(:project_incident_management_setting, project: project, send_email: true, create_issue: true)
+ end
+
+ let(:subject) { service.execute(token_input) }
+
+ before do
+ # We use `let_it_be(:project)` so we make sure to clear caches
+ project.clear_memoization(:licensed_feature_available)
+ end
+
+ shared_examples 'sends notification email' do
+ let(:notification_service) { spy }
+
+ it 'sends a notification for firing alerts only' do
+ expect(NotificationService)
+ .to receive(:new)
+ .and_return(notification_service)
+
+ expect(notification_service)
+ .to receive_message_chain(:async, :prometheus_alerts_fired)
+
+ expect(subject).to eq(true)
+ end
+ end
+
+ shared_examples 'processes incident issues' do |amount|
+ let(:create_incident_service) { spy }
+
+ it 'processes issues' do
+ expect(IncidentManagement::ProcessPrometheusAlertWorker)
+ .to receive(:perform_async)
+ .with(project.id, kind_of(Hash))
+ .exactly(amount).times
+
+ Sidekiq::Testing.inline! do
+ expect(subject).to eq(true)
+ end
+ end
+ end
+
+ shared_examples 'does not process incident issues' do
+ it 'does not process issues' do
+ expect(IncidentManagement::ProcessPrometheusAlertWorker)
+ .not_to receive(:perform_async)
+
+ expect(subject).to eq(true)
+ end
+ end
+
+ shared_examples 'persists events' do
+ let(:create_events_service) { spy }
+
+ it 'persists events' do
+ expect(Projects::Prometheus::Alerts::CreateEventsService)
+ .to receive(:new)
+ .and_return(create_events_service)
+
+ expect(create_events_service)
+ .to receive(:execute)
+
+ expect(subject).to eq(true)
+ end
+ end
+
+ shared_examples 'notifies alerts' do
+ it_behaves_like 'sends notification email'
+ it_behaves_like 'persists events'
+ end
+
+ shared_examples 'no notifications' do
+ let(:notification_service) { spy }
+ let(:create_events_service) { spy }
+
+ it 'does not notify' do
+ expect(notification_service).not_to receive(:async)
+ expect(create_events_service).not_to receive(:execute)
+
+ expect(subject).to eq(false)
+ end
+ end
+
+ context 'with valid payload' do
+ let(:alert_firing) { create(:prometheus_alert, project: project) }
+ let(:alert_resolved) { create(:prometheus_alert, project: project) }
+ let(:payload_raw) { payload_for(firing: [alert_firing], resolved: [alert_resolved]) }
+ let(:payload) { ActionController::Parameters.new(payload_raw).permit! }
+ let(:payload_alert_firing) { payload_raw['alerts'].first }
+ let(:token) { 'token' }
+
+ context 'with project specific cluster' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:cluster_enabled, :status, :configured_token, :token_input, :result) do
+ true | :installed | token | token | :success
+ true | :installed | nil | nil | :success
+ true | :updated | token | token | :success
+ true | :updating | token | token | :failure
+ true | :installed | token | 'x' | :failure
+ true | :installed | nil | token | :failure
+ true | :installed | token | nil | :failure
+ true | nil | token | token | :failure
+ false | :installed | token | token | :failure
+ end
+
+ with_them do
+ before do
+ cluster = create(:cluster, :provided_by_user,
+ projects: [project],
+ enabled: cluster_enabled)
+
+ if status
+ create(:clusters_applications_prometheus, status,
+ cluster: cluster,
+ alert_manager_token: configured_token)
+ end
+ end
+
+ case result = params[:result]
+ when :success
+ it_behaves_like 'notifies alerts'
+ when :failure
+ it_behaves_like 'no notifications'
+ else
+ raise "invalid result: #{result.inspect}"
+ end
+ end
+ end
+
+ context 'without project specific cluster' do
+ let!(:cluster) { create(:cluster, enabled: true) }
+
+ it_behaves_like 'no notifications'
+ end
+
+ context 'with manual prometheus installation' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:alerting_setting, :configured_token, :token_input, :result) do
+ true | token | token | :success
+ true | token | 'x' | :failure
+ true | token | nil | :failure
+ false | nil | nil | :success
+ false | nil | token | :failure
+ end
+
+ with_them do
+ let(:alert_manager_token) { token_input }
+
+ before do
+ create(:prometheus_service, project: project)
+
+ if alerting_setting
+ create(:project_alerting_setting,
+ project: project,
+ token: configured_token)
+ end
+ end
+
+ case result = params[:result]
+ when :success
+ it_behaves_like 'notifies alerts'
+ when :failure
+ it_behaves_like 'no notifications'
+ else
+ raise "invalid result: #{result.inspect}"
+ end
+ end
+ end
+
+ context 'alert emails' do
+ before do
+ create(:prometheus_service, project: project)
+ create(:project_alerting_setting, project: project, token: token)
+ end
+
+ context 'when incident_management_setting does not exist' do
+ let!(:setting) { nil }
+
+ it_behaves_like 'persists events'
+
+ it 'does not send notification email', :sidekiq_might_not_need_inline do
+ expect_any_instance_of(NotificationService)
+ .not_to receive(:async)
+
+ expect(subject).to eq(true)
+ end
+ end
+
+ context 'when incident_management_setting.send_email is true' do
+ it_behaves_like 'notifies alerts'
+ end
+
+ context 'incident_management_setting.send_email is false' do
+ let!(:setting) do
+ create(:project_incident_management_setting, send_email: false, project: project)
+ end
+
+ it_behaves_like 'persists events'
+
+ it 'does not send notification' do
+ expect(NotificationService).not_to receive(:new)
+
+ expect(subject).to eq(true)
+ end
+ end
+ end
+
+ context 'process incident issues' do
+ before do
+ create(:prometheus_service, project: project)
+ create(:project_alerting_setting, project: project, token: token)
+ end
+
+ context 'with create_issue setting enabled' do
+ before do
+ setting.update!(create_issue: true)
+ end
+
+ it_behaves_like 'processes incident issues', 2
+
+ context 'multiple firing alerts' do
+ let(:payload_raw) do
+ payload_for(firing: [alert_firing, alert_firing], resolved: [])
+ end
+
+ it_behaves_like 'processes incident issues', 2
+ end
+
+ context 'without firing alerts' do
+ let(:payload_raw) do
+ payload_for(firing: [], resolved: [alert_resolved])
+ end
+
+ it_behaves_like 'processes incident issues', 1
+ end
+ end
+
+ context 'with create_issue setting disabled' do
+ before do
+ setting.update!(create_issue: false)
+ end
+
+ it_behaves_like 'does not process incident issues'
+ end
+ end
+ end
+
+ context 'with invalid payload' do
+ context 'without version' do
+ let(:payload) { {} }
+
+ it_behaves_like 'no notifications'
+ end
+
+ context 'when version is not "4"' do
+ let(:payload) { { 'version' => '5' } }
+
+ it_behaves_like 'no notifications'
+ end
+
+ context 'with missing alerts' do
+ let(:payload) { { 'version' => '4' } }
+
+ it_behaves_like 'no notifications'
+ end
+
+ context 'when the payload is too big' do
+ let(:payload) { { 'the-payload-is-too-big' => true } }
+ let(:deep_size_object) { instance_double(Gitlab::Utils::DeepSize, valid?: false) }
+
+ before do
+ allow(Gitlab::Utils::DeepSize).to receive(:new).and_return(deep_size_object)
+ end
+
+ it_behaves_like 'no notifications'
+
+ it 'does not process issues' do
+ expect(IncidentManagement::ProcessPrometheusAlertWorker)
+ .not_to receive(:perform_async)
+
+ subject
+ end
+ end
+ end
+
+ private
+
+ def payload_for(firing: [], resolved: [])
+ status = firing.any? ? 'firing' : 'resolved'
+ alerts = firing + resolved
+ alert_name = alerts.first.title
+ prometheus_metric_id = alerts.first.prometheus_metric_id.to_s
+
+ alerts_map = \
+ firing.map { |alert| map_alert_payload('firing', alert) } +
+ resolved.map { |alert| map_alert_payload('resolved', alert) }
+
+ # See https://prometheus.io/docs/alerting/configuration/#%3Cwebhook_config%3E
+ {
+ 'version' => '4',
+ 'receiver' => 'gitlab',
+ 'status' => status,
+ 'alerts' => alerts_map,
+ 'groupLabels' => {
+ 'alertname' => alert_name
+ },
+ 'commonLabels' => {
+ 'alertname' => alert_name,
+ 'gitlab' => 'hook',
+ 'gitlab_alert_id' => prometheus_metric_id
+ },
+ 'commonAnnotations' => {},
+ 'externalURL' => '',
+ 'groupKey' => "{}:{alertname=\'#{alert_name}\'}"
+ }
+ end
+
+ def map_alert_payload(status, alert)
+ {
+ 'status' => status,
+ 'labels' => {
+ 'alertname' => alert.title,
+ 'gitlab' => 'hook',
+ 'gitlab_alert_id' => alert.prometheus_metric_id.to_s
+ },
+ 'annotations' => {},
+ 'startsAt' => '2018-09-24T08:57:31.095725221Z',
+ 'endsAt' => '0001-01-01T00:00:00Z',
+ 'generatorURL' => 'http://prometheus-prometheus-server-URL'
+ }
+ end
+end
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 66c2faac2dd..2e69c59c80a 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rspec/mocks'
-require 'toml-rb'
module TestEnv
extend ActiveSupport::Concern
@@ -87,7 +86,7 @@ module TestEnv
'conflict-resolvable-fork' => '404fa3f'
}.freeze
- TMP_TEST_PATH = Rails.root.join('tmp', 'tests', '**')
+ TMP_TEST_PATH = Rails.root.join('tmp', 'tests').freeze
REPOS_STORAGE = 'default'.freeze
SECOND_STORAGE_PATH = Rails.root.join('tmp', 'tests', 'second_storage')
@@ -140,7 +139,7 @@ module TestEnv
#
# Keeps gitlab-shell and gitlab-test
def clean_test_path
- Dir[TMP_TEST_PATH].each do |entry|
+ Dir[File.join(TMP_TEST_PATH, '**')].each do |entry|
unless test_dirs.include?(File.basename(entry))
FileUtils.rm_rf(entry)
end
@@ -164,7 +163,8 @@ module TestEnv
install_dir: gitaly_dir,
version: Gitlab::GitalyClient.expected_server_version,
task: "gitlab:gitaly:install[#{install_gitaly_args}]") do
- Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, { 'default' => repos_path }, force: true)
+ Gitlab::SetupHelper::Gitaly.create_configuration(gitaly_dir, { 'default' => repos_path }, force: true)
+ Gitlab::SetupHelper::Praefect.create_configuration(gitaly_dir, { 'praefect' => repos_path }, force: true)
start_gitaly(gitaly_dir)
end
end
@@ -192,17 +192,38 @@ module TestEnv
end
end
- @gitaly_pid = Integer(File.read('tmp/tests/gitaly.pid'))
+ gitaly_pid = Integer(File.read(TMP_TEST_PATH.join('gitaly.pid')))
+ praefect_pid = Integer(File.read(TMP_TEST_PATH.join('praefect.pid')))
- Kernel.at_exit { stop_gitaly }
+ Kernel.at_exit { stop(gitaly_pid) }
+ Kernel.at_exit { stop(praefect_pid) }
- wait_gitaly
+ wait('gitaly')
+ wait('praefect')
end
- def wait_gitaly
+ def stop(pid)
+ Process.kill('KILL', pid)
+ rescue Errno::ESRCH
+ # The process can already be gone if the test run was INTerrupted.
+ end
+
+ def gitaly_url
+ ENV.fetch('GITALY_REPO_URL', nil)
+ end
+
+ def socket_path(service)
+ TMP_TEST_PATH.join('gitaly', "#{service}.socket").to_s
+ end
+
+ def praefect_socket_path
+ "unix:" + socket_path(:praefect)
+ end
+
+ def wait(service)
sleep_time = 10
sleep_interval = 0.1
- socket = Gitlab::GitalyClient.address('default').sub('unix:', '')
+ socket = socket_path(service)
Integer(sleep_time / sleep_interval).times do
Socket.unix(socket)
@@ -211,19 +232,7 @@ module TestEnv
sleep sleep_interval
end
- raise "could not connect to gitaly at #{socket.inspect} after #{sleep_time} seconds"
- end
-
- def stop_gitaly
- return unless @gitaly_pid
-
- Process.kill('KILL', @gitaly_pid)
- rescue Errno::ESRCH
- # The process can already be gone if the test run was INTerrupted.
- end
-
- def gitaly_url
- ENV.fetch('GITALY_REPO_URL', nil)
+ raise "could not connect to #{service} at #{socket.inspect} after #{sleep_time} seconds"
end
def setup_workhorse
diff --git a/spec/support/praefect.rb b/spec/support/praefect.rb
new file mode 100644
index 00000000000..3218275c2aa
--- /dev/null
+++ b/spec/support/praefect.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require_relative 'helpers/test_env'
+
+RSpec.configure do |config|
+ config.before(:each, :praefect) do
+ allow(Gitlab.config.repositories.storages['default']).to receive(:[]).and_call_original
+ allow(Gitlab.config.repositories.storages['default']).to receive(:[]).with('gitaly_address')
+ .and_return(TestEnv.praefect_socket_path)
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb b/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb
index c6180a5a196..7bcd6191f1d 100644
--- a/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb
@@ -45,11 +45,11 @@ RSpec.shared_examples 'a BulkInsertSafe model' do |klass|
expect { target_class.bulk_insert!(items) }.to change { target_class.count }.by(items.size)
end
- it 'returns true' do
+ it 'returns an empty array' do
items = valid_items_for_bulk_insertion
expect(items).not_to be_empty
- expect(target_class.bulk_insert!(items)).to be true
+ expect(target_class.bulk_insert!(items)).to eq([])
end
end
@@ -69,7 +69,7 @@ RSpec.shared_examples 'a BulkInsertSafe model' do |klass|
# it is not always possible to create invalid items
if items.any?
- expect(target_class.bulk_insert!(items, validate: false)).to be(true)
+ expect(target_class.bulk_insert!(items, validate: false)).to eq([])
expect(target_class.count).to eq(items.size)
end
end