I've written functional tests for API endpoints built in Rails using shoulda testing framework.
An example looks like the following:
setup do
authenticated_xml_request('xml-file-name')
post :new
end
should respond_with :success
authenticated_xml_request is a test helper method that sets
#request.env['RAW_POST_DATA'] with XML content.
After upgrading the app from rails 2.3.3 to rails 2.3.8, the functional tests are failing because the XML content received is not merged in the params hash.
I'm setting the request with the correct mime type via #request.accept =
"text/xml"
I'm able to inspect the content of the request using request.raw_post but i'd like to keep the current setup working.
Also, running a test from the terminal using cURL or any other library (rest_http) in development mode, the API works perfectly well.
Any tips or help is much appreciated.
Now it's simpler:
post "/api/v1/users.xml", user.to_xml, "CONTENT_TYPE" => 'application/xml'
Note that you have to specify appropriate "CONTENT_TYPE". In other case your request will go as 'application/x-www-form-urlencoded' and xml won't be parsed properly.
I solved the issue by adding a custom patch to rails (test_process.rb file) to convert incoming xml to hash then merge into parameters hash.
on line 439:
parameters ||= {}
parameters.merge!(Hash.from_xml(#request.env['RAW_POST_DATA'])) if #request.env['RAW_POST_DATA'] && #request.env['CONTENT_TYPE']=='application/xml'
Related
I'm working on a Rails 5 JSON API, and I've noticed that it's providing sensible exception-handling out of the box.
For example, ActiveRecord::RecordNotFound results in a 404, ActionController::ParameterMissing results in 400, and these are all returned as well-formed JSON responses.
I've found plenty of documentation on rescue_from, can't locate what's providing this exception-handling for me.
I've dug around in the Rails guides, the docs, as well as in the rails console, e.g.
ActionController::API.new.rescue_handlers
=> []
ApplicationController.new.rescue_handlers
=> []
I'd like to know where the exception-handling is coming from because I've noticed that it's gracefully applied for real requests, but not in controller specs.
For example, if I run a local server and send it a malformed request with curl, I get a sensible error response. But if I try to reproduce that in a controller spec,
it "returns 404 if Yogurt doesn't exist" do
put :update, params: { id: 293459 }
end
the test throws the raw exception (ActiveRecord::RecordNotFound in this case).
When you set up a Rails 5 app with the --api option you end up using ActionController::API. https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/api.rb
The modules are built here: https://github.com/rails/rails/blob/a0061d2389a178b093f0d3f64f58236ffbe088e0/actionpack/lib/action_controller/api.rb#L104 so that's where your responses are coming ultimately from. Looks like the rendering is being handed off to metal at some point.
API actions are configured to return :json by default, so that's why your test is behaving differently than your browser.
For your spec to work the same way you'll have to supply a format:
put :update, params: { id: 293459 }, format: :json
I created a small application that generates reports (mainly PDFs) from an sqlite3 database exported from GNUCash. I separated the app with a possibility of sharing it with other VFW posts that are currently using GNUCash or stuck with a paper general ledger. I call it a quasi API in that most reports can be generated in both HTML and PDF or it can just send data. Before I separated it, I had tried a multiple database approach, adding the sqlite3 database to my database.yaml along with my main postgresql database. It worked but that made the possibility of sharing difficult and I was a little skeptical about the approach.
All is fine with PDFs (prawn generated). I use RestClient to get the reports from the pdf server running on the same machine on a different port. For example, to generate a General Ledger this controller get action is called:
def ledger_pdf
resp = RestClient.get "http://localhost:8600/ledgers/#{params[:id]}/ledger_pdf"
send_data resp.body, filename: "Ledger#{params[:id]}",
type: "application/pdf",
disposition: "inline"
end
On the pdf server, this action responds
def ledger_pdf
pdf = Pdf::Ledger.new(view_context,params)
send_data pdf.render, filename: "Ledger#{params[:id]}",
type: "application/pdf",
disposition: "inline"
end
Getting the HTML version became a little more challenging! On the pdf server I have a one page menu that lists all the reports available, some by date and others with a collection route. This was for the 'sharing' version where someone only needs the reports and does not have another rails application that does other stuff. The server could be stand alone running on some box.
Rather then getting the data and creating a page on the main application from the data, I got the menu from the pdf server in an iframe tag. I though this was working great when I discovered that, in a view:
<iframe src="http://localhost:8600/ledgers/" width="100%" height="100%" id="rails_iframe">error!</iframe>
was calling localhost:8600 on my machine (which was running a development version and fooled me into thinking it was working!) rather than on the production server. After some researching I discovered that you can't do that. I guess the RestClient call to localhost:8600 is hidden from the browser, but the iframe call is not.
I guess I can get the HTML versions using the same RestClient approach and return text/html and use nokogiri to get the body/html and yield it somehow, if I knew how to do that.
The html.slim is really simple and I could just duplicate it on my main server after getting the data, but then I'd have two code bases to maintain.
Another approach would be to make it a real API and make it public so that the iframe approach would work, something I was trying to avoid (authentication and all that).
I guess my question is, does someone have another approach or can refine or give me pointers on my approaches.
You just need to add another action to your 'pdf' server which will render menu:
def menu
render json: {first: 'first/report', second: 'second/report'}
end
And then just fetch that menu from your main server (which is facing users):
def menu
resp = RestClient.get ...
# parse resp.body to get menu structure
# convert JSON response from pdf server to HTML with your ERB template
#menu_items = ...
end
Then just have route on your main server to pass information from collection server as well, something like:
get "reports/:collection/:date/:report", to: 'reports#fetch'
# then in your controller
def fetch
resp = RestClient.get "...#{params[:collection]}/#{params[:date]}/#{params[:report]}
...
end
Personally, I wouldn't use Rails server for pdf generation. I would've just used cron script which would run Ruby script for PDF generation, and then I would just upload them to S3 or some other cloud storage. My Rails server would've just return the link for that S3 file.
I have a very specific issue...
I have 2 rails apps:
App 1 makes a request to App 2 and gets a valid JSONP response.
App 2 has been outfitted with Rack JSONP Middleware so it returns with the
correct wrapped callback (i.e. JQuery1919191([{[{"find_item":{"geo_lon":-74....etc)
For some reason App 1 throws the following error in javascript console:
JSONP Error: parsererror Error: jQueryXXXXXX was not called.
It works fine, however, when testing this in a non-rails context. See my fiddle for an example: http://jsfiddle.net/pBDyW/16/ This code (that works in this example) is the exact same code I'm using in my rails app.
What I'm confused about is HOW am I supposed to configure App 1 (the receiving app) to handle an incoming JSONP resuqest properly. I'm convinced it must be something with Rails, as calling this from a generic page, served in Apache, works fine.
Please HELP!
It turns out that there are (for whatever reason) subtleties in the way the rails app (App 1) seems to process the incoming request. I modified my approach, which was to render the JSON response in my controller with:
respond_to :json
def json_result
#blah = [stuff from database]
respond_with #blah
end
methods (recommended with the Rack JSON Middlware approach, and restored my JSON response to:
render :json => #find_results, :callback => params[:callback]
The difference here is an odd one. The rails app JSONP call will only succeed if I execute the response like this:
success: callbackFunction
and NOT
success: function(){//do stuff}
However, the latter example works in the JS fiddle. Weird. I guess it's just some kinda formatting discrepancy in Rails 3+.
Anyway, hope this was helpful to anyone who comes across it!
I am working on an application where I use paperclip for uploading images, then the image is manipulated in a flash app and returned to my application using application/octet-stream. The problem is that the parameters from flash are not available using params. I have seen examples where something like
File.open(..,..) {|f| f.write(request.body) }
but when I do this, the file is damaged some how.
How can I handle this in rails 3?
After you make sure that the request parameters have hit the Rails application, you may want to ensure that there were no parsing problems. Try to add these lines in you controller's action:
def update # (or whatever)
logger.debug "params: #{params.inspect}"
# I hope you do not test this using very large files ;)
logger.debug "request.raw_post: #{request.raw_post.inspect}"
# ...
end
Maybe the variable names got changed somehow? Maybe something escaped the parameter string one time too much?
Also, you have said that the file into which you want to save the request body is damaged. How exactly?
The request.body object does not need to be String. It may be a StringIO, for example, so you may want to type this:
File.open(..,..) {|f| f.write(request.body.read) }
Hi I do not have any front end in my app. I am willing to release just a RESTful API which can be used by different clients. Any pointers how should I proceed towards testing it with cucumber? Every action in the controller generates XML feed only. Any pointers or suggestions?
The visit function of webrat accepts a http_method as a second parameter. You can also test your api like in the following cucumber rule:
When /^I restfully delete (?:|the )user "([^\"]*)"$/ do |login|
visit(path_to("user \"#{login}\" page"), :delete)
end
I think Webrat is more than what you need.
For XML feed testing, you don't need a browser simulator like Webrat which would load pages and analyse all the markup (links, forms etc.) when you really don't have any HTML pages.
You rather need something like Curl (http://curl.haxx.se) or Curb (on rubyforge, which are ruby bindings for Curl), or Patron (on rubyforge).
These libraries can make a request header as per your liking (e.g. setting Content-Type, choosing among GET PUT POST DELETE HEAD etc.) and obtain the response, and probably follow 302 redirections when needed.
The response returned, can be then turned into XML object, and XML parsers available for Ruby can be used to test the output. Also, you can write XMLMapping classes (on rubyforge) to convert XML output into Ruby objects and test their attributes etc. This is much cleaner, IMHO.
jayzes has shared his cucumber test steps examples using Rack::Test::Methods, JSONpath, Nokogiri etc to write test for json/xml API, you might want to refer and create more for your own steps.
https://github.com/jayzes/cucumber-api-steps
Once you've set up your RESTful routes, you should be able to use Webrat to visit the different routes. You can then test that each route returns XML which meets your expectations.
Here's a blog post that describes how to test XML output in RSpec:
Testing XML output
Webrat is a headless browser, which simply means that you can simulate a browser without having to open a real browser like FireFox on your development machine. This means that you can simply type something like "visit 'users/'" into your defined steps and simulate a user accessing your application.
Finally the Pragmatic book on RSpec (still in beta), is a great resource on how to use Cucumber, Webrat and RSpec together and drive your application development with BDD.
I was trying to do that and got stuck in a major problem with restful_authentication (using AASM, one of the internal model of restful_auth it seems) and got to that solution to log in:
Given /^I am logged in with a new account$/ do
login = "test"
#current_user = User.new(
:login => login,
:password => 'generic',
:password_confirmation => 'generic',
:email => "#{login}#example.com",
:state => "active"
)
#current_user.save
x = User.find_by_login(login)
x.state = "active"
x.save!
visit "/login"
fill_in("login", :with => login)
fill_in("password", :with => 'generic')
click_button
response.body.should =~ /Logged in successfully/m
end
Modularize it for cleaner testing corpus, this is to demo the concept I found.