PageRenderTime 176ms CodeModel.GetById 2ms app.highlight 166ms RepoModel.GetById 1ms app.codeStats 0ms

/spec/controllers/users_controller_spec.rb

https://github.com/devinit/discourse
Ruby | 1221 lines | 1091 code | 130 blank | 0 comment | 6 complexity | ce4488ae7e0d25df4f418f71b558d16b MD5 | raw file
   1require 'spec_helper'
   2
   3describe UsersController do
   4
   5  describe '.show' do
   6    let!(:user) { log_in }
   7
   8    it 'returns success' do
   9      xhr :get, :show, username: user.username, format: :json
  10      response.should be_success
  11      json = JSON.parse(response.body)
  12
  13      json["user"]["has_title_badges"].should == false
  14
  15    end
  16
  17    it "returns not found when the username doesn't exist" do
  18      xhr :get, :show, username: 'madeuppity'
  19      response.should_not be_success
  20    end
  21
  22    it 'returns not found when the user is inactive' do
  23      inactive = Fabricate(:user, active: false)
  24      xhr :get, :show, username: inactive.username
  25      response.should_not be_success
  26    end
  27
  28    it "raises an error on invalid access" do
  29      Guardian.any_instance.expects(:can_see?).with(user).returns(false)
  30      xhr :get, :show, username: user.username
  31      response.should be_forbidden
  32    end
  33
  34    context "fetching a user by external_id" do
  35      before { user.create_single_sign_on_record(external_id: '997', last_payload: '') }
  36
  37      it "returns fetch for a matching external_id" do
  38        xhr :get, :show, external_id: '997'
  39        response.should be_success
  40      end
  41
  42      it "returns not found when external_id doesn't match" do
  43        xhr :get, :show, external_id: '99'
  44        response.should_not be_success
  45      end
  46    end
  47  end
  48
  49  describe '.user_preferences_redirect' do
  50    it 'requires the user to be logged in' do
  51      lambda { get :user_preferences_redirect }.should raise_error(Discourse::NotLoggedIn)
  52    end
  53
  54    it "redirects to their profile when logged in" do
  55      user = log_in
  56      get :user_preferences_redirect
  57      response.should redirect_to("/users/#{user.username_lower}/preferences")
  58    end
  59  end
  60
  61  describe '.authorize_email' do
  62    it 'errors out for invalid tokens' do
  63      get :authorize_email, token: 'asdfasdf'
  64      response.should be_success
  65      flash[:error].should be_present
  66    end
  67
  68    context 'valid token' do
  69      it 'authorizes with a correct token' do
  70        user = Fabricate(:user)
  71        email_token = user.email_tokens.create(email: user.email)
  72
  73        get :authorize_email, token: email_token.token
  74        response.should be_success
  75        flash[:error].should be_blank
  76        session[:current_user_id].should be_present
  77      end
  78    end
  79  end
  80
  81  describe '.activate_account' do
  82    before do
  83      UsersController.any_instance.stubs(:honeypot_or_challenge_fails?).returns(false)
  84    end
  85
  86    context 'invalid token' do
  87      before do
  88        EmailToken.expects(:confirm).with('asdfasdf').returns(nil)
  89        put :perform_account_activation, token: 'asdfasdf'
  90      end
  91
  92      it 'return success' do
  93        response.should be_success
  94      end
  95
  96      it 'sets a flash error' do
  97        flash[:error].should be_present
  98      end
  99    end
 100
 101    context 'valid token' do
 102      let(:user) { Fabricate(:user) }
 103
 104      context 'welcome message' do
 105        before do
 106          EmailToken.expects(:confirm).with('asdfasdf').returns(user)
 107        end
 108
 109        it 'enqueues a welcome message if the user object indicates so' do
 110          user.send_welcome_message = true
 111          user.expects(:enqueue_welcome_message).with('welcome_user')
 112          put :perform_account_activation, token: 'asdfasdf'
 113        end
 114
 115        it "doesn't enqueue the welcome message if the object returns false" do
 116          user.send_welcome_message = false
 117          user.expects(:enqueue_welcome_message).with('welcome_user').never
 118          put :perform_account_activation, token: 'asdfasdf'
 119        end
 120      end
 121
 122      context "honeypot" do
 123        it "raises an error if the honeypot is invalid" do
 124          UsersController.any_instance.stubs(:honeypot_or_challenge_fails?).returns(true)
 125          put :perform_account_activation, token: 'asdfasdf'
 126          response.should_not be_success
 127        end
 128      end
 129
 130      context 'response' do
 131        before do
 132          Guardian.any_instance.expects(:can_access_forum?).returns(true)
 133          EmailToken.expects(:confirm).with('asdfasdf').returns(user)
 134          put :perform_account_activation, token: 'asdfasdf'
 135        end
 136
 137        it 'returns success' do
 138          response.should be_success
 139        end
 140
 141        it "doesn't set an error" do
 142          flash[:error].should be_blank
 143        end
 144
 145        it 'logs in as the user' do
 146          session[:current_user_id].should be_present
 147        end
 148
 149        it "doesn't set @needs_approval" do
 150          assigns[:needs_approval].should be_blank
 151        end
 152      end
 153
 154      context 'user is not approved' do
 155        before do
 156          Guardian.any_instance.expects(:can_access_forum?).returns(false)
 157          EmailToken.expects(:confirm).with('asdfasdf').returns(user)
 158          put :perform_account_activation, token: 'asdfasdf'
 159        end
 160
 161        it 'returns success' do
 162          response.should be_success
 163        end
 164
 165        it 'sets @needs_approval' do
 166          assigns[:needs_approval].should be_present
 167        end
 168
 169        it "doesn't set an error" do
 170          flash[:error].should be_blank
 171        end
 172
 173        it "doesn't log the user in" do
 174          session[:current_user_id].should be_blank
 175        end
 176      end
 177
 178    end
 179  end
 180
 181  describe '.change_email' do
 182    let(:new_email) { 'bubblegum@adventuretime.ooo' }
 183
 184    it "requires you to be logged in" do
 185      lambda { xhr :put, :change_email, username: 'asdf', email: new_email }.should raise_error(Discourse::NotLoggedIn)
 186    end
 187
 188    context 'when logged in' do
 189      let!(:user) { log_in }
 190
 191      it 'raises an error without an email parameter' do
 192        lambda { xhr :put, :change_email, username: user.username }.should raise_error(ActionController::ParameterMissing)
 193      end
 194
 195      it "raises an error if you can't edit the user's email" do
 196        Guardian.any_instance.expects(:can_edit_email?).with(user).returns(false)
 197        xhr :put, :change_email, username: user.username, email: new_email
 198        response.should be_forbidden
 199      end
 200
 201      context 'when the new email address is taken' do
 202        let!(:other_user) { Fabricate(:coding_horror) }
 203        it 'raises an error' do
 204          lambda { xhr :put, :change_email, username: user.username, email: other_user.email }.should raise_error(Discourse::InvalidParameters)
 205        end
 206
 207        it 'raises an error if there is whitespace too' do
 208          lambda { xhr :put, :change_email, username: user.username, email: other_user.email + ' ' }.should raise_error(Discourse::InvalidParameters)
 209        end
 210      end
 211
 212      context 'when new email is different case of existing email' do
 213        let!(:other_user) { Fabricate(:user, email: 'case.insensitive@gmail.com')}
 214
 215        it 'raises an error' do
 216          lambda { xhr :put, :change_email, username: user.username, email: other_user.email.upcase }.should raise_error(Discourse::InvalidParameters)
 217        end
 218      end
 219
 220      context 'success' do
 221
 222        it 'has an email token' do
 223          lambda { xhr :put, :change_email, username: user.username, email: new_email }.should change(EmailToken, :count)
 224        end
 225
 226        it 'enqueues an email authorization' do
 227          Jobs.expects(:enqueue).with(:user_email, has_entries(type: :authorize_email, user_id: user.id, to_address: new_email))
 228          xhr :put, :change_email, username: user.username, email: new_email
 229        end
 230      end
 231    end
 232
 233  end
 234
 235  describe '.password_reset' do
 236    let(:user) { Fabricate(:user) }
 237
 238    context "you can view it even if login is required" do
 239      it "returns success" do
 240        SiteSetting.login_required = true
 241        get :password_reset, token: 'asdfasdf'
 242        response.should be_success
 243      end
 244    end
 245
 246    context 'invalid token' do
 247      before do
 248        get :password_reset, token: SecureRandom.hex
 249      end
 250
 251      it 'disallows login' do
 252        flash[:error].should be_present
 253        session[:current_user_id].should be_blank
 254        response.should be_success
 255      end
 256
 257    end
 258
 259    context 'valid token' do
 260      it 'returns success' do
 261        user = Fabricate(:user)
 262        token = user.email_tokens.create(email: user.email).token
 263
 264        get :password_reset, token: token
 265        put :password_reset, token: token, password: 'newpassword'
 266        response.should be_success
 267        flash[:error].should be_blank
 268      end
 269    end
 270
 271    context 'submit change' do
 272      before do
 273        EmailToken.expects(:confirm).with('asdfasdf').returns(user)
 274      end
 275
 276      it "logs in the user" do
 277        put :password_reset, token: 'asdfasdf', password: 'newpassword'
 278        session[:current_user_id].should be_present
 279      end
 280
 281      it "doesn't log in the user when not approved" do
 282        SiteSetting.expects(:must_approve_users?).returns(true)
 283        put :password_reset, token: 'asdfasdf', password: 'newpassword'
 284        session[:current_user_id].should be_blank
 285      end
 286    end
 287  end
 288
 289  describe '#create' do
 290
 291    before do
 292      UsersController.any_instance.stubs(:honeypot_value).returns(nil)
 293      UsersController.any_instance.stubs(:challenge_value).returns(nil)
 294      SiteSetting.stubs(:allow_new_registrations).returns(true)
 295      @user = Fabricate.build(:user)
 296      @user.password = "strongpassword"
 297    end
 298
 299    def post_user
 300      xhr :post, :create,
 301        name: @user.name,
 302        username: @user.username,
 303        password: "strongpassword",
 304        email: @user.email
 305    end
 306
 307    context 'when creating a non active user (unconfirmed email)' do
 308
 309      it 'returns a 500 when local logins are disabled' do
 310        SiteSetting.expects(:enable_local_logins).returns(false)
 311        post_user
 312
 313        expect(response.status).to eq(500)
 314      end
 315
 316      it 'returns an error when new registrations are disabled' do
 317        SiteSetting.stubs(:allow_new_registrations).returns(false)
 318        post_user
 319        json = JSON.parse(response.body)
 320        json['success'].should be_false
 321        json['message'].should be_present
 322      end
 323
 324      it 'creates a user correctly' do
 325        Jobs.expects(:enqueue).with(:user_email, has_entries(type: :signup))
 326        User.any_instance.expects(:enqueue_welcome_message).with('welcome_user').never
 327
 328        post_user
 329
 330        expect(JSON.parse(response.body)['active']).to be_false
 331      end
 332
 333      context "and 'must approve users' site setting is enabled" do
 334        before { SiteSetting.expects(:must_approve_users).returns(true) }
 335
 336        it 'does not enqueue an email' do
 337          Jobs.expects(:enqueue).never
 338          post_user
 339        end
 340
 341        it 'does not login the user' do
 342          post_user
 343          expect(session[:current_user_id]).to be_blank
 344        end
 345
 346        it 'indicates the user is not active in the response' do
 347          post_user
 348          expect(JSON.parse(response.body)['active']).to be_false
 349        end
 350
 351        it "shows the 'waiting approval' message" do
 352          post_user
 353          expect(JSON.parse(response.body)['message']).to eq(
 354            I18n.t 'login.wait_approval'
 355          )
 356        end
 357      end
 358    end
 359
 360    context 'when creating an active user (confirmed email)' do
 361      before { User.any_instance.stubs(:active?).returns(true) }
 362
 363      it 'enqueues a welcome email' do
 364        User.any_instance.expects(:enqueue_welcome_message).with('welcome_user')
 365        post_user
 366      end
 367
 368      it "shows the 'active' message" do
 369        User.any_instance.expects(:enqueue_welcome_message)
 370        post_user
 371        expect(JSON.parse(response.body)['message']).to eq(
 372          I18n.t 'login.active'
 373        )
 374      end
 375
 376      it "should be logged in" do
 377        User.any_instance.expects(:enqueue_welcome_message)
 378        post_user
 379        session[:current_user_id].should be_present
 380      end
 381
 382      it 'indicates the user is active in the response' do
 383        User.any_instance.expects(:enqueue_welcome_message)
 384        post_user
 385        expect(JSON.parse(response.body)['active']).to be_true
 386      end
 387
 388      it 'returns 500 status when new registrations are disabled' do
 389        SiteSetting.stubs(:allow_new_registrations).returns(false)
 390        post_user
 391        json = JSON.parse(response.body)
 392        json['success'].should be_false
 393        json['message'].should be_present
 394      end
 395
 396      context 'authentication records for' do
 397
 398        before do
 399          SiteSetting.expects(:must_approve_users).returns(true)
 400        end
 401
 402        it 'should create twitter user info if required' do
 403          SiteSetting.stubs(:enable_twitter_logins?).returns(true)
 404          twitter_auth = { twitter_user_id: 42, twitter_screen_name: "bruce" }
 405          auth = session[:authentication] = {}
 406          auth[:authenticator_name] = 'twitter'
 407          auth[:extra_data] = twitter_auth
 408          TwitterUserInfo.expects(:create)
 409
 410          post_user
 411        end
 412      end
 413    end
 414
 415    context 'after success' do
 416      before { post_user }
 417
 418      it 'should succeed' do
 419        should respond_with(:success)
 420      end
 421
 422      it 'has the proper JSON' do
 423        json = JSON::parse(response.body)
 424        json["success"].should be_true
 425      end
 426
 427      it 'should not result in an active account' do
 428        User.find_by(username: @user.username).active.should be_false
 429      end
 430    end
 431
 432    shared_examples 'honeypot fails' do
 433      it 'should not create a new user' do
 434        expect {
 435          xhr :post, :create, create_params
 436        }.to_not change { User.count }
 437      end
 438
 439      it 'should not send an email' do
 440        User.any_instance.expects(:enqueue_welcome_message).never
 441        xhr :post, :create, create_params
 442      end
 443
 444      it 'should say it was successful' do
 445        xhr :post, :create, create_params
 446        json = JSON::parse(response.body)
 447        json["success"].should be_true
 448      end
 449    end
 450
 451    context 'when honeypot value is wrong' do
 452      before do
 453        UsersController.any_instance.stubs(:honeypot_value).returns('abc')
 454      end
 455      let(:create_params) { {name: @user.name, username: @user.username, password: "strongpassword", email: @user.email, password_confirmation: 'wrong'} }
 456      include_examples 'honeypot fails'
 457    end
 458
 459    context 'when challenge answer is wrong' do
 460      before do
 461        UsersController.any_instance.stubs(:challenge_value).returns('abc')
 462      end
 463      let(:create_params) { {name: @user.name, username: @user.username, password: "strongpassword", email: @user.email, challenge: 'abc'} }
 464      include_examples 'honeypot fails'
 465    end
 466
 467    context "when 'invite only' setting is enabled" do
 468      before { SiteSetting.expects(:invite_only?).returns(true) }
 469
 470      let(:create_params) {{
 471        name: @user.name,
 472        username: @user.username,
 473        password: 'strongpassword',
 474        email: @user.email
 475      }}
 476
 477      include_examples 'honeypot fails'
 478    end
 479
 480    shared_examples 'failed signup' do
 481      it 'should not create a new User' do
 482        expect { xhr :post, :create, create_params }.to_not change { User.count }
 483      end
 484
 485      it 'should report failed' do
 486        xhr :post, :create, create_params
 487        json = JSON::parse(response.body)
 488        json["success"].should_not be_true
 489      end
 490    end
 491
 492    context 'when password is blank' do
 493      let(:create_params) { {name: @user.name, username: @user.username, password: "", email: @user.email} }
 494      include_examples 'failed signup'
 495    end
 496
 497    context 'when password param is missing' do
 498      let(:create_params) { {name: @user.name, username: @user.username, email: @user.email} }
 499      include_examples 'failed signup'
 500    end
 501
 502    context 'when an Exception is raised' do
 503
 504      [ ActiveRecord::StatementInvalid,
 505        RestClient::Forbidden ].each do |exception|
 506        before { User.any_instance.stubs(:save).raises(exception) }
 507
 508        let(:create_params) {
 509          { name: @user.name, username: @user.username,
 510            password: "strongpassword", email: @user.email}
 511        }
 512
 513        include_examples 'failed signup'
 514      end
 515    end
 516
 517  end
 518
 519  context '.username' do
 520    it 'raises an error when not logged in' do
 521      lambda { xhr :put, :username, username: 'somename' }.should raise_error(Discourse::NotLoggedIn)
 522    end
 523
 524    context 'while logged in' do
 525      let!(:user) { log_in }
 526      let(:new_username) { "#{user.username}1234" }
 527
 528      it 'raises an error without a new_username param' do
 529        lambda { xhr :put, :username, username: user.username }.should raise_error(ActionController::ParameterMissing)
 530      end
 531
 532      it 'raises an error when you don\'t have permission to change the username' do
 533        Guardian.any_instance.expects(:can_edit_username?).with(user).returns(false)
 534        xhr :put, :username, username: user.username, new_username: new_username
 535        response.should be_forbidden
 536      end
 537
 538      it 'raises an error when change_username fails' do
 539        User.any_instance.expects(:change_username).with(new_username).returns(false)
 540        lambda { xhr :put, :username, username: user.username, new_username: new_username }.should raise_error(Discourse::InvalidParameters)
 541      end
 542
 543      it 'should succeed when the change_username returns true' do
 544        User.any_instance.expects(:change_username).with(new_username).returns(true)
 545        xhr :put, :username, username: user.username, new_username: new_username
 546        response.should be_success
 547      end
 548
 549    end
 550  end
 551
 552  context '.check_username' do
 553    it 'raises an error without any parameters' do
 554      lambda { xhr :get, :check_username }.should raise_error(ActionController::ParameterMissing)
 555    end
 556
 557    shared_examples 'when username is unavailable' do
 558      it 'should return success' do
 559        response.should be_success
 560      end
 561
 562      it 'should return available as false in the JSON' do
 563        ::JSON.parse(response.body)['available'].should be_false
 564      end
 565
 566      it 'should return a suggested username' do
 567        ::JSON.parse(response.body)['suggestion'].should be_present
 568      end
 569    end
 570
 571    shared_examples 'when username is available' do
 572      it 'should return success' do
 573        response.should be_success
 574      end
 575
 576      it 'should return available in the JSON' do
 577        ::JSON.parse(response.body)['available'].should be_true
 578      end
 579    end
 580
 581    it 'returns nothing when given an email param but no username' do
 582      xhr :get, :check_username, email: 'dood@example.com'
 583      response.should be_success
 584    end
 585
 586    context 'username is available' do
 587      before do
 588        xhr :get, :check_username, username: 'BruceWayne'
 589      end
 590      include_examples 'when username is available'
 591    end
 592
 593    context 'username is unavailable' do
 594      let!(:user) { Fabricate(:user) }
 595      before do
 596        xhr :get, :check_username, username: user.username
 597      end
 598      include_examples 'when username is unavailable'
 599    end
 600
 601    shared_examples 'checking an invalid username' do
 602      it 'should return success' do
 603        response.should be_success
 604      end
 605
 606      it 'should not return an available key' do
 607        ::JSON.parse(response.body)['available'].should be_nil
 608      end
 609
 610      it 'should return an error message' do
 611        ::JSON.parse(response.body)['errors'].should_not be_empty
 612      end
 613    end
 614
 615    context 'has invalid characters' do
 616      before do
 617        xhr :get, :check_username, username: 'bad username'
 618      end
 619      include_examples 'checking an invalid username'
 620
 621      it 'should return the invalid characters message' do
 622        ::JSON.parse(response.body)['errors'].should include(I18n.t(:'user.username.characters'))
 623      end
 624    end
 625
 626    context 'is too long' do
 627      before do
 628        xhr :get, :check_username, username: generate_username(User.username_length.last + 1)
 629      end
 630      include_examples 'checking an invalid username'
 631
 632      it 'should return the "too long" message' do
 633        ::JSON.parse(response.body)['errors'].should include(I18n.t(:'user.username.long', max: User.username_length.end))
 634      end
 635    end
 636
 637    describe 'different case of existing username' do
 638      context "it's my username" do
 639        let!(:user) { Fabricate(:user, username: 'hansolo') }
 640        before do
 641          log_in_user(user)
 642          xhr :get, :check_username, username: 'HanSolo'
 643        end
 644        include_examples 'when username is available'
 645      end
 646
 647      context "it's someone else's username" do
 648        let!(:user) { Fabricate(:user, username: 'hansolo') }
 649        before do
 650          log_in
 651          xhr :get, :check_username, username: 'HanSolo'
 652        end
 653        include_examples 'when username is unavailable'
 654      end
 655
 656      context "an admin changing it for someone else" do
 657        let!(:user) { Fabricate(:user, username: 'hansolo') }
 658        before do
 659          log_in_user(Fabricate(:admin))
 660          xhr :get, :check_username, username: 'HanSolo', for_user_id: user.id
 661        end
 662        include_examples 'when username is available'
 663      end
 664    end
 665  end
 666
 667  describe '#invited' do
 668    it 'returns success' do
 669      user = Fabricate(:user)
 670
 671      xhr :get, :invited, username: user.username
 672
 673      expect(response).to be_success
 674    end
 675
 676    it 'filters by email' do
 677      inviter = Fabricate(:user)
 678      invitee = Fabricate(:user)
 679      invite = Fabricate(
 680        :invite,
 681        email: 'billybob@example.com',
 682        invited_by: inviter,
 683        user: invitee
 684      )
 685      Fabricate(
 686        :invite,
 687        email: 'jimtom@example.com',
 688        invited_by: inviter,
 689        user: invitee
 690      )
 691
 692      xhr :get, :invited, username: inviter.username, filter: 'billybob'
 693
 694      invites = JSON.parse(response.body)['invites']
 695      expect(invites).to have(1).item
 696      expect(invites.first).to include('email' => 'billybob@example.com')
 697    end
 698
 699    it 'filters by username' do
 700      inviter = Fabricate(:user)
 701      invitee = Fabricate(:user, username: 'billybob')
 702      invite = Fabricate(
 703        :invite,
 704        invited_by: inviter,
 705        email: 'billybob@example.com',
 706        user: invitee
 707      )
 708      Fabricate(
 709        :invite,
 710        invited_by: inviter,
 711        user: Fabricate(:user, username: 'jimtom')
 712      )
 713
 714      xhr :get, :invited, username: inviter.username, filter: 'billybob'
 715
 716      invites = JSON.parse(response.body)['invites']
 717      expect(invites).to have(1).item
 718      expect(invites.first).to include('email' => 'billybob@example.com')
 719    end
 720
 721    context 'with guest' do
 722      context 'with pending invites' do
 723        it 'does not return invites' do
 724          inviter = Fabricate(:user)
 725          Fabricate(:invite, invited_by: inviter)
 726
 727          xhr :get, :invited, username: inviter.username
 728
 729          invites = JSON.parse(response.body)['invites']
 730          expect(invites).to be_empty
 731        end
 732      end
 733
 734      context 'with redeemed invites' do
 735        it 'returns invites' do
 736          inviter = Fabricate(:user)
 737          invitee = Fabricate(:user)
 738          invite = Fabricate(:invite, invited_by: inviter, user: invitee)
 739
 740          xhr :get, :invited, username: inviter.username
 741
 742          invites = JSON.parse(response.body)['invites']
 743          expect(invites).to have(1).item
 744          expect(invites.first).to include('email' => invite.email)
 745        end
 746      end
 747    end
 748
 749    context 'with authenticated user' do
 750      context 'with pending invites' do
 751        context 'with permission to see pending invites' do
 752          it 'returns invites' do
 753            user = log_in
 754            inviter = Fabricate(:user)
 755            invite = Fabricate(:invite, invited_by: inviter)
 756            stub_guardian(user) do |guardian|
 757              guardian.stubs(:can_see_invite_details?).
 758                with(inviter).returns(true)
 759            end
 760
 761            xhr :get, :invited, username: inviter.username
 762
 763            invites = JSON.parse(response.body)['invites']
 764            expect(invites).to have(1).item
 765            expect(invites.first).to include("email" => invite.email)
 766          end
 767        end
 768
 769        context 'without permission to see pending invites' do
 770          it 'does not return invites' do
 771            user = log_in
 772            inviter = Fabricate(:user)
 773            invitee = Fabricate(:user)
 774            Fabricate(:invite, invited_by: inviter)
 775            stub_guardian(user) do |guardian|
 776              guardian.stubs(:can_see_invite_details?).
 777                with(inviter).returns(false)
 778            end
 779
 780            xhr :get, :invited, username: inviter.username
 781
 782            json = JSON.parse(response.body)['invites']
 783            expect(json).to be_empty
 784          end
 785        end
 786      end
 787
 788      context 'with redeemed invites' do
 789        it 'returns invites' do
 790          user = log_in
 791          inviter = Fabricate(:user)
 792          invitee = Fabricate(:user)
 793          invite = Fabricate(:invite, invited_by: inviter, user: invitee)
 794
 795          xhr :get, :invited, username: inviter.username
 796
 797          invites = JSON.parse(response.body)['invites']
 798          expect(invites).to have(1).item
 799          expect(invites.first).to include('email' => invite.email)
 800        end
 801      end
 802    end
 803  end
 804
 805  describe '#update' do
 806    context 'with guest' do
 807      it 'raises an error' do
 808        expect do
 809          xhr :put, :update, username: 'guest'
 810        end.to raise_error(Discourse::NotLoggedIn)
 811      end
 812    end
 813
 814    context 'with authenticated user' do
 815      context 'with permission to update' do
 816        it 'allows the update' do
 817          user = Fabricate(:user, name: 'Billy Bob')
 818          log_in_user(user)
 819
 820          put :update, username: user.username, name: 'Jim Tom', custom_fields: {test: :it}
 821
 822          expect(response).to be_success
 823
 824          user.reload
 825
 826          expect(user.name).to eq 'Jim Tom'
 827          expect(user.custom_fields['test']).to eq 'it'
 828        end
 829
 830        it 'returns user JSON' do
 831          user = log_in
 832
 833          put :update, username: user.username
 834
 835          json = JSON.parse(response.body)
 836          expect(json['user']['id']).to eq user.id
 837        end
 838      end
 839
 840      context 'without permission to update' do
 841        it 'does not allow the update' do
 842          user = Fabricate(:user, name: 'Billy Bob')
 843          log_in_user(user)
 844          guardian = Guardian.new(user)
 845          guardian.stubs(:ensure_can_edit!).with(user).raises(Discourse::InvalidAccess.new)
 846          Guardian.stubs(new: guardian).with(user)
 847
 848          put :update, username: user.username, name: 'Jim Tom'
 849
 850          expect(response).to be_forbidden
 851          expect(user.reload.name).not_to eq 'Jim Tom'
 852        end
 853      end
 854    end
 855  end
 856
 857  describe "badge_title" do
 858    let(:user) { Fabricate(:user) }
 859    let(:badge) { Fabricate(:badge) }
 860    let(:user_badge) { BadgeGranter.grant(badge, user) }
 861
 862    it "sets the user's title to the badge name if it is titleable" do
 863      log_in_user user
 864      xhr :put, :badge_title, user_badge_id: user_badge.id, username: user.username
 865      user.reload.title.should_not == badge.name
 866      badge.update_attributes allow_title: true
 867      xhr :put, :badge_title, user_badge_id: user_badge.id, username: user.username
 868      user.reload.title.should == badge.name
 869    end
 870  end
 871
 872  describe "search_users" do
 873
 874    let(:topic) { Fabricate :topic }
 875    let(:user)  { Fabricate :user, username: "joecabot", name: "Lawrence Tierney" }
 876
 877    before do
 878      ActiveRecord::Base.observers.enable :all
 879      Fabricate :post, user: user, topic: topic
 880    end
 881
 882    it "searches when provided the term only" do
 883      xhr :post, :search_users, term: user.name.split(" ").last
 884      response.should be_success
 885      json = JSON.parse(response.body)
 886      json["users"].map { |u| u["username"] }.should include(user.username)
 887    end
 888
 889    it "searches when provided the topic only" do
 890      xhr :post, :search_users, topic_id: topic.id
 891      response.should be_success
 892      json = JSON.parse(response.body)
 893      json["users"].map { |u| u["username"] }.should include(user.username)
 894    end
 895
 896    it "searches when provided the term and topic" do
 897      xhr :post, :search_users, term: user.name.split(" ").last, topic_id: topic.id
 898      response.should be_success
 899      json = JSON.parse(response.body)
 900      json["users"].map { |u| u["username"] }.should include(user.username)
 901    end
 902
 903    context "when `enable_names` is true" do
 904      before do
 905        SiteSetting.enable_names = true
 906      end
 907
 908      it "returns names" do
 909        xhr :post, :search_users, term: user.name
 910        json = JSON.parse(response.body)
 911        json["users"].map { |u| u["name"] }.should include(user.name)
 912      end
 913    end
 914
 915    context "when `enable_names` is false" do
 916      before do
 917        SiteSetting.stubs(:enable_names?).returns(false)
 918      end
 919
 920      it "returns names" do
 921        xhr :post, :search_users, term: user.name
 922        json = JSON.parse(response.body)
 923        json["users"].map { |u| u["name"] }.should_not include(user.name)
 924      end
 925    end
 926
 927  end
 928
 929  describe 'send_activation_email' do
 930    context 'for an existing user' do
 931      let(:user) { Fabricate(:user) }
 932
 933      before do
 934        UsersController.any_instance.stubs(:fetch_user_from_params).returns(user)
 935      end
 936
 937      context 'with a valid email_token' do
 938        it 'should send the activation email' do
 939          Jobs.expects(:enqueue).with(:user_email, has_entries(type: :signup))
 940          xhr :post, :send_activation_email, username: user.username
 941        end
 942      end
 943
 944      context 'without an existing email_token' do
 945        before do
 946          user.email_tokens.each {|t| t.destroy}
 947          user.reload
 948        end
 949
 950        it 'should generate a new token' do
 951          expect {
 952            xhr :post, :send_activation_email, username: user.username
 953          }.to change{ user.email_tokens(true).count }.by(1)
 954        end
 955
 956        it 'should send an email' do
 957          Jobs.expects(:enqueue).with(:user_email, has_entries(type: :signup))
 958          xhr :post, :send_activation_email, username: user.username
 959        end
 960      end
 961    end
 962
 963    context 'when username does not exist' do
 964      it 'should not send an email' do
 965        Jobs.expects(:enqueue).never
 966        xhr :post, :send_activation_email, username: 'nopenopenopenope'
 967      end
 968    end
 969  end
 970
 971  describe '.upload_user_image' do
 972
 973    it 'raises an error when not logged in' do
 974      lambda { xhr :put, :upload_user_image, username: 'asdf' }.should raise_error(Discourse::NotLoggedIn)
 975    end
 976
 977    context 'while logged in' do
 978
 979      let!(:user) { log_in }
 980
 981      let(:logo) { file_from_fixtures("logo.png") }
 982
 983      let(:user_image) do
 984        ActionDispatch::Http::UploadedFile.new({ filename: 'logo.png', tempfile: logo })
 985      end
 986
 987      it 'raises an error without a image_type param' do
 988        lambda { xhr :put, :upload_user_image, username: user.username }.should raise_error(ActionController::ParameterMissing)
 989      end
 990
 991      describe "with uploaded file" do
 992
 993        it 'raises an error when you don\'t have permission to upload an user image' do
 994          Guardian.any_instance.expects(:can_edit?).with(user).returns(false)
 995          xhr :post, :upload_user_image, username: user.username, image_type: "avatar"
 996          response.should be_forbidden
 997        end
 998
 999        it 'rejects large images' do
1000          SiteSetting.stubs(:max_image_size_kb).returns(1)
1001          xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar"
1002          response.status.should eq 422
1003        end
1004
1005        it 'rejects unauthorized images' do
1006          SiteSetting.stubs(:authorized_extensions).returns(".txt")
1007          xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar"
1008          response.status.should eq 422
1009        end
1010
1011        it 'is successful for avatars' do
1012          upload = Fabricate(:upload)
1013          Upload.expects(:create_for).returns(upload)
1014          # enqueues the user_image generator job
1015          xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "avatar"
1016          # returns the url, width and height of the uploaded image
1017          json = JSON.parse(response.body)
1018          json['url'].should == "/uploads/default/1/1234567890123456.png"
1019          json['width'].should == 100
1020          json['height'].should == 200
1021          json['upload_id'].should == upload.id
1022        end
1023
1024        it 'is successful for profile backgrounds' do
1025          upload = Fabricate(:upload)
1026          Upload.expects(:create_for).returns(upload)
1027          xhr :post, :upload_user_image, username: user.username, file: user_image, image_type: "profile_background"
1028          user.reload
1029
1030          user.user_profile.profile_background.should == "/uploads/default/1/1234567890123456.png"
1031
1032          # returns the url, width and height of the uploaded image
1033          json = JSON.parse(response.body)
1034          json['url'].should == "/uploads/default/1/1234567890123456.png"
1035          json['width'].should == 100
1036          json['height'].should == 200
1037        end
1038
1039      end
1040
1041      describe "with url" do
1042        let(:user_image_url) { "http://cdn.discourse.org/assets/logo.png" }
1043
1044        before { UsersController.any_instance.stubs(:is_api?).returns(true) }
1045
1046        describe "correct urls" do
1047
1048          before { FileHelper.stubs(:download).returns(logo) }
1049
1050          it 'rejects large images' do
1051            SiteSetting.stubs(:max_image_size_kb).returns(1)
1052            xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background"
1053            response.status.should eq 422
1054          end
1055
1056          it 'rejects unauthorized images' do
1057            SiteSetting.stubs(:authorized_extensions).returns(".txt")
1058            xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background"
1059            response.status.should eq 422
1060          end
1061
1062          it 'is successful for avatars' do
1063            upload = Fabricate(:upload)
1064            Upload.expects(:create_for).returns(upload)
1065            # enqueues the user_image generator job
1066            xhr :post, :upload_avatar, username: user.username, file: user_image_url, image_type: "avatar"
1067            json = JSON.parse(response.body)
1068            json['url'].should == "/uploads/default/1/1234567890123456.png"
1069            json['width'].should == 100
1070            json['height'].should == 200
1071            json['upload_id'].should == upload.id
1072          end
1073
1074          it 'is successful for profile backgrounds' do
1075            upload = Fabricate(:upload)
1076            Upload.expects(:create_for).returns(upload)
1077            xhr :post, :upload_user_image, username: user.username, file: user_image_url, image_type: "profile_background"
1078            user.reload
1079            user.user_profile.profile_background.should == "/uploads/default/1/1234567890123456.png"
1080
1081            # returns the url, width and height of the uploaded image
1082            json = JSON.parse(response.body)
1083            json['url'].should == "/uploads/default/1/1234567890123456.png"
1084            json['width'].should == 100
1085            json['height'].should == 200
1086          end
1087        end
1088
1089        it "should handle malformed urls" do
1090          xhr :post, :upload_user_image, username: user.username, file: "foobar", image_type: "profile_background"
1091          response.status.should eq 422
1092        end
1093
1094      end
1095
1096    end
1097
1098  end
1099
1100  describe '.pick_avatar' do
1101
1102    it 'raises an error when not logged in' do
1103      lambda { xhr :put, :pick_avatar, username: 'asdf', avatar_id: 1}.should raise_error(Discourse::NotLoggedIn)
1104    end
1105
1106    context 'while logged in' do
1107
1108      let!(:user) { log_in }
1109
1110      it 'raises an error when you don\'t have permission to toggle the avatar' do
1111        another_user = Fabricate(:user)
1112        xhr :put, :pick_avatar, username: another_user.username, upload_id: 1
1113        response.should be_forbidden
1114      end
1115
1116      it 'it successful' do
1117        xhr :put, :pick_avatar, username: user.username, upload_id: 111
1118        user.reload.uploaded_avatar_id.should == 111
1119        response.should be_success
1120
1121        xhr :put, :pick_avatar, username: user.username
1122        user.reload.uploaded_avatar_id.should == nil
1123        response.should be_success
1124      end
1125
1126    end
1127
1128  end
1129
1130  describe '.destroy_user_image' do
1131
1132    it 'raises an error when not logged in' do
1133      lambda { xhr :delete, :destroy_user_image, type: 'profile_background', username: 'asdf' }.should raise_error(Discourse::NotLoggedIn)
1134    end
1135
1136    context 'while logged in' do
1137
1138      let!(:user) { log_in }
1139
1140      it 'raises an error when you don\'t have permission to clear the profile background' do
1141        Guardian.any_instance.expects(:can_edit?).with(user).returns(false)
1142        xhr :delete, :destroy_user_image, username: user.username, image_type: 'profile_background'
1143        response.should be_forbidden
1144      end
1145
1146      it "requires the `image_type` param" do
1147        -> { xhr :delete, :destroy_user_image, username: user.username }.should raise_error(ActionController::ParameterMissing)
1148      end
1149
1150      it "only allows certain `image_types`" do
1151        -> { xhr :delete, :destroy_user_image, username: user.username, image_type: 'wat' }.should raise_error(Discourse::InvalidParameters)
1152      end
1153
1154      it 'can clear the profile background' do
1155        xhr :delete, :destroy_user_image, image_type: 'profile_background', username: user.username
1156        user.reload.user_profile.profile_background.should == ""
1157        response.should be_success
1158      end
1159
1160    end
1161  end
1162
1163  describe '.destroy' do
1164    it 'raises an error when not logged in' do
1165      lambda { xhr :delete, :destroy, username: 'nobody' }.should raise_error(Discourse::NotLoggedIn)
1166    end
1167
1168    context 'while logged in' do
1169      let!(:user) { log_in }
1170
1171      it 'raises an error when you cannot delete your account' do
1172        Guardian.any_instance.stubs(:can_delete_user?).returns(false)
1173        UserDestroyer.any_instance.expects(:destroy).never
1174        xhr :delete, :destroy, username: user.username
1175        response.should be_forbidden
1176      end
1177
1178      it "raises an error when you try to delete someone else's account" do
1179        UserDestroyer.any_instance.expects(:destroy).never
1180        xhr :delete, :destroy, username: Fabricate(:user).username
1181        response.should be_forbidden
1182      end
1183
1184      it "deletes your account when you're allowed to" do
1185        Guardian.any_instance.stubs(:can_delete_user?).returns(true)
1186        UserDestroyer.any_instance.expects(:destroy).with(user, anything).returns(user)
1187        xhr :delete, :destroy, username: user.username
1188        response.should be_success
1189      end
1190    end
1191  end
1192
1193  describe '.my_redirect' do
1194
1195    it "returns 404 if the user is not logged in" do
1196      get :my_redirect, path: "wat"
1197      response.should_not be_success
1198      response.should_not be_redirect
1199    end
1200
1201    context "when the user is logged in" do
1202      let!(:user) { log_in }
1203
1204      it "will not redirect to an invalid path" do
1205        get :my_redirect, path: "wat/..password.txt"
1206        response.should_not be_redirect
1207      end
1208
1209      it "will redirect to an valid path" do
1210        get :my_redirect, path: "preferences"
1211        response.should be_redirect
1212      end
1213
1214      it "permits forward slashes" do
1215        get :my_redirect, path: "activity/posts"
1216        response.should be_redirect
1217      end
1218    end
1219  end
1220
1221end