http_basic_authentication in request spec - ruby-on-rails

I'm very confused about latest rspec versions. I've found several answers to this same question but being several years old they apparently don't work anymore.
I have a request spec that tests an endpoint that is behind http_basic_authenticate_with. I couldn't find a way to make this work.
My latest attempt is:
it "returns data_services" do
request.headers.merge!(authenticated_header(ENV.fetch('HTTP_USERNAME'), ENV.fetch('HTTP_PASSWORD')))
get data_services_path
expect(response).to have_http_status(:ok)
end
Unfortunately there's no request object, which exists only after the get is performed. I've tried to pass the headers to the get method too, but no luck.
Is there any way to have requests specs for actions behing http simple auth?

If you're in a request spec (recommended from Rails v5+), you need to pass your headers to the get method as a keyword argument:
get data_services_path, headers: authenticated_header(...)
The syntax you're trying with request.headers is for controller specs, and the rspec docs recommend switching to request specs and not setting headers in controller specs.

Related

What does specifying the type in an RSpec actually affect?

I have never been sure what the difference between these options are
RSpec.describe V2::DirectMessagesController, type: :controller
vs
RSpec.describe V2::DirectMessagesController, type: :request
Or where to even look to figure it out
Request specs provide a thin wrapper around Rails' integration tests, and are designed to drive behavior through the full stack, including routing
(provided by Rails) and without stubbing (that's up to you).
A controller spec is an RSpec wrapper for a Rails functional test
(ActionController::TestCase::Behavior).
It allows you to simulate a single http request in each example, and then
specify expected outcomes such as:
rendered templates
redirects
instance variables assigned in the
controller to be shared with the view
cookies sent back with the
response
Controller spec docs: https://relishapp.com/rspec/rspec-rails/docs/controller-specs
Request spec docs: https://relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec

Perform HTTP OPTIONS request from Rails 5 for CORS pre-flight or otherwise

Edit: I re-added the options method in a pull request to Rails which should now be live. The answer below should no longer be necessary. Call process(:options, path, **args) in order to preform the options request.
See commit 1f979184efc27e73f42c5d86c7f19437c6719612 for more information if required.
I've read around the other answers and none of them seemed to work in Rails 5. It's surprising that Rails doesn't just ship with an options method, but here we are. Of course if you can use xdomain, you probably should (edit: I no longer hold this view, there are advantages to CORS) because it's both faster (no preflight check doubling latency!), easier (no need for silly headers / HTTP methods!), and more supported (works basically everywhere!) but sometimes you just need to support CORS and something about the CORS gem makes it not work for you.
At the top of your config/routes.rb file place the following:
match "/example/",
controller: "example_controller",
action: "options_request",
via: [:options]
And in your controller write:
def options_request
# Your code goes here.
end
If you are interested in writing an integration test there is some misinformation around the process method, which is not actually a public method. In order to support OPTIONS requests from your integration tests create an initializer (mine is at: config/initializers/integration_test_overrides.rb because I override a number of things) and add the following code:
class ActionDispatch::Integration::Session
def http_options_request(path)
process(:options, path)
end
end
So that you can call http_options_request from your integration test.

How catch http data repsponce in test via Capybara

In app we use 3-th part service, what sometimes get broken. We regular testing app by Capybara, Poltergeist. But for tests be more specific in error log I need catch in tests data response from get/post api calls to that 3-th par service. I know about Poltergeist method page.driver.network_traffic but there are no data here, useful for me only response.url and response.status , but also I want somehow get data. Thanks in advance.
Capybara is not suited or designed for API testing, see this blog post http://www.elabs.se/blog/34-capybara-and-testing-apis. There is no access to the get and post requests or responses without hacking the underlying code. Instead, try RackTest. RackTest was created to specifically test APIs: https://github.com/brynary/rack-test.
Edit: with rack-test the homepage documentation is not clear but you can use the Rack::Test::Methods mixin to get the response, see http://www.rubydoc.info/github/brynary/rack-test/Rack/Test/Methods.
For example:
require 'rack/test'
class MyTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
MyApp.new
end
def my_test
get '/'
assert last_response.ok?
assert_equal '<expected_response>', last_response.body
end
end

InvalidCrossOriginRequest when trying to send a Javascript Asset

I'm trying to create an "asset controller" shim which will filter static asset requests so only authorized users can get retrieve certain assets. I wanted to continue to use the asset pipeline so I setup a route like this
get 'assets/*assetfile' => 'assets#sendfile'
Then I created an AssetsController with one method "sendfile". Stripping it down to only the stuff that matters, it looks like this:
class AssetsController < ApplicationController
def sendfile
# Basically the following function forces the file
# path to be Rails.root/public/assets/basename
assetfilename=sanitize_filename(params[:assetfile] + '.' + params[:format])
send_file(assetfilename)
end
end
It looks like I have to run this in production mode as rails by-passes my route for assets in development. So I precompile my assets and I can verify in the controller that the files exist where they are expected to be.
However, now the problem is that I'm getting a "ActionController::InvalidCrossOriginRequest" when the Javascript asset is requested (just using the default application.* assets for now). I've read about this error and I understand that as of Rails 4.1 there are special cross-origin protections for Javascript assets. Sounds good to me, but I don't understand where the "cross-origin" part is coming from. Using firebug, I can see that the asset requests are being requested from the same domain as the original page.
I am certain that this is the problem because I can solve it by putting "skip_before_action :verify_authenticity_token" in the beginning of my controller. However, I really don't want to do this (I don't fully understand why this check is necessary, but I'm sure there are very good reasons).
The application.html.erb file is unchanged from the default generated file so I assume it's sending the CSRF token when the request is made, just as it would if I didn't have my own controller for assets.
So what am I missing?
Ok, I think I answered my own question (unsatisfactorily). Again, long post, so bear with me. I mistakenly forgot to add this to my original questions, but I'm using Ruby 2.2.0 and Rails 4.2.4.
From looking at the code in "actionpack-4.2.4/lib/action_controller/metal/request_forgery_protection.rb", it looks like Rails is doing two checks. The first check is the "verify_authenticity_token" method which does the expected validation of the authenticity token for POST requests. For GET requests, it ALSO sets a flag which causes a second check on the formed computed response to the request.
The check on the response simply says that if the request was NOT an XHR (AJAX) request AND the MIME Type of the response is "text/javascript", then raise an "ActionController::InvalidCrossOriginRequest", which was the error I was getting.
I verified this by setting the type to "application/javascript" for ".js" files in "send_file". Here's the code:
if request.format.js?
send_file(assetfilename, type: 'application/javascript')
else
send_file(assetfilename)
end
I can skip the response check all together by just adding the following line to the top of my controller class:
skip_after_action :verify_same_origin_request
The check on the response seems pretty weak to me and it's not clear how this really provides further protection against CSRF. But I'll post that in another question.

Rails API functional tests with XML body

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'

Resources