summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-02-03 15:12:41 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-02-03 15:12:41 +0000
commit67daaf4021a180166ad063e3a75ea777e96586a6 (patch)
tree9ba7459c9c149e151fd31fa1fa7f31a186602eff /spec
parent584ccdaf68710dec2c717a010cbab2610c0155ed (diff)
downloadgitlab-ce-67daaf4021a180166ad063e3a75ea777e96586a6.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
-rw-r--r--spec/channels/application_cable/connection_spec.rb8
-rw-r--r--spec/factories/gitlab/database/background_migration/batched_jobs.rb16
-rw-r--r--spec/features/admin/admin_sees_background_migrations_spec.rb2
-rw-r--r--spec/features/groups_spec.rb6
-rw-r--r--spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js14
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_job_spec.rb94
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb27
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_spec.rb27
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb9
-rw-r--r--spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb167
-rw-r--r--spec/lib/gitlab/rack_attack/request_spec.rb38
-rw-r--r--spec/requests/admin/background_migrations_controller_spec.rb2
-rw-r--r--spec/requests/api/commits_spec.rb10
-rw-r--r--spec/requests/rack_attack_global_spec.rb17
-rw-r--r--spec/support/helpers/rack_attack_spec_helpers.rb4
-rw-r--r--spec/support/helpers/session_helpers.rb16
-rw-r--r--spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb85
18 files changed, 363 insertions, 190 deletions
diff --git a/spec/channels/application_cable/connection_spec.rb b/spec/channels/application_cable/connection_spec.rb
index c10e0c0cab4..affde0095cf 100644
--- a/spec/channels/application_cable/connection_spec.rb
+++ b/spec/channels/application_cable/connection_spec.rb
@@ -3,15 +3,11 @@
require 'spec_helper'
RSpec.describe ApplicationCable::Connection, :clean_gitlab_redis_sessions do
- let(:session_id) { Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d') }
+ include SessionHelpers
context 'when session cookie is set' do
before do
- Gitlab::Redis::Sessions.with do |redis|
- redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
- end
-
- cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+ stub_session(session_hash)
end
context 'when user is logged in' do
diff --git a/spec/factories/gitlab/database/background_migration/batched_jobs.rb b/spec/factories/gitlab/database/background_migration/batched_jobs.rb
index cec20616f7f..3c7dcd03701 100644
--- a/spec/factories/gitlab/database/background_migration/batched_jobs.rb
+++ b/spec/factories/gitlab/database/background_migration/batched_jobs.rb
@@ -9,5 +9,21 @@ FactoryBot.define do
batch_size { 5 }
sub_batch_size { 1 }
pause_ms { 100 }
+
+ trait(:pending) do
+ status { 0 }
+ end
+
+ trait(:running) do
+ status { 1 }
+ end
+
+ trait(:failed) do
+ status { 2 }
+ end
+
+ trait(:succeeded) do
+ status { 3 }
+ end
end
end
diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb
index e71cb11b413..a3d0c7bdd4d 100644
--- a/spec/features/admin/admin_sees_background_migrations_spec.rb
+++ b/spec/features/admin/admin_sees_background_migrations_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe "Admin > Admin sees background migrations" do
let_it_be(:finished_migration) { create(:batched_background_migration, table_name: 'finished', status: :finished) }
before_all do
- create(:batched_background_migration_job, batched_migration: failed_migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3)
+ create(:batched_background_migration_job, :failed, batched_migration: failed_migration, batch_size: 10, min_value: 6, max_value: 15, attempts: 3)
end
before do
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 19f60ce55d3..925bbc47cf6 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe 'Group' do
click_button 'Create group'
expect(current_path).to eq(new_group_path)
- expect(page).to have_text('Please choose a group URL with no special characters or spaces.')
+ expect(page).to have_text('Choose a group path that does not start with a dash or end with a period. It can also contain alphanumeric characters and underscores.')
end
end
@@ -90,7 +90,7 @@ RSpec.describe 'Group' do
fill_in 'group_path', with: user.username
wait_for_requests
- expect(page).to have_content("Group path is already taken. We've suggested one that is available.")
+ expect(page).to have_content("Group path is unavailable. Path has been replaced with a suggested available path.")
end
it 'does not break after an invalid form submit' do
@@ -279,7 +279,7 @@ RSpec.describe 'Group' do
fill_in 'Group URL', with: subgroup.path
wait_for_requests
- expect(page).to have_content("Group path is already taken. We've suggested one that is available.")
+ expect(page).to have_content("Group path is unavailable. Path has been replaced with a suggested available path.")
end
end
end
diff --git a/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js b/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js
index 794deca42ac..ddabb7194cb 100644
--- a/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js
+++ b/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js
@@ -1,3 +1,4 @@
+import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import AccessibilityIssueBody from '~/reports/accessibility_report/components/accessibility_issue_body.vue';
@@ -29,7 +30,7 @@ describe('CustomMetricsForm', () => {
});
};
- const findIsNewBadge = () => wrapper.find({ ref: 'accessibility-issue-is-new-badge' });
+ const findIsNewBadge = () => wrapper.findComponent(GlBadge);
beforeEach(() => {
mountComponent(issue);
@@ -37,7 +38,6 @@ describe('CustomMetricsForm', () => {
afterEach(() => {
wrapper.destroy();
- wrapper = null;
});
it('Displays the issue message', () => {
@@ -52,7 +52,7 @@ describe('CustomMetricsForm', () => {
.find({ ref: 'accessibility-issue-learn-more' })
.attributes('href');
- expect(learnMoreUrl).toEqual(issue.learnMoreUrl);
+ expect(learnMoreUrl).toBe(issue.learnMoreUrl);
});
});
@@ -69,7 +69,7 @@ describe('CustomMetricsForm', () => {
.find({ ref: 'accessibility-issue-learn-more' })
.attributes('href');
- expect(learnMoreUrl).toEqual('https://www.w3.org/TR/WCAG20-TECHS/Overview.html');
+ expect(learnMoreUrl).toBe('https://www.w3.org/TR/WCAG20-TECHS/Overview.html');
});
});
@@ -86,7 +86,7 @@ describe('CustomMetricsForm', () => {
.find({ ref: 'accessibility-issue-learn-more' })
.attributes('href');
- expect(learnMoreUrl).toEqual('https://www.w3.org/TR/WCAG20-TECHS/Overview.html');
+ expect(learnMoreUrl).toBe('https://www.w3.org/TR/WCAG20-TECHS/Overview.html');
});
});
@@ -96,7 +96,7 @@ describe('CustomMetricsForm', () => {
});
it('Renders the new badge', () => {
- expect(findIsNewBadge().exists()).toEqual(true);
+ expect(findIsNewBadge().exists()).toBe(true);
});
});
@@ -106,7 +106,7 @@ describe('CustomMetricsForm', () => {
});
it('Does not render the new badge', () => {
- expect(findIsNewBadge().exists()).toEqual(false);
+ expect(findIsNewBadge().exists()).toBe(false);
});
});
});
diff --git a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
index d810cd2b570..7338ea657b9 100644
--- a/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_job_spec.rb
@@ -10,16 +10,82 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
it { is_expected.to have_many(:batched_job_transition_logs).with_foreign_key(:batched_background_migration_job_id) }
end
+ describe 'state machine' do
+ let_it_be(:job) { create(:batched_background_migration_job, :failed) }
+
+ context 'when a job is running' do
+ it 'logs the transition' do
+ expect(Gitlab::AppLogger).to receive(:info).with( { batched_job_id: job.id, message: 'BatchedJob transition', new_state: :running, previous_state: :failed } )
+
+ expect { job.run! }.to change(job, :started_at)
+ end
+ end
+
+ context 'when a job succeed' do
+ let(:job) { create(:batched_background_migration_job, :running) }
+
+ it 'logs the transition' do
+ expect(Gitlab::AppLogger).to receive(:info).with( { batched_job_id: job.id, message: 'BatchedJob transition', new_state: :succeeded, previous_state: :running } )
+
+ job.succeed!
+ end
+
+ it 'updates the finished_at' do
+ expect { job.succeed! }.to change(job, :finished_at).from(nil).to(Time)
+ end
+
+ it 'creates a new transition log' do
+ job.succeed!
+
+ transition_log = job.batched_job_transition_logs.first
+
+ expect(transition_log.next_status).to eq('succeeded')
+ expect(transition_log.exception_class).to be_nil
+ expect(transition_log.exception_message).to be_nil
+ end
+ end
+
+ context 'when a job fails' do
+ let(:job) { create(:batched_background_migration_job, :running) }
+
+ it 'logs the transition' do
+ expect(Gitlab::AppLogger).to receive(:info).with( { batched_job_id: job.id, message: 'BatchedJob transition', new_state: :failed, previous_state: :running } )
+
+ job.failure!
+ end
+
+ it 'tracks the exception' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(RuntimeError, { batched_job_id: job.id } )
+
+ job.failure!(error: RuntimeError.new)
+ end
+
+ it 'updates the finished_at' do
+ expect { job.failure! }.to change(job, :finished_at).from(nil).to(Time)
+ end
+
+ it 'creates a new transition log' do
+ job.failure!(error: RuntimeError.new)
+
+ transition_log = job.batched_job_transition_logs.first
+
+ expect(transition_log.next_status).to eq('failed')
+ expect(transition_log.exception_class).to eq('RuntimeError')
+ expect(transition_log.exception_message).to eq('RuntimeError')
+ end
+ end
+ end
+
describe 'scopes' do
let_it_be(:fixed_time) { Time.new(2021, 04, 27, 10, 00, 00, 00) }
- let_it_be(:pending_job) { create(:batched_background_migration_job, status: :pending, updated_at: fixed_time) }
- let_it_be(:running_job) { create(:batched_background_migration_job, status: :running, updated_at: fixed_time) }
- let_it_be(:stuck_job) { create(:batched_background_migration_job, status: :pending, updated_at: fixed_time - described_class::STUCK_JOBS_TIMEOUT) }
- let_it_be(:failed_job) { create(:batched_background_migration_job, status: :failed, attempts: 1) }
+ let_it_be(:pending_job) { create(:batched_background_migration_job, :pending, updated_at: fixed_time) }
+ let_it_be(:running_job) { create(:batched_background_migration_job, :running, updated_at: fixed_time) }
+ let_it_be(:stuck_job) { create(:batched_background_migration_job, :pending, updated_at: fixed_time - described_class::STUCK_JOBS_TIMEOUT) }
+ let_it_be(:failed_job) { create(:batched_background_migration_job, :failed, attempts: 1) }
- let!(:max_attempts_failed_job) { create(:batched_background_migration_job, status: :failed, attempts: described_class::MAX_ATTEMPTS) }
- let!(:succeeded_job) { create(:batched_background_migration_job, status: :succeeded) }
+ let!(:max_attempts_failed_job) { create(:batched_background_migration_job, :failed, attempts: described_class::MAX_ATTEMPTS) }
+ let!(:succeeded_job) { create(:batched_background_migration_job, :succeeded) }
before do
travel_to fixed_time
@@ -83,10 +149,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
subject { job.time_efficiency }
let(:migration) { build(:batched_background_migration, interval: 120.seconds) }
- let(:job) { build(:batched_background_migration_job, status: :succeeded, batched_migration: migration) }
+ let(:job) { build(:batched_background_migration_job, :succeeded, batched_migration: migration) }
context 'when job has not yet succeeded' do
- let(:job) { build(:batched_background_migration_job, status: :running) }
+ let(:job) { build(:batched_background_migration_job, :running) }
it 'returns nil' do
expect(subject).to be_nil
@@ -131,7 +197,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
describe '#split_and_retry!' do
- let!(:job) { create(:batched_background_migration_job, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3) }
+ let!(:job) { create(:batched_background_migration_job, :failed, batch_size: 10, min_value: 6, max_value: 15, attempts: 3) }
context 'when job can be split' do
before do
@@ -147,7 +213,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
min_value: 6,
max_value: 10,
batch_size: 5,
- status: 'failed',
+ status_name: :failed,
attempts: 0,
started_at: nil,
finished_at: nil,
@@ -161,7 +227,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
min_value: 11,
max_value: 15,
batch_size: 5,
- status: 'failed',
+ status_name: :failed,
attempts: 0,
started_at: nil,
finished_at: nil,
@@ -178,7 +244,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
context 'when job is not failed' do
- let!(:job) { create(:batched_background_migration_job, status: :succeeded) }
+ let!(:job) { create(:batched_background_migration_job, :succeeded) }
it 'raises an exception' do
expect { job.split_and_retry! }.to raise_error 'Only failed jobs can be split'
@@ -186,7 +252,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
end
context 'when batch size is already 1' do
- let!(:job) { create(:batched_background_migration_job, batch_size: 1, status: :failed) }
+ let!(:job) { create(:batched_background_migration_job, :failed, batch_size: 1) }
it 'raises an exception' do
expect { job.split_and_retry! }.to raise_error 'Job cannot be split further'
@@ -205,7 +271,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedJob, type: :model d
expect(job.batch_size).to eq(5)
expect(job.attempts).to eq(0)
- expect(job.status).to eq('failed')
+ expect(job.status_name).to eq(:failed)
end
end
end
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
index 04c18a98ee6..bb2c6b9a3ae 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_runner_spec.rb
@@ -96,13 +96,12 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
end
let!(:previous_job) do
- create(:batched_background_migration_job,
+ create(:batched_background_migration_job, :succeeded,
batched_migration: migration,
min_value: event1.id,
max_value: event2.id,
batch_size: 2,
- sub_batch_size: 1,
- status: :succeeded
+ sub_batch_size: 1
)
end
@@ -144,7 +143,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration has failed jobs' do
before do
- previous_job.update!(status: :failed)
+ previous_job.failure!
end
it 'retries the failed job' do
@@ -172,7 +171,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration has stuck jobs' do
before do
- previous_job.update!(status: :running, updated_at: 1.hour.ago - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
+ previous_job.update!(status_event: 'run', updated_at: 1.hour.ago - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
end
it 'retries the stuck job' do
@@ -186,7 +185,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration has possible stuck jobs' do
before do
- previous_job.update!(status: :running, updated_at: 1.hour.from_now - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
+ previous_job.update!(status_event: 'run', updated_at: 1.hour.from_now - Gitlab::Database::BackgroundMigration::BatchedJob::STUCK_JOBS_TIMEOUT)
end
it 'keeps the migration active' do
@@ -201,13 +200,13 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when the migration has batches to process and failed jobs' do
before do
migration.update!(max_value: event3.id)
- previous_job.update!(status: :failed)
+ previous_job.failure!
end
it 'runs next batch then retries the failed job' do
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.last)
- job_record.update!(status: :succeeded)
+ job_record.succeed!
end
expect { runner.run_migration_job(migration) }.to change { job_relation.count }.by(1)
@@ -264,12 +263,12 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
it 'runs all jobs inline until finishing the migration' do
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.first)
- job_record.update!(status: :succeeded)
+ job_record.succeed!
end
expect(migration_wrapper).to receive(:perform) do |job_record|
expect(job_record).to eq(job_relation.last)
- job_record.update!(status: :succeeded)
+ job_record.succeed!
end
expect { runner.run_entire_migration(migration) }.to change { job_relation.count }.by(2)
@@ -330,9 +329,9 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
pause_ms: 0
}
- create(:batched_background_migration_job, common_attributes.merge(status: :succeeded, min_value: 1, max_value: 2))
- create(:batched_background_migration_job, common_attributes.merge(status: :pending, min_value: 3, max_value: 4))
- create(:batched_background_migration_job, common_attributes.merge(status: :failed, min_value: 5, max_value: 6, attempts: 1))
+ create(:batched_background_migration_job, :succeeded, common_attributes.merge(min_value: 1, max_value: 2))
+ create(:batched_background_migration_job, :pending, common_attributes.merge(min_value: 3, max_value: 4))
+ create(:batched_background_migration_job, :failed, common_attributes.merge(min_value: 5, max_value: 6, attempts: 1))
end
it 'completes the migration' do
@@ -359,7 +358,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationRunner do
context 'when migration fails to complete' do
it 'raises an error' do
- batched_migration.batched_jobs.failed.update_all(attempts: Gitlab::Database::BackgroundMigration::BatchedJob::MAX_ATTEMPTS)
+ batched_migration.batched_jobs.with_status(:failed).update_all(attempts: Gitlab::Database::BackgroundMigration::BatchedJob::MAX_ATTEMPTS)
expect do
runner.finalize(
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
index 01d61a525e6..ea4ba4dd137 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
context 'when there are failed jobs' do
let(:batched_migration) { create(:batched_background_migration, status: :active, total_tuple_count: 100) }
- let!(:batched_job) { create(:batched_background_migration_job, batched_migration: batched_migration, status: :failed) }
+ let!(:batched_job) { create(:batched_background_migration_job, :failed, batched_migration: batched_migration) }
it 'raises an exception' do
expect { batched_migration.finished! }.to raise_error(ActiveRecord::RecordInvalid)
@@ -37,7 +37,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
context 'when the jobs are completed' do
let(:batched_migration) { create(:batched_background_migration, status: :active, total_tuple_count: 100) }
- let!(:batched_job) { create(:batched_background_migration_job, batched_migration: batched_migration, status: :succeeded) }
+ let!(:batched_job) { create(:batched_background_migration_job, :succeeded, batched_migration: batched_migration) }
it 'finishes the migration' do
batched_migration.finished!
@@ -64,7 +64,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
it 'returns the first active migration according to queue order' do
expect(described_class.active_migration).to eq(migration2)
- create(:batched_background_migration_job, batched_migration: migration1, batch_size: 1000, status: :succeeded)
+ create(:batched_background_migration_job, :succeeded, batched_migration: migration1, batch_size: 1000)
end
end
@@ -84,10 +84,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
let!(:migration_without_jobs) { create(:batched_background_migration) }
before do
- create(:batched_background_migration_job, batched_migration: migration1, batch_size: 1000, status: :succeeded)
- create(:batched_background_migration_job, batched_migration: migration1, batch_size: 200, status: :failed)
- create(:batched_background_migration_job, batched_migration: migration2, batch_size: 500, status: :succeeded)
- create(:batched_background_migration_job, batched_migration: migration2, batch_size: 200, status: :running)
+ create(:batched_background_migration_job, :succeeded, batched_migration: migration1, batch_size: 1000)
+ create(:batched_background_migration_job, :failed, batched_migration: migration1, batch_size: 200)
+ create(:batched_background_migration_job, :succeeded, batched_migration: migration2, batch_size: 500)
+ create(:batched_background_migration_job, :running, batched_migration: migration2, batch_size: 200)
end
it 'returns totals from successful jobs' do
@@ -268,7 +268,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject(:retry_failed_jobs) { batched_migration.retry_failed_jobs! }
context 'when there are failed migration jobs' do
- let!(:batched_background_migration_job) { create(:batched_background_migration_job, batched_migration: batched_migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3) }
+ let!(:batched_background_migration_job) { create(:batched_background_migration_job, :failed, batched_migration: batched_migration, batch_size: 10, min_value: 6, max_value: 15, attempts: 3) }
before do
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
@@ -312,9 +312,9 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
let(:batched_migration) { create(:batched_background_migration) }
before do
- create_list(:batched_background_migration_job, 5, status: :succeeded, batch_size: 1_000, batched_migration: batched_migration)
- create_list(:batched_background_migration_job, 1, status: :running, batch_size: 1_000, batched_migration: batched_migration)
- create_list(:batched_background_migration_job, 1, status: :failed, batch_size: 1_000, batched_migration: batched_migration)
+ create_list(:batched_background_migration_job, 5, :succeeded, batch_size: 1_000, batched_migration: batched_migration)
+ create_list(:batched_background_migration_job, 1, :running, batch_size: 1_000, batched_migration: batched_migration)
+ create_list(:batched_background_migration_job, 1, :failed, batch_size: 1_000, batched_migration: batched_migration)
end
it 'sums the batch_size of succeeded jobs' do
@@ -347,7 +347,6 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
let_it_be(:common_attrs) do
{
- status: :succeeded,
batched_migration: migration,
finished_at: end_time
}
@@ -357,7 +356,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject { migration.smoothed_time_efficiency(number_of_jobs: 10) }
it 'returns nil' do
- create_list(:batched_background_migration_job, 9, **common_attrs)
+ create_list(:batched_background_migration_job, 9, :succeeded, **common_attrs)
expect(subject).to be_nil
end
@@ -369,6 +368,8 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
subject { migration.smoothed_time_efficiency(number_of_jobs: number_of_jobs) }
+ let!(:jobs) { create_list(:batched_background_migration_job, number_of_jobs, :succeeded, **common_attrs.merge(batched_migration: migration)) }
+
before do
expect(migration).to receive_message_chain(:batched_jobs, :successful_in_execution_order, :reverse_order, :limit, :with_preloads)
.and_return(jobs)
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
index c1183a15e37..b3c4522a2a1 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_wrapper_spec.rb
@@ -35,8 +35,6 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
expect(job_instance).to receive(:perform)
expect(job_instance).to receive(:batch_metrics).and_return(test_metrics)
- expect(job_record).to receive(:update!).with(hash_including(attempts: 1, status: :running)).and_call_original
-
freeze_time do
subject
@@ -51,11 +49,10 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
context 'when running a job that failed previously' do
let!(:job_record) do
- create(:batched_background_migration_job,
+ create(:batched_background_migration_job, :failed,
batched_migration: active_migration,
pause_ms: pause_ms,
attempts: 1,
- status: :failed,
finished_at: 1.hour.ago,
metrics: { 'my_metrics' => 'some_value' }
)
@@ -67,10 +64,6 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper, '
expect(job_instance).to receive(:perform)
expect(job_instance).to receive(:batch_metrics).and_return(updated_metrics)
- expect(job_record).to receive(:update!).with(
- hash_including(attempts: 2, status: :running, finished_at: nil, metrics: {})
- ).and_call_original
-
freeze_time do
subject
diff --git a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
index a0e78186caa..c8e744ab262 100644
--- a/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb
@@ -119,123 +119,80 @@ RSpec.describe Gitlab::GithubImport::Importer::DiffNoteImporter, :aggregate_fail
.and_return(discussion_id)
end
- context 'when github_importer_use_diff_note_with_suggestions is disabled' do
- before do
- stub_feature_flags(github_importer_use_diff_note_with_suggestions: false)
+ it_behaves_like 'diff notes without suggestion'
+
+ context 'when the note has suggestions' do
+ let(:note_body) do
+ <<~EOB
+ Suggestion:
+ ```suggestion
+ what do you think to do it like this
+ ```
+ EOB
end
- it_behaves_like 'diff notes without suggestion'
+ before do
+ stub_user_finder(user.id, true)
+ end
- context 'when the note has suggestions' do
- let(:note_body) do
- <<~EOB
+ it 'imports the note as diff note' do
+ expect { subject.execute }
+ .to change(DiffNote, :count)
+ .by(1)
+
+ note = project.notes.diff_notes.take
+ expect(note).to be_valid
+ expect(note.noteable_type).to eq('MergeRequest')
+ expect(note.noteable_id).to eq(merge_request.id)
+ expect(note.project_id).to eq(project.id)
+ expect(note.author_id).to eq(user.id)
+ expect(note.system).to eq(false)
+ expect(note.discussion_id).to eq(discussion_id)
+ expect(note.commit_id).to eq('original123abc')
+ expect(note.line_code).to eq(note_representation.line_code)
+ expect(note.type).to eq('DiffNote')
+ expect(note.created_at).to eq(created_at)
+ expect(note.updated_at).to eq(updated_at)
+ expect(note.position.to_h).to eq({
+ base_sha: merge_request.diffs.diff_refs.base_sha,
+ head_sha: merge_request.diffs.diff_refs.head_sha,
+ start_sha: merge_request.diffs.diff_refs.start_sha,
+ new_line: 15,
+ old_line: nil,
+ new_path: file_path,
+ old_path: file_path,
+ position_type: 'text',
+ line_range: nil
+ })
+ expect(note.note)
+ .to eq <<~NOTE
Suggestion:
- ```suggestion
+ ```suggestion:-0+0
what do you think to do it like this
```
- EOB
- end
-
- it 'imports the note' do
- stub_user_finder(user.id, true)
-
- expect { subject.execute }
- .to change(LegacyDiffNote, :count)
- .and not_change(DiffNote, :count)
-
- note = project.notes.diff_notes.take
- expect(note).to be_valid
- expect(note.note)
- .to eq <<~NOTE
- Suggestion:
- ```suggestion:-0+0
- what do you think to do it like this
- ```
- NOTE
- end
- end
- end
-
- context 'when github_importer_use_diff_note_with_suggestions is enabled' do
- before do
- stub_feature_flags(github_importer_use_diff_note_with_suggestions: true)
+ NOTE
end
- it_behaves_like 'diff notes without suggestion'
+ context 'when the note diff file creation fails' do
+ it 'falls back to the LegacyDiffNote' do
+ exception = ::DiffNote::NoteDiffFileCreationError.new('Failed to create diff note file')
- context 'when the note has suggestions' do
- let(:note_body) do
- <<~EOB
- Suggestion:
- ```suggestion
- what do you think to do it like this
- ```
- EOB
- end
+ expect_next_instance_of(::Import::Github::Notes::CreateService) do |service|
+ expect(service)
+ .to receive(:execute)
+ .and_raise(exception)
+ end
- before do
- stub_user_finder(user.id, true)
- end
+ expect(Gitlab::GithubImport::Logger)
+ .to receive(:warn)
+ .with(
+ message: 'Failed to create diff note file',
+ 'error.class': 'DiffNote::NoteDiffFileCreationError'
+ )
- it 'imports the note as diff note' do
expect { subject.execute }
- .to change(DiffNote, :count)
- .by(1)
-
- note = project.notes.diff_notes.take
- expect(note).to be_valid
- expect(note.noteable_type).to eq('MergeRequest')
- expect(note.noteable_id).to eq(merge_request.id)
- expect(note.project_id).to eq(project.id)
- expect(note.author_id).to eq(user.id)
- expect(note.system).to eq(false)
- expect(note.discussion_id).to eq(discussion_id)
- expect(note.commit_id).to eq('original123abc')
- expect(note.line_code).to eq(note_representation.line_code)
- expect(note.type).to eq('DiffNote')
- expect(note.created_at).to eq(created_at)
- expect(note.updated_at).to eq(updated_at)
- expect(note.position.to_h).to eq({
- base_sha: merge_request.diffs.diff_refs.base_sha,
- head_sha: merge_request.diffs.diff_refs.head_sha,
- start_sha: merge_request.diffs.diff_refs.start_sha,
- new_line: 15,
- old_line: nil,
- new_path: file_path,
- old_path: file_path,
- position_type: 'text',
- line_range: nil
- })
- expect(note.note)
- .to eq <<~NOTE
- Suggestion:
- ```suggestion:-0+0
- what do you think to do it like this
- ```
- NOTE
- end
-
- context 'when the note diff file creation fails' do
- it 'falls back to the LegacyDiffNote' do
- exception = ::DiffNote::NoteDiffFileCreationError.new('Failed to create diff note file')
-
- expect_next_instance_of(::Import::Github::Notes::CreateService) do |service|
- expect(service)
- .to receive(:execute)
- .and_raise(exception)
- end
-
- expect(Gitlab::GithubImport::Logger)
- .to receive(:warn)
- .with(
- message: 'Failed to create diff note file',
- 'error.class': 'DiffNote::NoteDiffFileCreationError'
- )
-
- expect { subject.execute }
- .to change(LegacyDiffNote, :count)
- .and not_change(DiffNote, :count)
- end
+ .to change(LegacyDiffNote, :count)
+ .and not_change(DiffNote, :count)
end
end
end
diff --git a/spec/lib/gitlab/rack_attack/request_spec.rb b/spec/lib/gitlab/rack_attack/request_spec.rb
index 37db588ce16..9b882f26480 100644
--- a/spec/lib/gitlab/rack_attack/request_spec.rb
+++ b/spec/lib/gitlab/rack_attack/request_spec.rb
@@ -192,6 +192,44 @@ RSpec.describe Gitlab::RackAttack::Request do
end
end
+ describe '#frontend_request?', :allow_forgery_protection do
+ subject { request.send(:frontend_request?) }
+
+ let(:path) { '/' }
+
+ # Define these as local variables so we can use them in the `where` block.
+ valid_token = SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH)
+ other_token = SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH)
+
+ where(:session, :env, :expected) do
+ {} | {} | false # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
+ {} | { 'HTTP_X_CSRF_TOKEN' => valid_token } | false
+ { _csrf_token: valid_token } | { 'HTTP_X_CSRF_TOKEN' => other_token } | false
+ { _csrf_token: valid_token } | { 'HTTP_X_CSRF_TOKEN' => valid_token } | true
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+ end
+
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(rate_limit_frontend_requests: false)
+ end
+
+ where(:session, :env) do
+ {} | {} # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
+ {} | { 'HTTP_X_CSRF_TOKEN' => valid_token }
+ { _csrf_token: valid_token } | { 'HTTP_X_CSRF_TOKEN' => other_token }
+ { _csrf_token: valid_token } | { 'HTTP_X_CSRF_TOKEN' => valid_token }
+ end
+
+ with_them do
+ it { is_expected.to be(false) }
+ end
+ end
+ end
+
describe '#deprecated_api_request?' do
subject { request.send(:deprecated_api_request?) }
diff --git a/spec/requests/admin/background_migrations_controller_spec.rb b/spec/requests/admin/background_migrations_controller_spec.rb
index c7d5d5cae08..67c9c4df827 100644
--- a/spec/requests/admin/background_migrations_controller_spec.rb
+++ b/spec/requests/admin/background_migrations_controller_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Admin::BackgroundMigrationsController, :enable_admin_mode do
let(:migration) { create(:batched_background_migration, status: 'failed') }
before do
- create(:batched_background_migration_job, batched_migration: migration, batch_size: 10, min_value: 6, max_value: 15, status: :failed, attempts: 3)
+ create(:batched_background_migration_job, :failed, batched_migration: migration, batch_size: 10, min_value: 6, max_value: 15, attempts: 3)
allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class|
allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10])
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 27572b38451..156a4cf5ff3 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -5,6 +5,7 @@ require 'mime/types'
RSpec.describe API::Commits do
include ProjectForksHelper
+ include SessionHelpers
let(:user) { create(:user) }
let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
@@ -384,14 +385,7 @@ RSpec.describe API::Commits do
context 'when using warden' do
it 'increments usage counters', :clean_gitlab_redis_sessions do
- session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
- session_hash = { 'warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]] }
-
- Gitlab::Redis::Sessions.with do |redis|
- redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
- end
-
- cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+ stub_session('warden.user.user.key' => [[user.id], user.encrypted_password[0, 29]])
expect(::Gitlab::UsageDataCounters::WebIdeCounter).to receive(:increment_commits_count)
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).to receive(:track_web_ide_edit_action)
diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb
index 25eae57a134..f2126e3cf9c 100644
--- a/spec/requests/rack_attack_global_spec.rb
+++ b/spec/requests/rack_attack_global_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_caching do
include RackAttackSpecHelpers
+ include SessionHelpers
let(:settings) { Gitlab::CurrentSettings.current_application_settings }
@@ -63,6 +64,22 @@ RSpec.describe 'Rack Attack global throttles', :use_clean_rails_memory_store_cac
end
end
+ describe 'API requests from the frontend', :api, :clean_gitlab_redis_sessions do
+ context 'when unauthenticated' do
+ it_behaves_like 'rate-limited frontend API requests' do
+ let(:throttle_setting_prefix) { 'throttle_unauthenticated' }
+ end
+ end
+
+ context 'when authenticated' do
+ it_behaves_like 'rate-limited frontend API requests' do
+ let_it_be(:personal_access_token) { create(:personal_access_token) }
+
+ let(:throttle_setting_prefix) { 'throttle_authenticated' }
+ end
+ end
+ end
+
describe 'API requests authenticated with personal access token', :api do
let_it_be(:user) { create(:user) }
let_it_be(:token) { create(:personal_access_token, user: user) }
diff --git a/spec/support/helpers/rack_attack_spec_helpers.rb b/spec/support/helpers/rack_attack_spec_helpers.rb
index d50a6382a40..c82a87dc58e 100644
--- a/spec/support/helpers/rack_attack_spec_helpers.rb
+++ b/spec/support/helpers/rack_attack_spec_helpers.rb
@@ -26,14 +26,14 @@ module RackAttackSpecHelpers
{ 'AUTHORIZATION' => "Basic #{encoded_login}" }
end
- def expect_rejection(&block)
+ def expect_rejection(name = nil, &block)
yield
expect(response).to have_gitlab_http_status(:too_many_requests)
expect(response.headers.to_h).to include(
'RateLimit-Limit' => a_string_matching(/^\d+$/),
- 'RateLimit-Name' => a_string_matching(/^throttle_.*$/),
+ 'RateLimit-Name' => name || a_string_matching(/^throttle_.*$/),
'RateLimit-Observed' => a_string_matching(/^\d+$/),
'RateLimit-Remaining' => a_string_matching(/^\d+$/),
'Retry-After' => a_string_matching(/^\d+$/)
diff --git a/spec/support/helpers/session_helpers.rb b/spec/support/helpers/session_helpers.rb
index 236585296e5..394a401afca 100644
--- a/spec/support/helpers/session_helpers.rb
+++ b/spec/support/helpers/session_helpers.rb
@@ -1,6 +1,22 @@
# frozen_string_literal: true
module SessionHelpers
+ # Stub a session in Redis, for use in request specs where we can't mock the session directly.
+ # This also needs the :clean_gitlab_redis_sessions tag on the spec.
+ def stub_session(session_hash)
+ unless RSpec.current_example.metadata[:clean_gitlab_redis_sessions]
+ raise 'Add :clean_gitlab_redis_sessions to your spec!'
+ end
+
+ session_id = Rack::Session::SessionId.new(SecureRandom.hex)
+
+ Gitlab::Redis::Sessions.with do |redis|
+ redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
+ end
+
+ cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+ end
+
def expect_single_session_with_authenticated_ttl
expect_single_session_with_expiration(Settings.gitlab['session_expire_delay'] * 60)
end
diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
index 8bffd1f71e9..a42a1fda62e 100644
--- a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
@@ -10,6 +10,8 @@ RSpec.shared_examples 'when the snippet is not found' do
end
RSpec.shared_examples 'snippet edit usage data counters' do
+ include SessionHelpers
+
context 'when user is sessionless' do
it 'does not track usage data actions' do
expect(::Gitlab::UsageDataCounters::EditorUniqueCounter).not_to receive(:track_snippet_editor_edit_action)
@@ -20,14 +22,7 @@ RSpec.shared_examples 'snippet edit usage data counters' do
context 'when user is not sessionless', :clean_gitlab_redis_sessions do
before do
- session_id = Rack::Session::SessionId.new('6919a6f1bb119dd7396fadc38fd18d0d')
- session_hash = { 'warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]] }
-
- Gitlab::Redis::Sessions.with do |redis|
- redis.set("session:gitlab:#{session_id.private_id}", Marshal.dump(session_hash))
- end
-
- cookies[Gitlab::Application.config.session_options[:key]] = session_id.public_id
+ stub_session('warden.user.user.key' => [[current_user.id], current_user.encrypted_password[0, 29]])
end
it 'tracks usage data actions', :clean_gitlab_redis_sessions do
diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
index b294467d482..c6c6c44dce8 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -580,3 +580,88 @@ RSpec.shared_examples 'rate-limited unauthenticated requests' do
end
end
end
+
+# Requires let variables:
+# * throttle_setting_prefix: "throttle_authenticated", "throttle_unauthenticated"
+RSpec.shared_examples 'rate-limited frontend API requests' do
+ let(:requests_per_period) { 1 }
+ let(:csrf_token) { SecureRandom.base64(ActionController::RequestForgeryProtection::AUTHENTICITY_TOKEN_LENGTH) }
+ let(:csrf_session) { { _csrf_token: csrf_token } }
+ let(:personal_access_token) { nil }
+
+ let(:api_path) { '/projects' }
+
+ # These don't actually exist, so a 404 is the expected response.
+ let(:files_api_path) { '/projects/1/repository/files/ref/path' }
+ let(:packages_api_path) { '/projects/1/packages/foo' }
+ let(:deprecated_api_path) { '/groups/1?with_projects=true' }
+
+ def get_api(path: api_path, csrf: false)
+ headers = csrf ? { 'X-CSRF-Token' => csrf_token } : nil
+ get api(path, personal_access_token: personal_access_token), headers: headers
+ end
+
+ def expect_not_found(&block)
+ yield
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ before do
+ stub_application_setting(
+ "#{throttle_setting_prefix}_enabled" => true,
+ "#{throttle_setting_prefix}_requests_per_period" => requests_per_period,
+ "#{throttle_setting_prefix}_api_enabled" => true,
+ "#{throttle_setting_prefix}_api_requests_per_period" => requests_per_period,
+ "#{throttle_setting_prefix}_web_enabled" => true,
+ "#{throttle_setting_prefix}_web_requests_per_period" => requests_per_period,
+ "#{throttle_setting_prefix}_files_api_enabled" => true,
+ "#{throttle_setting_prefix}_packages_api_enabled" => true,
+ "#{throttle_setting_prefix}_deprecated_api_enabled" => true
+ )
+
+ stub_session(csrf_session)
+ end
+
+ context 'with a CSRF token' do
+ it 'uses the rate limit for web requests' do
+ requests_per_period.times { get_api csrf: true }
+
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true }
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: files_api_path }
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: packages_api_path }
+ expect_rejection("#{throttle_setting_prefix}_web") { get_api csrf: true, path: deprecated_api_path }
+
+ # API rate limit is not triggered yet
+ expect_ok { get_api }
+ expect_not_found { get_api path: files_api_path }
+ expect_not_found { get_api path: packages_api_path }
+ expect_not_found { get_api path: deprecated_api_path }
+ end
+
+ context 'without a CSRF session' do
+ let(:csrf_session) { nil }
+
+ it 'always uses the rate limit for API requests' do
+ requests_per_period.times { get_api csrf: true }
+
+ expect_rejection("#{throttle_setting_prefix}_api") { get_api csrf: true }
+ expect_rejection("#{throttle_setting_prefix}_api") { get_api }
+ end
+ end
+ end
+
+ context 'without a CSRF token' do
+ it 'uses the rate limit for API requests' do
+ requests_per_period.times { get_api }
+
+ expect_rejection("#{throttle_setting_prefix}_api") { get_api }
+
+ # Web and custom API rate limits are not triggered yet
+ expect_ok { get_api csrf: true }
+ expect_not_found { get_api path: files_api_path }
+ expect_not_found { get_api path: packages_api_path }
+ expect_not_found { get_api path: deprecated_api_path }
+ end
+ end
+end