Curl command to test JSON posts

1
curl -i -H "Content-Type: application/json" -H "Accept: application/json" -X POST -d '{"user":{"username_or_email":"username","password":"password"}}' http://fas/test_json_post
Posted: April 4th, 2010 | Author: jay | Filed under: Code | Tags: , , , , , | No Comments »

Lost sleep over JSON and Rack::PostBodyContentTypeParser

I’ve been fighting this issue the last couple nights. I wrote earlier about how Rack::PostBodyContentTypeParser can automagically turn a posted JSON object into a Rack / Sinatra params hash. So, I wrote some tests to make sure this was the case and moved on. Well, it turns out in real life things weren’t working and I couldn’t figure out why. Everything looked cool, but the hash wasn’t getting set when I did an AJAX call in the browser – everything was empty. I looked at everything, from the server, to the JS library, to the browser, to setting different content types in prototype.js etc… UGH!

The short of it is that Rack::PostBodyContentTypeParser requires exactly application/json in order to automagically turn the posted JSON object into Rack params and prototype.js (and jquery.js were adding an encoding type of charset=UTF-8 so the entire header entry was coming across as this CONTENT_TYPE: application/json; charset=UTF-8. So, as a fix, I’m just including the Rack::PostBodyContentTypeParser in the Sinatra application with one small change. Here’s the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
module Rack
 
  # A Rack middleware for parsing POST/PUT body data when Content-Type is
  # not one of the standard supported types, like <tt>application/json</tt>.
  #
  # TODO: Find a better name.
  #
  class PostBodyContentTypeParser
 
    # Constants
    #
    CONTENT_TYPE = 'CONTENT_TYPE'.freeze
    POST_BODY = 'rack.input'.freeze
    FORM_INPUT = 'rack.request.form_input'.freeze
    FORM_HASH = 'rack.request.form_hash'.freeze
 
    # Supported Content-Types
    #
 
    ################## turned into regex so it matches type with encoding data...
    #APPLICATION_JSON = 'application/json'.freeze
    APPLICATION_JSON = /^application\/json/.freeze
 
    def initialize(app)
      @app = app
    end
 
    def call(env)
      case env[CONTENT_TYPE]
      when APPLICATION_JSON
        env.update(FORM_HASH => JSON.parse(env[POST_BODY].read), FORM_INPUT => env[POST_BODY])
      end
      @app.call(env)
    end
 
  end
end

I tested that this worked by writing the following:

1
2
3
4
5
6
7
8
9
10
def test_post_as_json_converts_to_params
  # sanity check that post with normal params works...
  post '/test_params_as_json', :param1=>"param one"
  assert_equal last_response.body,"params[:param1]=param one"
  post '/test_params_as_json', {:param1=>"param one"}.to_json, "CONTENT_TYPE"=>"application/json"
  assert_equal last_response.body,"params[:param1]=param one"
  # this is the problem, adding a charset to the content type seems to breaks rack-contrib/post_body_content_type_parser.rb
  post '/test_params_as_json', {:param1=>"param one"}.to_json, "CONTENT_TYPE"=>"application/json; charset=UTF-8"
  assert_equal last_response.body,"params[:param1]=param one"    
end
Posted: April 4th, 2010 | Author: jay | Filed under: Code | Tags: , , , , , , , , | 4 Comments »

Battling autotest

Looking for a reason that autotest is skipping some of my test files….

UPDATE: Found the culprit. In my .autotest file I had some exclusions which were matching on the log in blog

1
2
3
4
5
Autotest.add_hook :initialize do |autotest|
  %w{.git .svn .hg .DS_Store db log tmp vendor ._* .sqlite3}.each do |exception| 
    autotest.add_exception(exception) 
  end
end

I think I’ll just comment the exception line out for now as I don’t think it’s necessary.

Posted: March 27th, 2010 | Author: jay | Filed under: Code | Tags: , , | No Comments »

When tests are acting strange and you can’t figure out what’s wrong…

Check that you didn’t copy / paste / didn’t change the test class definition line.

example:

My BlogPost class was mis-titled…

1
2
class FundastacheSiteTest < Test::Unit::TestCase
...

There went an hour…

Posted: March 25th, 2010 | Author: jay | Filed under: Code | Tags: , , , | No Comments »

Including common routines in test_helper.rb

I should have done this earlier. To do a number of tests, I have to repeat the same actions like create a user, log a user in, etc. I was putting these actions at the top of each test file, but have wised up and moved them to the test_helper.rb file. Here’s how that looks now:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module Test::Unit
  class TestCase
    ...
 
    def create_user(user_hash={})
      post '/user/signup', {:user=>{:username=>"testuser",:email=>"test@test.com",:password=>"pass1",:password_confirmation=>"pass1"}.merge(user_hash)}
    end
 
    def login(user_hash={})
      post "/user/login", :user=>{:username_or_email=>'testuser',:password=>'pass1'}.merge(user_hash)
    end
 
  end
end
Posted: March 24th, 2010 | Author: jay | Filed under: Code | Tags: , , , , | 1 Comment »

Setting a domain name (env['HTTP_HOST']) to use in Rack::Test

This may not be the best way to do this, but this is what I used at the top of my test file so that a helper method has a value for Rack’s env['HTTP_HOST']:

1
2
3
4
5
6
7
class FundastacheUserTest < Test::Unit::TestCase
  ...
  def env
    last_request.env['HTTP_HOST']="example.org"
    last_request.env
  end
  ...

The test tests to see that using the activation link (that a user gets in an email) activates the user. It looks something like this:

1
2
3
4
5
6
7
8
9
10
11
12
  def test_using_activation_link_should_activate_account
    create_user
    user=User.first
    path=activation_link(user)
    path=~/http\:\/\/example.org(.*)/
    get $1
    follow_redirect!
    assert last_response.ok?
    assert last_response.body.include?('Account activated!')
    user=User.first
    assert user.activated
  end

and the helper method looks something like this:

1
2
3
4
5
6
7
8
9
10
 
module FundAStache
  module Helpers    
    ...
    def activation_link(user)
      "http://#{env['HTTP_HOST']}/user/activate/#{user.activation_token}"
    end
    ...
  end
end
Posted: March 13th, 2010 | Author: jay | Filed under: Code | Tags: , , , , , | No Comments »

Trying out Pony for email

Email is going to be used to activate an account, and I’m leaning towards using the Pony gem to simplify the creations and sending.

Here’s how simple Pony is:

1
Pony.mail(:to => 'test@example.com', :from=>'test@example.com', :subject=>'spam', :body=>(erb :registration_email))

and here’s how you can test it with the pony-test gem:

1
2
3
4
5
6
7
8
9
10
  def test_valid_new_user_signup_should_create_email
    count=all_email.count
    create_user
    assert_equal count+1, all_email.count
  end
 
  def test_signup_email_should_contain_activation_link
    create_user
    assert_match /http\:\/\//, current_email.body
  end

Don’t forget to nclude the Pony test helpers in your test class…

1
include Pony::TestHelpers
Posted: March 12th, 2010 | Author: jay | Filed under: Code | Tags: , , , , | No Comments »

Testing helpers in Sinatra

How do you test your helper methods in a Sinatra::Base app?

1
2
3
4
5
6
7
module FundAStache
  module Helpers    
    def logged_in?
      false # you shall not pass...
    end
  end
end

Include them in your tests…

1
2
3
4
class FundastacheUserTest < Test::Unit::TestCase
  include Rack::Test::Methods
  include FundAStache::Helpers
  ...
Posted: March 9th, 2010 | Author: jay | Filed under: Code | Tags: , , , , , | No Comments »

Here’s what my test_helper.rb file looks like…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require 'fundastache'
require 'test/unit'
require "rack/test"
require "rack/flash/test"
 
ENV['RACK_ENV'] = 'test'
 
# set test environment
set :environment, :test
set :run, false
set :raise_errors, true
set :logging, false
 
DataMapper.setup(:default, "sqlite3::memory:") 
DataMapper.auto_migrate!
Posted: March 9th, 2010 | Author: jay | Filed under: Code | Tags: , , , | No Comments »