rails rspec integration_tests and http authentication - ruby-on-rails-3

I am using Rspec 2 and capybara and defined the basic integration test, i.e.
describe "EeRequisitions" do
describe "GET /ee_requisitions" do
it "works! (now write some real specs)" do
get ee_requisitions_path
response.status.should be(200)
end
end
end
Since the app uses HTTP basic authentication and since capybara says it includes Rack::Test, I expected that adding the line:
authorize 'user', 'password'
would handle it (I've since lost the stackoverflow post that told me that). Unfortunately it didn't - it kept throwing a 'method not found' error. I finally found the solution in a comment to this post: Rails/Rspec Make tests pass with http basic authentication where Matt Connelly pointed me to this gist: https://gist.github.com/mattconnolly/4158961 which finally solved my problem.
However, I'm still wondering why the Rack::Test approach failed as it seems to have worked for others.

Does including the line
include Rack::Test::Methods
at the top of the tests make a difference?

Related

How can I run a Controller Action from Rails Console, but setting the request.shot?

I'm working on a multi-tenant application, so pretty much everything throughout (starting with routing) expects to have a "request.host" that looks like "tenant_id.myapp.com".
I'm trying to run a controller action from the Rails console, but I haven't been able to figure this one out.
The easiest thing to do seems to be "app.get", but I can't figure out how to set the host.
I also saw an answer that mentions using "ActionController::TestProcess", which as far as I understand has been removed from Rails (or if not, I haven't found how to include it)
Any other options?
Thanks!
Daniel
I just verified it in my console(Pry)
# Send request with fake HTTP_HOST
>>app.get(app.root_path, nil, {"HTTP_HOST" => "123.myapp.com"})
>>#=>200
# Then check if it works
>>app.request.env
>>#...
>># "HTTP_HOST" : "123.myapp.com"

Setting default_url_options in test environment doesn't seem to work

I've put the following code into my config/environments/test.rb file:
config.action_mailer.default_url_options = { :host => "localhost:3000" }
however when I run my tests, all routes use http://test.host. I'm trying to work with an API that won't accept http://test.host as a valid callback URI, so I need to change this to properly receive the API response. Any idea why this isn't working? (I'm using RSpec, Guard, and Spork in my testing suite).
EDIT: Possibly relevant - this is being done inside of a controller spec.
EDIT2: It seems that it changes after a request is made via get, post, etc. Running the following code within the test:
Rails.logger.debug users_url
get 'http://google.com'
Rails.logger.debug users_url
would produce the following output:
http://localhost:3000/users
...get request related response here
http://google.com/users
Nowadays you can just set them in your test.rb like so:
Rails.application.routes.default_url_options[:host]= 'localhost:3000'
Rails.application.routes.default_url_options[:host]= 'localhost:3000'
In the developemnt.rb / test.rb, can be more concise as following:
Rails.application.configure do
# ... other config ...
routes.default_url_options[:host] = 'localhost:3000'
end
From my experience, url_options will not be passed into tests without a bit of hacking.
See e.g.
How to set locale default_url_options for functional tests (Rails)
http://www.ruby-forum.com/topic/3448797
I've frequently encountered this problem when trying to set the locale in tests. I've never used action mailer, though, so there may be a simpler way to do it.
The solution I've found for setting the default locale in url_options is just to patch actiondispatch and force it to use whatever locale I want it to. You could adapt this to your case this way:
class ActionDispatch::Routing::RouteSet
def url_for_with_default_url_options(options)
url_for_without_default_url_options(options.merge(:host => "localhost:3000" ))
end
alias_method_chain :url_for, :default_url_options
end
I put that code in a file in spec/support so it is used in rspec tests and I also require it from my env.rb file so I can use it in cucumber tests as well.
Keep in mind that this will patch it everywhere, in both test code and in actual code running under tests, and it will override any other settings for the :host key that you try to pass in (since the patch merges the fix on top of the options passed into url_for). In your case I believe that shouldn't be a problem.
Monkey patching is not a very elegant solution, though, and I used this after everything else failed. You might find a simpler solution specific to action mailer.
How are you running your tests? Maybe appending RAILS_ENV=test might help.

logging info with rails

Moving over from django / python, I am having a little trouble getting the rails logger to log all the information I want. I am wondering how/if the following can be achieved:
Having in the log format(ter) include the specific file, function name and line where the logging statement itself was found. Essentially the equivalent of LOG_MSG_FORMAT = '%(asctime)s %(levelname)s %(filename)s:%(funcName)s(%(lineno)d) : %(message)s' in python logger?
Being able to log all requests, via something similar to a django request logging middleware. Particularly, being able to log the username (if logged in) of every request.
Am I missing something obvious? or does this require (lots of) custom code?
I just found this railtie gem that might help although I imagine it will take some "custom code" to append username to logs. See the Readme section on logging specific controllers and models.
logging-rails railtie
I don't know about getting the file, function, and line number, but it's pretty easy to log from application_controller:
class ApplicationController < ActionController::Base
before_filter :log_user
def log_user
if current_user
Rails.logger.info "Processing Request for #{current_user.name}"
end
end
end
Just to add a quick note in case this is useful for someone:
The lograge gem makes rails logs much similar to django's, plus allows very neat customization, adding parameters such as remote ip address, current_user etc.
It also reduces verbosity of rendered layouts, which I anyway found unnecessary for production. It also plays nicely with logging-rails railtie mentioned by #lukewendling.
Sadly, I still couldn't find anything that shows the file/function/line number like you can easily do with django, but I guess that's just too much to ask.

Capybara-webkit tries to open example.com

I'm using capybara, capybara-webkit, capybara-screenshot together with cucumber. (Ruby 1.9.3, Rails 3.1.3) and Capybara.javascript_driver = :webkit is also set env.rb
Unfortunately running a cucumber spec with #javascript will never succeed for some reason and the error screenshots just capture example.com.
The URL which I actually try to open is generated with a rails router result for one of my models e.g. with visit products_url
So how can I avoid that it ends up querying example.com?
Any input is very welcome.
Just because the comment is messed up - here's what I found was the solution:
Capybara.run_server = true
Capybara.server_port = 7787
Before '#javascript' do
Capybara.app_host = "http://127.0.0.1:#{Capybara.server_port}"
end
Try using visit products_path instead. They do not recommend using absolute URLs in "Gotchas" section of README.
For me, there was a much sneaker "gotcha" (and I was using Capybara with Rspec). Originally in a spec I had:
visit "foos/5"
This worked fine with Rack::Test but when I wanted to switch to the webkit driver to test js interactions, I got that exception (Unable to load URL: file:///products (Capybara::Driver::Webkit::WebkitInvalidResponseError)).
What I had to do was change the path I passed to visit, like so:
visit "/foos/5"
Succes!!
Here's another potential gotcha, posted for others that might have this issue. I was testing action caching, and the key Rails generates looks like "views/www.example.com/products". This happens even if you use products_path as the url. This can lead to the need to set your server name so you can know in advance what cache key to expect.

Raising route not found error

I'm writing a book on Rails 3 at the moment and past-me has written in Chapter 3 or so that when a specific feature is run that a routing error is generated. Now, it's unlike me to go writing things that aren't true, so I'm pretty sure this happened once in the past.
I haven't yet been able to duplicate the scenario myself, but I'm pretty confident it's one of the forgotten settings in the environment file.
To duplicate this issue:
Generate a new rails project
important: Remove the public/index.html file
Add cucumber-rails and capybara to the "test" group in your Gemfile
run bundle install
run rails g cucumber:skeleton
Generate a new feature, call it features/creating_projects.feature
Inside this feature put:
This:
Feature: Creating projects
In order to value
As a role
I want feature
Scenario: title
Given I am on the homepage
When you run this feature using bundle exec cucumber features/creating_projects.feature it should fail with a "No route matches /" error, because you didn't define the root route. However, what I and others are seeing is that it doesn't.
Now I've set a setting in test.rb that will get this exception page to show, but I would rather Rails did a hard-raise of the exception so that it showed up in Cucumber as a failing step, like I'm pretty sure it used to, rather than a passing step.
Does anybody know what could have changed since May-ish of last year for Rails to not do this? I'm pretty confident it's some setting in config/environments/test.rb, but for the life of me I cannot figure it out.
After I investigate the Rails source code, it seems like the ActionDispatch::ShowExceptions middleware that responsible of raising exception ActionController::RoutingError is missing in the test environment. Confirmed by running rake middleware and rake middleware RAILS_ENV=test.
You can see that in https://github.com/josh/rack-mount/blob/master/lib/rack/mount/route_set.rb#L152 it's returning X-Cascade => 'pass' header, and it's ActionDispatch::ShowExceptions's responsibility to pick it up (in https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L52)
So the reason you're seeing that your test case is passing because rack-mount is returning "Not Found" text, with status 404.
I'll git blame people and get it fix for you. It's this conditional here: https://github.com/rails/rails/blob/master/railties/lib/rails/application.rb#L159. If the setting is true, the error got translated right but we got error page output. If it's false, then this middleware doesn't get loaded at all. Hold on ...
Update: To clearify the previous block, you're hitting the dead end here. If you're setting action_dispatch.show_exceptions to false, you'll not get that middleware loaded, resulted in the 404 error from rack-mount got rendered. Whereas if you're setting action_dispatch.show_exceptions to true, that middleware will got loaded but it will rescue the error and render a nice "exception" page for you.