I'm trying to send back user information from the backend to the frontend but I'm having a hard time understanding how this is supposed to be formatted.
CONTROLLLER
def create
user = User.new(user_params)
if user.save
auth_token = Knock::AuthToken.new(payload: { sub: user.id })
created_user = {
userEmail: user.email,
userID: user.id
}
respond_to do |format|
format.json { render json: auth_token, created_user }
end
else
respond_to do |format|
format.json { render json: { errors: user.errors.full_messages }, status: :unprocessable_entity }
end
end
end
The problem is in the success path. It's giving me this error when I try to run my test.
ERROR:
SyntaxError:
/Users/Desktop/Work/ping-party/app/controllers/api/v1/users_controller.rb:23: syntax error, unexpected '}', expecting =>
/Users/Desktop/Work/ping-party/app/controllers/api/v1/users_controller.rb:29: syntax error, unexpected keyword_end, expecting '}'
/Users/Desktop/Work/ping-party/app/controllers/api/v1/users_controller.rb:37: syntax error, unexpected keyword_end, expecting '}'
TEST:
context "POST /v1/users" do
it "create a new user" do
post '/api/v1/users',
params: {
user: {
email: "test#idea.com",
password: "password123"
}
}
res = JSON.parse(response.body)
binding.pry
expect(res).to include("jwt")
expect(res).to include("user")
expect(User.count).to eq(1)
end
end
It says there is a syntax error but for the life of me I can't see what I'm doing wrong. Can anyone help? Thank You.
The problem is here:
render json: auth_token, created_user
The syntax is incorrect. Probably (according to your tests) you want something like:
render json: { user: created_user, jwt: auth_token.token)
More info.
You can store auth_token and created_user in a hash:
auth_token = Knock::AuthToken.new(payload: { sub: user.id })
created_user = {
userEmail: user.email,
userID: user.id
}
output = { auth_token: auth_token, created_user: created_user }
...
format.json { render json: output }
Then rails can render output like json.
Related
I try testing my #UPDATE method in my Users Controller. The update of my fields (firstname, lastname, email, phone_number) works totally fine on my localhost but I can't make my test pass. Here is my code
I have this in my Users Controller :
def update
#user = User.find(params[:id])
authorize(#user)
#user.skip_reconfirmation! if #user.is_a_customer?
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to user_account_path, notice:
I18n.t('User_was_successfully_updated') }
format.json { render json: #user, status: :ok}
else
format.html { render :edit }
format.json { render json: #user.errors, status:
:unprocessable_entity }
end
end
and here is my RSPEC test :
RSpec.describe UsersController, type: :controller do
describe "PATCH update" do
let(:user) { create(:customer_user) }
before :each do
sign_in user
end
it "update the user information" do
allow(User).to receive(:find_by).and_return(user)
new_attributes = {
firstname: 'Roger',
lastname: 'Rabbit',
email: 'roger.rabbit#lapin.fr',
mobile_phone_number: '0606060606',
gender: 'female',
}
patch :update, params: { locale: I18n.locale,
id: user.id,
user: new_attributes
}
expect(user.firstname).to eq(new_attributes['firstname'])
expect(user.lastname).to eq(new_attributes['lastname'])
expect(user.email).to eq(new_attributes[:email])
expect(user.mobile_phone_number).to
eq(new_attributes[:mobile_phone_number])
end
Finally, here is the error message :
0) UsersController PATCH update user is a user update the user information
Failure/Error: expect(user.email).to eq(new_attributes[:email])
expected: "roger.rabbit#lapin.fr"
got: "customer#ecity.fr"
(compared using ==)
# ./spec/controllers/users_controller_spec.rb:29:in `block (4 levels) in <top (required)>'
Two more things :
- the test pass for firstname and lastname
- new_attributes['email'] and new_attributes['mobile_phone_number'] return nils ; that's why I symbolized key for the two last expectations
You need to reload the user object.
patch :update, ...
user.reload # ⇐ THIS
expect(user.firstname).to ...
Without reloading you have the stale object in your test and Rails has no ability to magically understand the object in the database has changed.
I have been working on a test for my function inside Ruby on Rails. However, the test (which expects a status code of :success) fails after receiving a status code 406. Here's the exact failure log:
Failure: Expected response to be a <:success>, but was <406>.
test_should_post_comment_through_token_successfully(CommentControllerTest)
test/functional/comment_controller_test.rb:271:in `block in <class:CommentControllerTest>'
I read a little about the 406 response, and found out that it stands of "Not Acceptable". so I tried setting the Accept, Content-Type, Accept-Language and Accept-Charset headers but I have had no luck.
Here's the code for my test:
test 'should post comment through token successfully' do
#params = {
id: 1,
body: "Test Comment",
username: "Bob"
}
#headers = {
"Accept" => "application/json",
"Accept-Language" => "en-US",
"Accept-Charset" => "utf-8",
"Content-Type" => "application/json",
"Token" => "abcdefg12345"
}
get :create_by_token, #params, #headers
assert_response :success
end
The create_by_token function inside the controller:
def create_by_token
#node = Node.find params[:id]
#user = User.find_by_username params[:username]
#body = params[:body]
#token = request.headers['Token']
p request.headers["Accept"]
p request.headers["Content-Type"]
p request.headers["Token"]
if #user && #user.token == #token
begin
#comment = create_comment(#node, #user, #body)
msg = {
status: :created,
message: "Created"
}
respond_to do |format|
format.xml { render xml: msg.to_xml }
format.json { render json: msg.to_json }
end
rescue CommentError
msg = {
status: :bad_request,
message: "Bad Request"
}
respond_to do |format|
format.xml { render xml: msg.to_xml }
format.json { render json: msg.to_json }
end
end
else
msg = {
status: :unauthorized,
message: "Unauthorized"
}
respond_to do |format|
format.xml { render xml: msg.to_xml }
format.json { render json: msg.to_json }
end
end
end
My route:
post '/bot/comment.:format', to: 'comment#create_by_token'
Am I missing something crucial? How do I go about solving this issue?
I would be happy to provide any other information you would need.
Seems this might be an error with respond_to do block. Kindly check with the routes whether you have configured as resources or resource.
Do update to resources than singular which will help with respond_to do block.
You can also try update your routes as/;
resources :samples, defaults: {format: :json}
Oh, stupid me. I realized that among all the params I was passing, the format was also being passed inside the URL. However, as in the test I was not mentioning a URL which I could pass in the format as the suffix (.xml or .json), I would have to mention the format inside the params explicitly. Here's the updated test code:
test 'should post comment through token successfully' do
#params = {
id: 1,
body: "Test Comment",
username: "Bob",
format: 'json'
}
#headers = {
"token" => "abcdefg12345"
}
post :create_by_token, #params, #headers
assert_response :success
end
Kudos #Sowmiya for leading me to this conclusion. Your answer was not exactly the solution I needed, but it provoked me to think.
I have custom JSON authentication API (without devise, etc). How do I render #user.authentication_token to response header instead of body?
in sessions_controller.rb
def create
if #user && #user.authenticate(params[:user][:password])
#user.sign_in_count += 1
#user.save!
render status: 200,
json: { success: true,
info: "Logged in sucessfully.",
data: { auth_token: #user.authentication_token } }
else
render status: :unprocessable_entity,
json: { success: false,
info: "Login failed." }
end
end
Try the response.header["auth_token"]
def create
if #user && #user.authenticate(params[:user][:password])
#user.sign_in_count += 1
#user.save!
render status: 200,
json: { success: true,
info: "Logged in sucessfully."}
response.headers["auth_token"] = #user.authentication_token
else
render status: :unprocessable_entity,
json: { success: false,
info: "Login failed." }
end
end
There is a headers object available in your controller action. So you can do the following:
headers['X-User-Token'] = #user.authentication_token
My app uses the stripe.js java library to generate the stripe_card_token same as ryan bates did here.
I'm trying to add unit tests to my controller and I'm getting troubles in the generation of the token. I couldn't find the function to generate the token anywhere in the Stripe API, as it seems to be available only in the javascript.
How can I generate the stripe_token in the test?
Here's the part of the controller dealing with stripe:
def save_with_payment!
if valid?
customer = Stripe::Customer.create(
description: "#{user.email} - #{user.id} - #{billing_name}",
email: billing_email,
plan: plan,
card: stripe_card_token)
self.stripe_customer_token = customer.id
plan = validate_actual_plan customer.subscriptions.data[0].plan.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card.
Your card wasn't charged. #{e.message}"
return false
end
Here's the controller:
def create
#subscription = Subscription.new(params[:subscription])
#subscription.user_id = current_user.id
#subscription.expiration_date = 1.month.from_now
#subscription.stripe_card_token = params[:subscription][:stripe_card_token]
respond_to do |format|
if #subscription.save_with_payment!
if current_user.upgrade_plan :premium
format.html { redirect_to user_trades_path(current_user), notice: 'Subscription was successfully created. Compliments you are now subscribed to the premium plan' }
format.json { render json: user_trades_path(current_user), status: :created, location: #subscription }
else
format.html { redirect_to home_pricing_path, notice: 'Error while upgrading your account, please contact us' }
format.json { render json: home_pricing_path, status: :created, location: #subscription }
end
else
format.html { render action: "new" }
format.json { render json: #subscription.errors, status: :unprocessable_entity }
end
end
end
This is the Coffeescript generating the token:
processCard: ->
card =
number: $(page.cardNumber).val()
cvc: $(page.cardCode).val()
expMonth: $(page.cardMonth).val()
expYear: $(page.cardYear).val()
Stripe.card.createToken(card, #handleStripeResponse)
Here's the test:
test "should create subscription" do
begin
StripeMock.start
card = {
number: "4242424242424242",
cvc: "123",
expMonth: 1.month.from_now.month,
expYear: 1.year.from_now.year,
}
token = Stripe.card.createToken(card)
assert_difference('Subscription.count') do
post :create, subscription: {
billing_email: #subscription.billing_email,
billing_city: #subscription.billing_city,
billing_country: #subscription.billing_country,
billing_name: #subscription.billing_name,
billing_street2: #subscription.billing_street2,
billing_street: #subscription.billing_street,
billing_zip: #subscription.billing_zip,
stripe_card_token: token,
plan: #subscription.plan }
end
assert_redirected_to subscription_path(assigns(:subscription))
ensure
StripeMock.stop
end
end
I figured that creating the token was indeed possible with the Stripe API, as described here:
https://stripe.com/docs/api#create_card_token
I had also found an issue with the plan I was going to subscribe, because I'm using StripeMock, there's no connection to the real/test stripe at all. this means that my mock doesn't know about the plans I try to subscribe.
I had to generate a little function to generate the plan as well.
here's its code:
def create_plan
plan = {
:amount => 1800,
:interval => 'month',
:name => 'premium',
:currency => 'gbp',
:id => 'premium'
}
response = Stripe::Plan.create plan
end
I'm trying to do a really simple request spec for testing my API methods in my application. Right now I'm getting 302 message when I should be getting 200 with this test.
The spec:
require 'spec_helper'
describe UsersController do
describe "#create" do
it "creates a new user " do
post '/users', user: FactoryGirl.attributes_for(:user)
expect(response.status).to eq 200
end
end
end
The factory:
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "Person #{n}" }
sequence(:username) { |n| "Person#{n}" }
sequence(:email) { |n| "person_#{n}#example.com"}
password "foobar"
password_confirmation "foobar"
end
end
The controller method:
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
What am I missing?
So you need to make the asynchronous post request, it should be something like below,
it "creates a new user " do
xhr :post, '/users', user: FactoryGirl.attributes_for(:user)
expect(response.status).to eq 200
end
Try this, it might work
To solve the issue I need to add the ACCEPT header to my request, like so:
post "/users", { user: #user } , { accept: 'application/json' }
Doing this fixed the issue which was the server not interpreting the request as expecting json in return.