I am trying to pass the test cases for pages 404 & 500. But I am having a lot of problems
1) Firstly I have a page 500.html.erb inside app/views/errors/ which is not getting called.
2) If I run the below test my system freeze and I need to restart my system
3) If I comment this line expect{get "/errors/foo"}.to raise_exception(ActionController::RoutingError). So in my controller Action name page 500 gets passed as params but still, my system get freeze
Can anyone help me to fix this issue
errors_spec.rb
require "spec_helper"
describe "Errors" do
before do
allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("production"))
end
it "renders the 500 page" do
get "/errors/500"
expect(response.status).to eq(500)
end
it "renders the 404 page" do
get "/errors/404"
expect(response.status).to eq(404)
end
it "raises an exception if the page doesn't exist" do
expect {get "/errors/foo"}.to raise_exception(ActionController::RoutingError)
end
end
errors_controller.rb
class ErrorsController < ApplicationController
skip_before_filter :authenticate_user!
EXTERNAL_ERRORS = ['sso']
VALID_ERRORS = ['404', '403', '500', 'maintenance'] + EXTERNAL_ERRORS
def show
status = external_error? ? 500 : 200
render page , status: status
end
def blocked
end
private
def page
if VALID_ERRORS.include?(params[:id])
params[:id]
else
raise(ActionController::RoutingError.new("/errors/#{params[:id]} not found"))
end
end
def external_error?
EXTERNAL_ERRORS.include?(params[:id])
end
end
In your code, you are setting status 200 when /errors/500 gets called.
def show
# external_error? returns false.
status = external_error? ? 500 : 200
render page , status: status # Status is 200.
end
Use debuggers like pry or byebug to check status. There is not any problem in your test case. Try this.
class ErrorsController < ApplicationController
skip_before_filter :authenticate_user!
EXTERNAL_ERRORS = ['sso']
VALID_ERRORS = ['404', '403', '500', 'maintenance'] + EXTERNAL_ERRORS
def show
status = error_500? ? 500 : 200
render page , status: status
end
def blocked
end
private
def page
if VALID_ERRORS.include?(params[:id])
params[:id]
else
raise(ActionController::RoutingError.new("/errors/#{params[:id]} not found"))
end
end
def external_error?
EXTERNAL_ERRORS.include?(params[:id])
end
def error_500?
['500'].include?(params[:id]) || external_error?
end
end
Related
I am trying to write the allow method in RSpec. My rails controller is
module Users
class ProfilesController < ApplicationController
# Update user profile
def update
payload = { name: params[:user][:name],email: params[:user][:email]}
response = send_request_to_update_in_company(payload)
if response['code'] == 200
if User.first.update(user_params)
render json: { message: "User successfully updated"}, status: :ok
else
head :unprocessable_entity
end
else
render json: { error: 'Error updating user in Company' },status: :unprocessable_entity
end
end
private
def send_request_to_update_in_comapny(payload)
response = Api::V1::CompanyRequestService.new(
payload: payload.merge(company_api_access_details),
url: 'customers/update_name_email',
request_method: Net::HTTP::Post
).call
JSON.parse(response.body)
end
end
end
When I write the bellow code in my test file
allow(Users::ProfilesController).to receive(:send_request_to_update_in_company).and_return({ 'code' => 500 })
I am getting the following error in terminal
Users::ProfilesController does not implement: send_request_to_update_in_comapny
enter code here
With allow_any_instance_of I am able to get the code working. But how can I implement it using allow?
Yes, allow_any_instance_of works because, as the name suggests, it allows any instance of Users::ProfilesController to respond to the instance method send_request_to_update_in_company with your mock return value.
However, your line
allow(Users::ProfilesController).to receive(:send_request_to_update_in_company)
is telling RSpec to mock a class method called send_request_to_update_in_company, which doesn't exist. And so, you're seeing the error message saying so.
You don't say where your test is situated, but generally wherever it is, it's not a good idea to either test or stub out a private method.
I'd be inclined to instead create a mock Api::V1::CompanyRequestService object to return a fake response, which your controller code can then parse as expected and produce the expected JSON. For example
mock_request = instance_double(Api::V1::CompanyRequestService)
allow(mock_request).to receive(:call).and_return('{"code": 500}')
allow(Api::V1::CompanyRequestService).to receive(:new).and_return(mock_request)
Another approach might be to leave your service alone, and instead use tools like VCR or WebMock to provide mocked JSON values at the network layer - your code can think it's calling out to the internet, but really it gets back responses that you define in your tests.
How about this way:
spec/requests/users/profiles_controller_spec.rb
require 'rails_helper'
RSpec.describe "Users::ProfilesControllers", type: :request do
describe "Test call to special function: " do
let(:controller) { Users::ProfilesController.new }
it "Should response to code 500" do
response = controller.send_request_to_update_in_company("test")
expect(response).to eq({"code"=>"500", "test1"=>"abc", "test2"=>"def"})
end
it "Should return to true" do
response = controller.true_flag?
expect(response).to eq(true)
end
end
end
app/controllers/users/profiles_controller.rb
module Users
class ProfilesController < ApplicationController
# Update user profile
def update
payload = { name: params[:user][:name],email: params[:user][:email]}
response = send_request_to_update_in_company(payload)
Rails.logger.debug "Ok71 = response['code'] = #{response['code']}"
# if response['code'] == 200
# if User.first.update(user_params)
# render json: { message: "User successfully updated"}, status: :ok
# else
# head :unprocessable_entity
# end
# else
# render json: { error: 'Error updating user in Company' },status: :unprocessable_entity
# end
end
# Not private, and not mistake to 'send_request_to_update_in_comapny'
def send_request_to_update_in_company(payload)
response = Api::V1::CompanyRequestService.new(
payload: "for_simple_payload_merge_values",
url: 'for_simple_customers/update_name_email',
request_method: "for_simple_request_method"
).call
Rails.logger.debug "Ok66 = Start to log response"
Rails.logger.debug response
JSON.parse(response.body)
end
# Simple function to test
def true_flag?
true
end
end
end
app/services/api/v1/company_request_service.rb
class Api::V1::CompanyRequestService < ActionController::API
def initialize(payload="test1", url="test2", request_method="test3")
#payload = payload
#url = url
#request_method = request_method
end
def call
#object = Example.new
#object.body = {code: "500", test1: "abc", test2: "def"}.to_json
return #object
end
end
class Example
attr_accessor :body
def initialize(body={code: "000", test1: "init_value_abc", test2: "init_value_def"}.to_json)
#body = body
end
end
I use simple code to simulate your project. Modify it to suitable your working! Tell me about your its thinking. Thank you!
This is my controller
class Api::V1::WebhooksController < ApplicationController
include Api::V1::WebhookHelper
include Api::V1::LogHelper
skip_before_action :verify_authenticity_token
after_action :handle_wehbook
# Schedule plan
def schedule_plan
add_log(status: "webhook", message: "New Plan is scheduled.")
end
def handle_wehbook
if webhook_verified?
webhook_verified!
render status: 200, json: { error: 'webhook is verified.' }
else
webhook_verified!(verified: false)
render status: 500, json: { error: 'webhook is not verified.' }
end
end
end
This is Webhook Helper.
I am sure in WebhookHelper, it never redirects or renders anything.
require 'openssl'
require 'base64'
module Api::V1::WebhookHelper
include Api::V1::LogHelper
def webhook_verified?
digest = OpenSSL::Digest.new('sha256')
hmac = OpenSSL::HMAC.digest(digest, secret, request.body.read)
hash = Base64.encode64(hmac).strip
hash == signature
end
def secret
ENV["API_KEY"]
end
def signature
request.headers["HTTP_X_SIGNATURE"]
end
def webhook_verified!(verified: true)
if verified
add_log(status: "webhook", message: "Webhook is verified.") # only puts log
else
add_log(status: "webhook", type: "warning", message: "Webhook is not verified.") # only puts log
end
end
end
I am getting this issue.
AbstractController::DoubleRenderError (Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".):
app/controllers/api/v1/webhooks_controller.rb:31:in `handle_wehbook'
I am not sure I am calling render or redirect multiple times in my action.
Anyone can help me?
Your handle_wehbook function is an after_action, it runs after some other action.
The latter has already rendered something (it may be an rails error or a redirect) thus the double render
After adding decent_exposure the following spec is failed:
it "redirects to root_path if product cannot be found" do
product = create :product
get :show, {id: 2}
expect(response).to redirect_to(root_path)
end
I use CanCanCan and add the following code to ApplicationController, so it rescues when the record couldn't be found and redirects to root path:
rescue_from ActiveRecord::RecordNotFound do
redirect_to root_path, alert: controller_name.singularize.capitalize << " cannot be found"
end
And it do works, it redirects if a record doesn't exist and I see 'Completed 302 Found' message in console. But spec fails with the message 'Expected response to be a redirect, but was <200>'. Seems to be it's due to 'Rendered products/show.html.haml within layouts/application' before 'Redirected to http://localhost:3000/' which appears after adding decent_exposure.
In controller I have only:
expose(:product, attributes: :product_params)
expose(:products) { Product.paginate(page: params[:page], per_page: 5) }
without Show action.
Thank you for any help!
Added Show action:
def show
return unless product
end
Now it works correctly.
Post model change to URL parameters to title
class Post < ActiveRecord::Base
def to_param
"#{id}-#{title}"
end
end
When any one type http://0.0.0.0:3000/posts/4 it redirect to belong particular post
When any one type post id, How redirect to 404 page?
I think you could just check if id is number or no. And do somehing like that:
render file: "#{Rails.root}/public/404.html", layout: false, status: 404
like:
in application.rb:
def check_id(arg)
if params[arg] && params[arg].match(/\A[0-9]+\z/)
render_404
false
end
end
def render_404
render file: "#{Rails.root}/public/404.html", layout: false, status: 404
end
in controller.rb:
before_filter -> { check_id(:id) }
In case you don't want to display a 404 error page from your controller, you can just redirect to your root path like this:
rescue ActiveRecord::RecordNotFound
redirect_to root_path
Method to_param needs to build path to resource: apidock.com/rails/ActiveRecord/Base/to_param
class User < ActiveRecord::Base
def to_param # overridden
name
end
end
user = User.find_by_name('Phusion')
user_path(user) # => "/users/Phusion"
How to make friendly URLs you can find out there
If you want to have user-friendly links, simple way is gem friendly-id
I have a bit of code that checks the response code of a list of URL's and presents them back - I am having trouble with a few of URL's that are hanging which causes the application not load at all. How can I make the request to give up after 30 seconds and check the next URL marking the skipped URL as failure.
below is my current code;
(Model/status.rb)
require "net/http"
require "uri"
class Status
def initialize(url)
#url = url
end
def get_status
response.code
end
def active?
["200","203","302"].include?(get_status) ? true : false
end
private
def lookup
URI.parse(#url)
end
def http
Net::HTTP.new(lookup.host, lookup.port)
end
def request
Net::HTTP::Get.new(lookup.request_uri)
end
def response
http.request(request)
end
end
(controllers/welcome_controller.rb)
class WelcomeController < ApplicationController
def index
#syndication = [
["http://v1.syndication.u01.example.uk/organisations?apikey=bbccdd", "U.INT 01"],
["http://v1.syndication.u02.example.uk/organisations?apikey=bbccdd", "U.INT 02"],
].collect { |url| logger.info("Boom #{url[0]}"); ["#{url[1]} (#{url[0]})", Status.new(url[0]).active?] }
end
end
Got the answer..
adding the following to my "def get_status"
def get_status
begin
response.code
rescue Exception => e
Rails.logger.info("Error #{e}")
end
end
This logged the error and the went to the next URL