I am trying to test my graphql schema without any need for authentication.
I have added skip_before_action :verify_authenticity_token to the GraphqlController, and when using postman (copied by a curl request from graphiql), and I am seeing a successful query in development mode.
In postman I have the query in the body {"query":"{\n user(id: 1) {\n id\n created_at\n updated_at\n jwt\n}\n}\n","variables":null,"operationName":null}, and Content-Type application/json in the header and this works fine.
Now in test mode, I am hitting the auth initializer for Omniauth:
Rails.application.config.middleware.use OmniAuth::Builder do
provider(
:auth0,
Auth0::Config["app_client_id"],
Auth0::Config["app_client_secret"],
Auth0::Config["domain"],
callback_path: "/auth/auth0/callback"
)
end
Although I don't want to since I don't want any headers required in this post request.
Here is my rspec request:
require 'graphlient'
RSpec.shared_context "GraphQL Client", shared_context: :metadata do
let(:client) do
Graphlient::Client.new('https://api.example.org/graphql') do |client|
client.http do |h|
h.connection do |c|
c.use Faraday::Adapter::Rack, app
end
end
end
end
end
and here's the actual test
it 'retrieves schema' do
expect(client.schema).to be_a GraphQL::Schema
end
with the error:
Failure/Error:
expect { client.schema.status }
.to raise_error(Graphlient::Errors::ServerError)
expected Graphlient::Errors::ServerError, got #<ArgumentError: Received wrong number of arguments. [nil, nil, nil, {:callback_path=>"/auth/auth0/callback"}]> with backtrace:
# /usr/local/bundle/gems/omniauth-auth0-1.4.2/lib/omniauth/strategies/auth0.rb:41:in `initialize'
I think I got it! I forgot to add keys for test in config/auth0.yml.
It was a hidden file.
Related
I am trying to test that 'ApiClient.do_request' is sending the correct arguments and calling 'RestClient::Request.execute.' However, my below specs aren't working as intended. RSpec is unable to properly call 'RestClient::Request.execute,' as the method's 'response' variable keeps coming back as nil, which is why I get the below TypeError when JSON.parse(response) is called. What's strange to me is that I have no issues when walking through the process in Rails console. I'm sure there's something obvious I'm missing here... Any ideas why this is happening, or perhaps suggestions on how I can test this more effectively?
Model:
class ApiClient < ActiveRecord::Base
BASE_PATH = "http://api.bandsintown.com/artists/"
APP_ID = ENV["APP_ID"]
def do_request(method:, base_url:BASE_PATH, app_id:APP_ID, url:, format:"json", options: nil)
response = RestClient::Request.execute(method: method.to_sym,
url: "#{base_url}#{url}.#{format}?api_version=2.0&app_id=#{app_id}#{options}",
timeout: 10)
JSON.parse(response)
end
Spec:
describe ApiClient do
describe "do_request" do
context "when all required arguments are present" do
it "RestClient executes the request" do
#test_client = ApiClient.new
expect(RestClient::Request).to receive(:execute).with(:method=>:get, :url=> "http://api.bandsintown.com/artists/Damien%20Jurado/events/search.json?api_version=2.0&app_id=ShowBoatTest&location=San+Diego,CA&radius=15", :timeout=>10)
#test_client.do_request(method:"get", app_id:"ShowBoatTest",url:"Damien%20Jurado/events/search",options:"&location=San+Diego,CA&radius=15")
end
end
end
RSpec Error:
1) ApiClient do_request when all required arguments are present RestClient executes the request
Failure/Error: JSON.parse(response)
TypeError:
no implicit conversion of nil into String
# /Users/slamflipstrom/.rvm/gems/ruby-2.1.5/gems/json-1.8.3/lib/json/common.rb:155:in `initialize'
# /Users/slamflipstrom/.rvm/gems/ruby-2.1.5/gems/json-1.8.3/lib/json/common.rb:155:in `new'
# /Users/slamflipstrom/.rvm/gems/ruby-2.1.5/gems/json-1.8.3/lib/json/common.rb:155:in `parse'
# ./app/models/api_client.rb:12:in `do_request'
# ./spec/models/api_client_spec.rb:13:in `block (4 levels) in <top (required)>'
Not considering minor syntax errors like api_version=2.0&app_id=ShowBoat in your ApiClient class, the problem seems to be caused by your RestClient::Request.execute request returning nil.
Debug or inspect the value of response and make sure it is not nil.
class ApiClient < ActiveRecord::Base
BASE_PATH = "http://api.bandsintown.com/artists/"
APP_ID = ENV["APP_ID"]
def do_request(method:, base_url:BASE_PATH, app_id:APP_ID, url:, format:"json", options: nil)
response = RestClient::Request.execute(method: method.to_sym,
url: "#{base_url}#{url}.#{format}?api_version=2.0&app_id=#{app_id}#{options}",
timeout: 10)
puts response.inspect
response ? JSON.parse(response) : []
end
On the other hand, you are testing ApiClient class's implementation. In other words, you're testing that the class is internally calling some other class in specific manner. If you decide to replace the RestClient with HTTParty for example at some later time, your tests will fail (even though they shouldn't, because your class is still working correctly).
So, testing the class's implementation makes your tests "fragile". Instead, give an input and test for the expected correct output.
describe ApiClient do
context "with all required arguments" do
describe "#do_request" do
it "returns object in correct format" do
response = subject.do_request(method:"get", app_id:"ShowBoatTest",url:"Damien%20Jurado/events/search",options:"&location=San+Diego,CA&radius=15")
expect(response.body).to include(title: "Damien Jurado # The Casbah in San Diego, CA")
# etc.
end
end
end
end
I have a problem, I can run a test that uses vcr on its own and it works, it creates the cassette and it uses that on the next test. Great.
The problem is when I run all my tests together this particular test fails, because webmock disables http connections, I have seen this example on the Github repo page that explains how to expect real and not stubbed requests
My question is how Do I say: Allow Http connections for requests UNLESS there is a cassette. It should also CREATE the cassette when HTTP connections are allowed.
The VCR Settings
require 'vcr'
VCR.configure do | c |
if !ARGV.first.nil?
c.default_cassette_options = { :record => :new_episodes, :erb => true }
c.filter_sensitive_data('<BLACKBIRD_API_KEY>') {YAML.load(File.read('config/application.yml'))['BLACKBIRD_API_KEY'].to_s}
c.filter_sensitive_data('<BLACKBIRD_API_URL>') {YAML.load(File.read('config/application.yml'))['BLACKBIRD_API_URL'].to_s}
c.debug_logger = File.open(ARGV.first, 'w')
c.cassette_library_dir = 'spec/vcr'
c.hook_into :webmock
end
end
the above if statement exists because not EVERY test creates a cassette. So we want them to run when a cassette isn't needed.
The Test
require 'spec_helper'
describe Xaaron::Publishers::Users do
context "publish created users" do
before(:each) do
Xaaron.configuration.reset
no_user_member_roles_relation
Xaaron.configuration.publish_to_black_bird = true
Xaaron.configuration.black_bird_api_url = YAML.load(File.read('config/application.yml'))['BLACKBIRD_API_URL']
Xaaron.configuration.black_bird_api_key =YAML.load(File.read('config/application.yml'))['BLACKBIRD_API_KEY']
end
it "should publish to blackbird" do
VCR.use_cassette 'publisher/create_user_response' do
expect(
Xaaron::Publishers::Users.publish_new_user({user: {
first_name: 'adsadsad', user_name: 'sasdasdasdsa' ,
email: 'asdassad#sample.com', auth_token: 'asdsadasdasdsa'
}}).code
).to eql 200
end
end
end
end
Runs fine on its own, creates the cassette, fails when run with all other tests due to webmock.
The Failure
Failure/Error: Xaaron::Publishers::Users.publish_new_user({user: {
WebMock::NetConnectNotAllowedError:
Real HTTP connections are disabled. Unregistered request: GET some_site_url_here with headers {'Http-Authorization'=>'api_key_here', 'User-Agent'=>'Typhoeus - https://github.com/typhoeus/typhoeus'}
You can stub this request with the following snippet:
stub_request(:get, "some site url here").
with(:headers => {'Http-Authorization'=>'some api key here', 'User-Agent'=>'Typhoeus - https://github.com/typhoeus/typhoeus'}).
to_return(:status => 200, :body => "", :headers => {})
I have an Rspec test that makes a POST request and sends a header with it because authentication is required:
it 'creates a client for an organization if none exists' do
VCR.use_cassette('create_client') do
post "/organizations/#{#organization.id}/clients", nil, { 'TOKEN' => #token }
expect(last_response.status).to be(201)
expect(json_response).to be_a(Hash)
expect(json_response["organization_id"]).to eq(#organization.id)
expect(json_response.keys).to include('auth_token')
end
expect(#organization.client).to_not be_nil
end
This passes without problems when I run the tests on my local machine, but will fail on the CI server (in this case Codeship):
Failure/Error: post "/organizations/#{#organization.id}/clients", nil, { 'TOKEN' => #token }
MyModule::MyClass::Errors::InvalidOptionError:
bad URI(is not URI?):
When I remove the header part from the post request, the test will obviously fail because the token header is required, but the post request will go through without errors.
Any ideas why this is happening? I'd appreciate any input.
Turns out it was a problem with Elasticsearch on Codeship.
I'm building an API in Rails 4 using rspec_api_documentation and have been really impressed. Having opted to use DoorKeeper to secure my endpoints, I'm successfully able to test this all from the console, and got it working.
Where I am having difficulty now is how to spec it out, and stub the token.
DoorKeeper's documentation suggests using the following:
describe Api::V1::ProfilesController do
describe 'GET #index' do
let(:token) { stub :accessible? => true }
before do
controller.stub(:doorkeeper_token) { token }
end
it 'responds with 200' do
get :index, :format => :json
response.status.should eq(200)
end
end
end
However, I've written an acceptance test in line with rspec_api_documentation. This is the projects_spec.rb that I've written:
require 'spec_helper'
require 'rspec_api_documentation/dsl'
resource "Projects" do
header "Accept", "application/json"
header "Content-Type", "application/json"
let(:token) { stub :accessible? => true }
before do
controller.stub(:doorkeeper_token) { token }
end
get "/api/v1/group_runs" do
parameter :page, "Current page of projects"
example_request "Getting a list of projects" do
status.should == 200
end
end
end
When I run the test I get the following:
undefined local variable or method `controller' for #<RSpec::Core
I suspect this is because it's not explicitly a controller spec, but as I said, I'd rather stick to this rspec_api_documentation way of testing my API.
Surely someone has had to do this? Is there another way I could be stubbing the token?
Thanks in advance.
I had the same problem and I created manually the access token with a specified token. By doing that, I was then able to use my defined token in the Authorization header :
resource "Projects" do
let(:oauth_app) {
Doorkeeper::Application.create!(
name: "My Application",
redirect_uri: "urn:ietf:wg:oauth:2.0:oob"
)
}
let(:access_token) { Doorkeeper::AccessToken.create!(application: oauth_app) }
let(:authorization) { "Bearer #{access_token.token}" }
header 'Authorization', :authorization
get "/api/v1/group_runs" do
example_request "Getting a list of projects" do
status.should == 200
end
end
end
I wouldn't recommend stubbing out DoorKeeper in an rspec_api_documentation acceptance test. One of the benefits of RAD is seeing all of the headers in the examples that it generates. If you're stubbing out OAuth2, then people reading the documentation won't see any of the OAuth2 headers while they're trying to make a client.
I'm also not sure it's possible to do this nicely. RAD is very similar to a Capybara feature test and a quick search makes it seem difficult to do.
RAD has an OAuth2MacClient which you can possibly use, here.
require 'spec_helper'
resource "Projects" do
let(:client) { RspecApiDocumentation::OAuth2MACClient.new(self) }
get "/api/v1/group_runs" do
example_request "Getting a list of projects" do
status.should == 200
end
end
end
I am building a 2-Legged OAuth provider for my api. Everything is hooked up properly and I can make signed calls from the rails console. The problem I have is that I am having trouble integrating OAuth into the controller_spec.
Here is an example of a working call on my server:
coneybeare $ rails c test
Loading test environment (Rails 3.2.0)
rails test: main
>> consumer = OAuth::Consumer.new("one_key", "MyString", :site => [REDACTED])
# => #<OAuth::Consumer:0x007f9d01252268 #key="one_key", #secret="MyString", #options={:signature_method=>"HMAC-SHA1", :request_token_path=>"/oauth/request_token", :authorize_path=>"/oauth/authorize", :access_token_path=>"/oauth/access_token", :proxy=>nil, :scheme=>:header, :http_method=>:post, :oauth_version=>"1.0", :site=>[REDACTED]}>
ruby: main
>> req = consumer.create_signed_request(:get, "/api/v1/client_applications.json", nil)
# => #<Net::HTTP::Get GET>
ruby: main
>> res = Net::HTTP.start([REDACTED]) {|http| http.request(req) }
# => #<Net::HTTPOK 200 OK readbody=true>
ruby: main
>> puts res.body
{"client_applications":[{"id":119059960,"name":"FooBar1","url":"http://test1.com"},{"id":504489040,"name":"FooBar2","url":"http://test2.com"}]}
# => nil
And here is what I am doing in my controller tests:
require 'oauth/client/action_controller_request'
describe Api::ClientApplicationsController do
include OAuthControllerSpecHelper
…
…
it "assigns all client_applications as #client_applications" do
consumer = OAuth::Consumer.new("one_key", "MyString", :site => [REDACTED])
ActionController::TestRequest.use_oauth=true
#request.configure_oauth(consumer)
#request.apply_oauth!
puts "request.env['Authorization'] = #{#request.env['Authorization']}"
get :index, {:api_version => 'v1', :format => :json}
response.should be_success # Just this for now until I can get authorization, then proper controller testing
end
end
The output of that test:
request.env['Authorization'] = OAuth oauth_consumer_key="one_key", oauth_nonce="gzAbvBSWyFtIYKfuokMAdu6VnH39EHeXvebbH2qUtE", oauth_signature="juBkJo5K0WLu9mYqHVC3Ar%2FATUs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1328474800", oauth_version="1.0"
1) Api::ClientApplicationsController GET index assigns all client_applications as #client_applications
Failure/Error: response.should be_success
expected success? to return true, got false
And the corresponding server call from the rails log:
Processing by Api::ClientApplicationsController#index as JSON
Parameters: {"api_version"=>1}
Rendered text template (0.0ms)
Filter chain halted as #<OAuth::Controllers::ApplicationControllerMethods::Filter:0x007f85a51a8858 #options={:interactive=>false, :strategies=>:two_legged}, #strategies=[:two_legged]> rendered or redirected
Completed 401 Unauthorized in 15ms (Views: 14.1ms | ActiveRecord: 0.0ms)
(0.2ms) ROLLBACK
I just can't figure out why it's not working :/ Am I making an obvious mistake?
If you'd like to test it in a request spec and actually need to test without stubbing, you can build an OAuth consumer and sign a request like this:
#access_token = FactoryGirl.create :access_token
#consumer = OAuth::Consumer.new(#access_token.app.key, #access_token.app.secret, :site => "http://www.example.com/")
#path = "/path/to/request"
#request = #consumer.create_signed_request(:get, #path, OAuth::AccessToken.new(#consumer, #access_token.token, #access_token.secret))
get #path, nil, { 'HTTP_AUTHORIZATION' => #request.get_fields('authorization').first }
I would take a look as to how the Omniauth test helpers work, specifically these files: https://github.com/intridea/omniauth/tree/master/lib/omniauth/test. See their wiki page on integration testing for ideas of how this is set up. I realize that you're building a provider, not a client, but this may be a good starting point. Also, as some of the commenters have already said, I don't know if you can do this with a controller test; you may need a request or integration test to fully simulate the rack environment.
Turns out that the best way to test my controller was the simplest as well. Instead of trying to sign each test so the controller gets the right information (something that indeed does belong in a request spec not a controller spec), I figured out that I could just give the controller the information it needed manually.
To do this, I simply had to stub 2 methods:
fixtures :client_applications
before(:each) do
#client_application1 = client_applications(:client_application1)
Api::ClientApplicationsController::Authenticator.any_instance.stub(:allow?).and_return(true)
controller.stub(:client_application).and_return(#client_application1)
end
Stubbing the allow? method caused the rack auth to be fooled into thinking it was authenticated. allow? also set the client_application based on the credentials though, so I had to stub that as well. Now that the auth is out of the way, I can test my controller properly.