Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby tutorial Ch9 Exercise #9 - Don't Allow Admin to Delete Themselves

I am a total newbie to Ruby and Rails which is why I am going through the Rails Tutorial by Michael Hartl. I am stuck on Chapter 9, Exercise #9. I have updated the def destroy code in the Users Controller to:

def destroy
  user = User.find(params[:id])
  if (current_user == user) && (current_user.admin?)
    flash[:error] = "Can not delete own admin account!"
  else
    user.destroy
    flash[:success] = "User destroyed."
  end
redirect_to users_path
end

This seems to work when I test in the browser by adding the "delete" link to the current_user when admin is logged in. But the exercise says to write a test first - which I did but does not seem to work. Here is what I have for the test:

describe "as admin user" do
  let(:user_admin) { FactoryGirl.create(:admin) }

  before { sign_in user_admin }

  describe "submitting a DELETE request to destroy own admin account" do
    before { delete user_path(user_admin) }
    it { should have_selector('div.alert.alert-error', text: 'delete own admin') }
  end
end

Maybe what I am testing should not be tested. How do you test the modification of the def destroy code in the Users Controller?

like image 490
Scott Wynn Avatar asked Mar 31 '12 14:03

Scott Wynn


2 Answers

I tried to get the code in the original post to work, but had no luck. Instead, I got it to work like this (got it to fail, then pass). This tests for the proper redirect as well as the correct flash message.

THE TEST : authentication_pages_spec.rb

  describe "as admin user" do
    let(:admin) { FactoryGirl.create(:admin) }
    before { sign_in admin }

    describe "can't delete self by submitting DELETE request to Users#destroy" do
      before { delete user_path(admin) }
      specify { response.should redirect_to(users_path), 
                  flash[:error].should =~ /Can not delete own admin account!/i }
    end
  end

IMPLEMENTATION : Users#destroy

def destroy
    user = User.find(params[:id])
    if (current_user == user) && (current_user.admin?)
      flash[:error] = "Can not delete own admin account!"
    else
      user.destroy
      flash[:success] = "User destroyed."
    end
  redirect_to users_path
  end

Perhaps the reason the original test wasn't working is because the way we're issuing the request? I tried adding each of the following individually to the describe block and all failed:

it { should have_selector('div.alert.alert-error', text: 'delete own admin') }

it { should have_selector('title', text: 'All users') }
it { should have_selector('h1', text: 'All users') }

So, seems like Capybara is not actually redirecting to the page to check for these selectors. I tried 'title' and 'h1', thinking maybe there was some problem with the selector 'div.alert.alert-error' ... but the 'title' and 'h1' failed with same "expected CSS to return something" ...

Anyone know more about how specify { response.should ... } style tests work? If they don't follow redirects when they hit the controller action?

like image 154
Brett Sanders Avatar answered Sep 21 '22 16:09

Brett Sanders


I am also new to the Rails Tutorial (and Rails in general) and had this same problem, and your question helped me figure out the answer.

I am still not certain why your code fails, exactly, but the following steps definitely worked.

First, modify the test code slightly to use the following structure (here I have left out where to put this describe block - you have the right place already):

describe "deleting herself" do
  it "should not be possible" do
    expect { delete user_path(admin) }.to_not change(User, :count).by(-1)
  end
end

Note that I am using an expect{} block to track the number of User objects. This definitely causes the tests to go Red (which is good at this point), whereas checking for the Flash will also make the test go Red, but checking for the error flash doesn't seem to work here. I really don't know why! Maybe something to do with the double redirect that happens?

Next, write the protection code to make the tests green again. Your code works (I think), but I think my code is a bit more idiomatic in that it uses the session helpers defined earlier in Chapter 9.

def destroy
  user = User.find(params[:id])
  if (current_user? user) && (current_user.admin?)
    flash[:error] = "You are not allowed to delete yourself as an admin."
  else
    user.destroy
    flash[:success] = "User destroyed. ID: #{user.id}"
  end
  redirect_to users_path
end

This change made my test go 'green' again, which successfully completes Exercise 10.

like image 20
eblume Avatar answered Sep 19 '22 16:09

eblume