Setting http status codes - ruby-on-rails

redirect_to browse_path(asset.parent_id), notice: "successfully created file!", status: 201
201 is the status you should set if you've created a resource. While the above method works for a create action, the spec for its action no longer does:
subject { response }
describe '.create' do
context 'when orphan' do
before do
post :create, asset: { parent_id: nil, uploaded_file: file }
end
it { should have_http_status 201 }
it { should redirect_to '/' }
end
end
The status expectation passes, but the redirect_to expectation fails:
Expected response to be a <redirect>, but was <201>
I accept that it's no longer a 302 redirect, but it does still redirect the user to a new route (which I want to test). The redirect_to spec passes if I set it to the "wrong" code of 302, rather than 201:
redirect_to browse_path(asset.parent_id), notice: "successfully created file!", status: 302
so should I bother with setting status codes? I'll admit I actually have no idea how the browser uses them and my applications functions just as well if I painstakingly set them in my actions or don't (just use 302 redirects and 200 successes).
If status codes are important, how should I get my above specs to pass?

From the docs:
The status code can either be a standard HTTP Status code as an
integer, or a symbol representing the downcased, underscored and
symbolized description. Note that the status code must be a 3xx HTTP
code, or redirection will not occur.
(emphasis added)
Simply put, it is an error to redirect with a 201 status code.

You can assert off response.body or other response attributes within rspec. In this case the thing you are after is response.header["Location"]
You can choose to dodge the problem with capybara/rspec where you can assert current_url and still assert the status code.
redirect_to is just a dumb mid level helper and you need to reach to a slightly lower level in response.something or higher level with capybara to get where you want to be.

One way is this:
its(:status){ should eq 201 }
its(:location){ should eq 'http://test.host/' }

Related

Rails isn't returning from this action

The problem I have is very simple. Basically, I have a Rails action for my videos page. It makes a HTTP request and gets some response back. But I am trying to add error checking right now. My problem is it will NOT LEAVE this action after entering the if block. It appears to continue to try to run all the code after the if block... Which is bad because if it enters the if block, it means we didn't get a 200 OK response. There's nothing to run. Just throw an error message and yeet!
It is entering the if block (Gyazo link here).
def videos
# get current_user's wistia_project_id & authorization token
#current_user = current_user
project_id = #current_user.wistia_project_id
auth_token = "blah"
request = "https://api.wistia.com/v1/projects/#{project_id}.json?api_password=#{auth_token}"
#response = HTTP.get(request)
puts "BEFORE"
# handle errors (4xx & 5xx)
# catches client errors and server errors
# should print out and then LEAVE this entire action. (return)
if #response.status.client_error? || #response.status.server_error?
puts "INSIDE"
render text: "Sorry, error"
return
end
#response = HTTP.get(request).body
# get embed code for each video using the hashed_id, put in list
# BUT!!! for some reason it gets here and there's an error
# because there is nothing in the response (errors)
#video_iframe_urls = JSON.parse(#response)['medias'].map do |p|
"https://fast.wistia.com/embed/iframe/#{p["hashed_id"]}?version=v1&controlsVisibleOnLoad=true&playerColor=aae3d8"
end
end
render text: "Sorry, error"
change this line to plain
render plain: "Sorry, error"
the problem is not
it will NOT LEAVE this action after entering the if block.
because render is not support text options.you code actually is
if #response.status.client_error? || #response.status.server_error?
puts "INSIDE"
render
return
end
the render method render template name by action name as default

Difference between response and last_response in RSpec

I have a test that checks if the requested page returns a 200 status code:
expect(response).to have_http_status(:success)
However, if I explicitly return a different status code using the following the test:
return render json: { error: 'error message' }, status: :unprocessable_entity
it still passes.
Why do response and last_response have different statuses:
response.status # 200
last_response.status # 422
response is provided by ActionController::TestCase.
From the docs:
An ActionDispatch::TestResponse object, representing the response of the last HTTP response.
For reference, here are the rspec docs for controller tests. This may help clear up how response is supposed to be used.
last_response comes from Rack::MockResponse < Rack::Response
From the docs:
Return the last response received in the session. Raises an error if no requests have been sent yet.
In your test case, you probably used a method that allows you to mock visiting a page. This will set your response.status to 200 as you've had a successful request. If you then use Rack to stimulate an endpoint, e.g.:
put '/users', {my_user: 'blah'}
and you do it with incorrect parameters, then your last_response.status will be 422.
Ultimately, the confusion comes down the similarity of naming between ActionController and Rack::MockResponse, which I agree is rather confusing.

Issues with testing in rails

I am writing tests for my application. I have written quite a few tests and they were working fine. However, two of my tests keep failing and I am not sure why that is. The first test is a unsuccessful editing of a form.
The test is given below
test "unsuccessfull editing of the scoreboard" do
log_in_as(#user)
get edit_scoreboard_path(#scoreboard)
assert_template 'scoreboards/edit' (doesn't work)
patch scoreboard_path(#scoreboard), scoreboard: { name_of_scoreboard: "a"* 51,
name_of_organization: "b"*60,
name_of_activity: "c"*60 }
assert_template 'scoreboard/edit' (doesn't work)
end
The error associated with this test is given below.
ScoreboardEditTest#test_unsuccessfull_editing_of_the_scoreboard [/home/ubuntu/workspace/test/integration/scoreboard_edit_test.rb:13]:
expecting <"scoreboards/edit"> but rendering with <[]>
The controller for the following test is also given below.
def update
#scoreboard = Scoreboard.find(params[:id])
if #scoreboard.update_attributes(scoreboard_params)
redirect_to #scoreboard
flash[:success] = "Updated Successfully"
else
render 'edit'
end
end
After you get the edit_scoreboard_path, the edit scoreboard template should show up. I am not exactly sure why it gives me the error. I have the exact same thing with the user model and its working just fine. I think I am missing something in my understanding of it works.
The second test is a valid creation of a scoreboard. The test is given below.
test "valid creation of the scoreboard with passing validations" do
log_in_as(#user)
get new_scoreboard_path
assert_difference 'Scoreboard.count', 1 do
post scoreboards_path, scoreboard: {name_of_scoreboard: "abc",
name_of_organization: "def",
name_of_activity: "ghi" }
end
assert_redirected_to scoreboard_path(#scoreboard) (doesn't work)
assert_equal 'Scoreboard created successfully', flash[:success]
end
It redirecting to the wrong scoreboard id. In the fixtures I have the id set as 1. The error message is given below.
Expected response to be a redirect to <http://www.example.com/scoreboards/1> but was a redirect to <http://www.example.com/scoreboards/941832920>.
I am not sure exactly what this means. As I mentioned, I have the ID set in the fixtures. I even manually set the id to '941832920'. It still gave me an error. Not sure why its doing that.
For your 2nd test, I don't think you can set an id, it gets assigned by the database when the record is saved. A better way to check whether you get directed to the correct scoreboard path would be the preferred format: assert_redirected_to scoreboard_path(assigns(:scoreboard)).

Can't return 200 to Stripe Webhook

I am trying to test receive JSON webhooks from Stripe.
I have read:
Stripe Webhook on Rails
https://stripe.com/docs/webhooks
They require a 200 status response in order to acknowledge receipt.
I want to solve this before moving on to dealing with the JSON.
routes
post 'webhook' => 'web_hook#webhook'
controller
Stripe.api_key = "sk_test_whatsupbuttercup"
class WebHookController < ApplicationController
protect_from_forgery :except => :webhook
def webhook
render status: 200
end
end
With this setup, when I test a webhook, Stripe receives a 500 error.
If you only want to return a status use
head :ok
Instead of render. :ok is the corresponding symbol for 200 but you can also use it with the status code itself.
head 200
A full list of codes and corresponding symbols can be found here...
http://guides.rubyonrails.org/layouts_and_rendering.html
Whenever you get a 500 error (or any time you're confused about how your app is behaving actually) you should look in your logs. In this case you'll probably find that there's an ActionView::MissingTemplate error because you're rendering but not including anything to render.

Check on the status code of url before redirect_to in controller rails

I have action check_status in instances_controller, and I want to check on status code of URL before redirect_to it.
if status_code is 200 redirect_to it, else go to view page.
This is the pseudo-code of check_status action:
def check_status
if "http://www.web.com".status_code == 200
redirect_to "http://www.web.com"
else
#DO Nothing, and go to its view
end
end
For example get '/about' => 'instances#check_status', and i want to check if (web.com) get status=200 visit (web.com)
You can do this - but beware:
Your user will have to wait for your response check to complete before they get redirected. If you're checking a slow server, that could be up to 30 seconds before they get sent somewhere else.
There's no guarantee that the user will get the same result you got when you checked.
Here's some code that uses Ruby's Net::HTTP module to perform that web request:
require 'net/http'
def check_status(url)
uri = URI(url)
Net::HTTP.start(uri.host, uri.port) do |http|
request = Net::HTTP::Head.new uri.request_uri
response = http.request request
if response == Net::HTTPSuccess
redirect_to url and return
end
end
end
Make sure you're passing in full URLs to this method, complete with an http:// or https:// prefix, or this won't work.
If you were worried about that performance hit, you could cache the results for a short while and check those before returning. That is, when you look up a URL you can save the time of the lookup & the status code retrieved. Then, on the next lookup, if you've checked that domain in the past 24 hours, return the redirect immediately rather than checking it again.
For me just this one is working to check the response
The == one gets no match...
response.is_a? Net::HTTPSuccess
In addition to Alex's answer, you can use curl tool instead of Net::HTTP module.
system('curl www.google.com') # => true
system('curl www.google_not_valid.com') # => false

Resources