+
+### Embedding Grafana panels in Markdown is deprecated
+
+Planned removal: GitLab 16.0
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+The ability to add Grafana panels in GitLab Flavored Markdown is deprecated in 15.9 and will be removed in 16.0.
+We intend to replace this feature with the ability to [embed charts](https://gitlab.com/groups/gitlab-org/opstrace/-/epics/33) with the [GitLab Observability UI](https://gitlab.com/gitlab-org/opstrace/opstrace-ui).
+
+
+
### GitLab Runner platforms and setup instructions in GraphQL API
diff --git a/doc/user/group/epics/manage_epics.md b/doc/user/group/epics/manage_epics.md
index da35d3e88dd..fd26e2bdb3a 100644
--- a/doc/user/group/epics/manage_epics.md
+++ b/doc/user/group/epics/manage_epics.md
@@ -116,10 +116,10 @@ Prerequisites:
To reorder list items, when viewing an epic:
-1. Hover over the list item row to make the drag icon (**{drag-vertical}**) visible.
-1. Select and hold the drag icon.
+1. Hover over the list item row to make the grip icon (**{grip}**) visible.
+1. Select and hold the grip icon.
1. Drag the row to the new position in the list.
-1. Release the drag icon.
+1. Release the grip icon.
## Bulk edit epics
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
index 212c8ee54be..ca737878ea5 100644
--- a/doc/user/project/issues/managing_issues.md
+++ b/doc/user/project/issues/managing_issues.md
@@ -174,10 +174,10 @@ Prerequisites:
To reorder list items, when viewing an issue:
-1. Hover over the list item row to make the drag icon (**{drag-vertical}**) visible.
-1. Select and hold the drag icon.
+1. Hover over the list item row to make the grip icon (**{grip}**) visible.
+1. Select and hold the grip icon.
1. Drag the row to the new position in the list.
-1. Release the drag icon.
+1. Release the grip icon.
## Close an issue
diff --git a/lib/gitlab/ci/artifacts/logger.rb b/lib/gitlab/ci/artifacts/logger.rb
index 628f4129df4..73f0409f8b1 100644
--- a/lib/gitlab/ci/artifacts/logger.rb
+++ b/lib/gitlab/ci/artifacts/logger.rb
@@ -29,17 +29,19 @@ module Gitlab
)
end
- def self.log_created(artifact)
- payload = Gitlab::ApplicationContext.current.merge(
- message: 'Artifact created',
- job_artifact_id: artifact.id,
- size: artifact.size,
- type: artifact.file_type,
- build_id: artifact.job_id,
- project_id: artifact.project_id
- )
+ def self.log_created(job_artifacts)
+ Array(job_artifacts).each do |artifact|
+ payload = Gitlab::ApplicationContext.current.merge(
+ message: 'Artifact created',
+ job_artifact_id: artifact.id,
+ size: artifact.size,
+ type: artifact.file_type,
+ build_id: artifact.job_id,
+ project_id: artifact.project_id
+ )
- Gitlab::AppLogger.info(payload)
+ Gitlab::AppLogger.info(payload)
+ end
end
def self.log_deleted(job_artifacts, method)
diff --git a/lib/gitlab/redis.rb b/lib/gitlab/redis.rb
index ed4f6015603..fff086c38a8 100644
--- a/lib/gitlab/redis.rb
+++ b/lib/gitlab/redis.rb
@@ -12,6 +12,7 @@ module Gitlab
Gitlab::Redis::Queues,
Gitlab::Redis::RateLimiting,
Gitlab::Redis::RepositoryCache,
+ Gitlab::Redis::ClusterRateLimiting,
Gitlab::Redis::Sessions,
Gitlab::Redis::SharedState,
Gitlab::Redis::TraceChunks
diff --git a/lib/gitlab/redis/cluster_rate_limiting.rb b/lib/gitlab/redis/cluster_rate_limiting.rb
new file mode 100644
index 00000000000..e9d1e4f0c3f
--- /dev/null
+++ b/lib/gitlab/redis/cluster_rate_limiting.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Redis
+ class ClusterRateLimiting < ::Gitlab::Redis::Wrapper
+ def self.config_fallback
+ Cache
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/redis/multi_store.rb b/lib/gitlab/redis/multi_store.rb
index c0fab462786..2a5c606be43 100644
--- a/lib/gitlab/redis/multi_store.rb
+++ b/lib/gitlab/redis/multi_store.rb
@@ -169,11 +169,15 @@ module Gitlab
end
def use_primary_and_secondary_stores?
- feature_enabled?("use_primary_and_secondary_stores_for")
+ feature_table_exists? &&
+ Feature.enabled?("use_primary_and_secondary_stores_for_#{instance_name.underscore}") && # rubocop:disable Cop/FeatureFlagUsage
+ !same_redis_store?
end
def use_primary_store_as_default?
- feature_enabled?("use_primary_store_as_default_for")
+ feature_table_exists? &&
+ Feature.enabled?("use_primary_store_as_default_for_#{instance_name.underscore}") && # rubocop:disable Cop/FeatureFlagUsage
+ !same_redis_store?
end
def increment_pipelined_command_error_count(command_name)
@@ -213,15 +217,10 @@ module Gitlab
private
- # @return [Boolean]
- def feature_enabled?(prefix)
- feature_table_exists? &&
- Feature.enabled?("#{prefix}_#{instance_name.underscore}") && # rubocop:disable Cop/FeatureFlagUsage
- !same_redis_store?
- end
-
# @return [Boolean]
def feature_table_exists?
+ # Use table_exists? (which uses ActiveRecord's schema cache) instead of Feature.feature_flags_available?
+ # as the latter runs a ';' SQL query which causes a connection to be checked out.
Feature::FlipperFeature.table_exists?
rescue StandardError
false
diff --git a/lib/gitlab/redis/rate_limiting.rb b/lib/gitlab/redis/rate_limiting.rb
index 4ae1d55e4ce..3a9fb63a495 100644
--- a/lib/gitlab/redis/rate_limiting.rb
+++ b/lib/gitlab/redis/rate_limiting.rb
@@ -3,13 +3,24 @@
module Gitlab
module Redis
class RateLimiting < ::Gitlab::Redis::Wrapper
- # The data we store on RateLimiting used to be stored on Cache.
- def self.config_fallback
- Cache
- end
+ class << self
+ # The data we store on RateLimiting used to be stored on Cache.
+ def config_fallback
+ Cache
+ end
- def self.cache_store
- @cache_store ||= ActiveSupport::Cache::RedisCacheStore.new(redis: pool, namespace: Cache::CACHE_NAMESPACE)
+ def cache_store
+ @cache_store ||= ActiveSupport::Cache::RedisCacheStore.new(redis: pool, namespace: Cache::CACHE_NAMESPACE)
+ end
+
+ private
+
+ def redis
+ primary_store = ::Redis.new(::Gitlab::Redis::ClusterRateLimiting.params)
+ secondary_store = ::Redis.new(params)
+
+ MultiStore.new(primary_store, secondary_store, name.demodulize)
+ end
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 140dad41659..e98cb7bdf17 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -36464,7 +36464,7 @@ msgstr ""
msgid "Runners|Active"
msgstr ""
-msgid "Runners|Add notes, like who owns the runner or what it should be used for."
+msgid "Runners|Add notes such as the runner owner or what it should be used for."
msgstr ""
msgid "Runners|Administrator"
@@ -36715,6 +36715,9 @@ msgstr ""
msgid "Runners|Online:"
msgstr ""
+msgid "Runners|Only administrators can view this."
+msgstr ""
+
msgid "Runners|Owner"
msgstr ""
@@ -38610,6 +38613,9 @@ msgstr ""
msgid "SecurityReports|There was an error deleting the comment."
msgstr ""
+msgid "SecurityReports|There was an error dismissing the finding. Please try again."
+msgstr ""
+
msgid "SecurityReports|There was an error dismissing the vulnerabilities."
msgstr ""
diff --git a/spec/frontend/issues/show/components/description_spec.js b/spec/frontend/issues/show/components/description_spec.js
index 309930afa4d..6345238e0e8 100644
--- a/spec/frontend/issues/show/components/description_spec.js
+++ b/spec/frontend/issues/show/components/description_spec.js
@@ -25,6 +25,7 @@ import {
} from 'jest/work_items/mock_data';
import {
descriptionProps as initialProps,
+ descriptionHtmlWithList,
descriptionHtmlWithCheckboxes,
descriptionHtmlWithTask,
} from '../mock_data/mock_data';
@@ -36,6 +37,7 @@ jest.mock('~/lib/utils/url_utility', () => ({
jest.mock('~/task_list');
jest.mock('~/behaviors/markdown/render_gfm');
+const mockSpriteIcons = '/icons.svg';
const showModal = jest.fn();
const hideModal = jest.fn();
const showDetailsModal = jest.fn();
@@ -57,11 +59,13 @@ const createWorkItemFromTaskSuccessHandler = jest
describe('Description component', () => {
let wrapper;
+ let originalGon;
Vue.use(VueApollo);
const findGfmContent = () => wrapper.find('[data-testid="gfm-content"]');
const findTextarea = () => wrapper.find('[data-testid="textarea"]');
+ const findListItems = () => findGfmContent().findAll('ul > li');
const findTaskActionButtons = () => wrapper.findAll('.task-list-item-actions');
const findTaskLink = () => wrapper.find('a.gfm-issue');
const findModal = () => wrapper.findComponent(GlModal);
@@ -71,6 +75,7 @@ describe('Description component', () => {
props = {},
provide,
createWorkItemFromTaskHandler = createWorkItemFromTaskSuccessHandler,
+ ...options
} = {}) {
wrapper = shallowMountExtended(Description, {
propsData: {
@@ -103,10 +108,14 @@ describe('Description component', () => {
},
}),
},
+ ...options,
});
}
beforeEach(() => {
+ originalGon = window.gon;
+ window.gon = { sprite_icons: mockSpriteIcons };
+
setWindowLocation(TEST_HOST);
if (!document.querySelector('.issuable-meta')) {
@@ -120,6 +129,8 @@ describe('Description component', () => {
});
afterAll(() => {
+ window.gon = originalGon;
+
$('.issuable-meta .flash-container').remove();
});
@@ -261,6 +272,37 @@ describe('Description component', () => {
});
});
+ describe('with list', () => {
+ beforeEach(async () => {
+ createComponent({
+ props: {
+ descriptionHtml: descriptionHtmlWithList,
+ },
+ attachTo: document.body,
+ });
+ await nextTick();
+ });
+
+ it('shows list items', () => {
+ expect(findListItems()).toHaveLength(3);
+ });
+
+ it('shows list items drag icons', () => {
+ const dragIcon = findListItems().at(0).find('.drag-icon');
+
+ expect(dragIcon.classes()).toEqual(
+ expect.arrayContaining(['s14', 'gl-icon', 'gl-cursor-grab', 'gl-opacity-0']),
+ );
+ expect(dragIcon.attributes()).toMatchObject({
+ 'aria-hidden': 'true',
+ role: 'img',
+ });
+ expect(dragIcon.find('use').attributes()).toEqual({
+ href: `${mockSpriteIcons}#grip`,
+ });
+ });
+ });
+
describe('with work_items_mvc_2 feature flag enabled', () => {
describe('empty description', () => {
beforeEach(() => {
diff --git a/spec/frontend/issues/show/mock_data/mock_data.js b/spec/frontend/issues/show/mock_data/mock_data.js
index 909789b7a0f..9f0b6fb1148 100644
--- a/spec/frontend/issues/show/mock_data/mock_data.js
+++ b/spec/frontend/issues/show/mock_data/mock_data.js
@@ -59,6 +59,14 @@ export const appProps = {
publishedIncidentUrl,
};
+export const descriptionHtmlWithList = `
+
+ - todo 1
+ - todo 2
+ - todo 3
+
+`;
+
export const descriptionHtmlWithCheckboxes = `
-
diff --git a/spec/frontend/releases/stores/modules/detail/mutations_spec.js b/spec/frontend/releases/stores/modules/detail/mutations_spec.js
index 944769d22cc..bf40af9a897 100644
--- a/spec/frontend/releases/stores/modules/detail/mutations_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/mutations_spec.js
@@ -89,6 +89,15 @@ describe('Release edit/new mutations', () => {
expect(state.release.tagName).toBe(newTag);
});
+
+ it('nulls out existing release', () => {
+ state.release = release;
+ state.existingRelease = release;
+ const newTag = 'updated-tag-name';
+ mutations[types.UPDATE_RELEASE_TAG_NAME](state, newTag);
+
+ expect(state.existingRelease).toBe(null);
+ });
});
describe(`${types.UPDATE_RELEASE_TAG_MESSAGE}`, () => {
@@ -304,6 +313,17 @@ describe('Release edit/new mutations', () => {
expect(state.tagNotes).toBe('');
expect(state.isFetchingTagNotes).toBe(false);
});
+
+ it('nulls out existing release', () => {
+ state.existingRelease = release;
+ const message = 'there was an error';
+ state.isFetchingTagNotes = true;
+ state.tagNotes = 'tag notes';
+
+ mutations[types.RECEIVE_TAG_NOTES_ERROR](state, { message });
+
+ expect(state.existingRelease).toBe(null);
+ });
});
describe(`${types.UPDATE_INCLUDE_TAG_NOTES}`, () => {
it('sets whether or not to include the tag notes', () => {
diff --git a/spec/lib/gitlab/auth/ip_rate_limiter_spec.rb b/spec/lib/gitlab/auth/ip_rate_limiter_spec.rb
index 3d9be4c3489..c5703cf5c24 100644
--- a/spec/lib/gitlab/auth/ip_rate_limiter_spec.rb
+++ b/spec/lib/gitlab/auth/ip_rate_limiter_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Gitlab::Auth::IpRateLimiter, :use_clean_rails_memory_store_cachin
before do
stub_rack_attack_setting(options)
- Rack::Attack.reset!
+ Gitlab::Redis::RateLimiting.with(&:flushdb)
Rack::Attack.clear_configuration
Gitlab::RackAttack.configure(Rack::Attack)
end
diff --git a/spec/lib/gitlab/ci/artifacts/logger_spec.rb b/spec/lib/gitlab/ci/artifacts/logger_spec.rb
index 7753cb0d25e..09d8a549640 100644
--- a/spec/lib/gitlab/ci/artifacts/logger_spec.rb
+++ b/spec/lib/gitlab/ci/artifacts/logger_spec.rb
@@ -9,23 +9,27 @@ RSpec.describe Gitlab::Ci::Artifacts::Logger do
describe '.log_created' do
it 'logs information about created artifact' do
- artifact = create(:ci_job_artifact, :archive)
+ artifact_1 = create(:ci_job_artifact, :archive)
+ artifact_2 = create(:ci_job_artifact, :metadata)
+ artifacts = [artifact_1, artifact_2]
- expect(Gitlab::AppLogger).to receive(:info).with(
- hash_including(
- message: 'Artifact created',
- job_artifact_id: artifact.id,
- size: artifact.size,
- type: artifact.file_type,
- build_id: artifact.job_id,
- project_id: artifact.project_id,
- 'correlation_id' => an_instance_of(String),
- 'meta.feature_category' => 'test',
- 'meta.caller_id' => 'caller'
+ artifacts.each do |artifact|
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ hash_including(
+ message: 'Artifact created',
+ job_artifact_id: artifact.id,
+ size: artifact.size,
+ type: artifact.file_type,
+ build_id: artifact.job_id,
+ project_id: artifact.project_id,
+ 'correlation_id' => an_instance_of(String),
+ 'meta.feature_category' => 'test',
+ 'meta.caller_id' => 'caller'
+ )
)
- )
+ end
- described_class.log_created(artifact)
+ described_class.log_created(artifacts)
end
end
diff --git a/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb b/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb
index a8dc7897082..45a15fb5f36 100644
--- a/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Config::External::File::Artifact do
+RSpec.describe Gitlab::Ci::Config::External::File::Artifact, feature_category: :pipeline_authoring do
let(:parent_pipeline) { create(:ci_pipeline) }
let(:variables) {}
let(:context) do
@@ -31,7 +31,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Artifact do
describe '#valid?' do
subject(:valid?) do
- external_file.validate!
+ Gitlab::Ci::Config::External::Mapper::Verifier.new(context).process([external_file])
external_file.valid?
end
@@ -162,7 +162,8 @@ RSpec.describe Gitlab::Ci::Config::External::File::Artifact do
user: anything
}
expect(context).to receive(:mutate).with(expected_attrs).and_call_original
- external_file.validate!
+
+ expect(valid?).to be_truthy
external_file.content
end
end
diff --git a/spec/lib/gitlab/ci/config/external/file/base_spec.rb b/spec/lib/gitlab/ci/config/external/file/base_spec.rb
index 8475c3a8b19..55d95d0c1f8 100644
--- a/spec/lib/gitlab/ci/config/external/file/base_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Config::External::File::Base do
+RSpec.describe Gitlab::Ci::Config::External::File::Base, feature_category: :pipeline_authoring do
let(:variables) {}
let(:context_params) { { sha: 'HEAD', variables: variables } }
let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) }
@@ -51,7 +51,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Base do
describe '#valid?' do
subject(:valid?) do
- file.validate!
+ Gitlab::Ci::Config::External::Mapper::Verifier.new(context).process([file])
file.valid?
end
diff --git a/spec/lib/gitlab/ci/config/external/file/local_spec.rb b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
index cf1892d467c..e1619bca405 100644
--- a/spec/lib/gitlab/ci/config/external/file/local_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
@@ -112,7 +112,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local, feature_category: :pip
describe '#valid?' do
subject(:valid?) do
- local_file.validate!
+ Gitlab::Ci::Config::External::Mapper::Verifier.new(context).process([local_file])
local_file.valid?
end
@@ -142,9 +142,12 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local, feature_category: :pip
let(:variables) { Gitlab::Ci::Variables::Collection.new([{ 'key' => 'GITLAB_TOKEN', 'value' => 'secret', 'masked' => true }]) }
let(:location) { '/lib/gitlab/ci/templates/secret/existent-file.yml' }
- it 'returns false and adds an error message about an empty file' do
+ before do
allow_any_instance_of(described_class).to receive(:fetch_local_content).and_return("")
- local_file.validate!
+ end
+
+ it 'returns false and adds an error message about an empty file' do
+ expect(valid?).to be_falsy
expect(local_file.errors).to include("Local file `lib/gitlab/ci/templates/xxxxxx/existent-file.yml` is empty!")
end
end
@@ -208,7 +211,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Local, feature_category: :pip
let(:variables) { Gitlab::Ci::Variables::Collection.new([{ 'key' => 'GITLAB_TOKEN', 'value' => 'secret_file', 'masked' => true }]) }
before do
- local_file.validate!
+ Gitlab::Ci::Config::External::Mapper::Verifier.new(context).process([local_file])
end
it 'returns an error message' do
diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
index 400ca10e645..3e990d28bb2 100644
--- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project, feature_category: :p
describe '#valid?' do
subject(:valid?) do
- project_file.validate!
+ Gitlab::Ci::Config::External::Mapper::Verifier.new(context).process([project_file])
project_file.valid?
end
@@ -79,7 +79,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project, feature_category: :p
{ project: project.full_path, file: '/file.yml' }
end
- around(:all) do |example|
+ around do |example|
create_and_delete_files(project, { '/file.yml' => 'image: image:1.0' }) do
example.run
end
@@ -102,7 +102,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project, feature_category: :p
{ project: project.full_path, ref: 'master', file: '/file.yml' }
end
- around(:all) do |example|
+ around do |example|
create_and_delete_files(project, { '/file.yml' => 'image: image:1.0' }) do
example.run
end
@@ -118,7 +118,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Project, feature_category: :p
let(:variables) { Gitlab::Ci::Variables::Collection.new([{ 'key' => 'GITLAB_TOKEN', 'value' => 'secret_file', 'masked' => true }]) }
- around(:all) do |example|
+ around do |example|
create_and_delete_files(project, { '/secret_file.yml' => '' }) do
example.run
end
diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
index 8d93cdcf378..2ce3c257a43 100644
--- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Config::External::File::Remote do
+RSpec.describe Gitlab::Ci::Config::External::File::Remote, feature_category: :pipeline_authoring do
include StubRequests
let(:variables) { Gitlab::Ci::Variables::Collection.new([{ 'key' => 'GITLAB_TOKEN', 'value' => 'secret_file', 'masked' => true }]) }
@@ -55,7 +55,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do
describe "#valid?" do
subject(:valid?) do
- remote_file.validate!
+ Gitlab::Ci::Config::External::Mapper::Verifier.new(context).process([remote_file])
remote_file.valid?
end
@@ -138,7 +138,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do
describe "#error_message" do
subject(:error_message) do
- remote_file.validate!
+ Gitlab::Ci::Config::External::Mapper::Verifier.new(context).process([remote_file])
remote_file.error_message
end
diff --git a/spec/lib/gitlab/ci/config/external/file/template_spec.rb b/spec/lib/gitlab/ci/config/external/file/template_spec.rb
index 074e7a1d32d..83e98874118 100644
--- a/spec/lib/gitlab/ci/config/external/file/template_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/template_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Config::External::File::Template do
+RSpec.describe Gitlab::Ci::Config::External::File::Template, feature_category: :pipeline_authoring do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
@@ -46,7 +46,7 @@ RSpec.describe Gitlab::Ci::Config::External::File::Template do
describe "#valid?" do
subject(:valid?) do
- template_file.validate!
+ Gitlab::Ci::Config::External::Mapper::Verifier.new(context).process([template_file])
template_file.valid?
end
diff --git a/spec/lib/gitlab/redis/cluster_rate_limiting_spec.rb b/spec/lib/gitlab/redis/cluster_rate_limiting_spec.rb
new file mode 100644
index 00000000000..3eba3233f08
--- /dev/null
+++ b/spec/lib/gitlab/redis/cluster_rate_limiting_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Redis::ClusterRateLimiting, feature_category: :redis do
+ include_examples "redis_new_instance_shared_examples", 'cluster_rate_limiting', Gitlab::Redis::Cache
+end
diff --git a/spec/migrations/20230131125844_add_project_id_name_id_version_index_to_installable_npm_packages_spec.rb b/spec/migrations/20230131125844_add_project_id_name_id_version_index_to_installable_npm_packages_spec.rb
new file mode 100644
index 00000000000..5d8c7ab4745
--- /dev/null
+++ b/spec/migrations/20230131125844_add_project_id_name_id_version_index_to_installable_npm_packages_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe AddProjectIdNameIdVersionIndexToInstallableNpmPackages, feature_category: :package_registry do
+ it 'schedules an index creation' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(ActiveRecord::Base.connection.indexes('packages_packages').map(&:name))
+ .not_to include('idx_packages_on_project_id_name_id_version_when_installable_npm')
+ }
+
+ migration.after -> {
+ expect(ActiveRecord::Base.connection.indexes('packages_packages').map(&:name))
+ .to include('idx_packages_on_project_id_name_id_version_when_installable_npm')
+ }
+ end
+ end
+end
diff --git a/spec/migrations/20230201171450_finalize_backfill_environment_tier_migration_spec.rb b/spec/migrations/20230201171450_finalize_backfill_environment_tier_migration_spec.rb
new file mode 100644
index 00000000000..3fc9c7d8af7
--- /dev/null
+++ b/spec/migrations/20230201171450_finalize_backfill_environment_tier_migration_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe FinalizeBackfillEnvironmentTierMigration, :migration, feature_category: :continuous_delivery do
+ let(:batched_migrations) { table(:batched_background_migrations) }
+
+ let!(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ shared_examples 'finalizes the migration' do
+ it 'finalizes the migration' do
+ allow_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |runner|
+ expect(runner).to receive(:finalize).with('BackfillEnvironmentTiers', :environments, :id, [])
+ end
+ end
+ end
+
+ context 'when migration is missing' do
+ it 'warns migration not found' do
+ expect(Gitlab::AppLogger)
+ .to receive(:warn).with(/Could not find batched background migration for the given configuration:/)
+
+ migrate!
+ end
+ end
+
+ context 'with migration present' do
+ let!(:group_member_namespace_id_backfill) do
+ batched_migrations.create!(
+ job_class_name: 'BackfillEnvironmentTiers',
+ table_name: :environments,
+ column_name: :id,
+ job_arguments: [],
+ interval: 2.minutes,
+ min_value: 1,
+ max_value: 2,
+ batch_size: 1000,
+ sub_batch_size: 200,
+ gitlab_schema: :gitlab_main,
+ status: 3 # finished
+ )
+ end
+
+ context 'when migration finished successfully' do
+ it 'does not raise exception' do
+ expect { migrate! }.not_to raise_error
+ end
+ end
+
+ context 'with different migration statuses' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:status, :description) do
+ 0 | 'paused'
+ 1 | 'active'
+ 4 | 'failed'
+ 5 | 'finalizing'
+ end
+
+ with_them do
+ before do
+ group_member_namespace_id_backfill.update!(status: status)
+ end
+
+ it_behaves_like 'finalizes the migration'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb
index 711002e28af..51913b28306 100644
--- a/spec/services/ci/job_artifacts/create_service_spec.rb
+++ b/spec/services/ci/job_artifacts/create_service_spec.rb
@@ -37,7 +37,7 @@ RSpec.describe Ci::JobArtifacts::CreateService do
it 'logs the created artifact' do
expect(Gitlab::Ci::Artifacts::Logger)
.to receive(:log_created)
- .with(an_instance_of(Ci::JobArtifact))
+ .with([an_instance_of(Ci::JobArtifact)])
subject
end
@@ -132,6 +132,14 @@ RSpec.describe Ci::JobArtifacts::CreateService do
expect(new_artifact).to be_public_accessibility
end
+ it 'logs the created artifact and metadata' do
+ expect(Gitlab::Ci::Artifacts::Logger)
+ .to receive(:log_created)
+ .with([an_instance_of(Ci::JobArtifact), an_instance_of(Ci::JobArtifact)])
+
+ subject
+ end
+
context 'when accessibility level passed as private' do
before do
params.merge!('accessibility' => 'private')
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 82ed6eb4c95..3f457890f35 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -382,7 +382,7 @@ RSpec.shared_examples 'tracking when dry-run mode is set' do
end
def reset_rack_attack
- Rack::Attack.reset!
+ Gitlab::Redis::RateLimiting.with(&:flushdb)
Rack::Attack.clear_configuration
Gitlab::RackAttack.configure(Rack::Attack)
end