I am implementing rspec test for destroy
action, the concept is that signed in user can only destroy his own posts, and cannot destroy posts creates by other users.
The `new_post` is created by a user named `creator`, and another user named `user1` signed in and try to delete the `new_post`, it should not be able to delete it, because of the ` before_action :authenticate_user!, only: %i[create destroy]` in Posts controller
class PostsController < ApplicationController
before_action :set_post, only: %i[show edit update destroy]
before_action :current_user, only: %i[create destroy]
before_action :authenticate_user!, only: %i[create destroy]
.
.
.
def destroy
@post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:content, :picture)
end
end
require 'rails_helper'
RSpec.describe PostsController, type: :controller do
context 'DELETE #destroy' do
let(:user1) {User.create!(name:"John", email:"john@mail.com", password:"password")}
let(:creator) { User.create!(name: "creator", email: "creaor@gmail.com", password: "password") }
let(:new_post){creator.posts.create!(content: "Neque porro quisquam est qui dolorem ipsum")}
it 'A user cannot delete a post created by other user' do
sign_in user1
p (new_post)
expect { delete :destroy, params: { id: new_post.id } }.to change(Post, :count).by(0)
end
end
end
1) PostsController DELETE #destroy A user cannot delete a post created by other user
Failure/Error: expect { delete :destroy, params: { id: new_post.id } }.to change(Post, :count).by(0)
expected `Post.count` to have changed by 0, but was changed by -1
I believe you need to add an authorization check to your code. authenticate_user!
authenticates that the person making the request is logged in. However, it does not check if the user is authorized to make the request they're making.
See Authentication versus Authorization for a bit more discussion on the two concepts. And take a look at https://stackoverflow.com/a/25654844/868533 for a good overview of popular authorization gems in Rails. To be clear, you almost definitely want a way to authenticate users (Devise
) along with an authorization gem.
Assuming you decide to go with CanCanCan
(which is a common option that I've used in past), you'd add an Ability
class like:
class Ability
include CanCan::Ability
def initialize(user)
if user.present?
can :destroy, Post, user_id: user.id
end
end
end
Then you could add before_action :check_authorization, only: %i[destroy]
as a new before_action
on your controller and your tests should pass without any modification.