Rails DESTROY action on nested resource without ID - ruby-on-rails

I have the following resource set up in my routes.rb file:
namespace :api, path: '/', defaults: { format: 'json' }, constraints: { subdomain: 'api' } do
namespace :v1 do
resources :yums do
resources :likes, only: [:create, :destroy, :index] do
match '/', to: 'likes#destroy', via: 'delete'
end
end
end
end
I also wrote a spec to test my requests:
describe "Like API requests" do
before (:each) do
host! 'api.example.com'
#user = FactoryGirl.create(:user)
#other_user = FactoryGirl.create(:user)
#yum = FactoryGirl.create(:yum, user: #other_user)
end
describe "liking a Yum" do
it "should increase the Yum's Like count" do
expect do
post "/v1/yums/#{#yum.id}/likes", { authorization: token_header(#user.auth_token) }
end.to change(#yum.reload.likes, :count).by(1)
end
end
describe "unliking a Yum" do
it "should increase the Yum's Like count" do
expect do
delete "/v1/yums/#{#yum.id}/likes", { authorization: token_header(#user.auth_token) }
end.to change(#yum.reload.likes, :count).by(-1)
end
end
end
I want the Likes controller to not require an ID for the destroy action since I use methods on my user model to like and unlike, but this scheme doesn't seem to be working, what am I doing wrong?

Try to add a route in your routes.rb like :
match "v1/yums/:yum_id/likes", to: "likes#destroy", via: "delete", defaults: { id: nil }

Related

Rails Api test UrlGenerationError

I am building an API and, upon writing the tests, I run into a strange UrlGenerator Error.
I have an API on version one and this is my Users controller.
class Api::V1::UsersController < ApplicationController
respond_to :json
def show
respond_with User.find(params[:id])
end
end
Here is the spec for that users controller
require 'rails_helper'
RSpec.describe Api::V1::UsersController, type: :controller do
before(:each) { request.headers['Accept'] = "application/vnd.marketplace.v1" }
describe "GET #show" do
before(:each) do
#user = FactoryBot.create :user
get :show, format: :json
end
it "returns the information about a reporter on a hash" do
user_response = JSON.parse(response.body, symbolize_names: true)
expect(user_response[:email]).to eql #user.email
end
it { should respond_with 200 }
end
end
When I run this spec I get the following error message: `Failure/Error: get :show, format: :json
ActionController::UrlGenerationError:
No route matches {:action=>"show", :controller=>"api/v1/users", :format=>:json}`
I have only one route for my API:
api_user GET /users/:id(.:format) api/v1/users#show {:subdomain=>"api", :format=>:json}
Does anybody know why I would be getting this error? It seems to me that, based on the route returned from the api routes list, this should be working. My routes.rb file is listed below:
namespace :api, defaults: { format: :json }, constraints: { subdomain: 'api' }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users, :only => [:show]
end
end
The problem is that the show route that you have defined requires an :id parameter, but the call for get :show from the test does not send it.
From Rspec, you can send the id with something like:
get :show, params: { id: #user.id }, format: :json

How to test session constrained routes with rspec routing specs?

I have such code in my routes.rb:
Rails.application.routes.draw do
authorized = ->(request) { request.session[:user_id].present? }
not_authorized = ->(request) { request.session[:user_id].blank? }
constraints authorized do
resources :users
end
constraints not_authorized do
get 'login' => 'auth#login_page'
post 'login' => 'auth#create_session'
get '*unmatched_route', to: 'auth#login_page'
root 'auth#login_page'
end
end
And I have such users_routing_spec.rb file:
require "rails_helper"
RSpec.describe UsersController, type: :routing do
describe "routing" do
it "routes to #index" do
expect(:get => "/users").to route_to("users#index")
end
end
end
This test fails as it routes to 'auth#login_page' because there is no user_id
in session.
How can I call auth#create_session in advance to the expectation?
There is no request or #request object and I also can't make a manual request to 'auth#create_session'

Functional test for scoped controller can't find the right route

My routes.rb is :
Rails.application.routes.draw do
namespace :api, path: "/", constraint: { subdomain: "api" }, defaults: { format: :json } do
scope module: :v1 do
resources :users, only: [:show]
end
end
end
My users_controller.rb is :
class Api::V1::UsersController < ApplicationController
respond_to :json
def show
respond_with User.find(params[:id])
end
end
My test is :
require 'test_helper'
class Api::V1::UsersControllerTest < ActionController::TestCase
test '#show displays the specific user' do
#user = FactoryGirl.create(:user)
get :show, id: #user.id, format: :json
assert_response :success
end
end
Why does this test give the following error:
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"api/v1/users", :format=>:json, :id=>1}
I just needed to add the constraint to the test:
get :show, id: #user.id, format: :json, :constraint=>{:subdomain=>"api"}

Undefined method route_name in Rspec controller test

When running my controller test, I get this error:
NoMethodError:
undefined method `api_challenge_url' for #<Api::V1::ChallengesController:0x007f829b233460>
Which, is in fact, not a route that exists. My routes file looks like this:
namespace :api, { format: :json, constraints: { subdomain: 'api' }, path: '/'} do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
resources :users, only: [:show, :create, :update, :destroy] do
resources :challenges, only: [:create, :show]
end
end
end
And my controller test looks like this:
RSpec.describe Api::V1::ChallengesController, type: :controller do
describe "POST #create" do
context "when successfully created" do
before(:each) do
#user = FactoryGirl.create(:user)
#challenge_attributes = FactoryGirl.attributes_for(:challenge)
post :create, user_id: #user.id, challenge: #challenge_attributes, format: :json
end
it "should render the JSON for the created challenge" do
challenge_response = JSON.parse(response.body, symbolize_names: true)
expect challenge_response[:description].to eql #challenge_attributes["description"]
expect challenge_response[:title].to eql #challenge_attributes["title"]
end
end
end
end
But for the life of me, I can't why it's calling the wrong route name. The output of the relevant part of the rake routes looks like this:
api_user_challenges POST /users/:user_id/challenges(.:format) api/v1/challenges#create {:subdomain=>"api"}
I've tried a few different formats in the post method, is there some idiomatic way of doing this that I'm missing?
Try adding some configuration to include url helpers into your test suite:
RSpec.configure do |c|
c.include Rails.application.routes.url_helpers
# Other configurations ...
end
And if you prefer using xxx_url over xxx_path, remember to config action_controller.default_url_options in your config/environments/test.rb, for example:
config.action_controller.default_url_options = {
host: 'www.mysite.org',
protocol: 'https'
}

RSpec returning 'undefined local variable or method `signin_path''

When I run RSpec I am getting 4 errors all stating:
undefined local variable or method `signin_path' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x4203fa8>
But I am running other tests that use this helper and path with no problem, it is only in the relationship controller tests that do not work with it.
relationship_controller_spec.rb
require 'spec_helper'
describe RelationshipsController do
let(:user) { FactoryGirl.create(:user) }
let(:other_user) { FactoryGirl.create(:user) }
before { sign_in user }
describe "creating a relationship with Ajax" do
it "should increment the Relationship count" do
expect do
xhr :post, :create, relationship: { followed_id: other_user.id }
end.to change(Relationship, :count).by(1)
end
it "should respond with success" do
xhr :post, :create, relationship: { followed_id: other_user.id }
response.should be_success
end
end
describe "destroying a relationship with Ajax" do
before { user.follow!(other_user) }
let(:relationship) { user.relationships.find_by_followed_id(other_user) }
it "should decrement the Relationship count" do
expect do
xhr :delete, :destroy, id: relationship.id
end.to change(Relationship, :count).by(-1)
end
it "should respond with success" do
xhr :delete, :destroy, id: relationship.id
response.should be_success
end
end
end
Utilities.rb:
def sign_in(user)
visit signin_path
fill_in 'Email', with: user.email.upcase
fill_in 'Password', with: user.password
click_button 'Sign In'
cookies[:remember_token] = user.remember_token
end
routes.rb:
resources :users do
member do
get :following, :followers
end
end
resources :sessions, only: [:create, :destroy, :new]
resources :microposts, only: [:create, :destroy]
resources :relationships, only: [:create, :destroy]
# Application Root
root to: 'static_pages#home'
# Static Pages
match '/help', to: 'static_pages#help'
match '/about', to: 'static_pages#about'
match '/contact', to: 'static_pages#contact'
# Users
match '/signup', to: 'users#new'
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
Controller specs cannot interact with pages in the same way that feature specs do, so your sign_in utility will not work for them.
If your controller specs rely on a signed-in user, you will need to stub your current_user method. If you use Devise, here's a helpful page from their wiki: https://github.com/plataformatec/devise/wiki/How-To:-Stub-authentication-in-controller-specs

Resources