Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

class UserMerger
  def initialize(source_user, target_user, acting_user = nil)
    @source_user = source_user
    @target_user = target_user
    @acting_user = acting_user
    @user_id = source_user.id
    @source_primary_email = source_user.email
  end

  def merge!
    update_username
    move_posts
    update_user_ids
    merge_given_daily_likes
    merge_post_timings
    merge_user_visits
    update_site_settings
    merge_user_attributes

    DiscourseEvent.trigger(:merging_users, @source_user, @target_user)
    update_user_stats

    delete_source_user
    log_merge

    @target_user.reload
  end

  protected

  def update_username
    return if @source_user.username == @target_user.username

    if @acting_user
      ::MessageBus.publish "/merge_user",
                           { message: I18n.t("admin.user.merge_user.updating_username") },
                           user_ids: [@acting_user.id]
    end
    UsernameChanger.update_username(
      user_id: @source_user.id,
      old_username: @source_user.username,
      new_username: @target_user.username,
      avatar_template: @target_user.avatar_template,
      asynchronous: false,
    )
  end

  def move_posts
    posts =
      Post
        .with_deleted
        .where(user_id: @source_user.id)
        .order(:topic_id, :post_number)
        .pluck(:topic_id, :id)

    return if posts.count == 0

    if @acting_user
      ::MessageBus.publish "/merge_user",
                           { message: I18n.t("admin.user.merge_user.changing_post_ownership") },
                           user_ids: [@acting_user.id]
    end

    last_topic_id = nil
    post_ids = []

    posts.each do |current_topic_id, current_post_id|
      if last_topic_id != current_topic_id && post_ids.any?
        change_post_owner(last_topic_id, post_ids)
        post_ids = []
      end

      last_topic_id = current_topic_id
      post_ids << current_post_id
    end

    change_post_owner(last_topic_id, post_ids) if post_ids.any?
  end

  def change_post_owner(topic_id, post_ids)
    PostOwnerChanger.new(
      topic_id: topic_id,
      post_ids: post_ids,
      new_owner: @target_user,
      acting_user: Discourse.system_user,
      skip_revision: true,
    ).change_owner!
  end

  def merge_given_daily_likes
    if @acting_user
      ::MessageBus.publish "/merge_user",
                           { message: I18n.t("admin.user.merge_user.merging_given_daily_likes") },
                           user_ids: [@acting_user.id]
    end

    sql = <<~SQL
      INSERT INTO given_daily_likes AS g (user_id, likes_given, given_date, limit_reached)
        SELECT
          :target_user_id                AS user_id,
          COUNT(1)                       AS likes_given,
          a.created_at::DATE             AS given_date,
          COUNT(1) >= :max_likes_per_day AS limit_reached
        FROM post_actions AS a
        WHERE a.user_id = :target_user_id
              AND a.deleted_at IS NULL
              AND EXISTS(
                  SELECT 1
                  FROM given_daily_likes AS g
                  WHERE g.user_id = :source_user_id AND a.created_at::DATE = g.given_date
              )
        GROUP BY given_date
      ON CONFLICT (user_id, given_date)
        DO UPDATE
          SET likes_given = EXCLUDED.likes_given,
            limit_reached = EXCLUDED.limit_reached
    SQL

    DB.exec(
      sql,
      source_user_id: @source_user.id,
      target_user_id: @target_user.id,
      max_likes_per_day: SiteSetting.max_likes_per_day,
      action_type_id: PostActionType.types[:like],
    )
  end

  def merge_post_timings
    if @acting_user
      ::MessageBus.publish "/merge_user",
                           { message: I18n.t("admin.user.merge_user.merging_post_timings") },
                           user_ids: [@acting_user.id]
    end

    update_user_id(
      :post_timings,
      conditions: ["x.topic_id = y.topic_id", "x.post_number = y.post_number"],
    )
    sql = <<~SQL
      UPDATE post_timings AS t
      SET msecs = LEAST(t.msecs::bigint + s.msecs, 2^31 - 1)
      FROM post_timings AS s
      WHERE t.user_id = :target_user_id AND s.user_id = :source_user_id
            AND t.topic_id = s.topic_id AND t.post_number = s.post_number
    SQL

    DB.exec(sql, source_user_id: @source_user.id, target_user_id: @target_user.id)
  end

  def merge_user_visits
    if @acting_user
      ::MessageBus.publish "/merge_user",
                           { message: I18n.t("admin.user.merge_user.merging_user_visits") },
                           user_ids: [@acting_user.id]
    end

    update_user_id(:user_visits, conditions: "x.visited_at = y.visited_at")

    sql = <<~SQL
      UPDATE user_visits AS t
      SET posts_read = t.posts_read + s.posts_read,
        mobile       = t.mobile OR s.mobile,
        time_read    = t.time_read + s.time_read
      FROM user_visits AS s
      WHERE t.user_id = :target_user_id AND s.user_id = :source_user_id
            AND t.visited_at = s.visited_at
    SQL

    DB.exec(sql, source_user_id: @source_user.id, target_user_id: @target_user.id)
  end

  def update_site_settings
    if @acting_user
      ::MessageBus.publish "/merge_user",
                           { message: I18n.t("admin.user.merge_user.updating_site_settings") },
                           user_ids: [@acting_user.id]
    end

    SiteSetting
      .all_settings(include_hidden: true)
      .each do |setting|
        if setting[:type] == "username" && setting[:value] == @source_user.username
          SiteSetting.set_and_log(setting[:setting], @target_user.username)
        end
      end
  end

  def update_user_stats
    if @acting_user
      ::MessageBus.publish "/merge_user",
                           { message: I18n.t("admin.user.merge_user.updating_user_stats") },
                           user_ids: [@acting_user.id]
    end

    # topics_entered
    DB.exec(<<~SQL, target_user_id: @target_user.id)
      UPDATE user_stats
      SET topics_entered = (
        SELECT COUNT(topic_id)
        FROM topic_views
        WHERE user_id = :target_user_id
      )
      WHERE user_id = :target_user_id
    SQL

    # time_read and days_visited
    DB.exec(<<~SQL, target_user_id: @target_user.id)
      UPDATE user_stats
      SET time_read  = COALESCE(x.time_read, 0),
        days_visited = COALESCE(x.days_visited, 0)
      FROM (
             SELECT
               SUM(time_read) AS time_read,
               COUNT(1)       AS days_visited
             FROM user_visits
             WHERE user_id = :target_user_id
           ) AS x
      WHERE user_id = :target_user_id
    SQL

    # posts_read_count
    DB.exec(<<~SQL, target_user_id: @target_user.id)
      UPDATE user_stats
      SET posts_read_count = (
        SELECT COUNT(1)
        FROM post_timings AS pt
        WHERE pt.user_id = :target_user_id AND EXISTS(
            SELECT 1
            FROM topics AS t
            WHERE t.archetype = 'regular' AND t.deleted_at IS NULL
        ))
      WHERE user_id = :target_user_id
    SQL

    # likes_given, likes_received, new_since, read_faq, first_post_created_at
    DB.exec(<<~SQL, source_user_id: @source_user.id, target_user_id: @target_user.id)
      UPDATE user_stats AS t
      SET likes_given         = t.likes_given + s.likes_given,
        likes_received        = t.likes_received + s.likes_received,
        new_since             = LEAST(t.new_since, s.new_since),
        read_faq              = LEAST(t.read_faq, s.read_faq),
        first_post_created_at = LEAST(t.first_post_created_at, s.first_post_created_at)
      FROM user_stats AS s
      WHERE t.user_id = :target_user_id AND s.user_id = :source_user_id
    SQL
  end

  def merge_user_attributes
    if @acting_user
      ::MessageBus.publish "/merge_user",
                           { message: I18n.t("admin.user.merge_user.merging_user_attributes") },
                           user_ids: [@acting_user.id]
    end

    DB.exec(<<~SQL, source_user_id: @source_user.id, target_user_id: @target_user.id)
      UPDATE users AS t
      SET created_at              = LEAST(t.created_at, s.created_at),
        updated_at                = LEAST(t.updated_at, s.updated_at),
        seen_notification_id      = GREATEST(t.seen_notification_id, s.seen_notification_id),
        last_posted_at            = GREATEST(t.last_seen_at, s.last_seen_at),
        last_seen_at              = GREATEST(t.last_seen_at, s.last_seen_at),
        admin                     = t.admin OR s.admin,
        last_emailed_at           = GREATEST(t.last_emailed_at, s.last_emailed_at),
        trust_level               = GREATEST(t.trust_level, s.trust_level),
        previous_visit_at         = GREATEST(t.previous_visit_at, s.previous_visit_at),
        date_of_birth             = COALESCE(t.date_of_birth, s.date_of_birth),
        ip_address                = COALESCE(t.ip_address, s.ip_address),
        moderator                 = t.moderator OR s.moderator,
        title                     = COALESCE(t.title, s.title),
        primary_group_id          = COALESCE(t.primary_group_id, s.primary_group_id),
        registration_ip_address   = COALESCE(t.registration_ip_address, s.registration_ip_address),
        first_seen_at             = LEAST(t.first_seen_at, s.first_seen_at),
        manual_locked_trust_level = GREATEST(t.manual_locked_trust_level, s.manual_locked_trust_level)
      FROM users AS s
      WHERE t.id = :target_user_id AND s.id = :source_user_id
    SQL

    DB.exec(<<~SQL, source_user_id: @source_user.id, target_user_id: @target_user.id)
      UPDATE user_profiles AS t
      SET location           = COALESCE(t.location, s.location),
        website              = COALESCE(t.website, s.website),
        bio_raw              = COALESCE(t.bio_raw, s.bio_raw),
        bio_cooked           = COALESCE(t.bio_cooked, s.bio_cooked),
        bio_cooked_version   = COALESCE(t.bio_cooked_version, s.bio_cooked_version),
        profile_background_upload_id = COALESCE(t.profile_background_upload_id, s.profile_background_upload_id),
        dismissed_banner_key = COALESCE(t.dismissed_banner_key, s.dismissed_banner_key),
        granted_title_badge_id = COALESCE(t.granted_title_badge_id, s.granted_title_badge_id),
        card_background_upload_id = COALESCE(t.card_background_upload_id, s.card_background_upload_id),
        views                = t.views + s.views
      FROM user_profiles AS s
      WHERE t.user_id = :target_user_id AND s.user_id = :source_user_id
    SQL
  end

  def update_user_ids
    if @acting_user
      ::MessageBus.publish "/merge_user",
                           { message: I18n.t("admin.user.merge_user.updating_user_ids") },
                           user_ids: [@acting_user.id]
    end

    Category.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    update_user_id(:category_users, conditions: ["x.category_id = y.category_id"])

    update_user_id(:developers)

    update_user_id(:draft_sequences, conditions: "x.draft_key = y.draft_key")
    update_user_id(:drafts, conditions: "x.draft_key = y.draft_key")

    update_user_id(:dismissed_topic_users, conditions: "x.topic_id = y.topic_id")

    EmailLog.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    GroupHistory.where(acting_user_id: @source_user.id).update_all(acting_user_id: @target_user.id)
    GroupHistory.where(target_user_id: @source_user.id).update_all(target_user_id: @target_user.id)

    update_user_id(:group_users, conditions: "x.group_id = y.group_id")

    IncomingEmail.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    IncomingLink.where(user_id: @source_user.id).update_all(user_id: @target_user.id)
    IncomingLink.where(current_user_id: @source_user.id).update_all(
      current_user_id: @target_user.id,
    )

    InvitedUser.where(user_id: @source_user.id).update_all(user_id: @target_user.id)
    Invite
      .with_deleted
      .where(invited_by_id: @source_user.id)
      .update_all(invited_by_id: @target_user.id)
    Invite
      .with_deleted
      .where(deleted_by_id: @source_user.id)
      .update_all(deleted_by_id: @target_user.id)

    update_user_id(:muted_users, conditions: "x.muted_user_id = y.muted_user_id")
    update_user_id(
      :muted_users,
      user_id_column_name: "muted_user_id",
      conditions: "x.user_id = y.user_id",
    )

    update_user_id(:ignored_users, conditions: "x.ignored_user_id = y.ignored_user_id")
    update_user_id(
      :ignored_users,
      user_id_column_name: "ignored_user_id",
      conditions: "x.user_id = y.user_id",
    )

    Notification.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    update_user_id(
      :post_actions,
      conditions: [
        "x.post_id = y.post_id",
        "x.post_action_type_id = y.post_action_type_id",
        "x.targets_topic = y.targets_topic",
      ],
    )

    PostAction.where(deleted_by_id: @source_user.id).update_all(deleted_by_id: @target_user.id)
    PostAction.where(deferred_by_id: @source_user.id).update_all(deferred_by_id: @target_user.id)
    PostAction.where(agreed_by_id: @source_user.id).update_all(agreed_by_id: @target_user.id)
    PostAction.where(disagreed_by_id: @source_user.id).update_all(disagreed_by_id: @target_user.id)

    PostRevision.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    Post
      .with_deleted
      .where(deleted_by_id: @source_user.id)
      .update_all(deleted_by_id: @target_user.id)
    Post
      .with_deleted
      .where(last_editor_id: @source_user.id)
      .update_all(last_editor_id: @target_user.id)
    Post.with_deleted.where(locked_by_id: @source_user.id).update_all(locked_by_id: @target_user.id)
    Post
      .with_deleted
      .where(reply_to_user_id: @source_user.id)
      .update_all(reply_to_user_id: @target_user.id)

    Reviewable.where(created_by_id: @source_user.id).update_all(created_by_id: @target_user.id)
    ReviewableHistory.where(created_by_id: @source_user.id).update_all(
      created_by_id: @target_user.id,
    )

    SearchLog.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    update_user_id(:tag_users, conditions: "x.tag_id = y.tag_id")

    Theme.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    update_user_id(:topic_allowed_users, conditions: "x.topic_id = y.topic_id")

    TopicEmbed
      .with_deleted
      .where(deleted_by_id: @source_user.id)
      .update_all(deleted_by_id: @target_user.id)

    TopicLink.where(user_id: @source_user.id).update_all(user_id: @target_user.id)
    TopicLinkClick.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    TopicTimer
      .with_deleted
      .where(deleted_by_id: @source_user.id)
      .update_all(deleted_by_id: @target_user.id)

    update_user_id(
      :topic_timers,
      conditions: [
        "x.status_type = y.status_type",
        "x.topic_id = y.topic_id",
        "y.deleted_at IS NULL",
      ],
    )

    update_user_id(:topic_users, conditions: "x.topic_id = y.topic_id")

    update_user_id(:topic_views, conditions: "x.topic_id = y.topic_id")

    Topic
      .with_deleted
      .where(deleted_by_id: @source_user.id)
      .update_all(deleted_by_id: @target_user.id)

    UnsubscribeKey.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    Upload.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    update_user_id(:user_archived_messages, conditions: "x.topic_id = y.topic_id")

    update_user_id(
      :user_actions,
      user_id_column_name: "user_id",
      conditions: [
        "x.action_type = y.action_type",
        "x.target_topic_id IS NOT DISTINCT FROM y.target_topic_id",
        "x.target_post_id IS NOT DISTINCT FROM y.target_post_id",
        "(x.acting_user_id IN (:source_user_id, :target_user_id) OR x.acting_user_id IS NOT DISTINCT FROM y.acting_user_id)",
      ],
    )
    update_user_id(
      :user_actions,
      user_id_column_name: "acting_user_id",
      conditions: [
        "x.action_type = y.action_type",
        "x.user_id = y.user_id",
        "x.target_topic_id IS NOT DISTINCT FROM y.target_topic_id",
        "x.target_post_id IS NOT DISTINCT FROM y.target_post_id",
      ],
    )

    update_user_id(
      :user_badges,
      conditions: [
        "x.badge_id = y.badge_id",
        "x.seq = y.seq",
        "x.post_id IS NOT DISTINCT FROM y.post_id",
      ],
    )

    UserBadge.where(granted_by_id: @source_user.id).update_all(granted_by_id: @target_user.id)

    update_user_id(:user_custom_fields, conditions: "x.name = y.name")

    if @target_user.human?
      update_user_id(
        :user_emails,
        conditions: "x.email = y.email OR y.primary = false",
        updates: '"primary" = false',
      )
    end

    UserExport.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    UserHistory.where(target_user_id: @source_user.id).update_all(target_user_id: @target_user.id)
    UserHistory.where(acting_user_id: @source_user.id).update_all(acting_user_id: @target_user.id)

    UserProfileView.where(user_profile_id: @source_user.id).update_all(
      user_profile_id: @target_user.id,
    )
    UserProfileView.where(user_id: @source_user.id).update_all(user_id: @target_user.id)

    UserWarning.where(user_id: @source_user.id).update_all(user_id: @target_user.id)
    UserWarning.where(created_by_id: @source_user.id).update_all(created_by_id: @target_user.id)

    User.where(approved_by_id: @source_user.id).update_all(approved_by_id: @target_user.id)
  end

  def delete_source_user
    if @acting_user
      ::MessageBus.publish "/merge_user",
                           { message: I18n.t("admin.user.merge_user.deleting_source_user") },
                           user_ids: [@acting_user.id]
    end

    @source_user.reload

    @source_user.skip_email_validation = true
    @source_user.update(
      admin: false,
      email: "#{@source_user.username}_#{SecureRandom.hex}@no-email.invalid",
    )

    UserDestroyer.new(Discourse.system_user).destroy(@source_user, quiet: true)
  end

  def log_merge
    logger = StaffActionLogger.new(@acting_user || Discourse.system_user)
    logger.log_user_merge(@target_user, @source_user.username, @source_primary_email || "")
  end

  def update_user_id(table_name, opts = {})
    builder = update_user_id_sql_builder(table_name, opts)
    builder.exec(source_user_id: @source_user.id, target_user_id: @target_user.id)
  end

  def update_user_id_sql_builder(table_name, opts = {})
    user_id_column_name = opts[:user_id_column_name] || :user_id
    conditions = Array.wrap(opts[:conditions])
    updates = Array.wrap(opts[:updates])

    builder = DB.build(<<~SQL)
      UPDATE #{table_name} AS x
      /*set*/
      WHERE x.#{user_id_column_name} = :source_user_id AND NOT EXISTS(
          SELECT 1
          FROM #{table_name} AS y
          /*where*/
      )
    SQL

    builder.set("#{user_id_column_name} = :target_user_id")
    updates.each { |u| builder.set(u) }

    builder.where("y.#{user_id_column_name} = :target_user_id")
    conditions.each { |c| builder.where(c) }

    builder
  end
end

```

Response:
# frozen_string_literal: true

RSpec.describe UserMerger do
  fab!(:target_user) { Fabricate(:user, username: "alice", email: "alice@example.com") }
  fab!(:source_user) { Fabricate(:user, username: "alice1", email: "alice@work.com") }
  fab!(:walter) { Fabricate(:walter_white) }
  fab!(:coding_horror)

  fab!(:p1) { Fabricate(:post) }
  fab!(:p2) { Fabricate(:post) }
  fab!(:p3) { Fabricate(:post) }
  fab!(:p4) { Fabricate(:post) }
  fab!(:p5) { Fabricate(:post) }
  fab!(:p6) { Fabricate(:post) }

  before { Group.refresh_automatic_groups! }

  def merge_users!(source = nil, target = nil)
    source ||= source_user
    target ||= target_user
    UserMerger.new(source, target).merge!
  end

  it "changes owner of topics and posts" do
    topic1 = Fabricate(:topic, user: source_user)
    post1 = Fabricate(:post, topic: topic1, user: source_user)
    post2 = Fabricate(:post, topic: topic1, user: walter)
    post3 = Fabricate(:post, topic: topic1, user: target_user)
    post4 = Fabricate(:post, topic: topic1, user: walter)
    post5 = Fabricate(:post, topic: topic1, user: source_user)

    topic2 = Fabricate(:topic, user: walter)
    post6 = Fabricate(:post, topic: topic2, user: walter)
    post7 = Fabricate(:post, topic: topic2, user: source_user)
    post8 = Fabricate(:post, topic: topic2, user: source_user, deleted_at: Time.now)

    merge_users!

    [topic1, post1, post3, post5, post7, post8].each do |x|
      expect(x.reload.user).to eq(target_user)
    end

    [post2, post4, topic2, post6].each { |x| expect(x.reload.user).to eq(walter) }
  end

  it "changes owner of personal messages" do
    pm_topic =
      Fabricate(
        :private_message_topic,
        topic_allowed_users: [
          Fabricate.build(:topic_allowed_user, user: target_user),
          Fabricate.build(:topic_allowed_user, user: walter),
          Fabricate.build(:topic_allowed_user, user: source_user),
        ],
      )

    post1 = Fabricate(:post, topic: pm_topic, user: source_user)
    post2 = Fabricate(:post, topic: pm_topic, user: walter)
    post3 = Fabricate(:post, topic: pm_topic, user: target_user)
    post4 = Fabricate(:post, topic: pm_topic, user: source_user, deleted_at: Time.now)

    small1 = pm_topic.add_small_action(source_user, "invited_user", "carol")
    small2 = pm_topic.add_small_action(target_user, "invited_user", "david")
    small3 = pm_topic.add_small_action(walter, "invited_user", "eve")

    merge_users!

    expect(post1.reload.user).to eq(target_user)
    expect(post2.reload.user).to eq(walter)
    expect(post3.reload.user).to eq(target_user)
    expect(post4.reload.user).to eq(target_user)

    expect(small1.reload.user).to eq(target_user)
    expect(small2.reload.user).to eq(target_user)
    expect(small3.reload.user).to eq(walter)
  end

  it "changes owner of categories" do
    category = Fabricate(:category, user: source_user)
    merge_users!

    expect(category.reload.user).to eq(target_user)
  end

  it "merges category notification settings" do
    category1 = Fabricate(:category)
    category2 = Fabricate(:category)
    category3 = Fabricate(:category)
    watching = CategoryUser.notification_levels[:watching]

    CategoryUser.batch_set(source_user, :watching, [category1.id, category2.id])
    CategoryUser.batch_set(target_user, :watching, [category2.id, category3.id])

    merge_users!

    category_ids =
      CategoryUser.where(user_id: target_user.id, notification_level: watching).pluck(:category_id)
    expect(category_ids).to contain_exactly(category1.id, category2.id, category3.id)

    category_ids =
      CategoryUser.where(user_id: source_user.id, notification_level: watching).pluck(:category_id)
    expect(category_ids).to be_empty
  end

  context "with developer flag" do
    it "moves the developer flag when the target user isn't a developer yet" do
      Developer.create!(user_id: source_user.id)
      merge_users!

      expect(Developer.where(user_id: source_user.id).count).to eq(0)
      expect(Developer.where(user_id: target_user.id).count).to eq(1)
    end

    it "deletes the source's developer flag when the target user is already a developer" do
      Developer.create!(user_id: source_user.id)
      Developer.create!(user_id: target_user.id)
      merge_users!

      expect(Developer.where(user_id: source_user.id).count).to eq(0)
      expect(Developer.where(user_id: target_user.id).count).to eq(1)
    end
  end

  context "with drafts" do
    def create_draft(user, key, text)
      seq = DraftSequence.next!(user, key)
      Draft.set(user, key, seq, text)
    end

    def current_target_user_draft(key)
      seq = DraftSequence.current(target_user, key)
      Draft.get(target_user, key, seq)
    end

    it "merges drafts" do
      key_topic_17 = "#{Draft::EXISTING_TOPIC}#{17}"
      key_topic_19 = "#{Draft::EXISTING_TOPIC}#{19}"

      create_draft(source_user, Draft::NEW_TOPIC, "new topic draft by alice1")
      create_draft(source_user, key_topic_17, "draft by alice1")
      create_draft(source_user, key_topic_19, "draft by alice1")
      create_draft(target_user, key_topic_19, "draft by alice")

      merge_users!

      expect(current_target_user_draft(Draft::NEW_TOPIC)).to eq("new topic draft by alice1")
      expect(current_target_user_draft(key_topic_17)).to eq("draft by alice1")
      expect(current_target_user_draft(key_topic_19)).to eq("draft by alice")

      expect(DraftSequence.where(user_id: source_user.id).count).to eq(0)
      expect(Draft.where(user_id: source_user.id).count).to eq(0)
    end
  end

  it "updates email logs" do
    Fabricate(:email_log, user: source_user)
    merge_users!

    expect(EmailLog.where(user_id: source_user.id).count).to eq(0)
    expect(EmailLog.where(user_id: target_user.id).count).to eq(1)
  end

  context "with likes" do
    def given_daily_like_count_for(user, date)
      GivenDailyLike.find_for(user.id, date).pluck(:likes_given)[0] || 0
    end

    it "merges likes" do
      now = Time.zone.now

      freeze_time(now - 1.day)
      PostActionCreator.like(source_user, p1)
      PostActionCreator.like(source_user, p2)
      PostActionCreator.like(target_user, p2)
      PostActionCreator.like(target_user, p3)

      freeze_time(now)
      PostActionCreator.like(source_user, p4)
      PostActionCreator.like(source_user, p5)
      PostActionCreator.like(target_user, p5)
      PostActionCreator.like(source_user, p6)
      PostActionDestroyer.destroy(source_user, p6, :like)

      merge_users!

      [p1, p2, p3, p4, p5].each { |p| expect(p.reload.like_count).to eq(1) }
      expect(PostAction.with_deleted.where(user_id: source_user.id).count).to eq(0)
      expect(PostAction.with_deleted.where(user_id: target_user.id).count).to eq(6)

      expect(given_daily_like_count_for(source_user, Date.yesterday)).to eq(0)
      expect(given_daily_like_count_for(target_user, Date.yesterday)).to eq(3)
      expect(given_daily_like_count_for(source_user, Date.today)).to eq(0)
      expect(given_daily_like_count_for(target_user, Date.today)).to eq(2)
    end
  end

  it "updates group history" do
    group = Fabricate(:group)
    group.add_owner(source_user)
    logger = GroupActionLogger.new(source_user, group)
    logger.log_add_user_to_group(walter)
    logger.log_add_user_to_group(target_user)

    group = Fabricate(:group)
    group.add_owner(target_user)
    logger = GroupActionLogger.new(target_user, group)
    logger.log_add_user_to_group(walter)
    logger.log_add_user_to_group(source_user)

    merge_users!

    expect(GroupHistory.where(acting_user_id: source_user.id).count).to eq(0)
    expect(GroupHistory.where(acting_user_id: target_user.id).count).to eq(4)

    expect(GroupHistory.where(target_user_id: source_user.id).count).to eq(0)
    expect(GroupHistory.where(target_user_id: target_user.id).count).to eq(2)
  end

  it "merges group memberships" do
    group1 = Fabricate(:group)
    group1.add_owner(target_user)
    group1.bulk_add([walter.id, source_user.id])

    group2 = Fabricate(:group)
    group2.bulk_add([walter.id, target_user.id])

    group3 = Fabricate(:group)
    group3.add_owner(source_user)
    group3.add(walter)

    merge_users!

    [group1, group2, group3].each do |g|
      owner = [group1, group3].include?(g)
      expect(GroupUser.where(group_id: g.id, user_id: target_user.id, owner: owner).count).to eq(1)
      expect(Group.where(id: g.id).pick(:user_count)).to eq(2)
    end
    expect(GroupUser.where(user_id: source_user.id).count).to eq(0)
  end

  it "updates incoming emails" do
    email = Fabricate(:incoming_email, user: source_user)
    merge_users!

    expect(email.reload.user).to eq(target_user)
  end

  it "updates incoming links" do
    link1 = Fabricate(:incoming_link, user: source_user)
    link2 = Fabricate(:incoming_link, current_user_id: source_user.id)

    merge_users!

    expect(link1.reload.user).to eq(target_user)
    expect(link2.reload.current_user_id).to eq(target_user.id)
  end

  it "updates invites" do
    invite1 = Fabricate(:invite, invited_by: walter)
    Fabricate(:invited_user, invite: invite1, user: source_user)
    invite2 = Fabricate(:invite, invited_by: source_user)
    invite3 = Fabricate(:invite, invited_by: source_user)
    invite3.trash!(source_user)

    merge_users!

    [invite1, invite2, invite3].each { |x| x.reload }

    expect(invite1.invited_users.first.user).to eq(target_user)
    expect(invite2.invited_by).to eq(target_user)
    expect(invite3.invited_by).to eq(target_user)
    expect(invite3.deleted_by).to eq(target_user)
  end

  it "merges muted users" do
    muted1 = Fabricate(:user)
    muted2 = Fabricate(:user)
    muted3 = Fabricate(:user)

    MutedUser.create!(user_id: source_user.id, muted_user_id: muted1.id)
    MutedUser.create!(user_id: source_user.id, muted_user_id: muted2.id)
    MutedUser.create!(user_id: target_user.id, muted_user_id: muted2.id)
    MutedUser.create!(user_id: target_user.id, muted_user_id: muted3.id)
    MutedUser.create!(user_id: walter.id, muted_user_id: source_user.id)
    MutedUser.create!(user_id: coding_horror.id, muted_user_id: source_user.id)
    MutedUser.create!(user_id: coding_horror.id, muted_user_id: target_user.id)

    merge_users!

    [muted1, muted2, muted3].each do |m|
      expect(MutedUser.where(user_id: target_user.id, muted_user_id: m.id).count).to eq(1)
    end
    expect(MutedUser.where(user_id: source_user.id).count).to eq(0)

    expect(MutedUser.where(user_id: walter.id, muted_user_id: target_user.id).count).to eq(1)
    expect(MutedUser.where(user_id: coding_horror.id, muted_user_id: target_user.id).count).to eq(1)
    expect(MutedUser.where(muted_user_id: source_user.id).count).to eq(0)
  end

  it "merges ignored users" do
    ignored1 = Fabricate(:user)
    ignored2 = Fabricate(:user)
    ignored3 = Fabricate(:user)

    Fabricate(:ignored_user, user: source_user, ignored_user: ignored1)
    Fabricate(:ignored_user, user: source_user, ignored_user: ignored2)
    Fabricate(:ignored_user, user: target_user, ignored_user: ignored2)
    Fabricate(:ignored_user, user: target_user, ignored_user: ignored3)
    Fabricate(:ignored_user, user: walter, ignored_user: source_user)
    Fabricate(:ignored_user, user: coding_horror, ignored_user: source_user)
    Fabricate(:ignored_user, user: coding_horror, ignored_user: target_user)

    merge_users!

    [ignored1, ignored2, ignored3].each do |m|
      expect(IgnoredUser.where(user_id: target_user.id, ignored_user_id: m.id).count).to eq(1)
    end
    expect(IgnoredUser.where(user_id: source_user.id).count).to eq(0)

    expect(IgnoredUser.where(user_id: walter.id, ignored_user_id: target_user.id).count).to eq(1)
    expect(
      IgnoredUser.where(user_id: coding_horror.id, ignored_user_id: target_user.id).count,
    ).to eq(1)
    expect(IgnoredUser.where(ignored_user_id: source_user.id).count).to eq(0)
  end

  context "with notifications" do
    it "updates notifications" do
      Fabricate(:notification, user: source_user)
      Fabricate(:notification, user: source_user)
      Fabricate(:notification, user: walter)

      merge_users!

      expect(Notification.where(user_id: target_user.id).count).to eq(2)
      expect(Notification.where(user_id: source_user.id).count).to eq(0)
    end
  end

  context "with post actions" do
    it "merges post actions" do
      type_ids = PostActionType.public_type_ids + [PostActionType.flag_types.values.first]

      type_ids.each do |type|
        PostActionCreator.new(source_user, p1, type).perform
        PostActionCreator.new(source_user, p2, type).perform
        PostActionCreator.new(target_user, p2, type).perform
        PostActionCreator.new(target_user, p3, type).perform
      end

      merge_users!

      type_ids.each do |type|
        expect(
          PostAction.where(user_id: target_user.id, post_action_type_id: type).pluck(:post_id),
        ).to contain_exactly(p1.id, p2.id, p3.id)
      end

      expect(PostAction.where(user_id: source_user.id).count).to eq(0)
    end

    it "updates post actions" do
      action1 = PostActionCreator.create(source_user, p1, :off_topic).post_action
      action1.update_attribute(:deleted_by_id, source_user.id)

      action2 = PostActionCreator.create(source_user, p2, :off_topic).post_action
      action2.update_attribute(:deferred_by_id, source_user.id)

      action3 = PostActionCreator.create(source_user, p3, :off_topic).post_action
      action3.update_attribute(:agreed_by_id, source_user.id)

      action4 = PostActionCreator.create(source_user, p4, :off_topic).post_action
      action4.update_attribute(:disagreed_by_id, source_user.id)

      merge_users!

      expect(action1.reload.deleted_by_id).to eq(target_user.id)
      expect(action2.reload.deferred_by_id).to eq(target_user.id)
      expect(action3.reload.agreed_by_id).to eq(target_user.id)
      expect(action4.reload.disagreed_by_id).to eq(target_user.id)
    end
  end

  it "updates post revisions" do
    post = p1
    post_revision = Fabricate(:post_revision, post: post, user: source_user)

    merge_users!
    expect(post_revision.reload.user).to eq(target_user)
  end

  context "with post timings" do
    def create_post_timing(post, user, msecs)
      PostTiming.create!(
        topic_id: post.topic_id,
        post_number: post.post_number,
        user_id: user.id,
        msecs: msecs,
      )
    end

    def post_timing_msecs_for(post, user)
      PostTiming.where(
        topic_id: post.topic_id,
        post_number: post.post_number,
        user_id: user.id,
      ).pluck(:msecs)[
        0
      ] || 0
    end

    it "merges post timings" do
      post1 = p1
      post2 = p2
      post3 = p3
      post4 = p4

      create_post_timing(post1, source_user, 12_345)
      create_post_timing(post2, source_user, 9876)
      create_post_timing(post4, source_user, 2**31 - 100)
      create_post_timing(post2, target_user, 3333)
      create_post_timing(post3, target_user, 10_000)
      create_post_timing(post4, target_user, 5000)

      merge_users!

      expect(post_timing_msecs_for(post1, target_user)).to eq(12_345)
      expect(post_timing_msecs_for(post2, target_user)).to eq(13_209)
      expect(post_timing_msecs_for(post3, target_user)).to eq(10_000)
      expect(post_timing_msecs_for(post4, target_user)).to eq(2**31 - 1)

      expect(PostTiming.where(user_id: source_user.id).count).to eq(0)
    end
  end

  context "with posts" do
    it "updates user ids of posts" do
      source_user.update_attribute(:moderator, true)

      topic = Fabricate(:topic)
      Fabricate(:post, topic: topic, user: source_user)

      post2 = Fabricate(:basic_reply, topic: topic, user: walter)
      post2.revise(source_user, raw: "#{post2.raw} foo")
      PostLocker.new(post2, source_user).lock
      post2.trash!(source_user)

      merge_users!
      post2.reload

      expect(post2.deleted_by).to eq(target_user)
      expect(post2.last_editor).to eq(target_user)
      expect(post2.locked_by_id).to eq(target_user.id)
      expect(post2.reply_to_user).to eq(target_user)
    end

    it "updates post action counts" do
      posts = {}

      PostActionType.types.each do |type_name, type_id|
        posts[type_name] = post = Fabricate(:post, user: walter)
        PostActionCreator.new(source_user, post, type_id).perform
        PostActionCreator.new(target_user, post, type_id).perform
      end

      merge_users!

      posts.each do |type, post|
        post.reload
        expect(post.public_send("#{type}_count")).to eq(1)
      end
    end
  end

  it "updates reviewables and reviewable history" do
    reviewable = Fabricate(:reviewable_queued_post, created_by: source_user)

    merge_users!

    expect(reviewable.reload.created_by).to eq(target_user)
    expect(reviewable.reviewable_histories.first.created_by).to eq(target_user)
  end

  describe "search logs" do
    after { SearchLog.clear_debounce_cache! }

    it "updates search log entries" do
      SearchLog.log(
        term: "hello",
        search_type: :full_page,
        ip_address: "192.168.0.1",
        user_id: source_user.id,
      )
      SearchLog.log(
        term: "world",
        search_type: :full_page,
        ip_address: "192.168.0.1",
        user_id: source_user.id,
      )
      SearchLog.log(
        term: "star trek",
        search_type: :full_page,
        ip_address: "192.168.0.2",
        user_id: target_user.id,
      )
      SearchLog.log(
        term: "bad",
        search_type: :full_page,
        ip_address: "192.168.0.3",
        user_id: walter.id,
      )

      merge_users!

      expect(SearchLog.where(user_id: target_user.id).count).to eq(3)
      expect(SearchLog.where(user_id: source_user.id).count).to eq(0)
      expect(SearchLog.where(user_id: walter.id).count).to eq(1)
    end
  end

  it "merges tag notification settings" do
    tag1 = Fabricate(:tag)
    tag2 = Fabricate(:tag)
    tag3 = Fabricate(:tag)
    watching = TagUser.notification_levels[:watching]

    TagUser.batch_set(source_user, :watching, [tag1.name, tag2.name])
    TagUser.batch_set(target_user, :watching, [tag2.name, tag3.name])

    merge_users!

    tag_ids = TagUser.where(user_id: target_user.id, notification_level: watching).pluck(:tag_id)
    expect(tag_ids).to contain_exactly(tag1.id, tag2.id, tag3.id)

    tag_ids = TagUser.where(user_id: source_user.id, notification_level: watching).pluck(:tag_id)
    expect(tag_ids).to be_empty
  end

  it "updates themes" do
    theme = Fabricate(:theme, user: source_user)
    merge_users!

    expect(theme.reload.user_id).to eq(target_user.id)
  end

  it "merges allowed users for topics" do
    pm_topic1 =
      Fabricate(
        :private_message_topic,
        topic_allowed_users: [
          Fabricate.build(:topic_allowed_user, user: target_user),
          Fabricate.build(:topic_allowed_user, user: walter),
          Fabricate.build(:topic_allowed_user, user: source_user),
        ],
      )

    pm_topic2 =
      Fabricate(
        :private_message_topic,
        topic_allowed_users: [
          Fabricate.build(:topic_allowed_user, user: walter),
          Fabricate.build(:topic_allowed_user, user: source_user),
        ],
      )

    merge_users!

    expect(pm_topic1.allowed_users).to contain_exactly(target_user, walter)
    expect(pm_topic2.allowed_users).to contain_exactly(target_user, walter)
    expect(TopicAllowedUser.where(user_id: source_user.id).count).to eq(0)
  end

  it "updates topic embeds" do
    topic_embed = Fabricate(:topic_embed, embed_url: "http://example.com/post/248")
    topic_embed.trash!(source_user)

    merge_users!

    expect(topic_embed.reload.deleted_by).to eq(target_user)
  end

  it "updates topic links" do
    topic = Fabricate(:topic, user: source_user)
    post = Fabricate(:post_with_external_links, user: source_user, topic: topic)
    TopicLink.extract_from(post)
    link = topic.topic_links.first

    TopicLinkClick.create!(topic_link_id: link.id, user_id: source_user.id, ip_address: "127.0.0.1")
    TopicLinkClick.create!(topic_link_id: link.id, user_id: target_user.id, ip_address: "127.0.0.1")
    TopicLinkClick.create!(topic_link_id: link.id, user_id: walter.id, ip_address: "127.0.0.1")

    merge_users!

    expect(TopicLink.where(user_id: target_user.id).count).to be > 0
    expect(TopicLink.where(user_id: source_user.id).count).to eq(0)

    expect(TopicLinkClick.where(user_id: target_user.id).count).to eq(2)
    expect(TopicLinkClick.where(user_id: source_user.id).count).to eq(0)
    expect(TopicLinkClick.where(user_id: walter.id).count).to eq(1)
  end

  context "with topic timers" do
    def create_topic_timer(topic, user, status_type, deleted_by = nil)
      timer =
        Fabricate(
          :topic_timer,
          topic: topic,
          user: user,
          status_type: TopicTimer.types[status_type],
        )
      timer.trash!(deleted_by) if deleted_by
      timer.reload
    end

    it "merges topic timers" do
      topic1 = Fabricate(:topic)
      timer1 = create_topic_timer(topic1, source_user, :close, Discourse.system_user)
      timer2 = create_topic_timer(topic1, source_user, :close)
      timer3 = create_topic_timer(topic1, source_user, :reminder, source_user)
      timer4 = create_topic_timer(topic1, target_user, :reminder, target_user)
      timer5 = create_topic_timer(topic1, source_user, :reminder)

      topic2 = Fabricate(:topic)
      timer6 = create_topic_timer(topic2, target_user, :close)
      timer7 = create_topic_timer(topic2, target_user, :reminder, Discourse.system_user)
      create_topic_timer(topic2, source_user, :reminder, Discourse.system_user)

      merge_users!

      [timer1, timer2, timer3, timer4, timer5, timer6, timer7].each do |t|
        expect(t.reload.user).to eq(target_user)
      end

      expect(TopicTimer.with_deleted.where(user_id: source_user.id).count).to eq(0)
      expect(TopicTimer.with_deleted.where(deleted_by_id: target_user.id).count).to eq(2)
      expect(TopicTimer.with_deleted.where(deleted_by_id: source_user.id).count).to eq(0)
    end
  end

  it "merges topic notification settings" do
    topic1 = Fabricate(:topic)
    topic2 = Fabricate(:topic)
    topic3 = Fabricate(:topic)
    watching = TopicUser.notification_levels[:watching]

    Fabricate(:topic_user, notification_level: watching, topic: topic1, user: source_user)
    Fabricate(:topic_user, notification_level: watching, topic: topic2, user: source_user)
    Fabricate(:topic_user, notification_level: watching, topic: topic2, user: target_user)
    Fabricate(:topic_user, notification_level: watching, topic: topic3, user: target_user)

    merge_users!

    topic_ids =
      TopicUser.where(user_id: target_user.id, notification_level: watching).pluck(:topic_id)
    expect(topic_ids).to contain_exactly(topic1.id, topic2.id, topic3.id)

    topic_ids =
      TopicUser.where(user_id: source_user.id, notification_level: watching).pluck(:topic_id)
    expect(topic_ids).to be_empty
  end

  it "merges topic views" do
    topic1 = Fabricate(:topic)
    topic2 = Fabricate(:topic)
    topic3 = Fabricate(:topic)
    ip = "127.0.0.1"

    TopicViewItem.add(topic1.id, ip, source_user.id)
    TopicViewItem.add(topic2.id, ip, source_user.id)
    TopicViewItem.add(topic2.id, ip, target_user.id)
    TopicViewItem.add(topic3.id, ip, target_user.id)

    merge_users!

    topic_ids = TopicViewItem.where(user_id: target_user.id).pluck(:topic_id)
    expect(topic_ids).to contain_exactly(topic1.id, topic2.id, topic3.id)
    expect(TopicViewItem.where(user_id: source_user.id).count).to eq(0)
  end

  it "updates topics" do
    topic = Fabricate(:topic)
    Fabricate(:post, user: walter, topic: topic)
    Fabricate(:post, user: source_user, topic: topic)
    topic.trash!(source_user)

    merge_users!
    topic.reload

    expect(topic.deleted_by).to eq(target_user)
    expect(topic.last_poster).to eq(target_user)
  end

  it "updates unsubscribe keys" do
    UnsubscribeKey.create_key_for(source_user, UnsubscribeKey::DIGEST_TYPE)
    UnsubscribeKey.create_key_for(target_user, UnsubscribeKey::DIGEST_TYPE)
    UnsubscribeKey.create_key_for(walter, UnsubscribeKey::DIGEST_TYPE)

    merge_users!

    expect(UnsubscribeKey.where(user_id: target_user.id).count).to eq(2)
    expect(UnsubscribeKey.where(user_id: source_user.id).count).to eq(0)
  end

  it "updates uploads" do
    Fabricate(:upload, user: source_user)
    Fabricate(:upload, user: target_user)
    Fabricate(:upload, user: walter)

    merge_users!

    expect(Upload.where(user_id: target_user.id).count).to eq(2)
    expect(Upload.where(user_id: source_user.id).count).to eq(0)
  end

  context "with user actions" do
    # action_type and user_id are not nullable
    # target_topic_id and acting_user_id are nullable, but always have a value

    fab!(:post1) { p1 }
    fab!(:post2) { p2 }

    def log_like_action(acting_user, user, post)
      UserAction.log_action!(
        action_type: UserAction::LIKE,
        user_id: user.id,
        acting_user_id: acting_user.id,
        target_topic_id: post.topic_id,
        target_post_id: post.id,
      )
    end

    def log_got_private_message(acting_user, user, topic)
      UserAction.log_action!(
        action_type: UserAction::GOT_PRIVATE_MESSAGE,
        user_id: user.id,
        acting_user_id: acting_user.id,
        target_topic_id: topic.id,
        target_post_id: -1,
      )
    end

    it "merges when target_post_id is set" do
      _a1 = log_like_action(source_user, walter, post1)
      a2 = log_like_action(target_user, walter, post1)
      a3 = log_like_action(source_user, walter, post2)

      merge_users!

      expect(UserAction.count).to eq(2)

      action_ids =
        UserAction.where(
          action_type: UserAction::LIKE,
          user_id: walter.id,
          acting_user_id: target_user.id,
        ).pluck(:id)
      expect(action_ids).to contain_exactly(a2.id, a3.id)
    end

    it "merges when acting_user is neither source_user nor target_user" do
      pm_topic1 =
        Fabricate(
          :private_message_topic,
          topic_allowed_users: [
            Fabricate.build(:topic_allowed_user, user: walter),
            Fabricate.build(:topic_allowed_user, user: source_user),
            Fabricate.build(:topic_allowed_user, user: target_user),
            Fabricate.build(:topic_allowed_user, user: coding_horror),
          ],
        )

      pm_topic2 =
        Fabricate(
          :private_message_topic,
          topic_allowed_users: [
            Fabricate.build(:topic_allowed_user, user: walter),
            Fabricate.build(:topic_allowed_user, user: source_user),
          ],
        )

      pm_topic3 =
        Fabricate(
          :private_message_topic,
          topic_allowed_users: [
            Fabricate.build(:topic_allowed_user, user: walter),
            Fabricate.build(:topic_allowed_user, user: target_user),
          ],
        )

      _a1 = log_got_private_message(walter, source_user, pm_topic1)
      a2 = log_got_private_message(walter, target_user, pm_topic1)
      _a3 = log_got_private_message(walter, coding_horror, pm_topic1)
      a4 = log_got_private_message(walter, source_user, pm_topic2)
      a5 = log_got_private_message(walter, target_user, pm_topic3)

      merge_users!

      expect(UserAction.count).to eq(4)

      action_ids =
        UserAction.where(
          action_type: UserAction::GOT_PRIVATE_MESSAGE,
          user_id: target_user.id,
          acting_user_id: walter.id,
        ).pluck(:id)
      expect(action_ids).to contain_exactly(a2.id, a4.id, a5.id)
    end
  end

  it "merges archived messages" do
    pm_topic1 =
      Fabricate(
        :private_message_topic,
        topic_allowed_users: [
          Fabricate.build(:topic_allowed_user, user: target_user),
          Fabricate.build(:topic_allowed_user, user: walter),
          Fabricate.build(:topic_allowed_user, user: source_user),
        ],
      )

    pm_topic2 =
      Fabricate(
        :private_message_topic,
        topic_allowed_users: [
          Fabricate.build(:topic_allowed_user, user: walter),
          Fabricate.build(:topic_allowed_user, user: source_user),
        ],
      )

    UserArchivedMessage.archive!(source_user.id, pm_topic1)
    UserArchivedMessage.archive!(target_user.id, pm_topic1)
    UserArchivedMessage.archive!(source_user.id, pm_topic2)
    UserArchivedMessage.archive!(walter.id, pm_topic2)

    merge_users!

    topic_ids = UserArchivedMessage.where(user_id: target_user.id).pluck(:topic_id)
    expect(topic_ids).to contain_exactly(pm_topic1.id, pm_topic2.id)
    expect(UserArchivedMessage.where(user_id: source_user.id).count).to eq(0)
  end

  context "with badges" do
    def create_badge(badge, user, opts = {})
      UserBadge.create!(
        badge: badge,
        user: user,
        granted_by: opts[:granted_by] || Discourse.system_user,
        granted_at: opts[:granted_at] || Time.now,
        post: opts[:post],
        seq: opts[:seq] || 0,
      )
    end

    it "merges user badges" do
      anniversary_badge = Badge.find(Badge::Anniversary)
      create_badge(anniversary_badge, source_user, seq: 1)
      b1 = create_badge(anniversary_badge, target_user, seq: 1)
      b2 = create_badge(anniversary_badge, source_user, seq: 2)

      great_post_badge = Badge.find(Badge::GreatPost)
      b3 = create_badge(great_post_badge, target_user, post: Fabricate(:post, user: target_user))
      b4 = create_badge(great_post_badge, source_user, post: Fabricate(:post, user: source_user))

      autobiographer_badge = Badge.find(Badge::Autobiographer)
      b5 = create_badge(autobiographer_badge, source_user)

      merge_users!

      user_badge_ids = UserBadge.where(user_id: target_user.id).pluck(:id)
      expect(user_badge_ids).to contain_exactly(b1.id, b2.id, b3.id, b4.id, b5.id)
      expect(UserBadge.where(user_id: source_user.id).count).to eq(0)
    end

    it "updates granted_by for user badges" do
      badge = Badge.create!(name: "Hero", badge_type_id: BadgeType::Gold)
      user_badge = create_badge(badge, walter, seq: 1, granted_by: source_user)

      merge_users!

      expect(user_badge.reload.granted_by).to eq(target_user)
    end
  end

  it "merges user custom fields" do
    UserCustomField.create!(user_id: source_user.id, name: "foo", value: "123")
    UserCustomField.create!(user_id: source_user.id, name: "bar", value: "456")
    UserCustomField.create!(user_id: source_user.id, name: "duplicate", value: "source")
    UserCustomField.create!(user_id: target_user.id, name: "duplicate", value: "target")
    UserCustomField.create!(user_id: target_user.id, name: "baz", value: "789")

    merge_users!

    fields = UserCustomField.where(user_id: target_user.id).pluck(:name, :value)
    expect(fields).to contain_exactly(%w[foo 123], %w[bar 456], %w[duplicate target], %w[baz 789])
    expect(UserCustomField.where(user_id: source_user.id).count).to eq(0)
  end

  it "merges email addresses" do
    merge_users!

    emails = UserEmail.where(user_id: target_user.id).pluck(:email, :primary)
    expect(emails).to contain_exactly(["alice@example.com", true], ["alice@work.com", false])
    expect(UserEmail.where(user_id: source_user.id).count).to eq(0)
  end

  it "skips merging email addresses when a secondary email address exists" do
    merge_users!(source_user, target_user)

    alice2 = Fabricate(:user, username: "alice2", email: "alice@foo.com")
    merge_users!(alice2, target_user)

    emails = UserEmail.where(user_id: target_user.id).pluck(:email, :primary)
    expect(emails).to contain_exactly(["alice@example.com", true], ["alice@work.com", false])
    expect(UserEmail.where(user_id: source_user.id).count).to eq(0)
  end

  it "skips merging email addresses when target user is not human" do
    target_user = Discourse.system_user
    merge_users!(source_user, target_user)

    emails = UserEmail.where(user_id: target_user.id).pluck(:email, :primary)
    expect(emails).to contain_exactly([target_user.email, true])
    expect(UserEmail.exists?(user_id: source_user.id)).to eq(false)
  end

  it "updates exports" do
    UserExport.create(file_name: "user-archive-alice1-190218-003249", user_id: source_user.id)

    merge_users!

    expect(UserExport.where(user_id: target_user.id).count).to eq(1)
    expect(UserExport.where(user_id: source_user.id).count).to eq(0)
  end

  it "updates user history" do
    UserHistory.create(
      action: UserHistory.actions[:notified_about_get_a_room],
      target_user_id: source_user.id,
    )
    UserHistory.create(
      action: UserHistory.actions[:anonymize_user],
      target_user_id: walter.id,
      acting_user_id: source_user.id,
    )

    merge_users!
    UserHistory.where(
      action: UserHistory.actions[:merge_user],
      target_user_id: target_user.id,
    ).delete_all

    expect(UserHistory.where(target_user_id: target_user.id).count).to eq(1)
    expect(UserHistory.where(target_user_id: source_user.id).count).to eq(0)

    expect(UserHistory.where(acting_user_id: target_user.id).count).to eq(1)
    expect(UserHistory.where(acting_user_id: source_user.id).count).to eq(0)
  end

  it "updates user profile views" do
    ip = "127.0.0.1"
    UserProfileView.add(source_user.id, ip, walter.id, Time.now, true)
    UserProfileView.add(source_user.id, ip, target_user.id, Time.now, true)
    UserProfileView.add(target_user.id, ip, source_user.id, Time.now, true)
    UserProfileView.add(walter.id, ip, source_user.id, Time.now, true)

    merge_users!

    expect(UserProfileView.where(user_profile_id: target_user.id).count).to eq(3)
    expect(UserProfileView.where(user_profile_id: walter.id).count).to eq(1)
    expect(UserProfileView.where(user_profile_id: source_user.id).count).to eq(0)

    expect(UserProfileView.where(user_id: target_user.id).count).to eq(3)
    expect(UserProfileView.where(user_id: walter.id).count).to eq(1)
    expect(UserProfileView.where(user_id: source_user.id).count).to eq(0)
  end

  it "merges user visits" do
    freeze_time DateTime.parse("2010-01-01 12:00")

    UserVisit.create!(
      user_id: source_user.id,
      visited_at: 2.days.ago,
      posts_read: 22,
      mobile: false,
      time_read: 400,
    )
    UserVisit.create!(
      user_id: source_user.id,
      visited_at: Date.yesterday,
      posts_read: 8,
      mobile: false,
      time_read: 100,
    )
    UserVisit.create!(
      user_id: target_user.id,
      visited_at: Date.yesterday,
      posts_read: 12,
      mobile: true,
      time_read: 270,
    )
    UserVisit.create!(
      user_id: target_user.id,
      visited_at: Date.today,
      posts_read: 10,
      mobile: true,
      time_read: 150,
    )

    merge_users!

    expect(UserVisit.where(user_id: target_user.id).count).to eq(3)
    expect(UserVisit.where(user_id: source_user.id).count).to eq(0)

    expect(
      UserVisit.where(
        user_id: target_user.id,
        visited_at: 2.days.ago,
        posts_read: 22,
        mobile: false,
        time_read: 400,
      ).count,
    ).to eq(1)
    expect(
      UserVisit.where(
        user_id: target_user.id,
        visited_at: Date.yesterday,
        posts_read: 20,
        mobile: true,
        time_read: 370,
      ).count,
    ).to eq(1)
    expect(
      UserVisit.where(
        user_id: target_user.id,
        visited_at: Date.today,
        posts_read: 10,
        mobile: true,
        time_read: 150,
      ).count,
    ).to eq(1)
  end

  it "updates user warnings" do
    UserWarning.create!(topic: Fabricate(:topic), user: source_user, created_by: walter)
    UserWarning.create!(topic: Fabricate(:topic), user: target_user, created_by: walter)
    UserWarning.create!(topic: Fabricate(:topic), user: walter, created_by: source_user)

    merge_users!

    expect(UserWarning.where(user_id: target_user.id).count).to eq(2)
    expect(UserWarning.where(user_id: source_user.id).count).to eq(0)

    expect(UserWarning.where(created_by_id: target_user.id).count).to eq(1)
    expect(UserWarning.where(created_by_id: source_user.id).count).to eq(0)
  end

  it "triggers :merging_users event" do
    events = DiscourseEvent.track_events { merge_users! }

    expect(events).to include(event_name: :merging_users, params: [source_user, target_user])
  end

  context "with site settings" do
    it "updates usernames in site settings" do
      SiteSetting.site_contact_username = source_user.username
      SiteSetting.embed_by_username = source_user.username

      merge_users!

      expect(SiteSetting.site_contact_username).to eq(target_user.username)
      expect(SiteSetting.embed_by_username).to eq(target_user.username)
    end

    it "updates only the old username in site settings" do
      SiteSetting.site_contact_username = source_user.username
      SiteSetting.embed_by_username = walter.username

      merge_users!

      expect(SiteSetting.site_contact_username).to eq(target_user.username)
      expect(SiteSetting.embed_by_username).to eq(walter.username)
    end
  end

  it "updates users" do
    walter.update!(approved_by: source_user)
    upload = Fabricate(:upload)

    source_user.update!(admin: true)

    source_user.user_profile.update!(
      card_background_upload: upload,
      profile_background_upload: upload,
    )

    merge_users!

    expect(walter.reload.approved_by).to eq(target_user)

    target_user.reload

    expect(target_user.admin).to eq(true)
    expect(target_user.card_background_upload).to eq(upload)
    expect(target_user.profile_background_upload).to eq(upload)
  end

  it "deletes the source user even when it's an admin" do
    source_user.update_attribute(:admin, true)

    expect(User.find_by_username(source_user.username)).to be_present
    merge_users!
    expect(User.find_by_username(source_user.username)).to be_nil
  end

  it "deletes the source user even when it is a member of a group that grants a trust level" do
    group = Fabricate(:group, grant_trust_level: 3)
    group.bulk_add([source_user.id, target_user.id])

    merge_users!

    expect(User.find_by_username(source_user.username)).to be_nil
  end

  it "works even when email domains are restricted" do
    SiteSetting.allowed_email_domains = "example.com|work.com"
    source_user.update_attribute(:admin, true)

    expect(User.find_by_username(source_user.username)).to be_present
    merge_users!
    expect(User.find_by_username(source_user.username)).to be_nil
  end

  it "deletes external auth infos of source user" do
    UserAssociatedAccount.create(
      user_id: source_user.id,
      provider_name: "facebook",
      provider_uid: "1234",
    )
    SingleSignOnRecord.create(
      user_id: source_user.id,
      external_id: "example",
      last_payload: "looks good",
    )

    merge_users!

    expect(UserAssociatedAccount.where(user_id: source_user.id).count).to eq(0)
    expect(SingleSignOnRecord.where(user_id: source_user.id).count).to eq(0)
  end

  it "deletes auth tokens" do
    Fabricate(:api_key, user: source_user)
    Fabricate(:readonly_user_api_key, user: source_user)
    Fabricate(:user_second_factor_totp, user: source_user)

    SiteSetting.verbose_auth_token_logging = true
    UserAuthToken.generate!(user_id: source_user.id, user_agent: "Firefox", client_ip: "127.0.0.1")

    merge_users!

    expect(ApiKey.where(user_id: source_user.id).count).to eq(0)
    expect(UserApiKey.where(user_id: source_user.id).count).to eq(0)
    expect(UserSecondFactor.where(user_id: source_user.id).count).to eq(0)
    expect(UserAuthToken.where(user_id: source_user.id).count).to eq(0)
    expect(UserAuthTokenLog.where(user_id: source_user.id).count).to eq(0)
  end

  it "cleans up all remaining references to the source user" do
    DirectoryItem.refresh!
    Fabricate(:email_change_request, user: source_user)
    Fabricate(:email_token, user: source_user)
    Fabricate(:user_avatar, user: source_user)

    merge_users!

    expect(DirectoryItem.where(user_id: source_user.id).count).to eq(0)
    expect(EmailChangeRequest.where(user_id: source_user.id).count).to eq(0)
    expect(EmailToken.where(user_id: source_user.id).count).to eq(0)
    expect(UserAvatar.where(user_id: source_user.id).count).to eq(0)

    expect(User.find_by_username(source_user.username)).to be_nil
  end

  it "updates the username" do
    Jobs::UpdateUsername
      .any_instance
      .expects(:execute)
      .with(
        {
          user_id: source_user.id,
          old_username: "alice1",
          new_username: "alice",
          avatar_template: target_user.avatar_template,
        },
      )
      .once

    merge_users!
  end

  it "correctly logs the merge" do
    expect { merge_users! }.to change { UserHistory.count }.by(1)

    log_entry = UserHistory.last
    expect(log_entry.action).to eq(UserHistory.actions[:merge_user])
    expect(log_entry.acting_user_id).to eq(Discourse::SYSTEM_USER_ID)
    expect(log_entry.target_user_id).to eq(target_user.id)
    expect(log_entry.context).to eq(
      I18n.t("staff_action_logs.user_merged", username: source_user.username),
    )
    expect(log_entry.email).to eq("alice@work.com")
  end
end