Set headers before request in Rails Minitest - ruby-on-rails

In Ruby on Rails Minitest, is there any way to set the headers before calling the request, within an ActionDispatch::IntegrationTest test?
Inside the before/setup is not a solution at the moment.
# Default way
test 'foo' do
get '/home', headers: { foo: "bar" }
end
# How I wish
test 'foo' do
#request.headers['foo'] = "bar"
get '/home'
end
# I already tried and it did not work...
test 'foo' do
request.headers['foo'] = 'bar'
#request.headers['foo'] = 'bar'
request.env['foo'] = 'bar'
#request.env['foo'] = 'bar'
request.env['HTTP_FOO'] = 'bar'
#request.env['HTTP_FOO'] = 'bar'
get '/home'
end
Ruby version: ruby 2.5.1p57
Rails version: 5.2.0
Minitest version: 5.1

The default way is good, but according to naming of headers custom header you should start with "X-" prefix.
test 'foo' do
get '/home', headers: { "X-Foo": "bar" }
end

Related

Rspec request specs and Rails 5

I'm starting a new project, my first with Rails 5.1.0. I have a pb with my first request spec.
describe 'Users', type: :request do
it 'are created from external data' do
json_string = File.read('path/to/test_data/user_data.json')
params = { user: JSON.parse(json_string) }
headers = { "CONTENT_TYPE" => "application/json" }
expect do
post '/api/v1/users', params.to_s, headers
end.to change {
User.count
}.by(1)
expect(response.status).to eq 200
end
end
this spec return the error ArgumentError: wrong number of arguments (given 3, expected 1). The official documentation don't say much.
If I take out the .to_s, and send a hash, like this:
post '/api/v1/users', params, headers
I got another error:
ArgumentError: unknown keyword: user
Any thought?
I think they changed the syntax recently. Now it should use keyword args. So, something like this:
post '/api/v1/users', params: params, headers: headers
Here's a little addendum to Sergio's answer. If you are upgrading from Rails 4 to Rails 5, have lots of tests, and aren't too keen on changing them all – at least not until you've finished upgrading – I've found a way to make them work with the old method signature.
In my spec_helper I added
module FixLegacyTestRequests
def get(path, par = {}, hdr = {})
process(:get, path, params: par, headers: hdr)
end
def post(path, par = {}, hdr = {})
process(:post, path, params: par, headers: hdr)
end
def put(path, par = {}, hdr = {})
process(:put, path, params: par, headers: hdr)
end
def delete(path, par = {}, hdr = {})
process(:delete, path, params: par, headers: hdr)
end
end
and then I added this configuration for each test:
RSpec.configure do |config|
config.before :each do |example|
extend(FixLegacyTestRequests) # to be removed at some point!
end
end
My tests went back to working, and I think it should be safe because it's only applied to the currently running test and shouldn't pollute any gem's code such as with a monkey patch.

RSpec be_equal not working

I am writing some rspec examples for a plain old ruby class in my rails project and I am facing the following problem.
I have this constructor:
class Server
def initialize(host='localhost',options={:port => 443, :password => '', :vpncmd_bin_path => '/usr/local/bin/vpncmd', :timeout => 5})
#host = host
#port = options[:port].present? ? options[:port] : 443
#password = options[:password].present? ? options[:password] : ''
#vpncmd_bin_path = options[:vpncmd_bin_path].present? ? options[:vpncmd_bin_path] : '/usr/local/bin/vpncmd'
#timeout = options[:timeout].present? ? options[:timeout] : 5
#hubs = {}
#hub_cache_dirty = true
#hub_password_cache = {}
end
...
end
This test example:
it "should have a default constructor that takes no argument" do
s = SoftEther::Server.new()
expect(s.host).to be_equal('localhost')
expect(s.port).to be_equal('443')
expect(s.timeout).to be_equal(5)
expect(s.vpncmd_bin_path).to be_equal('/usr/local/bin/vpncmd')
expect(s.password).to be_equal('')
end
And rspec gives me the following result with Rails 4.2.6, jruby-9.0.5.0 and 3.4.4:
1) SoftEtherSever should have a default constructor that takes no argument
Failure/Error: expect(s.host).to be_equal('localhost')
expected `"localhost".equal?("localhost")` to return true, got false
# ./spec/poro/softether_spec.rb:19:in `block in (root)'
What did I do wrong?
equal? checks whether two instances are the same. But it returns false when two strings contains the same value but refers to different objects:
"foo".equals?("foo")
# => false
What you should really use is eq()
expect(s.host).to eq('localhost')
Just to add an edge case to Simone's answer:
If you were to freeze the strings in question, you would get the result you expected:
irb(main):001:0> 'test'.equal? 'test'
=> false
irb(main):002:0> 'test'.freeze.equal? 'test'.freeze
=> true
In Ruby 2.3, this can be done by adding
# frozen_string_literal: true
to the top of the Ruby file.
With that said, Simone is right. You should use the eq matcher unless you truly want to test that you are using the same exact object instance. Then using equal is in order.

Why are my environment variables not being seen - rspec 3.1.0, rails engine 4.1.9

I have the following test file, in the before(:each)do ... end section I am using some enviroment variables, these - because I have no idea if spec reads from dummy or the engine config/ its self, are placed in both the dummy's config/application.yml and the engines root config/application.yml
require 'spec_helper'
describe Xaaron::UsersController, :type => :controller do
routes { Xaaron::Engine.routes }
context "publish created users" do
before(:each) do
Xaaron.configuration.reset
no_user_member_roles_relation
binding.pry
Xaaron.configuration.publish_to_black_bird = true
Xaaron.configuration.black_bird_api_url = ENV['BLACKBIRD_API_URL']
Xaaron.configuration.black_bird_api_key = ENV['BLACKBIRD_API_KEY']
end
it "should publish to blackbird" do
VCR.use_cassette 'publisher/create_user_response' do
post :create, user: {
first_name: 'sample', user_name: 'johnq',
email: 'sample_sample_sample#sample.com', email_confirmation: 'sample_sample_sample#sample.com',
password: 'SamplePassword', password_confirmation: 'SamplePassword'
}
expect(response).to redirect_to login_path
end
end
end
end
Now because this is local host testing, I don't care if I show you these (both apps are on localhost):
The application.yml file in both locations contain the following:
BLACKBIRD_API_KEY: d1cab0ac344f5074797b8c2b9ce90070e8af3405de8431fa17150a0f3420b41e
BLACKBIRD_API_URL: http://site.local.com::5000/api/internal/v1/
When the test is run it fails, because of a nil:NilClass, which happens in this method:
# We need to check if Blackbird is alive.
def is_alive?
# Do a simple get and capture the response
response = HTTParty.get(
Xaaron.configuration.black_bird_api_url + 'alive/',
:headers => { "HTTP_AUTHORIZATION" => Xaaron.configuration.black_bird_api_key }
)
# If the response is 200 return true, else reeturn thje response code
if response.code == 200
return true
else
return response.code
end
end
The exact line is:
Xaaron.configuration.black_bird_api_url + 'alive/'
When I place a binding.pry above this line and do Xaaron.configuration.black_bird_api_url I get nil, so when I do ENV['BLACKBIRD_API_URL'] I also get nil
So My question is:
Why are my environment variables nil when they are oh so very clearly set.
Rails doesn't load environment variables by default unless you specify them from the command line when you invoke Rails. You want to use figaro or dotenv to be able to load environment variables from a file. I believe you want figaro, since it reads from application.yml rather than a .env file.

Rspec: add some header requests inside routing specs

I'm working on a Rails application having a REST API in JSON format and versioned (according to this excellent Ryan's cast: http://railscasts.com/episodes/350-rest-api-versioning).
For instance, there is a spec/requests spec:
require 'spec_helper'
describe "My Friends" do
describe "GET /my/friends.json" do
it "should get my_friends_path" do
get v1_my_friends_path, {}, {'HTTP_ACCEPT' => 'application/vnd.myapp+json; level=1'}
response.status.should be(401)
end
end
end
And it works well. But (keeping this example) how can we write the routing spec? For instance this spec isn't correct:
require 'spec_helper'
describe "friends routing" do
it "routes to #index" do
get("/my/friends.json", nil, {'HTTP_ACCEPT' => 'application/vnd.myapp+json; level=1'}).
should route_to({ action: "index",
controller: "api/v1/private/my/friends",
format: "json" })
end
end
I tried different ways (such as request.headers['Accept'] and #request.headers['Accept'], where request is undefined and #request is nil); I really don't see how to do.
I'm on Ruby 1.9.3, Rails 3.2.6 and rspec-rails 2.11.0. Thanks.
By combining the ideas from Cristophe's and Piotr's answers, I came up with a solution that worked for me. I'm using rspec and rails 3.0.
it 'should route like i want it to' do
Rack::MockRequest::DEFAULT_ENV["HTTP_ACCEPT"] = "*/*"
{get: "/foo/bar"}.
should route_to(
controller: 'foo',
action: 'bar',
)
Rack::MockRequest::DEFAULT_ENV.delete "HTTP_ACCEPT"
end
Currently you can't send addititional Headers in Routing specs, this is due to line 608 in actionpack-3.2.5/lib/action_dispatch/routing/route_set.rb where it says:
env = Rack::MockRequest.env_for(path, {:method => method})
path is your requested path "/my/friends.json" and method is :get
The resulting env contains something like the following:
{
"rack.version"=>[1, 1],
"rack.input"=>#<StringIO:0xb908f5c>,
"rack.errors"=>#<StringIO:0xb908fac>,
"rack.multithread"=>true,
"rack.multiprocess"=>true,
"rack.run_once"=>false,
"REQUEST_METHOD"=>"GET",
"SERVER_NAME"=>"your-url.com", # if path was http://your-url.com/
"SERVER_PORT"=>"80",
"QUERY_STRING"=>"",
"PATH_INFO"=>"/",
"rack.url_scheme"=>"http",
"HTTPS"=>"off",
"SCRIPT_NAME"=>"",
"CONTENT_LENGTH"=>"0"
}
If you are able to mock Rack::MockRequest::env_for it should be possible to inject other headers than the ones generated by env_for (see Hash above).
Other than that you are currently using the route_to matcher wrong, you should call it on a Hash where you specify the method and the path like this:
{ get: '/' }.should route_to(controller: 'main', action: 'index')
Let us know if you were able to Mock out that env_for and let it return your headers, would be nice to know.
Regards
Christoph
before do
ActionDispatch::TestRequest::DEFAULT_ENV["action_dispatch.request.accepts"] = "application/vnd.application-v1+json"
end
after do
ActionDispatch::TestRequest::DEFAULT_ENV.delete("action_dispatch.request.accepts")
end
You can using rspec's and_wrap_original to mock the Rack::MockRequest.env_for method:
expect(Rack::MockRequest).to receive(:env_for).and_wrap_original do |original_method, *args, &block|
original_method.call(*args, &block).tap { |hash| hash['HTTP_ACCEPT'] = 'application/vnd.myapp+json; level=1' }
end
For Rails 3 and 4 I had done the following in an RSpec around hook:
around do |example|
Rack::MockRequest::DEFAULT_ENV['HTTP_ACCEPT'] = 'application/vnd.vendor+json; version=1'
example.run
Rack::MockRequest::DEFAULT_ENV.delete 'HTTP_ACCEPT'
end
Since Rack >= 2.0.3 (used by Rails 5) the Rack::MockRequest::DEFAULT_ENV hash is frozen.
You can redefine the constant and use Kernel.silence_warnings to silence the Ruby warnings:
around do |example|
silence_warnings do
Rack::MockRequest::DEFAULT_ENV = Rack::MockRequest::DEFAULT_ENV.dup
end
Rack::MockRequest::DEFAULT_ENV['HTTP_ACCEPT'] = 'application/vnd.vendor+json; version=1'
example.run
Rack::MockRequest::DEFAULT_ENV.delete 'HTTP_ACCEPT'
end
It's a bit of hack but it works like a charm.

Integration testing Rails API with basic authentication

I'm trying to get a test signing in using basic authentication. I've tried a few approaches. See code below for a list of failed attempts and code. Is there anything obvious I'm doing wrong. Thanks
class ClientApiTest < ActionController::IntegrationTest
fixtures :all
test "adding an entry" do
# No access to #request
##request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("someone#somemail.com:qwerty123")
# Not sure why this didn't work
#session["warden.user.user.key"] = [User, 1]
# This didn't work either
# url = URI.parse("http://localhost.co.uk/diary/people/1/entries.xml")
# req = Net::HTTP::Post.new(url.path)
# req.basic_auth 'someone#somemail.com', 'qwerty123'
post "/diary/people/1/entries.xml", {:diary_entry => {
:drink_product_id => 4,
:drink_amount_id => 1,
:quantity => 3
},
}
puts response.body
assert_response 200
end
end
It looks like you might be running rails3 -- Rails3 switched over to Rack::test so the syntax is different. You pass in an environment hash to set your request variables like headers.
Try something like:
path = "/diary/people/1/entries.xml"
params = {:diary_entry => {
:drink_product_id => 4,
:drink_amount_id => 1,
:quantity => 3}
env=Hash.new
env["CONTENT_TYPE"] = "application/json"
env["ACCEPT"] = "application/json"
env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("someone#somemail.com:qwerty123")
get(end_point, params, env)
This could work too, but it might be a sinatra only thing:
get '/protected', {}, {'HTTP_AUTHORIZATION' => encode_credentials('go', 'away')}
Sinatra test credit
This works for me in Rails 3
#request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials('username', 'password')
get :index

Resources