Commit a6763b30 authored by Patrick Bair's avatar Patrick Bair

Merge branch '331248-remove-old-migrations-in-release-14-4' into 'master'

Resolve "Remove old migrations in release 14"

See merge request gitlab-org/gitlab!74554
parents f08e6413 0f45b944
---
# While this FF is enabled by default, we want to keep it in place for now as there are still related migrations that
# have not been run in production yet. For additional info, see https://gitlab.com/gitlab-org/gitlab/-/issues/21801
# Discussion and removal MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60322
name: migrate_user_mentions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34378
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/231175
milestone: '13.3'
type: development
group: group::project management
default_enabled: true
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
module UserMentions
class CreateResourceUserMention
# Resources that have mentions to be migrated:
# issue, merge_request, epic, commit, snippet, design
BULK_INSERT_SIZE = 1_000
ISOLATION_MODULE = 'Gitlab::BackgroundMigration::UserMentions::Models'
def perform(resource_model, join, conditions, with_notes, start_id, end_id)
return unless Feature.enabled?(:migrate_user_mentions, default_enabled: true)
resource_model = "#{ISOLATION_MODULE}::#{resource_model}".constantize if resource_model.is_a?(String)
model = with_notes ? Gitlab::BackgroundMigration::UserMentions::Models::Note : resource_model
resource_user_mention_model = resource_model.user_mention_model
records = model.joins(join).where(conditions).where(id: start_id..end_id)
records.each_batch(of: BULK_INSERT_SIZE) do |records|
mentions = []
records.each do |record|
mention_record = record.build_mention_values(resource_user_mention_model.resource_foreign_key)
mentions << mention_record unless mention_record.blank?
end
resource_user_mention_model.insert_all(mentions) unless mentions.empty?
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Lib
module Banzai
# isolated Banzai::ReferenceParser
module ReferenceParser
# Returns the reference parser class for the given type
#
# Example:
#
# Banzai::ReferenceParser['isolated_mentioned_group']
#
# This would return the `::Gitlab::BackgroundMigration::UserMentions::Lib::Banzai::ReferenceParser::IsolatedMentionedGroupParser` class.
def self.[](name)
const_get("::Gitlab::BackgroundMigration::UserMentions::Lib::Banzai::ReferenceParser::#{name.to_s.camelize}Parser", false)
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Lib
module Banzai
module ReferenceParser
# isolated Banzai::ReferenceParser::MentionedGroupParser
class IsolatedMentionedGroupParser < ::Banzai::ReferenceParser::MentionedGroupParser
extend ::Gitlab::Utils::Override
self.reference_type = :user
override :references_relation
def references_relation
::Gitlab::BackgroundMigration::UserMentions::Models::Group
end
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Lib
module Banzai
module ReferenceParser
# isolated Banzai::ReferenceParser::MentionedGroupParser
class IsolatedMentionedProjectParser < ::Banzai::ReferenceParser::MentionedProjectParser
extend ::Gitlab::Utils::Override
self.reference_type = :user
override :references_relation
def references_relation
::Gitlab::BackgroundMigration::UserMentions::Models::Project
end
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Lib
module Banzai
module ReferenceParser
# isolated Banzai::ReferenceParser::MentionedGroupParser
class IsolatedMentionedUserParser < ::Banzai::ReferenceParser::MentionedUserParser
extend ::Gitlab::Utils::Override
self.reference_type = :user
override :references_relation
def references_relation
::Gitlab::BackgroundMigration::UserMentions::Models::User
end
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Lib
module Gitlab
# Extract possible GFM references from an arbitrary String for further processing.
class IsolatedReferenceExtractor < ::Gitlab::ReferenceExtractor
REFERABLES = %i(isolated_mentioned_group isolated_mentioned_user isolated_mentioned_project).freeze
REFERABLES.each do |type|
define_method("#{type}s") do
@references[type] ||= isolated_references(type)
end
end
def isolated_references(type)
context = ::Banzai::RenderContext.new(project, current_user)
processor = ::Gitlab::BackgroundMigration::UserMentions::Lib::Banzai::ReferenceParser[type].new(context)
refs = processor.process(html_documents)
refs[:visible]
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Lib
module Gitlab
# Gitlab::IsolatedVisibilityLevel module
#
# Define allowed public modes that can be used for
# GitLab projects to determine project public mode
#
module IsolatedVisibilityLevel
extend ::ActiveSupport::Concern
included do
scope :public_to_user, -> (user = nil) do
where(visibility_level: IsolatedVisibilityLevel.levels_for_user(user))
end
end
PRIVATE = 0 unless const_defined?(:PRIVATE)
INTERNAL = 10 unless const_defined?(:INTERNAL)
PUBLIC = 20 unless const_defined?(:PUBLIC)
class << self
def levels_for_user(user = nil)
return [PUBLIC] unless user
if user.can_read_all_resources?
[PRIVATE, INTERNAL, PUBLIC]
elsif user.external?
[PUBLIC]
else
[INTERNAL, PUBLIC]
end
end
end
def private?
visibility_level_value == PRIVATE
end
def internal?
visibility_level_value == INTERNAL
end
def public?
visibility_level_value == PUBLIC
end
def visibility_level_value
self[visibility_level_field]
end
end
end
end
end
end
end
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
module UserMentions
module Models
class Commit
include EachBatch
include Concerns::IsolatedMentionable
include Concerns::MentionableMigrationMethods
def self.user_mention_model
Gitlab::BackgroundMigration::UserMentions::Models::CommitUserMention
end
def user_mention_model
self.class.user_mention_model
end
def user_mention_resource_id
id
end
def user_mention_note_id
'NULL'
end
def self.no_quote_columns
[:note_id]
end
end
end
end
end
end
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
module UserMentions
module Models
class CommitUserMention < ActiveRecord::Base
self.table_name = 'commit_user_mentions'
self.inheritance_column = :_type_disabled
def self.resource_foreign_key
:commit_id
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Models
module Concerns
# isolated FeatureGate module
module IsolatedFeatureGate
def flipper_id
return if new_record?
"#{self.class.name}:#{id}"
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Models
module Concerns
# == IsolatedMentionable concern
#
# Shortcutted for isolation version of Mentionable to be used in mentions migrations
#
module IsolatedMentionable
extend ::ActiveSupport::Concern
class_methods do
# Indicate which attributes of the Mentionable to search for GFM references.
def attr_mentionable(attr, options = {})
attr = attr.to_s
mentionable_attrs << [attr, options]
end
end
included do
# Accessor for attributes marked mentionable.
cattr_accessor :mentionable_attrs, instance_accessor: false do
[]
end
if self < Participable
participant -> (user, ext) { all_references(user, extractor: ext) }
end
end
def all_references(current_user = nil, extractor: nil)
# Use custom extractor if it's passed in the function parameters.
if extractor
extractors[current_user] = extractor
else
extractor = extractors[current_user] ||=
Gitlab::BackgroundMigration::UserMentions::Lib::Gitlab::IsolatedReferenceExtractor.new(project, current_user)
extractor.reset_memoized_values
end
self.class.mentionable_attrs.each do |attr, options|
text = __send__(attr) # rubocop:disable GitlabSecurity/PublicSend
options = options.merge(
cache_key: [self, attr],
author: author,
skip_project_check: skip_project_check?
).merge(mentionable_params)
cached_html = self.try(:updated_cached_html_for, attr.to_sym)
options[:rendered] = cached_html if cached_html
extractor.analyze(text, options)
end
extractor
end
def extractors
@extractors ||= {}
end
def skip_project_check?
false
end
def build_mention_values(resource_foreign_key)
refs = all_references(author)
mentioned_users_ids = array_to_sql(refs.isolated_mentioned_users.pluck(:id))
mentioned_projects_ids = array_to_sql(refs.isolated_mentioned_projects.pluck(:id))
mentioned_groups_ids = array_to_sql(refs.isolated_mentioned_groups.pluck(:id))
return if mentioned_users_ids.blank? && mentioned_projects_ids.blank? && mentioned_groups_ids.blank?
{
"#{resource_foreign_key}": user_mention_resource_id,
note_id: user_mention_note_id,
mentioned_users_ids: mentioned_users_ids,
mentioned_projects_ids: mentioned_projects_ids,
mentioned_groups_ids: mentioned_groups_ids
}
end
def array_to_sql(ids_array)
return unless ids_array.present?
'{' + ids_array.join(", ") + '}'
end
private
def mentionable_params
{}
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Models
module Concerns
# Extract common no_quote_columns method used in determining the columns that do not need
# to be quoted for corresponding models
module MentionableMigrationMethods
extend ::ActiveSupport::Concern
class_methods do
def no_quote_columns
[
:note_id,
user_mention_model.resource_foreign_key
]
end
end
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Models
module Concerns
module Namespace
# isolate recursive traversal code for namespace hierarchy
module RecursiveTraversal
extend ActiveSupport::Concern
def root_ancestor
return self if persisted? && parent_id.nil?
strong_memoize(:root_ancestor) do
Gitlab::ObjectHierarchy
.new(self.class.where(id: id))
.base_and_ancestors
.reorder(nil)
.find_by(parent_id: nil)
end
end
# Returns all ancestors, self, and descendants of the current namespace.
def self_and_hierarchy
Gitlab::ObjectHierarchy
.new(self.class.where(id: id))
.all_objects
end
# Returns all the ancestors of the current namespaces.
def ancestors
return self.class.none unless parent_id
Gitlab::ObjectHierarchy
.new(self.class.where(id: parent_id))
.base_and_ancestors
end
# returns all ancestors upto but excluding the given namespace
# when no namespace is given, all ancestors upto the top are returned
def ancestors_upto(top = nil, hierarchy_order: nil)
Gitlab::ObjectHierarchy.new(self.class.where(id: id))
.ancestors(upto: top, hierarchy_order: hierarchy_order)
end
def self_and_ancestors(hierarchy_order: nil)
return self.class.where(id: id) unless parent_id
Gitlab::ObjectHierarchy
.new(self.class.where(id: id))
.base_and_ancestors(hierarchy_order: hierarchy_order)
end
# Returns all the descendants of the current namespace.
def descendants
Gitlab::ObjectHierarchy
.new(self.class.where(parent_id: id))
.base_and_descendants
end
def self_and_descendants
Gitlab::ObjectHierarchy
.new(self.class.where(id: id))
.base_and_descendants
end
end
end
end
end
end
end
end
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
module UserMentions
module Models
module DesignManagement
class Design < ActiveRecord::Base
include EachBatch
include Concerns::MentionableMigrationMethods
self.table_name = 'design_management_designs'
self.inheritance_column = :_type_disabled
def self.user_mention_model
Gitlab::BackgroundMigration::UserMentions::Models::DesignUserMention
end
def user_mention_model
self.class.user_mention_model
end
def user_mention_resource_id
id
end
def user_mention_note_id
'NULL'
end
end
end
end
end
end
end
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
module UserMentions
module Models
class DesignUserMention < ActiveRecord::Base
self.table_name = 'design_user_mentions'
self.inheritance_column = :_type_disabled
def self.resource_foreign_key
:design_id
end
end
end
end
end
end
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
module UserMentions
module Models
class Epic < ActiveRecord::Base
include EachBatch
include Concerns::IsolatedMentionable
include Concerns::MentionableMigrationMethods
include CacheMarkdownField
attr_mentionable :title, pipeline: :single_line
attr_mentionable :description
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description, issuable_state_filter_enabled: true
self.table_name = 'epics'
self.inheritance_column = :_type_disabled
belongs_to :author, class_name: "::Gitlab::BackgroundMigration::UserMentions::Models::User"
belongs_to :group, class_name: "::Gitlab::BackgroundMigration::UserMentions::Models::Group"
def self.user_mention_model
Gitlab::BackgroundMigration::UserMentions::Models::EpicUserMention
end
def user_mention_model
self.class.user_mention_model
end
def project
nil
end
def mentionable_params
{ group: group, label_url_method: :group_epics_url }
end
def user_mention_resource_id
id
end
def user_mention_note_id
'NULL'
end
end
end
end
end
end
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
module UserMentions
module Models
class EpicUserMention < ActiveRecord::Base
self.table_name = 'epic_user_mentions'
self.inheritance_column = :_type_disabled
def self.resource_foreign_key
:epic_id
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Models
# isolated Group model
class Group < ::Gitlab::BackgroundMigration::UserMentions::Models::Namespace
self.store_full_sti_class = false
self.inheritance_column = :_type_disabled
has_one :saml_provider
def root_saml_provider
root_ancestor.saml_provider
end
def self.declarative_policy_class
"GroupPolicy"
end
def max_member_access_for_user(user)
return GroupMember::NO_ACCESS unless user
return GroupMember::OWNER if user.admin?
max_member_access = members_with_parents.where(user_id: user)
.reorder(access_level: :desc)
.first
&.access_level
max_member_access || GroupMember::NO_ACCESS
end
def members_with_parents
# Avoids an unnecessary SELECT when the group has no parents
source_ids =
if has_parent?
self_and_ancestors.reorder(nil).select(:id)
else
id
end
group_hierarchy_members = GroupMember.active_without_invites_and_requests
.where(source_id: source_ids)
GroupMember.from_union([group_hierarchy_members,
members_from_self_and_ancestor_group_shares])
end
# rubocop: disable Metrics/AbcSize
def members_from_self_and_ancestor_group_shares
group_group_link_table = GroupGroupLink.arel_table
group_member_table = GroupMember.arel_table
source_ids =
if has_parent?
self_and_ancestors.reorder(nil).select(:id)
else
id
end
group_group_links_query = GroupGroupLink.where(shared_group_id: source_ids)
cte = Gitlab::SQL::CTE.new(:group_group_links_cte, group_group_links_query)
cte_alias = cte.table.alias(GroupGroupLink.table_name)
# Instead of members.access_level, we need to maximize that access_level at
# the respective group_group_links.group_access.
member_columns = GroupMember.attribute_names.map do |column_name|
if column_name == 'access_level'
smallest_value_arel([cte_alias[:group_access], group_member_table[:access_level]],
'access_level')
else
group_member_table[column_name]
end
end
GroupMember
.with(cte.to_arel)
.select(*member_columns)
.from([group_member_table, cte.alias_to(group_group_link_table)])
.where(group_member_table[:requested_at].eq(nil))
.where(group_member_table[:source_id].eq(group_group_link_table[:shared_with_group_id]))
.where(group_member_table[:source_type].eq('Namespace'))
end
# rubocop: enable Metrics/AbcSize
def smallest_value_arel(args, column_alias)
Arel::Nodes::As.new(
Arel::Nodes::NamedFunction.new('LEAST', args),
Arel::Nodes::SqlLiteral.new(column_alias))
end
end
end
end
end
end
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
module UserMentions
module Models
class MergeRequest < ActiveRecord::Base
include EachBatch
include Concerns::IsolatedMentionable
include CacheMarkdownField
include Concerns::MentionableMigrationMethods
attr_mentionable :title, pipeline: :single_line
attr_mentionable :description
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description, issuable_state_filter_enabled: true
self.table_name = 'merge_requests'
self.inheritance_column = :_type_disabled
belongs_to :author, class_name: "::Gitlab::BackgroundMigration::UserMentions::Models::User"
belongs_to :target_project, class_name: "::Gitlab::BackgroundMigration::UserMentions::Models::Project"
belongs_to :source_project, class_name: "::Gitlab::BackgroundMigration::UserMentions::Models::Project"
alias_attribute :project, :target_project
def self.user_mention_model
Gitlab::BackgroundMigration::UserMentions::Models::MergeRequestUserMention
end
def user_mention_model
self.class.user_mention_model
end
def user_mention_resource_id
id
end
def user_mention_note_id
'NULL'
end
end
end
end
end
end
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
module UserMentions
module Models
class MergeRequestUserMention < ActiveRecord::Base
self.table_name = 'merge_request_user_mentions'
self.inheritance_column = :_type_disabled
def self.resource_foreign_key
:merge_request_id
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Models
# isolated Namespace model
class Namespace < ActiveRecord::Base
self.inheritance_column = :_type_disabled
include Concerns::IsolatedFeatureGate
include Gitlab::BackgroundMigration::UserMentions::Lib::Gitlab::IsolatedVisibilityLevel
include ::Gitlab::Utils::StrongMemoize
include Gitlab::BackgroundMigration::UserMentions::Models::Concerns::Namespace::RecursiveTraversal
belongs_to :parent, class_name: "::Gitlab::BackgroundMigration::UserMentions::Models::Namespace"
def visibility_level_field
:visibility_level
end
def has_parent?
parent_id.present? || parent.present?
end
# Deprecated, use #licensed_feature_available? instead. Remove once Namespace#feature_available? isn't used anymore.
def feature_available?(feature)
licensed_feature_available?(feature)
end
# Overridden in EE::Namespace
def licensed_feature_available?(_feature)
false
end
end
end
end
end
end
Namespace.prepend_mod_with('Namespace')
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
module UserMentions
module Models
class Note < ActiveRecord::Base
include EachBatch
include Concerns::IsolatedMentionable
include CacheMarkdownField
self.table_name = 'notes'
self.inheritance_column = :_type_disabled
attr_mentionable :note, pipeline: :note
cache_markdown_field :note, pipeline: :note, issuable_state_filter_enabled: true
belongs_to :author, class_name: "::Gitlab::BackgroundMigration::UserMentions::Models::User"
belongs_to :noteable, polymorphic: true
belongs_to :project, class_name: "::Gitlab::BackgroundMigration::UserMentions::Models::Project"
def for_personal_snippet?
noteable && noteable.instance_of?(PersonalSnippet)
end
def for_project_noteable?
!for_personal_snippet? && !for_epic?
end
def skip_project_check?
!for_project_noteable?
end
def for_epic?
noteable && noteable_type == 'Epic'
end
def user_mention_resource_id
noteable_id || commit_id
end
def user_mention_note_id
id
end
def noteable
super unless for_commit?
end
def for_commit?
noteable_type == "Commit"
end
private
def mentionable_params
return super unless for_epic?
super.merge(banzai_context_params)
end
def banzai_context_params
return {} unless noteable
{ group: noteable.group, label_url_method: :group_epics_url }
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Models
# isolated Namespace model
class Project < ActiveRecord::Base
include Concerns::IsolatedFeatureGate
include Gitlab::BackgroundMigration::UserMentions::Lib::Gitlab::IsolatedVisibilityLevel
self.table_name = 'projects'
self.inheritance_column = :_type_disabled
belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id', class_name: "::Gitlab::BackgroundMigration::UserMentions::Models::Group"
belongs_to :namespace, class_name: "::Gitlab::BackgroundMigration::UserMentions::Models::Namespace"
alias_method :parent, :namespace
# Returns a collection of projects that is either public or visible to the
# logged in user.
def self.public_or_visible_to_user(user = nil, min_access_level = nil)
min_access_level = nil if user&.can_read_all_resources?
return public_to_user unless user
if user.is_a?(::Gitlab::BackgroundMigration::UserMentions::Models::User)
where('EXISTS (?) OR projects.visibility_level IN (?)',
user.authorizations_for_projects(min_access_level: min_access_level),
levels_for_user(user))
end
end
def grafana_integration
nil
end
def default_issues_tracker?
true # we do not care of the issue tracker type(internal or external) when parsing mentions
end
def visibility_level_field
:visibility_level
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
module UserMentions
module Models
# isolated Namespace model
class User < ActiveRecord::Base
include Concerns::IsolatedFeatureGate
self.table_name = 'users'
self.inheritance_column = :_type_disabled
has_many :project_authorizations, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
def authorizations_for_projects(min_access_level: nil, related_project_column: 'projects.id')
authorizations = project_authorizations
.select(1)
.where("project_authorizations.project_id = #{related_project_column}")
return authorizations unless min_access_level.present?
authorizations.where('project_authorizations.access_level >= ?', min_access_level)
end
def can_read_all_resources?
can?(:read_all_resources)
end
def can?(action, subject = :global)
Ability.allowed?(self, action, subject)
end
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::UserMentions::CreateResourceUserMention, schema: 20181228175414 do
context 'checks no_quote_columns' do
it 'has correct no_quote_columns' do
expect(Gitlab::BackgroundMigration::UserMentions::Models::MergeRequest.no_quote_columns).to match([:note_id, :merge_request_id])
end
it 'commit has correct no_quote_columns' do
expect(Gitlab::BackgroundMigration::UserMentions::Models::Commit.no_quote_columns).to match([:note_id])
end
end
end
# frozen_string_literal: true
RSpec.shared_examples 'resource mentions migration' do |migration_class, resource_class_name|
it 'migrates resource mentions' do
join = migration_class::JOIN
conditions = migration_class::QUERY_CONDITIONS
resource_class = "#{Gitlab::BackgroundMigration::UserMentions::Models}::#{resource_class_name}".constantize
expect do
subject.perform(resource_class_name, join, conditions, false, resource_class.minimum(:id), resource_class.maximum(:id))
end.to change { user_mentions.count }.by(1)
user_mention = user_mentions.last
expect(user_mention.mentioned_users_ids.sort).to eq(mentioned_users.pluck(:id).sort)
expect(user_mention.mentioned_groups_ids.sort).to eq([group.id])
expect(user_mention.mentioned_groups_ids.sort).not_to include(inaccessible_group.id)
# check that performing the same job twice does not fail and does not change counts
expect do
subject.perform(resource_class_name, join, conditions, false, resource_class.minimum(:id), resource_class.maximum(:id))
end.to change { user_mentions.count }.by(0)
end
end
RSpec.shared_examples 'resource notes mentions migration' do |migration_class, resource_class_name|
it 'migrates mentions from note' do
join = migration_class::JOIN
conditions = migration_class::QUERY_CONDITIONS
# there are 5 notes for each noteable_type, but two do not have mentions and
# another one's noteable_id points to an inexistent resource
expect(notes.where(noteable_type: resource_class_name).count).to eq 5
expect(user_mentions.count).to eq 0
expect do
subject.perform(resource_class_name, join, conditions, true, Note.minimum(:id), Note.maximum(:id))
end.to change { user_mentions.count }.by(2)
# check that the user_mention for regular note is created
user_mention = user_mentions.first
expect(Note.find(user_mention.note_id).system).to be false
expect(user_mention.mentioned_users_ids.sort).to eq(users.pluck(:id).sort)
expect(user_mention.mentioned_groups_ids.sort).to eq([group.id])
expect(user_mention.mentioned_groups_ids.sort).not_to include(inaccessible_group.id)
# check that the user_mention for system note is created
user_mention = user_mentions.second
expect(Note.find(user_mention.note_id).system).to be true
expect(user_mention.mentioned_users_ids.sort).to eq(users.pluck(:id).sort)
expect(user_mention.mentioned_groups_ids.sort).to eq([group.id])
expect(user_mention.mentioned_groups_ids.sort).not_to include(inaccessible_group.id)
# check that performing the same job twice does not fail and does not change counts
expect do
subject.perform(resource_class_name, join, conditions, true, Note.minimum(:id), Note.maximum(:id))
end.to change { user_mentions.count }.by(0)
end
end
RSpec.shared_examples 'schedules resource mentions migration' do |resource_class, is_for_notes|
before do
stub_const("#{described_class.name}::BATCH_SIZE", 1)
end
it 'schedules background migrations' do
Sidekiq::Testing.fake! do
freeze_time do
resource_count = is_for_notes ? Note.count : resource_class.count
expect(resource_count).to eq 5
migrate!
migration = described_class::MIGRATION
join = described_class::JOIN
conditions = described_class::QUERY_CONDITIONS
delay = described_class::DELAY
expect(migration).to be_scheduled_delayed_migration(1 * delay, resource_class.name, join, conditions, is_for_notes, resource1.id, resource1.id)
expect(migration).to be_scheduled_delayed_migration(2 * delay, resource_class.name, join, conditions, is_for_notes, resource2.id, resource2.id)
expect(migration).to be_scheduled_delayed_migration(3 * delay, resource_class.name, join, conditions, is_for_notes, resource3.id, resource3.id)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
end
end
RSpec.shared_examples 'resource migration not run' do |migration_class, resource_class_name|
it 'does not migrate mentions' do
join = migration_class::JOIN
conditions = migration_class::QUERY_CONDITIONS
resource_class = "#{Gitlab::BackgroundMigration::UserMentions::Models}::#{resource_class_name}".constantize
expect do
subject.perform(resource_class_name, join, conditions, false, resource_class.minimum(:id), resource_class.maximum(:id))
end.to change { user_mentions.count }.by(0)
end
end
RSpec.shared_examples 'resource notes migration not run' do |migration_class, resource_class_name|
it 'does not migrate mentions' do
join = migration_class::JOIN
conditions = migration_class::QUERY_CONDITIONS
expect do
subject.perform(resource_class_name, join, conditions, true, Note.minimum(:id), Note.maximum(:id))
end.to change { user_mentions.count }.by(0)
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment