RSpec.describe User do
  context "validations" do
    describe "#name" do
      it "should ensure good sized name works" do
        expect(FactoryBot.build(:user, :name => "x" * 4)).to be_valid
      end

      it "should ensure presence of name" do
        expect(FactoryBot.build(:user, :name => nil)).not_to be_valid
      end

      it "should ensure name is not too long" do
        expect(FactoryBot.build(:user, :name => "x" * 101)).not_to be_valid
      end
    end

    it "should ensure first_name is not too long" do
      expect(FactoryBot.build(:user, :first_name => "x" * 101)).not_to be_valid
    end

    it "should ensure last_name is not too long" do
      expect(FactoryBot.build(:user, :last_name => "x" * 101)).not_to be_valid
    end

    it "should ensure presence of user id" do
      expect(FactoryBot.build(:user, :userid => nil)).not_to be_valid
    end

    it "should invalidate incorrect email address" do
      expect(FactoryBot.build(:user, :email => "thisguy@@manageiq.com")).not_to be_valid
    end

    it "should validate email address with a value of nil" do
      expect(FactoryBot.build(:user, :email => nil)).to be_valid
    end

    it "should validate current_group with a value of nil" do
      user = FactoryBot.build(:user, :current_group_id => nil)
      expect(user).to be_valid
      expect(user.current_group_id).to be_nil
    end

    it "should save proper email address" do
      expect(FactoryBot.build(:user, :email => "that.guy@manageiq.com")).to be_valid
    end

    it "doesn't validate fields that did not change. specifically group, tenant, and userid" do
      u = FactoryBot.create(:user)
      expect { u.update(:lastlogon => Time.zone.now) }.to make_database_queries(:count => 1, :matching => /(select|update)/i)
    end
  end

  describe "#change_password" do
    let(:user) { FactoryBot.create(:user, :password => "smartvm") }

    it "should change user password" do
      password    = user.password
      newpassword = "newpassword"
      user.change_password(password, newpassword)
      expect(user.password).to eq(newpassword)
    end

    it "should raise an error when asked to change user password" do
      password    = "wrongpwd"
      newpassword = "newpassword"

      expect { user.change_password(password, newpassword) }
        .to raise_error(MiqException::MiqEVMLoginError)
    end
  end

  context "filter methods" do
    let(:user) { FactoryBot.create(:user, :miq_groups => [miq_group]) }
    let(:mfilters) { {"managed"   => "m"} }
    let(:bfilters) { {"belongsto" => "b"} }
    let(:miq_group) { FactoryBot.create(:miq_group, :entitlement => entitlement) }
    let(:entitlement) do
      entitlement = FactoryBot.create(:entitlement)
      entitlement.set_managed_filters(mfilters)
      entitlement.set_belongsto_filters(bfilters)
      entitlement.save!
      entitlement
    end

    it "should check for and get Managed and Belongs-to filters from the group" do
      expect(user.has_filters?).to be_truthy
      expect(user.get_managed_filters).to eq(mfilters)
      expect(user.get_belongsto_filters).to eq(bfilters)
    end
  end

  context "timezone methods" do
    let!(:miq_server) { EvmSpecHelper.local_miq_server }
    let(:user) { FactoryBot.create(:user) }

    describe "#get_timezone" do
      it "gets Server time zone setting" do
        expect(user.get_timezone).to eq("UTC")
      end
    end

    describe "#with_my_timezone" do
      it "sets the user's zone in a block" do
        user.settings.store_path(:display, :timezone, "Hawaii")
        user.with_my_timezone do
          expect(Time.zone.to_s).to eq("(GMT-10:00) Hawaii")
        end
        expect(Time.zone.to_s).to eq("(GMT+00:00) UTC")
      end
    end
  end

  describe ".missing_user_features" do
    it "user with group and role returns nil" do
      user = FactoryBot.create(:user_admin)
      expect(User.missing_user_features(user)).to be_nil
    end

    it "no user returns 'User'" do
      expect(User.missing_user_features(nil)).to eq "User"
    end

    it "missing group returns 'Group'" do
      user = FactoryBot.create(:user)
      expect(User.missing_user_features(user)).to eq "Group"
    end

    it "missing role returns 'Role'" do
      user = FactoryBot.create(:user_with_group)
      expect(User.missing_user_features(user)).to eq "Role"
    end
  end

  describe "role methods" do
    let(:user) do
      FactoryBot.create(:user,
                         :settings => {"Setting1" => 1, "Setting2" => 2, "Setting3" => 3},
                         :role     => role_name)
    end

    describe "#self_service?" do
      let(:role_name) { "user_self_service" }

      it "checks Self Service roles" do
        expect(user.self_service?).to be_truthy
        expect(user.super_admin_user?).to be_falsey

        user.current_group = nil
        expect(user.self_service?).to be_falsey
      end
    end

    describe "#limited_self_service?" do
      let(:role_name) { "user_limited_self_service" }

      it "checks Self Service roles" do
        expect(user.limited_self_service?).to be_truthy
        expect(user.super_admin_user?).to be_falsey

        user.current_group = nil
        expect(user.limited_self_service?).to be_falsey
      end
    end

    describe "#super_admin_user?" do
      let(:role_name) { "super_administrator" }

      it "checks Super Admin roles" do
        expect(user.super_admin_user?).to be_truthy

        user.current_group = nil
        expect(user.super_admin_user?).to be_falsey
      end
    end
  end

  describe '#fail_login!' do
    let(:user) { FactoryBot.create(:user, :password => "smartvm", :failed_login_attempts => 0) }

    it 'increases the number of failed login attempts' do
      user.fail_login!
      expect(user.reload.failed_login_attempts).to eq(1)
    end

    it 'queues an unlock task if the account is locked' do
      allow(user).to receive(:locked?).and_return(true)
      expect(user).to receive(:unlock_queue)
      user.fail_login!
    end

    it 'locks when reaching a number of failed logins' do
      stub_settings_merge(:authentication => {:max_failed_login_attempts => 2})
      expect(user).to receive(:unlock_queue).once

      expect(user.locked?).to be_falsey
      user.fail_login!
      expect(user.locked?).to be_falsey
      # this will lock it
      user.fail_login!
      expect(user.locked?).to be_truthy
    end

    it 'lock generates a single message on the queue (dedups)' do
      stub_settings_merge(:authentication => {:max_failed_login_attempts => 1, :locked_account_timeout => 1.week})
      EvmSpecHelper.local_miq_server

      expect(MiqQueue.count).to eq(0)
      user.fail_login!
      user.fail_login!
      expect(MiqQueue.count).to eq(1)
      user.fail_login!
      expect(MiqQueue.count).to eq(1)
      msg = MiqQueue.first
      expect(msg.deliver_on).to be_within(1.minute).of(1.week.from_now)
    end
  end

  context "#authorize_ldap" do
    before do
      @fq_user = "thin1@manageiq.com"
      @task = MiqTask.create(:name => "LDAP User Authorization of '#{@fq_user}'", :userid => @fq_user)
      @auth_config =
        {:authentication => {:ldapport => "389",
                             :basedn => "dc=manageiq,dc=com",
                             :follow_referrals => false,
                             :get_direct_groups => true,
                             :bind_dn => "evm_demo@manageiq.com",
                             :mode => "ldap", :user_proxies => [{}],
                             :user_type => "userprincipalname",
                             :bind_pwd => "blah",
                             :ldap_role => true,
                             :user_suffix => "manageiq.com",
                             :group_memberships_max_depth => 2,
                             :ldaphost => ["192.168.254.15"]}
        }
      stub_settings(@auth_config)
      @miq_ldap = double('miq_ldap')
      allow(@miq_ldap).to receive_messages(:bind => false)

      EvmSpecHelper.local_miq_server
    end

    it "will fail task if user object not found in ldap" do
      allow(@miq_ldap).to receive_messages(:get_user_object => nil)

      expect(AuditEvent).to receive(:failure).once
      authenticate = Authenticator::Ldap.new(@auth_config[:authentication])
      allow(authenticate).to receive_messages(:ldap => @miq_ldap)

      expect(authenticate.authorize(@task.id, @fq_user)).to be_nil

      @task.reload
      expect(@task.state).to eq("Finished")
      expect(@task.status).to eq("Error")
      expect(@task.message).to match(/unable to find user object/)
    end

    it "will fail task if user group doesn't match an EVM role" do
      allow(@miq_ldap).to receive_messages(:get_user_object => "user object")
      allow(@miq_ldap).to receive_messages(:get_attr => nil)
      allow(@miq_ldap).to receive_messages(:normalize => "a-username")

      authenticate = Authenticator::Ldap.new(@auth_config[:authentication])
      allow(authenticate).to receive_messages(:ldap => @miq_ldap)
      allow(authenticate).to receive_messages(:groups_for => [])

      expect(AuditEvent).to receive(:failure).once
      expect(authenticate.authorize(@task.id, @fq_user)).to be_nil

      @task.reload
      expect(@task.state).to eq("Finished")
      expect(@task.status).to eq("Error")
      expect(@task.message).to match(/unable to match user's group membership/)
    end
  end

  context "group assignment" do
    before do
      @group1 = FactoryBot.create(:miq_group, :description => "EvmGroup 1")
      @group2 = FactoryBot.create(:miq_group, :description => "EvmGroup 2")
      @group3 = FactoryBot.create(:miq_group, :description => "EvmGroup 3")
    end

    describe "#miq_groups=" do
      before do
        @user = FactoryBot.create(:user, :miq_groups => [@group3])
      end

      it "sets miq_groups" do
        expect(@user.miq_groups).to match_array [@group3]
      end

      it "sets current_group" do
        expect(@user.current_group).to eq(@group3)
      end

      it "when including current group" do
        @user.miq_groups = [@group1, @group2, @group3]
        expect(@user.valid?).to be_truthy
        expect(@user.current_group).to eq(@group3)
      end

      it "when not including currrent group" do
        @user.miq_groups = [@group1, @group2]
        expect(@user.valid?).to be_truthy
        expect(@user.current_group).to eq(@group1)
      end

      it "when nil" do
        expect { @user.miq_groups = nil }.to raise_error(NoMethodError)
      end
    end

    describe "#current_group=" do
      before do
        @user = FactoryBot.create(:user, :miq_groups => [@group1, @group2])
      end

      it "sets current_group" do
        expect(@user.current_group).to eq(@group1)
      end

      it "when belongs to miq_groups" do
        expect(@user.valid?).to be_truthy
      end

      it "when assigning to a group not under the user via object" do
        @user.current_group = @group3
        expect(@user.valid?).to be_falsey
      end

      it "when assigning to a group not under the user via group_id" do
        @user.current_group_id = @group3.id
        expect(@user.valid?).to be_falsey
      end

      it "when not belongs to miq_groups" do
        @user.miq_groups = [@group2, @group3]
        expect(@user.current_group).to eq(@group2)
      end

      it "when nil" do
        @user.current_group = nil
        expect(@user.valid?).to be_truthy
      end
    end
  end

  context "Testing active VM aggregation" do
    before do
      @ram_size = 1024
      @disk_size = 1_000_000
      @num_cpu = 2

      group = FactoryBot.create(:miq_group)
      @user = FactoryBot.create(:user, :miq_groups => [group])
      @ems = FactoryBot.create(:ems_vmware, :name => "test_vcenter")
      @storage  = FactoryBot.create(:storage, :name => "test_storage_nfs", :store_type => "NFS")

      @hw1 = FactoryBot.create(:hardware, :cpu_total_cores => @num_cpu, :memory_mb => @ram_size)
      @hw2 = FactoryBot.create(:hardware, :cpu_total_cores => @num_cpu, :memory_mb => @ram_size)
      @hw3 = FactoryBot.create(:hardware, :cpu_total_cores => @num_cpu, :memory_mb => @ram_size)
      @hw4 = FactoryBot.create(:hardware, :cpu_total_cores => @num_cpu, :memory_mb => @ram_size)
      @disk1 = FactoryBot.create(:disk, :device_type => "disk", :size => @disk_size, :hardware_id => @hw1.id)
      @disk2 = FactoryBot.create(:disk, :device_type => "disk", :size => @disk_size, :hardware_id => @hw2.id)
      @disk3 = FactoryBot.create(:disk, :device_type => "disk", :size => @disk_size, :hardware_id => @hw3.id)
      @disk4 = FactoryBot.create(:disk, :device_type => "disk", :size => @disk_size, :hardware_id => @hw4.id)

      @active_vm = FactoryBot.create(:vm_vmware,
                                      :name         => "Active VM",
                                      :evm_owner_id => @user.id,
                                      :ems_id       => @ems.id,
                                      :storage_id   => @storage.id,
                                      :hardware     => @hw1)
      @archived_vm = FactoryBot.create(:vm_vmware,
                                        :name         => "Archived VM",
                                        :evm_owner_id => @user.id,
                                        :hardware     => @hw2)
      @orphaned_vm = FactoryBot.create(:vm_vmware,
                                        :name         => "Orphaned VM",
                                        :evm_owner_id => @user.id,
                                        :storage_id   => @storage.id,
                                        :hardware     => @hw3)
      @retired_vm = FactoryBot.create(:vm_vmware,
                                       :name         => "Retired VM",
                                       :evm_owner_id => @user.id,
                                       :retired      => true,
                                       :hardware     => @hw4)
    end

    it "#active_vms" do
      expect(@user.active_vms).to match_array([@active_vm])
    end

    it "#allocated_memory" do
      expect(@user.allocated_memory).to eq(@ram_size.megabyte)
    end

    it "#allocated_vcpu" do
      expect(@user.allocated_vcpu).to eq(@num_cpu)
    end

    it "#allocated_storage" do
      expect(@user.allocated_storage).to eq(@disk_size)
    end

    it "#provisioned_storage" do
      expect(@user.provisioned_storage).to eq(@ram_size.megabyte + @disk_size)
    end

    %w[allocated_memory allocated_vcpu allocated_storage provisioned_storage].each do |vcol|
      it "should have virtual column #{vcol} " do
        expect(described_class).to have_virtual_column vcol.to_s, :integer
      end
    end
  end

  context "virtual string cols" do
    %w[ldap_group miq_group_description miq_user_role_name].each do |vcol|
      it "should have virtual column #{vcol} " do
        expect(described_class).to have_virtual_column vcol.to_s, :string
      end
    end
  end

  context ".authenticate_with_http_basic" do
    let(:user) { FactoryBot.create(:user, :password => "dummy") }

    before do
      EvmSpecHelper.local_miq_server
    end

    it "should login with good username/password" do
      expect(User.authenticate_with_http_basic(user.userid, user.password)).to eq([true, user.userid])
    end

    it "should fail with bad username" do
      bad_userid = "bad_userid"
      expect(User.authenticate_with_http_basic(bad_userid, user.password)).to eq([false, bad_userid])
    end

    it "should fail with bad password" do
      expect(User.authenticate_with_http_basic(user.userid, "bad_pwd")).to eq([false, user.userid])
    end
  end

  context ".seed" do
    include_examples ".seed called multiple times", 1
    include_examples "seeding users with", []
    include_examples "seeding users with", [MiqUserRole, MiqGroup]
  end

  context "#accessible_vms" do
    before do
      @user = FactoryBot.create(:user_admin)

      @self_service_role = FactoryBot.create(
        :miq_user_role,
        :name     => "ss_role",
        :settings => {:restrictions => {:vms => :user_or_group}}
      )
      @self_service_group = FactoryBot.create(:miq_group, :miq_user_role => @self_service_role)

      @limited_self_service_role = FactoryBot.create(
        :miq_user_role,
        :name     => "lss_role",
        :settings => {:restrictions => {:vms => :user}}
      )
      @limited_self_service_group = FactoryBot.create(:miq_group, :miq_user_role => @limited_self_service_role)

      @vm = []
      (1..5).each { |i| @vm[i] = FactoryBot.create(:vm_redhat, :name => "vm_#{i}") }
    end
    subject(:accessible_vms) { @user.accessible_vms }

    it "non self service user" do
      expect(accessible_vms.size).to eq(5)
    end

    it "self service user" do
      @user.update(:miq_groups => [@self_service_group])
      @vm[1].update(:evm_owner => @user)
      @vm[2].update(:miq_group => @self_service_group)

      expect(accessible_vms.size).to eq(2)
    end

    it "limited self service user" do
      @user.update(:miq_groups => [@limited_self_service_group])
      @vm[1].update(:evm_owner => @user)
      @vm[2].update(:miq_group => @self_service_group)
      @vm[3].update(:miq_group => @limited_self_service_group)

      expect(accessible_vms.size).to eq(1)
    end
  end

  describe "#current_group_by_description=" do
    subject { FactoryBot.create(:user, :miq_groups => [g1, g2], :current_group => g1) }
    let(:g1) { FactoryBot.create(:miq_group) }
    let(:g2) { FactoryBot.create(:miq_group) }

    it "ignores blank" do
      subject.current_group_by_description = ""
      expect(subject.current_group).to eq(g1)
      expect(subject.miq_group_description).to eq(g1.description)
    end

    it "ignores not found" do
      subject.current_group_by_description = "not_found"
      expect(subject.current_group).to eq(g1)
      expect(subject.miq_group_description).to eq(g1.description)
    end

    it "ignores a group that you do not belong" do
      subject.current_group_by_description = FactoryBot.create(:miq_group).description
      expect(subject.current_group).to eq(g1)
      expect(subject.miq_group_description).to eq(g1.description)
    end

    it "sets by description" do
      subject.current_group_by_description = g2.description
      expect(subject.current_group).to eq(g2)
      expect(subject.miq_group_description).to eq(g2.description)
    end

    context "as a super admin" do
      subject { FactoryBot.create(:user, :role => "super_administrator") }

      it "sets any group, regardless of group membership" do
        expect(subject).to be_super_admin_user

        subject.current_group_by_description = g2.description
        expect(subject.current_group).to eq(g2)
      end

      it "ignores groups from other regions" do
        expect(subject).to be_super_admin_user

        group = FactoryBot.create(:miq_group, :id => ApplicationRecord.id_in_region(1, ApplicationRecord.my_region_number + 1))

        subject.current_group_by_description = group.description
        expect(subject.current_group.description).not_to eq(group.description)
      end
    end
  end

  describe ".lookup_by_lower_email" do
    it "uses cache" do
      u = FactoryBot.build(:user_with_email)
      expect(User.lookup_by_lower_email(u.email.upcase, u)).to eq(u)
    end

    it "finds in the table" do
      u = FactoryBot.create(:user_with_email)
      expect(User.lookup_by_lower_email(u.email.upcase)).to eq(u)
    end
  end

  describe ".lookup_by_email" do
    it "looks up user by email" do
      u = FactoryBot.create(:user_with_email)

      expect(User.lookup_by_email(u.email)).to eq(u)
    end
  end

  describe ".lookup_by_userid" do
    it "looks up user by email" do
      u = FactoryBot.create(:user)

      expect(User.lookup_by_userid(u.userid)).to eq(u)
      expect(User.lookup_by_userid!(u.userid)).to eq(u)
    end
  end

  describe "#current_tenant" do
    let(:user1) { FactoryBot.create(:user_with_group) }

    it "sets the tenant" do
      User.with_user(user1) do
        expect(User.current_tenant).to be_truthy
        expect(User.current_tenant).to eq(user1.current_tenant)
      end
    end
  end

  describe "#current_user=" do
    let(:user1) { FactoryBot.create(:user) }

    it "sets the user" do
      User.current_user = user1
      expect(User.current_userid).to eq(user1.userid)
      expect(User.current_user).to eq(user1)
    end
  end

  describe "#with_user" do
    let(:user1) { FactoryBot.create(:user) }
    let(:user2) { FactoryBot.create(:user) }

    it "sets the user" do
      User.with_user(user1) do
        expect(User.current_userid).to eq(user1.userid)
        expect(User.current_user).to eq(user1)
        User.with_user(user2) do
          expect(User.current_userid).to eq(user2.userid)
          expect(User.current_user).to eq(user2)
        end
        expect(User.current_userid).to eq(user1.userid)
        expect(User.current_user).to eq(user1)
      end
    end

    # sorry. please help me delete this use case / parameter
    it "supports a userid with a nil user" do
      User.with_user(user1) do
        User.with_user(nil, "oleg") do
          expect(User.current_userid).to eq("oleg")
          expect(User.current_user).not_to be
        end
        expect(User.current_userid).to eq(user1.userid)
        expect(User.current_user).to eq(user1)
      end
    end
  end

  describe "#with_user_group" do
    let(:user1) { FactoryBot.create(:user_with_group) }
    let(:user2) { FactoryBot.create(:user_with_group) }

    it "sets the user and group" do
      User.with_user_group(user1, user2.current_group_id) do
        expect(User.current_userid).to eq(user1.userid)
        expect(User.current_user).to eq(user1)
        expect(User.current_user.current_group).to eq(user2.current_group)
        User.with_user_group(user2, user1.current_group) do
          expect(User.current_userid).to eq(user2.userid)
          expect(User.current_user).to eq(user2)
        end
        expect(User.current_userid).to eq(user1.userid)
        expect(User.current_user).to eq(user1)
      end
    end
  end

  describe "#change_current_group" do
    let(:group1) { FactoryBot.create(:miq_group) }
    let(:group2) { FactoryBot.create(:miq_group) }

    it "changes the user to a group other than the current one" do
      user = FactoryBot.create(:user, :miq_groups => [group1, group2], :current_group => group1)
      user.change_current_group
      expect(user.current_group).to eq(group2)
    end

    it "raises an error if there is no group other than the current one to switch to" do
      user = FactoryBot.create(:user, :miq_groups => [group1], :current_group => group1)
      expect { user.change_current_group }
        .to raise_error(RuntimeError, /The user's current group cannot be changed because the user does not belong to any other group/)
    end
  end

  context ".super_admin" do
    it "has super_admin" do
      FactoryBot.create(:miq_group, :role => "super_administrator")
      User.seed
      expect(User.super_admin).to be_super_admin_user
    end
  end

  context ".admin?" do
    it "admin? succeeds with admin account" do
      expect(User.admin?("admin")).to be_truthy
    end

    it "admin? fails with non-admin account" do
      expect(User.admin?("regular_user")).to be_falsey
    end
  end

  context ".authorize_user" do
    it "returns nil with blank userid" do
      expect(User.authorize_user("")).to be_nil
    end

    it "returns nil with admin userid" do
      expect(User.authorize_user("admin")).to be_nil
    end
  end

  describe ".authorize_user_with_system_token" do
    context "for all authentication modes" do
      it "returns nil with blank userid" do
        expect(User.authorize_user_with_system_token("")).to be_nil
      end

      it "returns nil with blank user_metadata" do
        expect(User.authorize_user_with_system_token("jdoe", {})).to be_nil
      end

      it "returns nil with admin userid" do
        expect(User.authorize_user_with_system_token("admin", :userid => "admin")).to be_nil
      end

      it "returns nil with valid request" do
        expect(User.authorize_user_with_system_token("jdoe@acme.com", :userid => "jdoe@acme.com")).to be_nil
      end

      it "returns nil with an invalid request" do
        expect(User.authorize_user_with_system_token("bob@acme.com", :userid => "jdoe@acme.com")).to be_nil
      end
    end

    context "for OIDC" do
      let(:user_group_details) { {:description => "super_admin", :features => "everything"} }

      let(:user_metadata) do
        {
          :userid      => "jdoe@acme.com",
          :name        => "John Doe",
          :first_name  => "John",
          :last_name   => "Doe",
          :email       => "jdoe@acme.com",
          :group_names => [user_group_details[:description]]
        }
      end

      before do
        @auth_oidc_config = {
          :mode          => "httpd",
          :httpd_role    => true,
          :oidc_enabled  => true,
          :saml_enabled  => false,
          :provider_type => "oidc"
        }
        stub_settings_merge(:authentication => @auth_oidc_config)

        FactoryBot.create(:miq_group, user_group_details)
        EvmSpecHelper.local_miq_server
      end

      it "returns nil with an invalid request for OIDC" do
        expect(User.authorize_user_with_system_token("invalid_userid", user_metadata)).to be_nil
      end

      it "successfully authorizes new user with a valid request for OIDC" do
        user_obj = User.authorize_user_with_system_token(user_metadata[:userid], user_metadata)

        expect(user_obj).to_not be_nil
        expect(user_obj.miq_groups.collect(&:description)).to eq(user_metadata[:group_names])
        expect(user_metadata).to include(user_obj.attributes.symbolize_keys.slice(:userid, :name, :first_name, :last_name, :email))
      end

      it "successfully authorizes existing user with a valid request for OIDC" do
        current_user = User.create(:userid     => "jdoe@acme.com",
                                   :name       => "Current John Doe",
                                   :first_name => "Current John",
                                   :last_name  => "Current Doe",
                                   :email      => "currentjdoe@acme.com")
        current_user.miq_groups = [FactoryBot.create(:miq_group, :description => "user_group", :features => "read")]

        user_obj = User.authorize_user_with_system_token(user_metadata[:userid], user_metadata)

        expect(user_obj).to_not be_nil
        expect(user_obj.id).to eq(current_user.id)
        expect(user_obj.miq_groups.collect(&:description)).to eq(user_metadata[:group_names])
        expect(user_metadata).to include(user_obj.attributes.symbolize_keys.slice(:userid, :name, :first_name, :last_name, :email))
      end
    end
  end

  describe ".in_all_regions" do
    it "properly handles select clause" do
      u = FactoryBot.create(:user)
      expect(User.select(:id, :email).in_all_regions(u.id)).to eq([u])
    end

    it "finds records with the same userid" do
      u = FactoryBot.create(:user)
      u2 = FactoryBot.create(:user, :userid => u.userid, :id => ApplicationRecord.id_in_region(1, u.region_id + 1))
      expect(User.in_all_regions(u.id)).to match_array([u, u2])
    end
  end

  describe ".with_roles_excluding" do
    it "handles multiple columns" do
      a1 = FactoryBot.create(:miq_group, :features => "good")
      a2 = FactoryBot.create(:miq_group, :features => "something")
      b = FactoryBot.create(:miq_group, :features => %w[good everything])
      c = FactoryBot.create(:miq_group, :features => "everything")

      u1 = FactoryBot.create(:user, :miq_groups => [a1])
      u2 = FactoryBot.create(:user, :miq_groups => [a1, a2])
      FactoryBot.create(:user, :miq_groups => [a1, b])
      FactoryBot.create(:user, :miq_groups => [c])

      expect(User.with_roles_excluding("everything").select(:id, :name)).to match_array([u1, u2])
    end
  end

  describe "#regional_users" do
    let(:other_id) { ApplicationRecord.id_in_region(1, ApplicationRecord.my_region_number + 1) }
    let(:user) { FactoryBot.create(:user) }
    let!(:regional_user) { FactoryBot.create(:user, :userid => user.userid.upcase, :id => other_id) }

    it "finds regional users" do
      FactoryBot.create(:user) # ensure these doen't come back
      FactoryBot.create(:user, :id => other_id + 1)

      expect(user.regional_users).to match_array([user, regional_user])
    end
  end

  describe "#check_reference" do
    let(:user) { FactoryBot.create(:user) }

    it "invoked from 'before_destroy' callback" do
      expect(user).to receive(:check_reference)
      user.destroy
    end

    it "throws 'abort' if reference to this user present in miq_requests table" do
      FactoryBot.create(:vm_migrate_request, :requester => user)
      expect { user.check_reference }.to throw_symbol(:abort)
    end
  end
end
