Rspec is different from the app - ruby

I'm currently working on a Ruby/Sinatra App and now I'm stuck because my rspec testing is not working properly on the controller. But when I tried my API using curl or web the code just works!.
Here are my file, spesificly on that line of code
listproduct_controller.rb
get '/' do
products = Product.all
payload = []
products.each do |product|
payload.push({ :exhibit_name => product.exhibit_name, :icon => product.icon })
end
return {
:status => 'SUCCESS',
:message => 200,
:payload => payload
}.to_json
end
and here are my spec file
listproduct_controller_spec.rb
context "GET to /products" do
before { allow(Product).to receive(:all) }
before { allow(Product).to receive(:find_by) }
it "returns status 200 OK" do
get '/'
puts(last_response)
expect(last_response).to be_ok
end
it "show a list of product's name and its icon" do
get '/'
#products = Product.all.to_a
expect(last_response.body).to include_json(#products)
end
end
When I puts the last_response on spec it shows this
500
{"Content-Type"=>"text/html", "Content-Length"=>"212150"}
#<Rack::BodyProxy:0x0000000480b1d8>
but when im using curl or apps it just works and return
200 status code with the payload
Can anyone help me what I did wrong?
UPDATE :
I solved it, it was the problem on the database, where all the product in the development database were not on the test database so it returns 500 of empty database.

Related

Set http status code 201 to an API response in action controller rails

I'm new to rails and I'm building a Rails app that will function as an API. Currently I don't have any models or database just an Api::ProductController controller:
class Api::ProductController < ApplicationController
def create
Rails.logger.info "product was created and the parameters are #{product_params[:name]}, #{product_params[:age]}"
end
private
def product_params
params.permit(:name, :age)
end
end
. As I continue and wrote the request Rspec:
RSpec.describe Api::productsController, type: :request do
it "creates a product" do
post "/api/products", params: { name: "name", age: "22"}
expect(response).to have_http_status(:created)
expect(response.body).to include("product was successfully created.")
end
end
But when I run the request rspec test on the command line I get the following error:
Failure/Error: expect(response).to have_http_status(:created)
expected the response to have status code :created (201) but it was :no_content (204)
My question is how can I set the status code to :created (201)? is the Head method a good approach? any solution or guiding would be appreciated!
It is common to return json presentation of created item.
Now there is no returning data from your action.
Try to use render json: product_params.to_json, status: :created
def create
Rails.logger.info "product was created and the parameters are #{product_params[:name]}, #{product_params[:age]}"
render status: :created
end

How to perform an HTTP redirect (302) using Rack?

I am messing around with my first Rack application (this is just for experimentation).
When a call comes in I do something like this:
class Application
def call(env)
# Totally ignore favicons for the time being
if env['PATH_INFO'] == '/favicon.ico'
return [ 404, {'Content-Type' => 'text/html'}, [] ]
elsif env['PATH_INFO'] == '/'
return [ 302, {'http-equiv' => "refresh", 'content' => "2;url=http://google.com"}, [] ]
end
...
I know this is horrible... but, again, this isn't a serious project.
I'm trying to figure out how to do the redirect. What I have does not work. Basically, when you hit / on my site I want to redirect that require to google.com.
Here is working app with re-direct to Google
require 'rack'
require 'rack/server'
class HelloWorld
def response
[ 302, {'Location' =>"http://google.com"}, [] ]
end
end
class HelloWorldApp
def self.call(env)
HelloWorld.new.response
end
end
Rack::Server.start :app => HelloWorldApp

Post to Sinatra API with hyperresource

I built an app in Sinatra with Roar (hal/JSON) and now I'm trying to post a new item from my client to this API.
In my Sinatra app I have routes like these:
get '/todos/new' do
#pagetitle = 'New Todo'
#todo = Todo.new
#todo.extend(TodoRepresenter)
#todo.to_json
erb :'todos/new'
end
post "/todos" do
#todo = Todo.new(params[:todo])
if #todo.save
redirect "todos/#{#todo.id}"
else
erb :"todos/new"
end
end
And my client.rb looks like this:
require 'hyperresource'
class ApiRequest < HyperResource
api = ApiRequest.new(root: 'http://127.0.0.1:9393',
headers: {'Accept' => 'application/vnd.http://127.0.0.1:9393.v1+json'})
api.post '/todos/new', { :title => "a"}
This doesn't work. The only way of get a working client is the get function:
require 'hyperresource'
class ApiRequest < HyperResource
api = ApiRequest.new(root: 'http://127.0.0.1:9393/todos/13',
headers: {'Accept' => 'application/vnd.http://127.0.0.1:9393.v1+json'})
todo = api.get
output = todo.body
puts output
I don't know how to solve this, and the Github page doesn't tell me either.
I changed the API slightly:
get '/todos' do
#pagetitle = 'New Todo'
#todo = Todo.new
erb :'todos/new'
end
post "/todos/new" do
#todo = Todo.new(params[:todo])
#todo.extend(TodoRepresenter)
#todo.to_json
if #todo.save
redirect "todos/#{#todo.id}"
else
erb :"todos/new"
end
end
and also the way of posting in my client:
todo = api.get.new_todo.post(title: "Test")
In my API console I now get:
D, [2014-11-04T17:11:41.875218 #11111] DEBUG -- : Todo Load (0.1ms) SELECT "todos".* FROM "todos" WHERE "todos"."id" = ? LIMIT 1 [["id", 0]]
ActiveRecord::RecordNotFound - Couldn't find Todo with 'id'=new:
and a lot of other code.
In my client console I get a lot of code, with a hyper resource server error and a lot of other code.

Ruby stubbing with faraday, can't get it to work

Sorry for the title, I'm too frustrated to come up with anything better right now.
I have a class, Judge, which has a method #stats. This stats method is supposed to send a GET request to an api and get some data as response. I'm trying to test this and stub the stats method so that I don't perform an actual request. This is what my test looks like:
describe Judge do
describe '.stats' do
context 'when success' do
subject { Judge.stats }
it 'returns stats' do
allow(Faraday).to receive(:get).and_return('some data')
expect(subject.status).to eq 200
expect(subject).to be_success
end
end
end
end
This is the class I'm testing:
class Judge
def self.stats
Faraday.get "some-domain-dot-com/stats"
end
end
This currently gives me the error: Faraday does not implement: get
So How do you stub this with faraday? I have seen methods like:
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get('http://stats-api.com') { [200, {}, 'Lorem ipsum'] }
end
But I can't seem to apply it the right way. What am I missing here?
Note that Faraday.new returns an instance of Faraday::Connection, not Faraday. So you can try using
allow_any_instance_of(Faraday::Connection).to receive(:get).and_return("some data")
Note that I don't know if returning "some data" as shown in your question is correct, because Faraday::Connection.get should return a response object, which would include the body and status code instead of a string. You might try something like this:
allow_any_instance_of(Faraday::Connection).to receive(:get).and_return(
double("response", status: 200, body: "some data")
)
Here's a rails console that shows the class you get back from Faraday.new
$ rails c
Loading development environment (Rails 4.1.5)
2.1.2 :001 > fara = Faraday.new
=> #<Faraday::Connection:0x0000010abcdd28 #parallel_manager=nil, #headers={"User-Agent"=>"Faraday v0.9.1"}, #params={}, #options=#<Faraday::RequestOptions (empty)>, #ssl=#<Faraday::SSLOptions (empty)>, #default_parallel_manager=nil, #builder=#<Faraday::RackBuilder:0x0000010abcd990 #handlers=[Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]>, #url_prefix=#<URI::HTTP:0x0000010abcd378 URL:http:/>, #proxy=nil>
2.1.2 :002 > fara.class
=> Faraday::Connection
Coming to this late, but incase anyone else is too, this is what worked for me - a combination of the approaches above:
let(:json_data) { File.read Rails.root.join("..", "fixtures", "ror", "501100000267.json") }
before do
allow_any_instance_of(Faraday::Connection).to receive(:get).and_return(
double(Faraday::Response, status: 200, body: json_data, success?: true)
)
end
Faraday the class has no get method, only the instance does. Since you are using this in a class method what you can do is something like this:
class Judge
def self.stats
connection.get "some-domain-dot-com/stats"
end
def self.connection=(val)
#connection = val
end
def self.connection
#connection ||= Faraday.new(some stuff to build up connection)
end
end
Then in your test you can just set up a double:
let(:connection) { double :connection, get: nil }
before do
allow(connection).to receive(:get).with("some-domain-dot-com/stats").and_return('some data')
Judge.connection = connection
end
I ran into the same problem with Faraday::Adapter::Test::Stubs erroring with Faraday does not implement: get. It seems you need to set stubs to a Faraday adapter, like so:
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.get("some-domain-dot-com/stats") { |env| [200, {}, 'egg'] }
end
test = Faraday.new do |builder|
builder.adapter :test, stubs
end
allow(Faraday).to receive(:new).and_return(test)
expect(Judge.stats.body).to eq "egg"
expect(Judge.stats.status).to eq 200
A better way to do this, rather than using allow_any_instance_of, is to set the default connection for Faraday, so that Faraday.get will use the connection you setup in your tests.
For example:
let(:stubs) { Faraday::Adapter::Test::Stubs.new }
let(:conn) { Faraday.new { |b| b.adapter(:test, stubs) } }
before do
stubs.get('/maps/api/place/details/json') do |_env|
[
200,
{ 'Content-Type': 'application/json' },
{ 'result' => { 'photos' => [] } }.to_json
]
end
Faraday.default_connection = conn
end
after do
Faraday.default_connection = nil
end

Standardizing api responses in a modular Sinatra application

I'm developing an api as a modular Sinatra web application and would like to standardize the responses that are returned without having to do so explicitly. I thought this could be achieved by using middleware but it fails in most scenarios. The below sample application is what I have so far.
config.ru
require 'sinatra/base'
require 'active_support'
require 'rack'
class Person
attr_reader :name, :surname
def initialize(name, surname)
#name, #surname = name, surname
end
end
class MyApp < Sinatra::Base
enable :dump_errors, :raise_errors
disable :show_exceptions
get('/string') do
"Hello World"
end
get('/hash') do
{"person" => { "name" => "john", "surname" => "smith" }}
end
get('/array') do
[1,2,3,4,5,6,7, "232323", '3245235']
end
get('/object') do
Person.new('simon', 'hernandez')
end
get('/error') do
raise 'Failure of some sort'
end
end
class ResponseMiddleware
def initialize(app)
#app = app
end
def call(env)
begin
status, headers, body = #app.call(env)
response = {'status' => 'success', 'data' => body}
format(status, headers, response)
rescue ::Exception => e
response = {'status' => 'error', 'message' => e.message}
format(500, {'Content-Type' => 'application/json'}, response)
end
end
def format(status, headers, response)
result = ActiveSupport::JSON.encode(response)
headers["Content-Length"] = result.length.to_s
[status, headers, result]
end
end
use ResponseMiddleware
run MyApp
Examples (in JSON):
/string
Expected: {"status":"success","data":"Hello World"}
Actual: {"status":"success","data":["Hello World"]}
/hash (works)
Expected: {"status":"success","data":{"person":{"name":"john","surname":"smith"}}}
Actual: {"status":"success","data":{"person":{"name":"john","surname":"smith"}}}
/array
Expected: {"status":"success","data": [1,2,3,4,5,6,7,"232323","3245235"]}
Actual: {"status":"error","message":"wrong number of arguments (7 for 1)"}
/object
Expected: {"status":"success","data":{"name":"simon","surname":"hernandez"}}
Actual: {"status":"success","data":[]}
/error (works)
Expected: {"status":"error","message":"Failure of some sort"}
Actual: {"status":"error","message":"Failure of some sort"}
If you execute the code, you will see that /hash and /error give back the required responses, but the rest do not. Ideally, I would not like to change anything in the MyApp class. It's currently being built on top of Sinatra 1.3.3, ActiveSupport 3.2.9 and Rack 1.4.1.
With some help from #sinatra on irc.freenode.org, I managed to get it down to what I want. I added the following to MyApp:
def route_eval
result = catch(:halt) { super }
throw :halt, {"result" => result}
end
I then changed the following line in ResponseMiddleware:
response = {'status' => 'success', 'data' => body}
to
response = {'status' => 'success', 'data' => body["result"]}
and all my test cases passed.

Resources