From f5ad0022f72ca1391dd02d04d1aa8ef4e31d05c6 Mon Sep 17 00:00:00 2001
From: OsamaSayegh <asooomaasoooma90@gmail.com>
Date: Fri, 8 Jun 2018 07:42:06 +0300
Subject: [PATCH] REFACTOR: admin users controller specs to requests (#5946)

---
 .../admin/users_controller_spec.rb            | 840 ------------------
 spec/requests/admin/users_controller_spec.rb  | 802 +++++++++++++++++
 2 files changed, 802 insertions(+), 840 deletions(-)
 delete mode 100644 spec/controllers/admin/users_controller_spec.rb

diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
deleted file mode 100644
index 8288d07675e..00000000000
--- a/spec/controllers/admin/users_controller_spec.rb
+++ /dev/null
@@ -1,840 +0,0 @@
-require 'rails_helper'
-require_dependency 'single_sign_on'
-
-describe Admin::UsersController do
-
-  it 'is a subclass of AdminController' do
-    expect(Admin::UsersController < Admin::AdminController).to eq(true)
-  end
-
-  context 'while logged in as an admin' do
-    before do
-      @user = log_in(:admin)
-    end
-
-    context '#index' do
-      it 'returns success' do
-        get :index, format: :json
-        expect(response.status).to eq(200)
-      end
-
-      it 'returns JSON' do
-        get :index, format: :json
-        expect(::JSON.parse(response.body)).to be_present
-      end
-
-      context 'when showing emails' do
-
-        it "returns email for all the users" do
-          get :index, params: { show_emails: "true" }, format: :json
-          data = ::JSON.parse(response.body)
-          data.each do |user|
-            expect(user["email"]).to be_present
-          end
-        end
-
-        it "logs only 1 enty" do
-          expect(UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count).to eq(0)
-
-          get :index, params: { show_emails: "true" }, format: :json
-
-          expect(UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: @user.id).count).to eq(1)
-        end
-
-      end
-    end
-
-    describe '#show' do
-      context 'an existing user' do
-        it 'returns success' do
-          get :show, params: { id: @user.id }, format: :json
-          expect(response.status).to eq(200)
-        end
-      end
-
-      context 'an existing user' do
-        it 'returns success' do
-          get :show, params: { id: 0 }, format: :json
-          expect(response).not_to be_successful
-        end
-      end
-    end
-
-    context '#approve_bulk' do
-
-      let(:evil_trout) { Fabricate(:evil_trout) }
-
-      it "does nothing without uesrs" do
-        User.any_instance.expects(:approve).never
-        put :approve_bulk, format: :json
-      end
-
-      it "won't approve the user when not allowed" do
-        Guardian.any_instance.expects(:can_approve?).with(evil_trout).returns(false)
-        User.any_instance.expects(:approve).never
-        put :approve_bulk, params: { users: [evil_trout.id] }, format: :json
-      end
-
-      it "approves the user when permitted" do
-        Guardian.any_instance.expects(:can_approve?).with(evil_trout).returns(true)
-        User.any_instance.expects(:approve).once
-        put :approve_bulk, params: { users: [evil_trout.id] }, format: :json
-      end
-
-    end
-
-    context '#generate_api_key' do
-      let(:evil_trout) { Fabricate(:evil_trout) }
-
-      it 'calls generate_api_key' do
-        User.any_instance.expects(:generate_api_key).with(@user)
-        post :generate_api_key, params: { user_id: evil_trout.id }, format: :json
-      end
-    end
-
-    context '#revoke_api_key' do
-
-      let(:evil_trout) { Fabricate(:evil_trout) }
-
-      it 'calls revoke_api_key' do
-        User.any_instance.expects(:revoke_api_key)
-        delete :revoke_api_key, params: { user_id: evil_trout.id }, format: :json
-      end
-
-    end
-
-    context '#approve' do
-
-      let(:evil_trout) { Fabricate(:evil_trout) }
-
-      it "raises an error when the user doesn't have permission" do
-        Guardian.any_instance.expects(:can_approve?).with(evil_trout).returns(false)
-        put :approve, params: { user_id: evil_trout.id }, format: :json
-        expect(response).to be_forbidden
-      end
-
-      it 'calls approve' do
-        User.any_instance.expects(:approve).with(@user)
-        put :approve, params: { user_id: evil_trout.id }, format: :json
-      end
-
-    end
-
-    context '#suspend' do
-      let(:user) { Fabricate(:evil_trout) }
-
-      it "works properly" do
-        Fabricate(:api_key, user: user)
-        expect(user).not_to be_suspended
-        put(
-          :suspend,
-          params: {
-            user_id: user.id,
-            suspend_until: 5.hours.from_now,
-            reason: "because I said so",
-            format: :json
-          }
-        )
-        expect(response.status).to eq(200)
-
-        user.reload
-        expect(user).to be_suspended
-        expect(user.suspended_at).to be_present
-        expect(user.suspended_till).to be_present
-        expect(ApiKey.where(user_id: user.id).count).to eq(0)
-
-        log = UserHistory.where(target_user_id: user.id).order('id desc').first
-        expect(log).to be_present
-        expect(log.details).to match(/because I said so/)
-      end
-
-      context "with an associated post" do
-        let(:post) { Fabricate(:post) }
-        let(:suspend_params) do
-          { user_id: user.id,
-            suspend_until: 5.hours.from_now,
-            reason: "because of this post",
-            post_id: post.id,
-            format: :json }
-        end
-
-        it "can have an associated post" do
-          put(:suspend, params: suspend_params)
-          expect(response.status).to eq(200)
-
-          log = UserHistory.where(target_user_id: user.id).order('id desc').first
-          expect(log).to be_present
-          expect(log.post_id).to eq(post.id)
-        end
-
-        it "can delete an associated post" do
-          put(:suspend, params: suspend_params.merge(post_action: 'delete'))
-          post.reload
-          expect(post.deleted_at).to be_present
-          expect(response.status).to eq(200)
-        end
-
-        it "can edit an associated post" do
-          put(:suspend, params: suspend_params.merge(
-            post_action: 'edit',
-            post_edit: 'this is the edited content'
-          ))
-          post.reload
-          expect(post.deleted_at).to be_blank
-          expect(post.raw).to eq("this is the edited content")
-          expect(response.status).to eq(200)
-        end
-      end
-
-      it "can send a message to the user" do
-        Jobs.expects(:enqueue).with(
-          :critical_user_email,
-          has_entries(
-            type: :account_suspended,
-            user_id: user.id
-          )
-        )
-
-        put(
-          :suspend,
-          params: {
-            user_id: user.id,
-            suspend_until: 10.days.from_now,
-            reason: "short reason",
-            message: "long reason",
-            format: :json
-          }
-        )
-        expect(response.status).to eq(200)
-
-        log = UserHistory.where(target_user_id: user.id).order('id desc').first
-        expect(log).to be_present
-        expect(log.details).to match(/short reason/)
-        expect(log.details).to match(/long reason/)
-      end
-
-      it "also revoke any api keys" do
-        User.any_instance.expects(:revoke_api_key)
-        put :suspend, params: { user_id: user.id }, format: :json
-      end
-
-    end
-
-    context '#revoke_admin' do
-      before do
-        @another_admin = Fabricate(:admin)
-      end
-
-      it 'raises an error unless the user can revoke access' do
-        Guardian.any_instance.expects(:can_revoke_admin?).with(@another_admin).returns(false)
-        put :revoke_admin, params: { user_id: @another_admin.id }, format: :json
-        expect(response).to be_forbidden
-      end
-
-      it 'updates the admin flag' do
-        put :revoke_admin, params: { user_id: @another_admin.id }, format: :json
-        @another_admin.reload
-        expect(@another_admin).not_to be_admin
-      end
-    end
-
-    context '#grant_admin' do
-      before do
-        @another_user = Fabricate(:coding_horror)
-      end
-
-      after do
-        $redis.flushall
-      end
-
-      it "raises an error when the user doesn't have permission" do
-        Guardian.any_instance.expects(:can_grant_admin?).with(@another_user).returns(false)
-        put :grant_admin, params: { user_id: @another_user.id }, format: :json
-        expect(response).to be_forbidden
-      end
-
-      it "returns a 404 if the username doesn't exist" do
-        put :grant_admin, params: { user_id: 123123 }, format: :json
-        expect(response).to be_forbidden
-      end
-
-      it 'updates the admin flag' do
-        expect(AdminConfirmation.exists_for?(@another_user.id)).to eq(false)
-        put :grant_admin, params: { user_id: @another_user.id }, format: :json
-        expect(AdminConfirmation.exists_for?(@another_user.id)).to eq(true)
-      end
-    end
-
-    context '#add_group' do
-      let(:user) { Fabricate(:user) }
-      let(:group) { Fabricate(:group) }
-
-      it 'adds the user to the group' do
-        post :add_group, params: {
-          group_id: group.id, user_id: user.id
-        }, format: :json
-
-        expect(response.status).to eq(200)
-        expect(GroupUser.where(user_id: user.id, group_id: group.id).exists?).to eq(true)
-
-        group_history = GroupHistory.last
-
-        expect(group_history.action).to eq(GroupHistory.actions[:add_user_to_group])
-        expect(group_history.acting_user).to eq(@user)
-        expect(group_history.target_user).to eq(user)
-
-        # Doing it again doesn't raise an error
-        post :add_group, params: {
-          group_id: group.id, user_id: user.id
-        }, format: :json
-
-        expect(response.status).to eq(200)
-      end
-    end
-
-    context '#primary_group' do
-      let(:group) { Fabricate(:group) }
-
-      before do
-        @another_user = Fabricate(:coding_horror)
-      end
-
-      it "raises an error when the user doesn't have permission" do
-        Guardian.any_instance.expects(:can_change_primary_group?).with(@another_user).returns(false)
-        put :primary_group, params: {
-          user_id: @another_user.id
-        }, format: :json
-
-        expect(response).to be_forbidden
-      end
-
-      it "returns a 404 if the user doesn't exist" do
-        put :primary_group, params: {
-          user_id: 123123
-        }, format: :json
-
-        expect(response).to be_forbidden
-      end
-
-      it "changes the user's primary group" do
-        group.add(@another_user)
-        put :primary_group, params: {
-          user_id: @another_user.id, primary_group_id: group.id
-        }, format: :json
-
-        @another_user.reload
-        expect(@another_user.primary_group_id).to eq(group.id)
-      end
-
-      it "doesn't change primary group if they aren't a member of the group" do
-        put :primary_group, params: {
-          user_id: @another_user.id, primary_group_id: group.id
-        }, format: :json
-
-        @another_user.reload
-        expect(@another_user.primary_group_id).to be_nil
-      end
-
-      it "remove user's primary group" do
-        group.add(@another_user)
-
-        put :primary_group, params: {
-          user_id: @another_user.id, primary_group_id: ""
-        }, format: :json
-
-        @another_user.reload
-        expect(@another_user.primary_group_id).to be(nil)
-      end
-    end
-
-    context '#trust_level' do
-      before do
-        @another_user = Fabricate(:coding_horror, created_at: 1.month.ago)
-      end
-
-      it "raises an error when the user doesn't have permission" do
-        Guardian.any_instance.expects(:can_change_trust_level?).with(@another_user).returns(false)
-        put :trust_level, params: {
-          user_id: @another_user.id
-        }, format: :json
-
-        expect(response).not_to be_successful
-      end
-
-      it "returns a 404 if the username doesn't exist" do
-        put :trust_level, params: {
-          user_id: 123123
-        }, format: :json
-
-        expect(response).not_to be_successful
-      end
-
-      it "upgrades the user's trust level" do
-        StaffActionLogger.any_instance.expects(:log_trust_level_change).with(@another_user, @another_user.trust_level, 2).once
-
-        put :trust_level, params: {
-          user_id: @another_user.id, level: 2
-        }, format: :json
-
-        @another_user.reload
-        expect(@another_user.trust_level).to eq(2)
-        expect(response.status).to eq(200)
-      end
-
-      it "raises no error when demoting a user below their current trust level (locks trust level)" do
-        stat = @another_user.user_stat
-        stat.topics_entered = SiteSetting.tl1_requires_topics_entered + 1
-        stat.posts_read_count = SiteSetting.tl1_requires_read_posts + 1
-        stat.time_read = SiteSetting.tl1_requires_time_spent_mins * 60
-        stat.save!
-        @another_user.update_attributes(trust_level: TrustLevel[1])
-
-        put :trust_level, params: {
-          user_id: @another_user.id,
-          level: TrustLevel[0]
-        }, format: :json
-
-        expect(response.status).to eq(200)
-        @another_user.reload
-        expect(@another_user.trust_level).to eq(TrustLevel[0])
-        expect(@another_user.manual_locked_trust_level).to eq(TrustLevel[0])
-      end
-    end
-
-    describe '#revoke_moderation' do
-      before do
-        @moderator = Fabricate(:moderator)
-      end
-
-      it 'raises an error unless the user can revoke access' do
-        Guardian.any_instance.expects(:can_revoke_moderation?).with(@moderator).returns(false)
-        put :revoke_moderation, params: {
-          user_id: @moderator.id
-        }, format: :json
-
-        expect(response).to be_forbidden
-      end
-
-      it 'updates the moderator flag' do
-        put :revoke_moderation, params: {
-          user_id: @moderator.id
-        }, format: :json
-
-        @moderator.reload
-        expect(@moderator.moderator).not_to eq(true)
-      end
-    end
-
-    context '#grant_moderation' do
-      before do
-        @another_user = Fabricate(:coding_horror)
-      end
-
-      it "raises an error when the user doesn't have permission" do
-        Guardian.any_instance.expects(:can_grant_moderation?).with(@another_user).returns(false)
-        put :grant_moderation, params: { user_id: @another_user.id }, format: :json
-        expect(response).to be_forbidden
-      end
-
-      it "returns a 404 if the username doesn't exist" do
-        put :grant_moderation, params: { user_id: 123123 }, format: :json
-        expect(response).to be_forbidden
-      end
-
-      it 'updates the moderator flag' do
-        put :grant_moderation, params: { user_id: @another_user.id }, format: :json
-        @another_user.reload
-        expect(@another_user.moderator).to eq(true)
-      end
-    end
-
-    context '#reject_bulk' do
-      let(:reject_me)     { Fabricate(:user) }
-      let(:reject_me_too) { Fabricate(:user) }
-
-      it 'does nothing without users' do
-        UserDestroyer.any_instance.expects(:destroy).never
-        delete :reject_bulk, format: :json
-      end
-
-      it "won't delete users if not allowed" do
-        Guardian.any_instance.stubs(:can_delete_user?).returns(false)
-        UserDestroyer.any_instance.expects(:destroy).never
-
-        delete :reject_bulk, params: {
-          users: [reject_me.id]
-        }, format: :json
-      end
-
-      it "reports successes" do
-        Guardian.any_instance.stubs(:can_delete_user?).returns(true)
-        UserDestroyer.any_instance.stubs(:destroy).returns(true)
-
-        delete :reject_bulk, params: {
-          users: [reject_me.id, reject_me_too.id]
-        }, format: :json
-
-        expect(response.status).to eq(200)
-        json = ::JSON.parse(response.body)
-        expect(json['success'].to_i).to eq(2)
-        expect(json['failed'].to_i).to eq(0)
-      end
-
-      context 'failures' do
-        before do
-          Guardian.any_instance.stubs(:can_delete_user?).returns(true)
-        end
-
-        it 'can handle some successes and some failures' do
-          UserDestroyer.any_instance.stubs(:destroy).with(reject_me, anything).returns(false)
-          UserDestroyer.any_instance.stubs(:destroy).with(reject_me_too, anything).returns(true)
-
-          delete :reject_bulk, params: {
-            users: [reject_me.id, reject_me_too.id]
-          }, format: :json
-
-          expect(response.status).to eq(200)
-          json = ::JSON.parse(response.body)
-          expect(json['success'].to_i).to eq(1)
-          expect(json['failed'].to_i).to eq(1)
-        end
-
-        it 'reports failure due to a user still having posts' do
-          UserDestroyer.any_instance.expects(:destroy).with(reject_me, anything).raises(UserDestroyer::PostsExistError)
-
-          delete :reject_bulk, params: {
-            users: [reject_me.id]
-          }, format: :json
-
-          expect(response.status).to eq(200)
-          json = ::JSON.parse(response.body)
-          expect(json['success'].to_i).to eq(0)
-          expect(json['failed'].to_i).to eq(1)
-        end
-      end
-    end
-
-    context '#destroy' do
-      let(:delete_me) { Fabricate(:user) }
-
-      it "returns a 403 if the user doesn't exist" do
-        delete :destroy, params: { id: 123123 }, format: :json
-        expect(response).to be_forbidden
-      end
-
-      context "user has post" do
-        let(:topic) { create_topic(user: delete_me) }
-
-        before do
-          _post = create_post(topic: topic, user: delete_me)
-        end
-
-        it "returns an api response that the user can't be deleted because it has posts" do
-          delete :destroy, params: { id: delete_me.id }, format: :json
-          expect(response).to be_forbidden
-          json = ::JSON.parse(response.body)
-          expect(json['deleted']).to eq(false)
-        end
-
-        it "doesn't return an error if delete_posts == true" do
-          delete :destroy, params: { id: delete_me.id, delete_posts: true }, format: :json
-          expect(response.status).to eq(200)
-        end
-      end
-
-      it "deletes the user record" do
-        UserDestroyer.any_instance.expects(:destroy).returns(true)
-        delete :destroy, params: { id: delete_me.id }, format: :json
-      end
-    end
-
-    context 'activate' do
-      before do
-        @reg_user = Fabricate(:inactive_user)
-      end
-
-      it "returns success" do
-        put :activate, params: { user_id: @reg_user.id }, format: :json
-        expect(response.status).to eq(200)
-        json = ::JSON.parse(response.body)
-        expect(json['success']).to eq("OK")
-      end
-
-      it "should confirm email even when the tokens are expired" do
-        @reg_user.email_tokens.update_all(confirmed: false, expired: true)
-
-        @reg_user.reload
-        expect(@reg_user.email_confirmed?).to eq(false)
-
-        put :activate, params: { user_id: @reg_user.id }, format: :json
-        expect(response.status).to eq(200)
-
-        @reg_user.reload
-        expect(@reg_user.email_confirmed?).to eq(true)
-      end
-    end
-
-    context 'log_out' do
-      before do
-        @reg_user = Fabricate(:user)
-      end
-
-      it "returns success" do
-        put :log_out, params: { user_id: @reg_user.id }, format: :json
-        expect(response.status).to eq(200)
-        json = ::JSON.parse(response.body)
-        expect(json['success']).to eq("OK")
-      end
-
-      it "returns 404 when user_id does not exist" do
-        put :log_out, params: { user_id: 123123 }, format: :json
-        expect(response).not_to be_successful
-      end
-    end
-
-    context 'silence' do
-      before do
-        @reg_user = Fabricate(:user)
-      end
-
-      it "raises an error when the user doesn't have permission" do
-        Guardian.any_instance.expects(:can_silence_user?).with(@reg_user).returns(false)
-        put :silence, params: { user_id: @reg_user.id }, format: :json
-        expect(response).to be_forbidden
-        @reg_user.reload
-        expect(@reg_user).not_to be_silenced
-      end
-
-      it "returns a 403 if the user doesn't exist" do
-        put :silence, params: { user_id: 123123 }, format: :json
-        expect(response).to be_forbidden
-      end
-
-      it "punishes the user for spamming" do
-        put :silence, params: { user_id: @reg_user.id }, format: :json
-        expect(response.status).to eq(200)
-        @reg_user.reload
-        expect(@reg_user).to be_silenced
-      end
-
-      it "can have an associated post" do
-        silence_post = Fabricate(:post, user: @reg_user)
-
-        put :silence, params: {
-          user_id: @reg_user.id,
-          post_id: silence_post.id,
-          post_action: 'edit',
-          post_edit: "this is the new contents for the post"
-        }, format: :json
-        expect(response.status).to eq(200)
-
-        silence_post.reload
-        expect(silence_post.raw).to eq("this is the new contents for the post")
-
-        log = UserHistory.where(
-          target_user_id: @reg_user.id,
-          action: UserHistory.actions[:silence_user]
-        ).first
-        expect(log).to be_present
-        expect(log.post_id).to eq(silence_post.id)
-
-        @reg_user.reload
-        expect(@reg_user).to be_silenced
-      end
-
-      it "will set a length of time if provided" do
-        future_date = 1.month.from_now.to_date
-        put(
-          :silence,
-          params: {
-            user_id: @reg_user.id,
-            silenced_till: future_date
-          },
-          format: :json
-        )
-        @reg_user.reload
-        expect(@reg_user.silenced_till).to eq(future_date)
-      end
-
-      it "will send a message if provided" do
-        Jobs.stubs(:enqueue)
-        Jobs.expects(:enqueue).with(
-          :critical_user_email,
-          has_entries(
-            type: :account_silenced,
-            user_id: @reg_user.id
-          )
-        )
-
-        put(
-          :silence,
-          params: {
-            user_id: @reg_user.id,
-            message: "Email this to the user"
-          },
-          format: :json
-        )
-      end
-    end
-
-    context 'unsilence' do
-      before do
-        @reg_user = Fabricate(:user)
-      end
-
-      it "raises an error when the user doesn't have permission" do
-        Guardian.any_instance.expects(:can_unsilence_user?).with(@reg_user).returns(false)
-        put :unsilence, params: { user_id: @reg_user.id }, format: :json
-        expect(response).to be_forbidden
-      end
-
-      it "returns a 403 if the user doesn't exist" do
-        put :unsilence, params: { user_id: 123123 }, format: :json
-        expect(response).to be_forbidden
-      end
-
-      it "punishes the user for spamming" do
-        UserSilencer.expects(:unsilence).with(@reg_user, @user, anything)
-        put :unsilence, params: { user_id: @reg_user.id }, format: :json
-      end
-    end
-
-    context 'ip-info' do
-
-      it "uses ipinfo.io webservice to retrieve the info" do
-        Excon.expects(:get).with("https://ipinfo.io/123.123.123.123/json", read_timeout: 10, connect_timeout: 10)
-        get :ip_info, params: { ip: "123.123.123.123" }, format: :json
-      end
-
-    end
-
-    context "delete_other_accounts_with_same_ip" do
-
-      it "works" do
-        Fabricate(:user, ip_address: "42.42.42.42")
-        Fabricate(:user, ip_address: "42.42.42.42")
-
-        UserDestroyer.any_instance.expects(:destroy).twice
-
-        delete :delete_other_accounts_with_same_ip, params: {
-          ip: "42.42.42.42", exclude: -1, order: "trust_level DESC"
-        }, format: :json
-      end
-
-    end
-
-    context ".invite_admin" do
-      it "doesn't work when not via API" do
-        controller.stubs(:is_api?).returns(false)
-
-        post :invite_admin, params: {
-          name: 'Bill', username: 'bill22', email: 'bill@bill.com'
-        }, format: :json
-
-        expect(response).not_to be_successful
-      end
-
-      it 'should invite admin' do
-        controller.stubs(:is_api?).returns(true)
-        Jobs.expects(:enqueue).with(:critical_user_email, anything).returns(true)
-
-        post :invite_admin, params: {
-          name: 'Bill', username: 'bill22', email: 'bill@bill.com'
-        }, format: :json
-
-        expect(response.status).to eq(200)
-
-        u = User.find_by_email('bill@bill.com')
-        expect(u.name).to eq("Bill")
-        expect(u.username).to eq("bill22")
-        expect(u.admin).to eq(true)
-      end
-
-      it "doesn't send the email with send_email falsy" do
-        controller.stubs(:is_api?).returns(true)
-        Jobs.expects(:enqueue).with(:user_email, anything).never
-
-        post :invite_admin, params: {
-          name: 'Bill', username: 'bill22', email: 'bill@bill.com', send_email: '0'
-        }, format: :json
-
-        expect(response.status).to eq(200)
-        json = ::JSON.parse(response.body)
-        expect(json["password_url"]).to be_present
-      end
-    end
-
-    context 'remove_group' do
-      it "also clears the user's primary group" do
-        g = Fabricate(:group)
-        u = Fabricate(:user, primary_group: g)
-        delete :remove_group, params: { group_id: g.id, user_id: u.id }, format: :json
-
-        expect(u.reload.primary_group).to be_nil
-      end
-    end
-  end
-
-  context '#sync_sso' do
-    let(:sso) { SingleSignOn.new }
-    let(:sso_secret) { "sso secret" }
-
-    before do
-      log_in(:admin)
-
-      SiteSetting.email_editable = false
-      SiteSetting.sso_url = "https://www.example.com/sso"
-      SiteSetting.enable_sso = true
-      SiteSetting.sso_overrides_email = true
-      SiteSetting.sso_overrides_name = true
-      SiteSetting.sso_overrides_username = true
-      SiteSetting.sso_secret = sso_secret
-      sso.sso_secret = sso_secret
-    end
-
-    it 'can sync up with the sso' do
-      sso.name = "Bob The Bob"
-      sso.username = "bob"
-      sso.email = "bob@bob.com"
-      sso.external_id = "1"
-
-      user = DiscourseSingleSignOn.parse(sso.payload)
-        .lookup_or_create_user
-
-      sso.name = "Bill"
-      sso.username = "Hokli$$!!"
-      sso.email = "bob2@bob.com"
-
-      post :sync_sso, params: Rack::Utils.parse_query(sso.payload), format: :json
-      expect(response.status).to eq(200)
-
-      user.reload
-      expect(user.email).to eq("bob2@bob.com")
-      expect(user.name).to eq("Bill")
-      expect(user.username).to eq("Hokli")
-    end
-
-    it 'should create new users' do
-      sso.name = "Dr. Claw"
-      sso.username = "dr_claw"
-      sso.email = "dr@claw.com"
-      sso.external_id = "2"
-      post :sync_sso, params: Rack::Utils.parse_query(sso.payload), format: :json
-      expect(response.status).to eq(200)
-
-      user = User.find_by_email('dr@claw.com')
-      expect(user).to be_present
-      expect(user.ip_address).to be_blank
-    end
-
-    it 'should return the right message if the record is invalid' do
-      sso.email = ""
-      sso.name = ""
-      sso.external_id = "1"
-
-      post :sync_sso, params: Rack::Utils.parse_query(sso.payload), format: :json
-      expect(response.status).to eq(403)
-      expect(JSON.parse(response.body)["message"]).to include("Primary email can't be blank")
-    end
-  end
-end
diff --git a/spec/requests/admin/users_controller_spec.rb b/spec/requests/admin/users_controller_spec.rb
index a89e92f9cf7..67cf0ee5b90 100644
--- a/spec/requests/admin/users_controller_spec.rb
+++ b/spec/requests/admin/users_controller_spec.rb
@@ -4,6 +4,808 @@ RSpec.describe Admin::UsersController do
   let(:admin) { Fabricate(:admin) }
   let(:user) { Fabricate(:user) }
 
+  it 'is a subclass of AdminController' do
+    expect(Admin::UsersController < Admin::AdminController).to eq(true)
+  end
+
+  before do
+    sign_in(admin)
+  end
+
+  describe '#index' do
+    it 'returns success with JSON' do
+      get "/admin/users/list.json"
+      expect(response.status).to eq(200)
+      expect(JSON.parse(response.body)).to be_present
+    end
+
+    context 'when showing emails' do
+      it "returns email for all the users" do
+        get "/admin/users/list.json", params: { show_emails: "true" }
+        expect(response.status).to eq(200)
+        data = ::JSON.parse(response.body)
+        data.each do |user|
+          expect(user["email"]).to be_present
+        end
+      end
+
+      it "logs only 1 enty" do
+        expect do
+          get "/admin/users/list.json", params: { show_emails: "true" }
+        end.to change { UserHistory.where(action: UserHistory.actions[:check_email], acting_user_id: admin.id).count }.by(1)
+        expect(response.status).to eq(200)
+      end
+    end
+  end
+
+  describe '#show' do
+    context 'an existing user' do
+      it 'returns success' do
+        get "/admin/users/#{user.id}.json"
+        expect(response.status).to eq(200)
+      end
+    end
+
+    context 'a non-existing user' do
+      it 'returns 404 error' do
+        get "/admin/users/0.json"
+        expect(response.status).to eq(404)
+      end
+    end
+  end
+
+  describe '#approve' do
+    let(:evil_trout) { Fabricate(:evil_trout, approved: false) }
+
+    it "raises an error when the user doesn't have permission" do
+      sign_in(user)
+      put "/admin/users/#{evil_trout.id}/approve.json"
+      expect(response.status).to eq(404)
+      evil_trout.reload
+      expect(evil_trout.approved).to eq(false)
+    end
+
+    it 'calls approve' do
+      put "/admin/users/#{evil_trout.id}/approve.json"
+      expect(response.status).to eq(200)
+      evil_trout.reload
+      expect(evil_trout.approved).to eq(true)
+    end
+  end
+
+  describe '#approve_bulk' do
+    let(:evil_trout) { Fabricate(:evil_trout, approved: false) }
+
+    it "does nothing without uesrs" do
+      put "/admin/users/approve-bulk.json"
+      evil_trout.reload
+      expect(response.status).to eq(200)
+      expect(evil_trout.approved).to eq(false)
+    end
+
+    it "won't approve the user when not allowed" do
+      sign_in(user)
+      put "/admin/users/approve-bulk.json", params: { users: [evil_trout.id] }
+      expect(response.status).to eq(404)
+      evil_trout.reload
+      expect(evil_trout.approved).to eq(false)
+    end
+
+    it "approves the user when permitted" do
+      put "/admin/users/approve-bulk.json", params: { users: [evil_trout.id] }
+      expect(response.status).to eq(200)
+      evil_trout.reload
+      expect(evil_trout.approved).to eq(true)
+    end
+  end
+
+  describe '#generate_api_key' do
+    it 'calls generate_api_key' do
+      post "/admin/users/#{user.id}/generate_api_key.json"
+      expect(response.status).to eq(200)
+      json = JSON.parse(response.body)
+      expect(json["api_key"]["user"]["id"]).to eq(user.id)
+      expect(json["api_key"]["key"]).to be_present
+    end
+  end
+
+  describe '#revoke_api_key' do
+    it 'calls revoke_api_key' do
+      ApiKey.create!(user: user, key: SecureRandom.hex)
+      delete "/admin/users/#{user.id}/revoke_api_key.json"
+      expect(response.status).to eq(200)
+      expect(ApiKey.where(user: user).count).to eq(0)
+    end
+  end
+
+  describe '#suspend' do
+    let(:post) { Fabricate(:post) }
+    let(:suspend_params) do
+      { suspend_until: 5.hours.from_now,
+        reason: "because of this post",
+        post_id: post.id }
+    end
+
+    it "works properly" do
+      expect(user).not_to be_suspended
+      put "/admin/users/#{user.id}/suspend.json", params: {
+        suspend_until: 5.hours.from_now,
+        reason: "because I said so"
+      }
+
+      expect(response.status).to eq(200)
+
+      user.reload
+      expect(user).to be_suspended
+      expect(user.suspended_at).to be_present
+      expect(user.suspended_till).to be_present
+
+      log = UserHistory.where(target_user_id: user.id).order('id desc').first
+      expect(log.details).to match(/because I said so/)
+    end
+
+    context "with an associated post" do
+      it "can have an associated post" do
+        put "/admin/users/#{user.id}/suspend.json", params: suspend_params
+
+        expect(response.status).to eq(200)
+
+        log = UserHistory.where(target_user_id: user.id).order('id desc').first
+        expect(log.post_id).to eq(post.id)
+      end
+
+      it "can delete an associated post" do
+        put "/admin/users/#{user.id}/suspend.json", params: suspend_params.merge(post_action: 'delete')
+        post.reload
+        expect(post.deleted_at).to be_present
+        expect(response.status).to eq(200)
+      end
+
+      it "can edit an associated post" do
+        put "/admin/users/#{user.id}/suspend.json", params: suspend_params.merge(
+          post_action: 'edit',
+          post_edit: 'this is the edited content'
+        )
+
+        expect(response.status).to eq(200)
+        post.reload
+        expect(post.deleted_at).to be_blank
+        expect(post.raw).to eq("this is the edited content")
+        expect(response.status).to eq(200)
+      end
+    end
+
+    it "can send a message to the user" do
+      put "/admin/users/#{user.id}/suspend.json", params: {
+        suspend_until: 10.days.from_now,
+        reason: "short reason",
+        message: "long reason"
+      }
+
+      expect(response.status).to eq(200)
+
+      expect(Jobs::CriticalUserEmail.jobs.size).to eq(1)
+      job_args = Jobs::CriticalUserEmail.jobs.first["args"].first
+      expect(job_args["type"]).to eq("account_suspended")
+      expect(job_args["user_id"]).to eq(user.id)
+
+      log = UserHistory.where(target_user_id: user.id).order('id desc').first
+      expect(log).to be_present
+      expect(log.details).to match(/short reason/)
+      expect(log.details).to match(/long reason/)
+    end
+
+    it "also revokes any api keys" do
+      Fabricate(:api_key, user: user)
+      put "/admin/users/#{user.id}/suspend.json", params: suspend_params
+
+      expect(response.status).to eq(200)
+      user.reload
+
+      expect(user).to be_suspended
+      expect(ApiKey.where(user_id: user.id).count).to eq(0)
+    end
+  end
+
+  describe '#revoke_admin' do
+    let(:another_admin) { Fabricate(:admin) }
+
+    it 'raises an error unless the user can revoke access' do
+      sign_in(user)
+      put "/admin/users/#{another_admin.id}/revoke_admin.json"
+      expect(response.status).to eq(404)
+      another_admin.reload
+      expect(another_admin.admin).to eq(true)
+    end
+
+    it 'updates the admin flag' do
+      put "/admin/users/#{another_admin.id}/revoke_admin.json"
+      expect(response.status).to eq(200)
+      another_admin.reload
+      expect(another_admin.admin).to eq(false)
+    end
+  end
+
+  describe '#grant_admin' do
+    let(:another_user) { Fabricate(:coding_horror) }
+
+    after do
+      $redis.flushall
+    end
+
+    it "raises an error when the user doesn't have permission" do
+      sign_in(user)
+      put "/admin/users/#{another_user.id}/grant_admin.json"
+      expect(response.status).to eq(404)
+      expect(AdminConfirmation.exists_for?(another_user.id)).to eq(false)
+    end
+
+    it "returns a 403 if the username doesn't exist" do
+      put "/admin/users/123123/grant_admin.json"
+      expect(response.status).to eq(403)
+    end
+
+    it 'updates the admin flag' do
+      expect(AdminConfirmation.exists_for?(another_user.id)).to eq(false)
+      put "/admin/users/#{another_user.id}/grant_admin.json"
+      expect(response.status).to eq(200)
+      expect(AdminConfirmation.exists_for?(another_user.id)).to eq(true)
+    end
+  end
+
+  describe '#add_group' do
+    let(:group) { Fabricate(:group) }
+
+    it 'adds the user to the group' do
+      post "/admin/users/#{user.id}/groups.json", params: {
+        group_id: group.id
+      }
+
+      expect(response.status).to eq(200)
+      expect(GroupUser.where(user_id: user.id, group_id: group.id).exists?).to eq(true)
+
+      group_history = GroupHistory.last
+
+      expect(group_history.action).to eq(GroupHistory.actions[:add_user_to_group])
+      expect(group_history.acting_user).to eq(admin)
+      expect(group_history.target_user).to eq(user)
+
+      # Doing it again doesn't raise an error
+      post "/admin/users/#{user.id}/groups.json", params: {
+        group_id: group.id
+      }
+
+      expect(response.status).to eq(200)
+    end
+  end
+
+  describe '#remove_group' do
+    it "also clears the user's primary group" do
+      g = Fabricate(:group)
+      u = Fabricate(:user, primary_group: g)
+      delete "/admin/users/#{u.id}/groups/#{g.id}.json"
+
+      expect(response.status).to eq(200)
+      expect(u.reload.primary_group).to eq(nil)
+    end
+  end
+
+  describe '#trust_level' do
+    let(:another_user) { Fabricate(:coding_horror, created_at: 1.month.ago) }
+
+    it "raises an error when the user doesn't have permission" do
+      sign_in(user)
+      put "/admin/users/#{another_user.id}/trust_level.json"
+      expect(response.status).to eq(404)
+    end
+
+    it "returns a 422 if the username doesn't exist" do
+      put "/admin/users/123123/trust_level.json"
+      expect(response.status).to eq(422)
+    end
+
+    it "upgrades the user's trust level" do
+      put "/admin/users/#{another_user.id}/trust_level.json", params: { level: 2 }
+
+      expect(response.status).to eq(200)
+      another_user.reload
+      expect(another_user.trust_level).to eq(2)
+
+      expect(UserHistory.where(
+        target_user: another_user,
+        acting_user: admin,
+        action: UserHistory.actions[:change_trust_level]
+      ).count).to eq(1)
+    end
+
+    it "raises no error when demoting a user below their current trust level (locks trust level)" do
+      stat = another_user.user_stat
+      stat.topics_entered = SiteSetting.tl1_requires_topics_entered + 1
+      stat.posts_read_count = SiteSetting.tl1_requires_read_posts + 1
+      stat.time_read = SiteSetting.tl1_requires_time_spent_mins * 60
+      stat.save!
+      another_user.update_attributes(trust_level: TrustLevel[1])
+
+      put "/admin/users/#{another_user.id}/trust_level.json", params: {
+        level: TrustLevel[0]
+      }
+
+      expect(response.status).to eq(200)
+      another_user.reload
+      expect(another_user.trust_level).to eq(TrustLevel[0])
+      expect(another_user.manual_locked_trust_level).to eq(TrustLevel[0])
+    end
+  end
+
+  describe '#grant_moderation' do
+    let(:another_user) { Fabricate(:coding_horror) }
+
+    it "raises an error when the user doesn't have permission" do
+      sign_in(user)
+      put "/admin/users/#{another_user.id}/grant_moderation.json"
+      expect(response.status).to eq(404)
+    end
+
+    it "returns a 403 if the username doesn't exist" do
+      put "/admin/users/123123/grant_moderation.json"
+      expect(response.status).to eq(403)
+    end
+
+    it 'updates the moderator flag' do
+      put "/admin/users/#{another_user.id}/grant_moderation.json"
+      expect(response.status).to eq(200)
+      another_user.reload
+      expect(another_user.moderator).to eq(true)
+    end
+  end
+
+  describe '#revoke_moderation' do
+    let(:moderator) { Fabricate(:moderator) }
+
+    it 'raises an error unless the user can revoke access' do
+      sign_in(user)
+      put "/admin/users/#{moderator.id}/revoke_moderation.json"
+      expect(response.status).to eq(404)
+      moderator.reload
+      expect(moderator.moderator).to eq(true)
+    end
+
+    it 'updates the moderator flag' do
+      put "/admin/users/#{moderator.id}/revoke_moderation.json"
+      expect(response.status).to eq(200)
+      moderator.reload
+      expect(moderator.moderator).to eq(false)
+    end
+  end
+
+  describe '#primary_group' do
+    let(:group) { Fabricate(:group) }
+    let(:another_user) { Fabricate(:coding_horror) }
+
+    it "raises an error when the user doesn't have permission" do
+      sign_in(user)
+      put "/admin/users/#{another_user.id}/primary_group.json"
+      expect(response.status).to eq(404)
+      another_user.reload
+      expect(another_user.primary_group_id).to eq(nil)
+    end
+
+    it "returns a 404 if the user doesn't exist" do
+      put "/admin/users/123123/primary_group.json"
+      expect(response.status).to eq(403)
+    end
+
+    it "changes the user's primary group" do
+      group.add(another_user)
+      put "/admin/users/#{another_user.id}/primary_group.json", params: {
+        primary_group_id: group.id
+      }
+
+      expect(response.status).to eq(200)
+      another_user.reload
+      expect(another_user.primary_group_id).to eq(group.id)
+    end
+
+    it "doesn't change primary group if they aren't a member of the group" do
+      put "/admin/users/#{another_user.id}/primary_group.json", params: {
+        primary_group_id: group.id
+      }
+
+      expect(response.status).to eq(200)
+      another_user.reload
+      expect(another_user.primary_group_id).to eq(nil)
+    end
+
+    it "remove user's primary group" do
+      group.add(another_user)
+
+      put "/admin/users/#{another_user.id}/primary_group.json", params: {
+        primary_group_id: ""
+      }
+
+      expect(response.status).to eq(200)
+      another_user.reload
+      expect(another_user.primary_group_id).to eq(nil)
+    end
+  end
+
+  describe '#destroy' do
+    let(:delete_me) { Fabricate(:user) }
+
+    it "returns a 403 if the user doesn't exist" do
+      delete "/admin/users/123123drink.json"
+      expect(response.status).to eq(403)
+    end
+
+    context "user has post" do
+      let(:topic) { Fabricate(:topic, user: delete_me) }
+      let!(:post) { Fabricate(:post, topic: topic, user: delete_me) }
+
+      it "returns an api response that the user can't be deleted because it has posts" do
+        delete "/admin/users/#{delete_me.id}.json"
+        expect(response.status).to eq(403)
+        json = ::JSON.parse(response.body)
+        expect(json['deleted']).to eq(false)
+      end
+
+      it "doesn't return an error if delete_posts == true" do
+        delete "/admin/users/#{delete_me.id}.json", params: { delete_posts: true }
+        expect(response.status).to eq(200)
+        expect(Post.where(id: post.id).count).to eq(0)
+        expect(Topic.where(id: topic.id).count).to eq(0)
+        expect(User.where(id: delete_me.id).count).to eq(0)
+      end
+    end
+
+    it "deletes the user record" do
+      delete "/admin/users/#{delete_me.id}.json"
+      expect(response.status).to eq(200)
+      expect(User.where(id: delete_me.id).count).to eq(0)
+    end
+  end
+
+  describe '#activate' do
+    let(:reg_user) { Fabricate(:inactive_user) }
+
+    it "returns success" do
+      put "/admin/users/#{reg_user.id}/activate.json"
+      expect(response.status).to eq(200)
+      json = ::JSON.parse(response.body)
+      expect(json['success']).to eq("OK")
+      reg_user.reload
+      expect(reg_user.active).to eq(true)
+    end
+
+    it "should confirm email even when the tokens are expired" do
+      reg_user.email_tokens.update_all(confirmed: false, expired: true)
+
+      reg_user.reload
+      expect(reg_user.email_confirmed?).to eq(false)
+
+      put "/admin/users/#{reg_user.id}/activate.json"
+      expect(response.status).to eq(200)
+
+      reg_user.reload
+      expect(reg_user.email_confirmed?).to eq(true)
+    end
+  end
+
+  describe '#log_out' do
+    let(:reg_user) { Fabricate(:user) }
+
+    it "returns success" do
+      post "/admin/users/#{reg_user.id}/log_out.json"
+      expect(response.status).to eq(200)
+      json = ::JSON.parse(response.body)
+      expect(json['success']).to eq("OK")
+    end
+
+    it "returns 404 when user_id does not exist" do
+      post "/admin/users/123123drink/log_out.json"
+      expect(response.status).to eq(404)
+    end
+  end
+
+  describe '#silence' do
+    let(:reg_user) { Fabricate(:user) }
+
+    it "raises an error when the user doesn't have permission" do
+      sign_in(user)
+      put "/admin/users/#{reg_user.id}/silence.json"
+      expect(response.status).to eq(404)
+      reg_user.reload
+      expect(reg_user).not_to be_silenced
+    end
+
+    it "returns a 403 if the user doesn't exist" do
+      put "/admin/users/123123/silence.json"
+      expect(response.status).to eq(403)
+    end
+
+    it "punishes the user for spamming" do
+      put "/admin/users/#{reg_user.id}/silence.json"
+      expect(response.status).to eq(200)
+      reg_user.reload
+      expect(reg_user).to be_silenced
+    end
+
+    it "can have an associated post" do
+      silence_post = Fabricate(:post, user: reg_user)
+
+      put "/admin/users/#{reg_user.id}/silence.json", params: {
+        post_id: silence_post.id,
+        post_action: 'edit',
+        post_edit: "this is the new contents for the post"
+      }
+      expect(response.status).to eq(200)
+
+      silence_post.reload
+      expect(silence_post.raw).to eq("this is the new contents for the post")
+
+      log = UserHistory.where(
+        target_user_id: reg_user.id,
+        action: UserHistory.actions[:silence_user]
+      ).first
+      expect(log).to be_present
+      expect(log.post_id).to eq(silence_post.id)
+
+      reg_user.reload
+      expect(reg_user).to be_silenced
+    end
+
+    it "will set a length of time if provided" do
+      future_date = 1.month.from_now.to_date
+      put "/admin/users/#{reg_user.id}/silence.json", params: {
+        silenced_till: future_date
+      }
+
+      expect(response.status).to eq(200)
+      reg_user.reload
+      expect(reg_user).to be_silenced
+      expect(reg_user.silenced_till).to eq(future_date)
+    end
+
+    it "will send a message if provided" do
+      expect do
+        put "/admin/users/#{reg_user.id}/silence.json", params: {
+          message: "Email this to the user"
+        }
+      end.to change { Jobs::CriticalUserEmail.jobs.size }.by(1)
+
+      expect(response.status).to eq(200)
+      reg_user.reload
+      expect(reg_user).to be_silenced
+    end
+  end
+
+  describe '#unsilence' do
+    let(:reg_user) { Fabricate(:user, silenced_till: 10.years.from_now) }
+
+    it "raises an error when the user doesn't have permission" do
+      sign_in(user)
+      put "/admin/users/#{reg_user.id}/unsilence.json"
+      expect(response.status).to eq(404)
+    end
+
+    it "returns a 403 if the user doesn't exist" do
+      put "/admin/users/123123/unsilence.json"
+      expect(response.status).to eq(403)
+    end
+
+    it "unsilences the user" do
+      put "/admin/users/#{reg_user.id}/unsilence.json"
+      expect(response.status).to eq(200)
+      reg_user.reload
+      expect(reg_user.silenced?).to eq(false)
+      log = UserHistory.where(
+        target_user_id: reg_user.id,
+        action: UserHistory.actions[:unsilence_user]
+      ).first
+      expect(log).to be_present
+    end
+  end
+
+  describe '#reject_bulk' do
+    let(:reject_me)     { Fabricate(:user) }
+    let(:reject_me_too) { Fabricate(:user) }
+
+    it 'does nothing without users' do
+      delete "/admin/users/reject-bulk.json"
+      expect(response.status).to eq(200)
+      expect(User.where(id: reject_me.id).count).to eq(1)
+      expect(User.where(id: reject_me_too.id).count).to eq(1)
+    end
+
+    it "won't delete users if not allowed" do
+      sign_in(user)
+      delete "/admin/users/reject-bulk.json", params: {
+        users: [reject_me.id]
+      }
+      expect(response.status).to eq(404)
+      expect(User.where(id: reject_me.id).count).to eq(1)
+    end
+
+    it "reports successes" do
+      delete "/admin/users/reject-bulk.json", params: {
+        users: [reject_me.id, reject_me_too.id]
+      }
+
+      expect(response.status).to eq(200)
+      json = ::JSON.parse(response.body)
+      expect(json['success'].to_i).to eq(2)
+      expect(json['failed'].to_i).to eq(0)
+      expect(User.where(id: reject_me.id).count).to eq(0)
+      expect(User.where(id: reject_me_too.id).count).to eq(0)
+    end
+
+    context 'failures' do
+      it 'can handle some successes and some failures' do
+        stat = reject_me_too.user_stat
+        stat.first_post_created_at = (SiteSetting.delete_user_max_post_age.to_i + 1).days.ago
+        stat.post_count = 10
+        stat.save!
+
+        delete "/admin/users/reject-bulk.json", params: {
+          users: [reject_me.id, reject_me_too.id]
+        }
+
+        expect(response.status).to eq(200)
+        json = ::JSON.parse(response.body)
+        expect(json['success'].to_i).to eq(1)
+        expect(json['failed'].to_i).to eq(1)
+        expect(User.where(id: reject_me.id).count).to eq(0)
+        expect(User.where(id: reject_me_too.id).count).to eq(1)
+      end
+
+      it 'reports failure due to a user still having posts' do
+        Fabricate(:post, user: reject_me)
+
+        delete "/admin/users/reject-bulk.json", params: {
+          users: [reject_me.id]
+        }
+
+        expect(response.status).to eq(200)
+        json = ::JSON.parse(response.body)
+        expect(json['success'].to_i).to eq(0)
+        expect(json['failed'].to_i).to eq(1)
+        expect(User.where(id: reject_me.id).count).to eq(1)
+      end
+    end
+  end
+
+  describe '#ip_info' do
+    it "uses ipinfo.io webservice to retrieve the info" do
+      ip = "192.168.1.1"
+      ip_data = {
+        city: "Jeddah",
+        country: "SA",
+        ip: ip
+      }
+      url = "https://ipinfo.io/#{ip}/json"
+
+      stub_request(:get, url).to_return(status: 200, body: ip_data.to_json)
+      get "/admin/users/ip-info.json", params: { ip: ip }
+      expect(response.status).to eq(200)
+      expect(JSON.parse(response.body).symbolize_keys).to eq(ip_data)
+    end
+  end
+
+  describe '#delete_other_accounts_with_same_ip' do
+    it "works" do
+      user_a = Fabricate(:user, ip_address: "42.42.42.42")
+      user_b = Fabricate(:user, ip_address: "42.42.42.42")
+
+      delete "/admin/users/delete-others-with-same-ip.json", params: {
+        ip: "42.42.42.42", exclude: -1, order: "trust_level DESC"
+      }
+      expect(response.status).to eq(200)
+      expect(User.where(id: user_a.id).count).to eq(0)
+      expect(User.where(id: user_b.id).count).to eq(0)
+    end
+  end
+
+  describe '#invite_admin' do
+    let(:api_key) { Fabricate(:api_key, user: admin, key: SecureRandom.hex) }
+    let(:api_params) do
+      { api_key: api_key.key, api_username: admin.username }
+    end
+
+    it "doesn't work when not via API" do
+      post "/admin/users/invite_admin.json", params: {
+        name: 'Bill', username: 'bill22', email: 'bill@bill.com'
+      }
+
+      expect(response.status).to eq(403)
+    end
+
+    it 'should invite admin' do
+      expect do
+        post "/admin/users/invite_admin.json", params: api_params.merge(
+          name: 'Bill', username: 'bill22', email: 'bill@bill.com'
+        )
+      end.to change { Jobs::CriticalUserEmail.jobs.size }.by(1)
+
+      expect(response.status).to eq(200)
+
+      u = User.find_by_email('bill@bill.com')
+      expect(u.name).to eq("Bill")
+      expect(u.username).to eq("bill22")
+      expect(u.admin).to eq(true)
+    end
+
+    it "doesn't send the email with send_email falsey" do
+      expect do
+        post "/admin/users/invite_admin.json", params: api_params.merge(
+          name: 'Bill', username: 'bill22', email: 'bill@bill.com', send_email: '0'
+        )
+      end.to change { Jobs::CriticalUserEmail.jobs.size }.by(0)
+
+      expect(response.status).to eq(200)
+      json = ::JSON.parse(response.body)
+      expect(json["password_url"]).to be_present
+    end
+  end
+
+  describe '#sync_sso' do
+    let(:sso) { SingleSignOn.new }
+    let(:sso_secret) { "sso secret" }
+
+    before do
+      SiteSetting.email_editable = false
+      SiteSetting.sso_url = "https://www.example.com/sso"
+      SiteSetting.enable_sso = true
+      SiteSetting.sso_overrides_email = true
+      SiteSetting.sso_overrides_name = true
+      SiteSetting.sso_overrides_username = true
+      SiteSetting.sso_secret = sso_secret
+      sso.sso_secret = sso_secret
+    end
+
+    it 'can sync up with the sso' do
+      sso.name = "Bob The Bob"
+      sso.username = "bob"
+      sso.email = "bob@bob.com"
+      sso.external_id = "1"
+
+      user = DiscourseSingleSignOn.parse(sso.payload).lookup_or_create_user
+
+      sso.name = "Bill"
+      sso.username = "Hokli$$!!"
+      sso.email = "bob2@bob.com"
+
+      post "/admin/users/sync_sso.json", params: Rack::Utils.parse_query(sso.payload)
+      expect(response.status).to eq(200)
+
+      user.reload
+      expect(user.email).to eq("bob2@bob.com")
+      expect(user.name).to eq("Bill")
+      expect(user.username).to eq("Hokli")
+    end
+
+    it 'should create new users' do
+      sso.name = "Dr. Claw"
+      sso.username = "dr_claw"
+      sso.email = "dr@claw.com"
+      sso.external_id = "2"
+      post "/admin/users/sync_sso.json", params: Rack::Utils.parse_query(sso.payload)
+      expect(response.status).to eq(200)
+
+      user = User.find_by_email('dr@claw.com')
+      expect(user).to be_present
+      expect(user.ip_address).to be_blank
+    end
+
+    it 'should return the right message if the record is invalid' do
+      sso.email = ""
+      sso.name = ""
+      sso.external_id = "1"
+
+      post "/admin/users/sync_sso.json", params: Rack::Utils.parse_query(sso.payload)
+      expect(response.status).to eq(403)
+      expect(JSON.parse(response.body)["message"]).to include("Primary email can't be blank")
+    end
+  end
+
   describe '#disable_second_factor' do
     let(:second_factor) { user.create_totp }