require 'test/unit'
require 'test_helper'
require 'thin_http'
require 'multipart_form_data'


#
# Test the ThinHTTP class functionality by interacting with a local WEBrick
# server.
#
class ThinHTTPTest < Test::Unit::TestCase

  include TestHelper

  def setup
    @th = ThinHTTP.new
  end

  def test_thin_http_object_defaults
    assert_equal @th.host, 'localhost'
    assert_equal @th.port, 2000
  end

  def test_get_index
    response = @th.get('/index.html')
    index_html = IO.read(sd('/htdocs/index.html'))
    assert_equal index_html, response 
  end

  def test_get_hash_params
    params = {:one => 'abc', 'two' => 'xyz'}
    response  = @th.get('/cgi/echo_get_params.cgi', params)
    assert_equal_url_params params, response
  end

  def test_get_string_params
    params = 'foo=bar&dead=beef'
    response  = @th.get('/cgi/echo_get_params.cgi', params)
    assert_equal_url_params params, response
  end

  def test_post_url_encoded_params
    params = {:one => 'abc', :two => 'xyz'}
    response = @th.post('/cgi/echo_post_params.cgi', params)
    assert_equal_url_params params, response
  end

  def test_post_mime_encoded_params
    form_data = MultipartFormData.new(
      :name => 'John Schmo',
      :address=>'1516 Nowhere Place'
    )
    response = @th.post('/cgi/echo_post_params.cgi', form_data, form_data.content_type)
    assert_equal form_data.content, response
  end

  def test_post_mime_encoded_params_and_files
    form_data = MultipartFormData.new(
      :comment => 'this is a cool test',
      :txtfile => Pathname.new(sd('/ascii.txt')),
      :imgfile => Pathname.new(sd('/image.jpg'))
    )
    response = @th.post('/cgi/echo_post_params.cgi', form_data, form_data.content_type)
    assert_equal form_data.content, response
  end

  def test_get_redirect_after_get_request
    assert_equal '', @th.path
    response = @th.get('/cgi/redirect_to_print_env.cgi')
    assert_equal '/cgi/print_env.cgi', @th.path
    assert_equal 'GET', print_env_var('METHOD', response)
  end

  def test_get_redirect_after_post_request
    assert_equal '', @th.path
    response = @th.post('/cgi/redirect_to_print_env.cgi', :post => 'test')
    assert_equal '/cgi/print_env.cgi', @th.path
    assert_equal 'GET', print_env_var('METHOD', response)
  end

  def test_exceed_maximum_redirects
    err = assert_raise(RuntimeError) {@th.get('/cgi/redirect_loop.cgi')}
    assert_equal 'exceeded redirection limit', err.message
  end

  def test_redirect_to_external_domain
    initial_host = @th.host
    initial_port = @th.port

    @th.get('/cgi/redirect_to_external.cgi')

    assert_not_equal initial_host, @th.host
    assert_not_equal initial_port, @th.port
  end

  # TODO more elaborate cookie tests.
  def test_set_and_get_cookie
    @th.get('/cgi/set_cookie.cgi')
    response = @th.get('/cgi/print_env.cgi')
    assert_equal 'cookie_key=cookie_value', print_env_var('COOKIE', response)
  end

  def test_add_and_delete_custom_headers
    user_agent_name = @th.class.to_s
    referer_url = 'http://ecentryx.com/index.html'

    @th.set_header('User-Agent', user_agent_name)
    @th.set_header('Referer', referer_url)
    response = @th.get('/cgi/print_env.cgi')

    assert_equal user_agent_name, print_env_var('USER_AGENT', response)
    assert_equal referer_url, print_env_var('REFERER', response)

    @th.unset_header('User-Agent')
    response = @th.get('/cgi/print_env.cgi')

    assert_nil print_env_var('USER_AGENT', response)
    assert_equal referer_url, print_env_var('REFERER', response)
  end


  private

  #
  # Test Helpers
  #

  #
  # Easy comparison of URL params, which may be a Hash (i.e. {k1=>v1, k2=v2})
  # or a String (i.e. "k1=v1&k2=v2").
  #
  def assert_equal_url_params expected, actual
    assert_equal url_params_to_hash(expected), url_params_to_hash(actual)
  end

  #
  # Converts +params+ to a Hash that contains only String keys. +params+
  # represents URL parameters as a key/value Hash or a URL encoded String. 
  #
  def url_params_to_hash params
    if params.is_a? Hash
      params.inject({}) do |hsh, (key,value)|
        hsh.store(key.to_s, value)
        hsh
      end
    else
      params.split('&').inject({}) do |hsh, key_value|
        key, value = key_value.split('=')
        hsh.store(key, value)
        hsh
      end
    end
  end

  #
  # Return the value of the environment variable +var_name+, otherwise nil.
  # Works in conjunction with the CGI script print_env.cgi.
  #
  def print_env_var var_name, response
    response.match(/#{var_name}=(.*)$/)[1]  rescue nil
  end

end
