Reputation: 4787
I manually give access to certain Deals to customers I choose on Active Admin panel. It's working "manually" but I don't know how to test that a customer can access his Deal but can't access another customer's deals.
Each deal pages can only be viewed by customer associated with it (owned by them if you want) by using CanCan abilities:
class CustomerAbility
include CanCan::Ability
def initialize(customer)
alias_action :show, :to => :read #this will have no change on the alias :read!
customer ||= Customer.new # guest customer (not logged in)
if customer.has_role? :superadmin
Log.info "Ability: customer is superadmin"
can :manage, :all
else
can :read, Deal do |Deal|
# Only customers who have been granted access in Active Admin to a deal can read
deal.customers.include? customer
end
end
end
end
Note: Customer and Deals have a has_many through relationships (a deal has many customers / a customer has many deals)
Here is the test i have so far but it's not right i think as i'm a TDD newbie:)
require 'spec_helper'
require "cancan/matchers"
describe DealsController do
let(:customer) { FactoryGirl.create(:customer) }
let(:deal) { FactoryGirl.create(:deal, :customers => [customer]) } # in array as a deal has_many customers
context "As signed-in CUSTOMER" do
before do
@customer = FactoryGirl.create(:customer) #the factory builds a basic customer i.e with 'prospect role' attributed by default
@deal = FactoryGirl.create(:deal, :customers => [@customer])
sign_in_customer @customer
end
describe "the customer can read=view the page of a Deal HE OWNS " do
it "can access the page" do
get :deal_page, { :id => @deal.id }
expect(current_path).to eq(???????)
# WHAT TO DO HERE ??????
page.should have_content('here is your deal, dear customer')
end
end
describe "the customer can NOT read the page of a Deal he does not own =owned by other customers and is redirected his customer panel" do
it "can't access the page" do
get :deal_page, { :id => @deal.id }
expect(response).to redirect_to(customer_panel_path)
# WHAT TO DO HERE ??????
flash[:alert].should eql("Sorry but you could not access this page as it is not your Deal!")
end
end
end
The issue is that in the tests where customer SHOULD have access to the deal page, rspec says he does not as he is redirected to the homepage. I know where the problem is: i feel rspec does not know that this created customer is assocciated with this created deal.
And here is where i define the Deal page: controllers/deals_controller.rb
def deal_page
@deal = Deal.find(params[:id])
authorize! :read, @deal # only allow customers with authorized access in AA; sends to customer_ability
respond_to do |format|
format.html
format.json { render json: @deal }
end
end
It seems a pretty basic test: how can i test that a customer can't access another customer pages (with a has_many relationship customer/deals) but i don'"t know how to tackle this issue.
# Edit If it helps to the issue:
appplication_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
# handle Cancan authorization exception
rescue_from CanCan::AccessDenied do |exception|
exception.default_message = t("errors.application_controller_exception_messages.only_open_to_admin")
if current_user # if it's user redirect to main HP
redirect_to root_path, :alert => exception.message
else # if it's a Customer redirect him to client interface HP
redirect_to customer_panel_path, :alert=> exception.message
end
end
def current_ability
@current_ability ||= case
when current_user
UserAbility.new(current_user)
when current_customer
CustomerAbility.new(current_customer)
end
end
EDIT #2
Evidence of this failure is that the following test SUCCEED when is should FAIL:
it "can access the deal page" do
get :deal_page, { :id => @deal.id }
expect(current_path).to eq(customer_panel_path)
end
Edit #3
Using Dave advice, wrote
before do
@customer = FactoryGirl.create(:customer) #the factory builds a basic customer i.e with 'prospect role' attributed by default
@deal = FactoryGirl.create(:deal, :customers => [@customer])
sign_in_customer @customer
end
(...)
it "can access the DEAL he OWNS = HIS deals" do
get :deal_page, { :id => @deal.id }
expect(current_path).to eq(deal_page_path(@deal))
end
but i get error:
DealsController As signed-in CUSTOMER with access to the deal page
Failure/Error: expect(current_path).to eq(deal_page_path(@deal))
expected: "/deals_page/2"
got: "/customer_panel"
(compared using ==)
It's like I don't manage to tell him that the created customer is associated with the created deal so the customer should be able to access it.
Here is the detailed test log:
Deal Exists (0.8ms) SELECT 1 AS one FROM "deals" WHERE LOWER("deals"."deal_code") = LOWER('CHA1FR001') LIMIT 1
SQL (2.1ms) INSERT INTO "deals" ("admin_user_id", "client_contact_point_name", blabla") VALUES ($1, $2, blabla...) RETURNING "id" [["admin_user_id", 1], ["client_contact_point_name", "henri Cool"], ["client_contact_point_profile_url", "http://example.com"], ....blabla...]
(...blabla)
Customer Exists (0.6ms) SELECT 1 AS one FROM "customers" WHERE (LOWER("customers"."email") = LOWER('[email protected]') AND "customers"."id" != 1) LIMIT 1
(...blabla)
Started GET "/customers/signin" for 127.0.0.1 at 2014-05-28 18:37:05 +0200
Processing by Customers::SessionsController#new as HTML
Rendered customers/sessions/new.html.erb within layouts/lightbox (40.0ms)
Rendered layouts/_metas.html.erb (0.4ms)
Rendered layouts/_messages.html.erb (0.7ms)
Rendered layouts/_footer.html.erb (1.2ms)
Completed 200 OK in 77ms (Views: 51.5ms | ActiveRecord: 0.0ms)
Started POST "/customers/signin" for 127.0.0.1 at 2014-05-28 18:37:05 +0200
Processing by Customers::SessionsController#create as HTML
Parameters: {"utf8"=>"✓", "customer"=>{"email"=>"[email protected]", "password"=>"[FILTERED]"}, "commit"=>"Log In"}
Customer Load (4.0ms) SELECT "customers".* FROM "customers" WHERE "customers"."email" = '[email protected]' ORDER BY "customers"."id" ASC LIMIT 1
SQL (1.0ms) UPDATE "customers" SET "remember_created_at" = $1, "updated_at" = $2 WHERE "customers"."id" = 1 [["remember_created_at", 2014-05-28 16:37:05 UTC], ["updated_at", 2014-05-28 18:37:05 +0200]]
SQL (1.2ms) UPDATE "customers" SET "last_sign_in_at" = $1, "current_sign_in_at" = $2, "last_sign_in_ip" = $3, "current_sign_in_ip" = $4, "sign_in_count" = $5, "updated_at" = $6 WHERE "customers"."id" = 1 [["last_sign_in_at", 2014-05-28 16:37:05 UTC], ["current_sign_in_at", 2014-05-28 16:37:05 UTC], ["last_sign_in_ip", "127.0.0.1"], ["current_sign_in_ip", "127.0.0.1"], ["sign_in_count", 1], ["updated_at", 2014-05-28 18:37:05 +0200]]
**Redirected to http://www.example.com/customer_panel**
Completed 302 Found in 33ms (ActiveRecord: 6.2ms)
Started GET "/customer_panel" for 127.0.0.1 at 2014-05-28 18:37:05 +0200
Processing by ClientreportingPagesController#index as HTML
Customer Load (0.5ms) SELECT "customers".* FROM "customers" WHERE "customers"."id" = 1 ORDER BY "customers"."id" ASC LIMIT 1
(1.2ms) SELECT COUNT(*) FROM "roles" INNER JOIN "customers_roles" ON "roles"."id" = "customers_roles"."role_id" WHERE "customers_roles"."customer_id" = $1 AND (((roles.name = 'prospect') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["customer_id", 1]]
Rendered layouts/_metas.html.erb (0.2ms)
(0.8ms) SELECT COUNT(*) FROM "roles" INNER JOIN "customers_roles" ON "roles"."id" = "customers_roles"."role_id" WHERE "customers_roles"."customer_id" = $1 AND (((roles.name = 'superadmin') AND (roles.resource_type IS NULL) AND (roles.resource_id IS NULL))) [["customer_id", 1]]
Rendered layouts/client_interface_partials
Completed 200 OK in 34ms (Views: 27.7ms | ActiveRecord: 2.4ms)
Processing by DealsController#deal_page as HTML
Parameters: {"id"=>"2"}
**Completed 401 Unauthorized in 1ms**
Rendered text template (0.1ms)
(0.5ms) ROLLBACK TO SAVEPOINT active_record_2
(0.3ms) ROLLBACK TO SAVEPOINT active_record_1
(0.3ms) ROLLBACK
2 lines in BOLD seem strange to me:
why does rspec send to example.com/customer_panel (i have in my spec_helper file told rspec that i test locally: Capybara.asset_host = 'http:// localhost:3000') ?
and why does rspec experience a "Completed 401 Unauthorized in 1ms at the end ?
Upvotes: 2
Views: 948
Reputation: 37617
In "the customer can read=view the page of a Deal HE OWNS",
expect(current_path).to eq(deal_path(@deal))
In "the customer can NOT read the page of a Deal he does not own ...", you're logging in as the customer that owns the deal. Make a different customer and log in as that one.
Also, you're not using the customer and deal that you define in the let
statements, so delete those. Or, better, delete the @customer
and @deal
assignments and replace @customer
and @deal
with customer
and deal
.
Upvotes: 1