forward 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,12 +9,15 @@ module Forward
9
9
  class BadResponse < StandardError; end
10
10
  class ResourceNotFound < StandardError; end
11
11
  class ResourceError < StandardError
12
- attr_reader :action, :errors
12
+ attr_reader :code, :type, :action, :api_message, :errors
13
13
 
14
- def initialize(action, json)
15
- @action = action
16
- @json = json
17
- @errors = json[:errors]
14
+ def initialize(code, action, json = {})
15
+ @code = code
16
+ @action = action
17
+ @json = json
18
+ @type = json[:type]
19
+ @api_message = json[:message]
20
+ @errors = json[:errors] || {}
18
21
  end
19
22
  end
20
23
 
@@ -35,11 +38,11 @@ module Forward
35
38
  end
36
39
 
37
40
  def self.token=(token)
38
- @@api_token = token
41
+ @api_token = token
39
42
  end
40
43
 
41
44
  def self.token
42
- defined?(@@api_token) ? @@api_token : nil
45
+ defined?(@api_token) ? @api_token : nil
43
46
  end
44
47
  end
45
48
  end
@@ -4,7 +4,7 @@ module Forward
4
4
 
5
5
  def self.create(log)
6
6
  resource = ClientLog.new(:create)
7
- resource.uri = '/api/client_logs'
7
+ resource.uri = '/api/v2/client_logs'
8
8
  params = {
9
9
  :client => Forward.client_string,
10
10
  :log => log
@@ -28,8 +28,6 @@ module Forward
28
28
  response = @http.request(@request)
29
29
 
30
30
  parse_response(response)
31
- rescue ResourceError => e
32
- self.class.dispatch_error(e)
33
31
  end
34
32
 
35
33
  def build_request(method, params = {})
@@ -77,10 +75,11 @@ module Forward
77
75
 
78
76
  def parse_response(response)
79
77
  log(:debug, "Response: [#{response.code}] `#{response.body}'")
78
+ code = response.code.to_i
80
79
 
81
- if response.code.to_i == 404
80
+ if code == 404
82
81
  raise ResourceNotFound
83
- elsif response.code.to_i != 200
82
+ elsif ![ 200, 422, 401 ].include? code
84
83
  raise BadResponse, "response code was: #{response.code}"
85
84
  elsif response['content-type'] !~ /^application\/json/
86
85
  raise BadResponse, "response was not JSON, unable to parse"
@@ -90,22 +89,22 @@ module Forward
90
89
 
91
90
  if json.is_a? Hash
92
91
  json.symbolize_keys!
93
- raise ResourceError.new(@action, json) if json.has_key?(:errors)
92
+ raise ResourceError.new(code, @action, json) if code != 200
94
93
  end
95
94
 
96
95
  json
97
96
  end
98
97
 
99
- def self.dispatch_error(error)
100
- Forward.log(:debug, "Dispatching ResourceError: action: #{error.action} errors: #{error.errors.inspect}")
101
- method = :"#{error.action}_error"
102
-
103
- if respond_to? method
104
- send(method, error.errors)
105
- else
106
- Forward::Client.cleanup_and_exit!('An error occured, please contact support@forwardhq.com')
107
- end
108
- end
98
+ # def self.dispatch_error(error)
99
+ # Forward.log(:debug, "Dispatching ResourceError: action: #{error.action} errors: #{error.errors.inspect}")
100
+ # method = :"#{error.action}_error"
101
+ #
102
+ # if respond_to? method
103
+ # send(method, error.errors)
104
+ # else
105
+ # Forward::Client.cleanup_and_exit!('An error occured, please contact support@forwardhq.com')
106
+ # end
107
+ # end
109
108
 
110
109
  private
111
110
 
@@ -4,7 +4,7 @@ module Forward
4
4
 
5
5
  def self.create(options = {})
6
6
  resource = Tunnel.new(:create)
7
- resource.uri = '/api/tunnels'
7
+ resource.uri = '/api/v2/tunnels'
8
8
  params = {
9
9
  :hostport => options[:port],
10
10
  :vhost => options[:host],
@@ -15,35 +15,39 @@ module Forward
15
15
  params[param] = options[param] unless options[param].nil?
16
16
  end
17
17
 
18
- resource.post(params)
18
+ resource.post(params)[:tunnel].symbolize_keys
19
+ rescue ResourceError => e
20
+ error_on_create(e, options)
19
21
  end
20
22
 
21
23
  def self.index
22
24
  resource = Tunnel.new(:index)
23
- resource.uri = "/api/tunnels"
25
+ resource.uri = "/api/v2/tunnels"
24
26
 
25
- resource.get
27
+ resource.get[:tunnels]
26
28
  end
27
29
 
28
30
  def self.destroy(id)
29
31
  resource = Tunnel.new(:destroy)
30
- resource.uri = "/api/tunnels/#{id}"
32
+ resource.uri = "/api/v2/tunnels/#{id}"
31
33
 
32
34
  resource.delete
35
+ rescue ResourceError => e
36
+ nil
33
37
  end
34
38
 
35
39
  def self.show(id)
36
40
  resource = Tunnel.new(:show)
37
- resource.uri = "/api/tunnels/#{id}"
41
+ resource.uri = "/api/v2/tunnels/#{id}"
38
42
 
39
- resource.get
43
+ resource.get[:tunnel].symbolize_keys
40
44
  rescue Forward::Api::ResourceNotFound
41
45
  nil
42
46
  end
43
47
 
44
48
  private
45
49
 
46
- def self.ask_to_destroy(message)
50
+ def self.ask_to_destroy(message, options)
47
51
  tunnels = index
48
52
 
49
53
  puts message
@@ -51,49 +55,42 @@ module Forward
51
55
  menu.prompt = "Choose a tunnel from the list to close or `q' to exit forward "
52
56
 
53
57
  tunnels.each do |tunnel|
54
- text = "tunnel forwarding port #{tunnel['hostport']}"
55
- menu.choice(text) { destroy_and_create(tunnel['_id']) }
58
+ text = "Forwarding port #{tunnel['hostport']}"
59
+ menu.choice(text) { destroy_and_create(tunnel['_id'], options) }
56
60
  end
57
61
  menu.hidden('quit') { Forward::Client.cleanup_and_exit! }
58
62
  menu.hidden('exit') { Forward::Client.cleanup_and_exit! }
59
63
  end
60
64
  end
61
65
 
62
- def self.destroy_and_create(id)
66
+ def self.destroy_and_create(id, options)
63
67
  Forward.log(:debug, "Destroying tunnel: #{id}")
64
68
  destroy(id)
65
69
  puts "tunnel removed, now we're creating a new one"
66
- create(Forward.client.options)
70
+ create(options)
67
71
  end
68
72
 
69
- def self.create_error(errors)
70
- Forward.log(:debug, "An error occured creating tunnel:\n#{errors.inspect}")
71
- base_errors = errors['base']
73
+ def self.error_on_create(error, options)
74
+ Forward.log(:debug, "An error occured creating tunnel:\n#{error.inspect}")
72
75
 
73
- if base_errors && base_errors.any? { |e| e.include? 'limit' }
74
- message = base_errors.select { |e| e.include? 'limit' }.first
76
+ if error.type == 'tunnel_limit_reached'
75
77
  Forward.log(:debug, 'Tunnel limit reached')
76
- ask_to_destroy(message)
78
+ ask_to_destroy(error.api_message, options)
79
+ elsif error.type =~ /(?:account_suspended|trial_expired)/i
80
+ Forward::Client.cleanup_and_exit!(error.api_message)
77
81
  else
78
82
  message = "We were unable to create your tunnel for the following reasons: \n"
79
- errors.each do |key, value|
80
- if key != 'base'
81
- message << " #{key} #{value.join(', ')}\n"
82
- elsif key == 'base'
83
- base_errors.each do |error|
84
- message << " #{error}\n"
85
- end
83
+ error.errors.each do |key, value|
84
+ if key == 'base'
85
+ message << " #{value.join(', ')}\n"
86
+ else
87
+ value.each { |m| message << " #{key} #{m}\n"}
86
88
  end
87
89
  end
88
90
  Forward::Client.cleanup_and_exit!(message)
89
91
  end
90
92
  end
91
93
 
92
- def self.destroy_error(errors)
93
- # TODO: this is where we will tie into the logger
94
- nil
95
- end
96
-
97
94
  end
98
95
  end
99
96
  end
@@ -4,11 +4,13 @@ module Forward
4
4
 
5
5
  def self.create
6
6
  resource = TunnelKey.new(:create)
7
- resource.uri = '/api/tunnel_keys'
7
+ resource.uri = '/api/v2/tunnel_keys'
8
8
 
9
9
  response = resource.post
10
10
 
11
11
  response[:private_key]
12
+ rescue
13
+ Forward::Client.cleanup_and_exit!
12
14
  end
13
15
 
14
16
  end
@@ -4,13 +4,14 @@ module Forward
4
4
 
5
5
  def self.api_token(email, password)
6
6
  resource = User.new(:api_token)
7
- resource.uri = '/api/users/api_token'
7
+ resource.uri = '/api/v2/users/api_token'
8
8
  params = { :email => email, :password => password }
9
9
 
10
- resource.post(params)
11
- end
10
+ user = resource.post(params)[:user].symbolize_keys
11
+ user[:id] = user.delete(:_id)
12
12
 
13
- def self.api_token_error(errors)
13
+ user
14
+ rescue ResourceError => e
14
15
  Forward::Client.cleanup_and_exit!('Unable to authenticate with email and password')
15
16
  end
16
17
 
@@ -20,8 +20,9 @@ module Forward
20
20
  if @tunnel.id
21
21
  @tunnel.poll_status
22
22
  else
23
- cleanup_and_exit!('Unable to create a tunnel. If this continues contact support@forwardhq.com')
23
+ Forward::Client.cleanup_and_exit!('Unable to create a tunnel. If this continues contact support@forwardhq.com')
24
24
  end
25
+ Forward.log(:debug, "Tunnel setup: #{@tunnel.inspect}")
25
26
 
26
27
  @tunnel
27
28
  end
@@ -48,6 +49,23 @@ module Forward
48
49
  true
49
50
  end
50
51
 
52
+ def self.forwarding_message(tunnel)
53
+ remote = "\033[04mhttps://#{@tunnel.subdomain}.fwd.wf\033[0m"
54
+
55
+ unless tunnel.cname.nil? || tunnel.cname.empty?
56
+ remote << " and \033[04mhttp://#{@tunnel.cname}\033[0m"
57
+ end
58
+
59
+ if !tunnel.vhost.nil? && tunnel.vhost !~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
60
+ local = tunnel.vhost
61
+ local << " port #{tunnel.hostport}" unless tunnel.hostport.to_i == 80
62
+ else
63
+ local = "port #{tunnel.hostport}"
64
+ end
65
+
66
+ "Forwarding #{local} to #{remote}\nCtrl-C to stop forwarding"
67
+ end
68
+
51
69
  def self.start(options = {})
52
70
  Forward.log(:debug, 'Starting client')
53
71
  trap(:INT) { cleanup_and_exit!('closing tunnel and exiting...') }
@@ -56,8 +74,9 @@ module Forward
56
74
  @tunnel = @client.setup_tunnel
57
75
  @session = Net::SSH.start(@tunnel.tunneler, Forward.ssh_user, @client.ssh_options)
58
76
 
59
- Forward.log(:debug, "Starting remote forward at #{@tunnel.subdomain}.fwd.wf on port #{@tunnel.port}")
60
- puts "Forwarding port #{@tunnel.hostport} at \033[04mhttps://#{@tunnel.subdomain}.fwd.wf\033[0m\nCtrl-C to stop forwarding"
77
+ Forward.log(:debug, "Starting remote forward at #{@tunnel.subdomain}.fwd.wf")
78
+ # puts "Forwarding port #{@tunnel.hostport} at \033[04mhttps://#{@tunnel.subdomain}.fwd.wf\033[0m\nCtrl-C to stop forwarding"
79
+ puts forwarding_message(@tunnel)
61
80
 
62
81
  @session.forward.remote(@tunnel.hostport, @tunnel.host, @tunnel.port)
63
82
  @session.loop { watch_session(@session) }
@@ -2,20 +2,24 @@ module Forward
2
2
  class Tunnel
3
3
  CHECK_INTERVAL = 7
4
4
 
5
- # The Tunnel resource ID.
5
+ # The Tunnel resource ID
6
6
  attr_reader :id
7
- # The domain for the Tunnel.
7
+ # The domain for the Tunnel
8
8
  attr_reader :subdomain
9
+ # The CNAME for the Tunnel
10
+ attr_reader :cname
9
11
  # The host
10
12
  attr_reader :host
11
13
  # The vhost
12
14
  attr_reader :vhost
13
15
  # The hostport (local port)
14
16
  attr_reader :hostport
15
- # The remote port.
17
+ # The remote port
16
18
  attr_reader :port
17
- # The tunneler host.
19
+ # The tunneler host
18
20
  attr_reader :tunneler
21
+ # The timeout
22
+ attr_reader :timeout
19
23
  # The amount of time in seconds the Tunnel has be inactive for
20
24
  attr_accessor :inactive_for
21
25
 
@@ -28,6 +32,7 @@ module Forward
28
32
  @response = Forward::Api::Tunnel.create(options)
29
33
  @id = @response[:_id]
30
34
  @subdomain = @response[:subdomain]
35
+ @cname = @response[:cname]
31
36
  @vhost = @response[:vhost]
32
37
  @hostport = @response[:hostport]
33
38
  @port = @response[:port]
@@ -1,3 +1,3 @@
1
1
  module Forward
2
- VERSION = '0.1.6'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -6,19 +6,19 @@ describe Forward::Api::TunnelKey do
6
6
  FakeWeb.allow_net_connect = false
7
7
  end
8
8
 
9
- it 'retrieves the public_key and returns it' do
9
+ it "retrieves the public_key and returns it" do
10
10
  fake_body = { :private_key => 'ssh-key 1234567890' }
11
11
 
12
- stub_api_request(:post, '/api/tunnel_keys', :body => fake_body.to_json)
12
+ stub_api_request(:post, '/api/v2/tunnel_keys', :body => fake_body.to_json)
13
13
 
14
14
  response = Api::TunnelKey.create
15
15
  response.must_equal fake_body[:private_key]
16
16
  end
17
17
 
18
- it 'exits with message if response has errors' do
19
- fake_body = { :errors => { :base => 'Unable to retrieve a private key' } }
18
+ it "exits with message if response has errors" do
19
+ fake_body = { :type => 'api_error' }
20
20
 
21
- stub_api_request(:post, '/api/tunnel_keys', :body => fake_body.to_json)
21
+ stub_api_request(:post, '/api/v2/tunnel_keys', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
22
22
 
23
23
  lambda {
24
24
  dev_null { Api::TunnelKey.create }
@@ -7,66 +7,104 @@ describe Forward::Api::Tunnel do
7
7
  Forward::Api.token = 'abc123'
8
8
  end
9
9
 
10
- it 'creates a tunnel and returns the attributes' do
11
- Forward.client = mock
12
- fake_body = { :_id => '1', :subdomain => 'foo', :port => 56789 }
10
+ it "creates a tunnel and returns the attributes" do
11
+ fake_body = { :tunnel => { :_id => '1', :subdomain => 'foo', :port => 56789 }}
13
12
 
14
- stub_api_request(:post, '/api/tunnels', :body => fake_body.to_json)
13
+ stub_api_request(:post, '/api/v2/tunnels', :body => fake_body.to_json)
15
14
 
16
15
  response = Forward::Api::Tunnel.create(:port => 3000)
17
16
 
18
- fake_body.each do |key, value|
19
- response[key].must_equal fake_body[key]
20
- end
17
+ response[:_id].must_equal fake_body[:tunnel][:_id]
18
+ response[:subdomain].must_equal fake_body[:tunnel][:subdomain]
19
+ response[:port].must_equal fake_body[:tunnel][:port]
21
20
  end
22
21
 
23
- it 'exits with message if create response has errors' do
24
- Forward.client = mock
25
- fake_body = { :errors => { :base => [ 'unable to create tunnel' ] } }
26
-
27
- stub_api_request(:post, '/api/tunnels', :body => fake_body.to_json)
28
-
29
- lambda {
30
- dev_null { Forward::Api::Tunnel.create(:port => 3000) }
31
- }.must_raise SystemExit
22
+ it "exits with message if create response is a resource_error" do
23
+ fake_body = {
24
+ :type => 'resource_error',
25
+ :errors => {
26
+ :base => [ 'did not work '],
27
+ :subdomain => [ 'is invalid', 'is too short' ]
28
+ }
29
+ }
30
+
31
+ stub_api_request(:post, '/api/v2/tunnels', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
32
+
33
+ out, err = capture_io do
34
+ begin
35
+ Forward::Api::Tunnel.create(:port => 3000)
36
+ rescue SystemExit; end
37
+ end
38
+ out.must_match /did not work/i
39
+ out.must_match /subdomain is invalid/i
40
+ out.must_match /subdomain is too short/i
32
41
  end
33
42
 
34
- it 'gives a choice and closes a tunnel if limit is reached' do
35
- Forward.client = mock
36
- post_options = [
37
- { :body => { :errors => { :base => [ 'you have reached your limit' ] } }.to_json },
38
- { :body => { :_id => '1', :subdomain => 'foo', :port => 56789 }.to_json }
39
- ]
40
- index_body = [ { :_id => 'abc123' }, { :_id => 'def456' } ]
43
+ it "exits with message if trial has expired" do
44
+ fake_body = {
45
+ :type => 'trial_expired',
46
+ :message => 'your trial has expired'
47
+ }
48
+
49
+ stub_api_request(:post, '/api/v2/tunnels', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
50
+
51
+ out, err = capture_io do
52
+ begin
53
+ Forward::Api::Tunnel.create(:port => 3000)
54
+ rescue SystemExit; end
55
+ end
56
+ out.must_match /trial has expired/i
57
+ end
58
+
59
+ it "exits with message if account has been suspended" do
60
+ fake_body = {
61
+ :type => 'account_suspended',
62
+ :message => 'your account has been suspended due for failed payment'
63
+ }
64
+
65
+ stub_api_request(:post, '/api/v2/tunnels', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
66
+
67
+ out, err = capture_io do
68
+ begin
69
+ Forward::Api::Tunnel.create(:port => 3000)
70
+ rescue SystemExit; end
71
+ end
72
+ out.must_match /account has been suspended/i
73
+ end
74
+
75
+ it "gives a choice and closes a tunnel if limit is reached" do
76
+ post_options = [
77
+ { :body => { :type => 'tunnel_limit_reached', :message => 'you have reached your limit' }.to_json, :status => [ 422, 'Unprocessable Entity' ] },
78
+ { :body => { :tunnel => { :_id => '1', :subdomain => 'foo', :port => 56789 } }.to_json }
79
+ ]
80
+ index_body = { :tunnels => [ { :_id => 'abc123', :hostport => 1234 }, { :_id => 'def456', :hostport => 1235 } ] }
41
81
 
42
- stub_api_request(:post, '/api/tunnels', post_options)
43
- stub_api_request(:get, '/api/tunnels', :body => index_body.to_json)
44
- STDIN.expects(:gets).returns('1')
45
- Forward::Api::Tunnel.expects(:destroy).with(index_body.first[:_id])
82
+ stub_api_request(:post, '/api/v2/tunnels', post_options)
83
+ stub_api_request(:get, '/api/v2/tunnels', :body => index_body.to_json)
84
+ STDIN.expects(:gets).returns('1')
85
+ Forward::Api::Tunnel.expects(:destroy).with(index_body[:tunnels].first[:_id])
46
86
 
47
- dev_null { Forward::Api::Tunnel.create(:port => 3000) }
48
- end
87
+ dev_null { Forward::Api::Tunnel.create(:port => 3000) }
88
+ end
49
89
 
50
- it 'destroys a tunnel and returns the attributes' do
51
- Forward.client = mock
52
- fake_body = { :_id => '1', :subdomain => 'foo', :port => 56789 }
90
+ it 'destroys a tunnel and returns the attributes' do
91
+ fake_body = { :_id => '1', :subdomain => 'foo', :port => 56789 }
53
92
 
54
- stub_api_request(:delete, '/api/tunnels/1', :body => fake_body.to_json)
93
+ stub_api_request(:delete, '/api/v2/tunnels/1', :body => fake_body.to_json)
55
94
 
56
- response = Forward::Api::Tunnel.destroy(1)
95
+ response = Forward::Api::Tunnel.destroy(1)
57
96
 
58
- fake_body.each do |key, value|
59
- response[key].must_equal fake_body[key]
60
- end
61
- end
97
+ fake_body.each do |key, value|
98
+ response[key].must_equal fake_body[key]
99
+ end
100
+ end
62
101
 
63
- it 'gracefully handles the error if destroy has errors' do
64
- Forward.client = mock
65
- fake_body = { :errors => { :base => 'unable to create tunnel' } }
102
+ it 'gracefully handles the error if destroy has errors' do
103
+ fake_body = { :type => 'api_error' }
66
104
 
67
- stub_api_request(:delete, '/api/tunnels/1', :body => fake_body.to_json)
105
+ stub_api_request(:delete, '/api/v2/tunnels/1', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
68
106
 
69
- Forward::Api::Tunnel.destroy(1).must_be_nil
70
- end
107
+ Forward::Api::Tunnel.destroy(1).must_be_nil
108
+ end
71
109
 
72
110
  end
@@ -6,19 +6,32 @@ describe Forward::Api::User do
6
6
  FakeWeb.allow_net_connect = false
7
7
  end
8
8
 
9
- it 'retrieves the users api token and returns it' do
10
- fake_body = { :api_token => '123abc' }
9
+ it "retrieves the users api token and returns it" do
10
+ fake_body = { :user => { :api_token => '123abc' } }
11
11
 
12
- stub_api_request(:post, '/api/users/api_token', :body => fake_body.to_json)
12
+ stub_api_request(:post, '/api/v2/users/api_token', :body => fake_body.to_json)
13
13
 
14
14
  response = Api::User.api_token('guy@example.com', 'secret')
15
15
  response[:api_token].must_equal '123abc'
16
16
  end
17
17
 
18
- it 'exits with message if response has errors' do
19
- fake_body = { :errors => { :base => 'Unable to authenticate user' } }
18
+ it "exits with message if authentication fails" do
19
+ fake_body = { :type => 'api_error' }
20
20
 
21
- stub_api_request(:post, '/api/users/api_token', :body => fake_body.to_json)
21
+ stub_api_request(:post, '/api/v2/users/api_token', :body => fake_body.to_json, :status => [ 401, 'Authentication Failed' ])
22
+
23
+ out, err = capture_io do
24
+ begin
25
+ Api::User.api_token('guy@example.com', 'secret')
26
+ rescue SystemExit; end
27
+ end
28
+ out.must_match /unable to authenticate/i
29
+ end
30
+
31
+ it "exits with message if response has errors" do
32
+ fake_body = { :type => 'api_error' }
33
+
34
+ stub_api_request(:post, '/api/v2/users/api_token', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
22
35
 
23
36
  lambda {
24
37
  dev_null { Api::User.api_token('guy@example.com', 'secret') }
@@ -2,7 +2,28 @@ require 'test_helper'
2
2
 
3
3
  describe Forward::Tunnel do
4
4
 
5
- it 'should have access to api resources via the api method ' do
5
+ it "create a tunnel instance" do
6
+ tunnel_response = {
7
+ :_id => '1234',
8
+ :subdomain => 'foo',
9
+ :cname => 'foo.bar.com',
10
+ :vhost => '127.0.0.1',
11
+ :hostport => '3000',
12
+ :port => 20000,
13
+ :tunneler_public => 'test.forwardhq.com',
14
+ :timeout => 0
15
+ }
6
16
 
17
+ Forward::Api::Tunnel.expects(:create).returns(tunnel_response)
18
+ tunnel = Forward::Tunnel.new
19
+
20
+ tunnel.id.must_equal tunnel_response[:_id]
21
+ tunnel.subdomain.must_equal tunnel_response[:subdomain]
22
+ tunnel.cname.must_equal tunnel_response[:cname]
23
+ tunnel.vhost.must_equal tunnel_response[:vhost]
24
+ tunnel.hostport.must_equal tunnel_response[:hostport]
25
+ tunnel.port.must_equal tunnel_response[:port]
26
+ tunnel.tunneler.must_equal tunnel_response[:tunneler_public]
27
+ tunnel.timeout.must_equal tunnel_response[:timeout]
7
28
  end
8
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forward
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-18 00:00:00.000000000 Z
12
+ date: 2012-12-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json