forward 0.3.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/Gemfile +0 -2
  4. data/README.md +24 -0
  5. data/Rakefile +3 -1
  6. data/bin/forward +1 -1
  7. data/forward.gemspec +17 -11
  8. data/lib/forward/api/resource.rb +51 -83
  9. data/lib/forward/api/tunnel.rb +41 -68
  10. data/lib/forward/api/user.rb +14 -11
  11. data/lib/forward/api.rb +7 -26
  12. data/lib/forward/cli.rb +55 -253
  13. data/lib/forward/command/account.rb +69 -0
  14. data/lib/forward/command/base.rb +62 -0
  15. data/lib/forward/command/config.rb +64 -0
  16. data/lib/forward/command/tunnel.rb +178 -0
  17. data/lib/forward/common.rb +44 -0
  18. data/lib/forward/config.rb +75 -118
  19. data/lib/forward/request.rb +72 -0
  20. data/lib/forward/socket.rb +125 -0
  21. data/lib/forward/static/app.rb +157 -0
  22. data/lib/forward/static/directory.erb +142 -0
  23. data/lib/forward/tunnel.rb +102 -40
  24. data/lib/forward/version.rb +1 -1
  25. data/lib/forward.rb +80 -63
  26. data/test/api/resource_test.rb +70 -54
  27. data/test/api/tunnel_test.rb +50 -51
  28. data/test/api/user_test.rb +33 -20
  29. data/test/cli_test.rb +0 -126
  30. data/test/command/account_test.rb +26 -0
  31. data/test/command/tunnel_test.rb +133 -0
  32. data/test/config_test.rb +103 -54
  33. data/test/forward_test.rb +47 -0
  34. data/test/test_helper.rb +35 -26
  35. data/test/tunnel_test.rb +50 -22
  36. metadata +210 -169
  37. data/forwardhq.crt +0 -112
  38. data/lib/forward/api/client_log.rb +0 -20
  39. data/lib/forward/api/tunnel_key.rb +0 -18
  40. data/lib/forward/client.rb +0 -110
  41. data/lib/forward/error.rb +0 -12
  42. data/test/api/tunnel_key_test.rb +0 -28
  43. data/test/api_test.rb +0 -0
  44. data/test/client_test.rb +0 -8
@@ -1,82 +1,98 @@
1
1
  require 'test_helper'
2
2
 
3
- describe Forward::Api::Resource do
4
- Api = Forward::Api
5
-
3
+ describe Forward::API::Resource do
6
4
  before :each do
7
- Api.token = 'abc123'
5
+ Forward::Config.accounts = { foo: 'abc123' }
6
+ Forward::Config.default_account = :foo
8
7
  end
9
8
 
10
9
  it 'builds http requests based on given method' do
11
- resource = Api::Resource.new
12
- resource.uri = '/path'
10
+ resource = Forward::API::Resource.new
13
11
 
14
- [ :get, :post, :put, :delete ].each do |method|
15
- resource.build_request(method)
16
- klass = eval("Net::HTTP::#{method.capitalize}")
17
- request = resource.instance_variable_get('@request')
12
+ [ :get, :post, :delete ].each do |method|
13
+ options = {}
18
14
 
19
- request.kind_of?(klass).must_equal true
15
+ resource.expects(:request).with(method, options)
16
+ resource.send(method, options)
20
17
  end
21
18
  end
22
19
 
23
- it 'builds http requests with json bodies' do
24
- resource = Api::Resource.new
25
- resource.uri = '/path'
20
+ it "adds json headers to every request" do
21
+ resource = Forward::API::Resource.new
22
+ headers = {
23
+ 'Accept' => 'application/json',
24
+ 'Content-Type' => 'application/json'
25
+ }
26
26
 
27
- [ :post, :put, :delete ].each do |method|
28
- params = { :foo => 'bar '}
29
- resource.build_request(method, params)
30
- klass = eval("Net::HTTP::#{method.capitalize}")
31
- request = resource.instance_variable_get('@request')
27
+ stub_request(:get, 'localhost/').
28
+ with(headers: headers).
29
+ to_return(status: 200)
32
30
 
33
- request.kind_of?(klass).must_equal true
34
- request.body.must_equal params.to_json
35
- end
31
+ EM.run_block {
32
+ Forward::API::Resource.new.get(path: '/') {}
33
+ }
36
34
  end
37
35
 
38
- it 'adds auth and json headers to the request' do
39
- resource = Api::Resource.new
40
- resource.uri = '/path'
41
- resource.build_request(:post)
42
- resource.add_headers!
43
- request = resource.instance_variable_get('@request')
44
-
45
- request['Authorization'].must_equal "Token token=#{Api.token}"
46
- request['Content-Type'].must_equal 'application/json'
47
- request['Accept'].must_equal 'application/json'
36
+ it "adds auth header if request is authenticated" do
37
+ resource = Forward::API::Resource.new
38
+ headers = {
39
+ 'Accept' => 'application/json',
40
+ 'Authorization' => 'Token token=abc123',
41
+ 'Content-Type' => 'application/json'
42
+ }
43
+
44
+ stub_request(:get, 'localhost/').
45
+ with(headers: headers).
46
+ to_return(status: 200)
47
+
48
+ EM.run_block {
49
+ Forward::API::Resource.new.get(path: '/', authenticated: true) {}
50
+ }
48
51
  end
49
52
 
53
+ it "adds a json body if params given" do
54
+ resource = Forward::API::Resource.new
55
+ params = { foo: 'bar' }
50
56
 
51
- it 'raises an error if the response code is not 200' do
52
- resource = Api::Resource.new
53
- response = mock
54
- response.stubs(:code).returns(403)
55
- response.stubs(:body)
57
+ stub_request(:post, 'localhost/').
58
+ with(body: params.to_json).
59
+ to_return(status: 200)
56
60
 
57
- lambda { resource.parse_response(response) }.must_raise Api::BadResponse
61
+ EM.run_block {
62
+ Forward::API::Resource.new.post(path: '/', params: params) {}
63
+ }
58
64
  end
59
65
 
60
- it 'raises an error if the response is not json' do
61
- resource = Api::Resource.new
62
- response = mock
63
- response.stubs(:code).returns(200)
64
- response.stubs(:body)
65
- response.stubs(:[]).with('content-type').returns('text/html')
66
+ it "exits with message if status is 401" do
67
+ resource = Forward::API::Resource.new
68
+
69
+ stub_request(:get, 'localhost/').
70
+ to_return(status: 401)
66
71
 
67
- lambda { resource.parse_response(response) }.must_raise Api::BadResponse
72
+ out, err = capture_io do
73
+ begin
74
+ EM.run_block {
75
+ Forward::API::Resource.new.get(path: '/') {}
76
+ }
77
+ rescue SystemExit; end
78
+ end
79
+ out.must_match /Unable to authenticate/i
68
80
  end
69
81
 
70
- it 'parses a json response' do
71
- resource = Api::Resource.new
72
- response = mock
73
- response.stubs(:code).returns(200)
74
- response.stubs(:[]).with('content-type').returns('application/json')
75
- response.stubs(:body).returns('{ "foo": "bar" }')
82
+ it "exits with message if response is invalid JSON" do
83
+ resource = Forward::API::Resource.new
84
+
85
+ stub_request(:get, 'localhost/').
86
+ to_return(status: 200, body: '{"foo"}')
76
87
 
77
- json = resource.parse_response(response)
78
- json.kind_of?(Hash).must_equal true
79
- json[:foo].must_equal 'bar'
88
+ out, err = capture_io do
89
+ begin
90
+ EM.run_block {
91
+ Forward::API::Resource.new.get(path: '/') {}
92
+ }
93
+ rescue SystemExit; end
94
+ end
95
+ out.must_match /Unable to connect to API/i
80
96
  end
81
97
 
82
98
  end
@@ -1,90 +1,89 @@
1
1
  require 'test_helper'
2
2
 
3
- describe Forward::Api::Tunnel do
3
+ describe Forward::API::Tunnel do
4
4
 
5
5
  before :each do
6
- FakeWeb.allow_net_connect = false
7
- Forward::Api.token = 'abc123'
6
+ Forward::Config.accounts = { foo: 'abc123' }
7
+ Forward::Config.default_account = :foo
8
8
  end
9
9
 
10
10
  it "creates a tunnel and returns the attributes" do
11
- fake_body = { :tunnel => { :_id => '1', :subdomain => 'foo', :port => 56789 }}
12
-
13
- stub_api_request(:post, '/api/v2/tunnels', :body => fake_body.to_json)
11
+ tunnel_options = { port: '3000', host: 'localhost' }
12
+ params = {
13
+ hostport: tunnel_options[:port],
14
+ vhost: tunnel_options[:host],
15
+ subdomain: nil, cname: nil,
16
+ username: nil, password: nil, no_auth: nil,
17
+ client: Forward.client_string,
18
+ }
19
+ body = { tunnel: { _id: '1', subdomain: 'foo', port: 56789 }}
14
20
 
15
- response = Forward::Api::Tunnel.create(:port => 3000)
21
+ stub_request(:post, 'localhost/api/v3/tunnels/').
22
+ with(body: params.to_json).
23
+ to_return(status: 200, body: body.to_json)
16
24
 
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]
25
+ EM.run_block {
26
+ Forward::API::Tunnel.create(tunnel_options) do |tunnel|
27
+ tunnel[:id].must_equal body[:id]
28
+ tunnel[:subdomain].must_equal body[:subdomain]
29
+ tunnel[:port].must_equal body[:port]
30
+ end
31
+ }
20
32
  end
21
33
 
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
- }
34
+ it "exits with message on 422" do
35
+ tunnel_options = { port: '3000', host: 'localhost' }
30
36
 
31
- stub_api_request(:post, '/api/v2/tunnels', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
37
+ stub_request(:post, 'localhost/api/v3/tunnels/').
38
+ to_return(status: 422)
32
39
 
33
40
  out, err = capture_io do
34
41
  begin
35
- Forward::Api::Tunnel.create(:port => 3000)
42
+ EM.run_block {
43
+ Forward::API::Tunnel.create(tunnel_options) { }
44
+ }
36
45
  rescue SystemExit; end
37
46
  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
47
+ out.must_match /unable to allocate tunnel/i
41
48
  end
42
49
 
43
- it "exits with message if trial has expired" do
44
- fake_body = {
45
- :type => 'trial_expired',
46
- :message => 'your trial has expired'
50
+ it "exits with message and displays account errors" do
51
+ tunnel_options = { port: '3000', host: 'localhost' }
52
+ body = {
53
+ type: 'account_error',
54
+ errors: { account: ['Trial has expired'] }
47
55
  }
48
56
 
49
- stub_api_request(:post, '/api/v2/tunnels', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
57
+ stub_request(:post, 'localhost/api/v3/tunnels/').
58
+ to_return(status: 422, body: body.to_json)
50
59
 
51
60
  out, err = capture_io do
52
61
  begin
53
- Forward::Api::Tunnel.create(:port => 3000)
62
+ EM.run_block {
63
+ Forward::API::Tunnel.create(tunnel_options) { }
64
+ }
54
65
  rescue SystemExit; end
55
66
  end
56
67
  out.must_match /trial has expired/i
57
68
  end
58
69
 
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'
70
+ it "exits with message on bad tunnel data" do
71
+ tunnel_options = { port: '3000', host: 'localhost' }
72
+ body = {
73
+ type: 'invalid_request_error'
63
74
  }
64
75
 
65
- stub_api_request(:post, '/api/v2/tunnels', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
76
+ stub_request(:post, 'localhost/api/v3/tunnels/').
77
+ to_return(status: 422, body: body.to_json)
66
78
 
67
79
  out, err = capture_io do
68
80
  begin
69
- Forward::Api::Tunnel.create(:port => 3000)
81
+ EM.run_block {
82
+ Forward::API::Tunnel.create(tunnel_options) { }
83
+ }
70
84
  rescue SystemExit; end
71
85
  end
72
- out.must_match /account has been suspended/i
86
+ out.must_match /invalid tunnel parameters/i
73
87
  end
74
88
 
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 } ] }
81
-
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])
86
-
87
- dev_null { Forward::Api::Tunnel.create(:port => 3000) }
88
- end
89
-
90
89
  end
@@ -1,41 +1,54 @@
1
1
  require 'test_helper'
2
2
 
3
- describe Forward::Api::User do
4
-
5
- before :each do
6
- FakeWeb.allow_net_connect = false
7
- end
3
+ describe Forward::API::User do
8
4
 
9
5
  it "retrieves the users api token and returns it" do
10
- fake_body = { :user => { :api_token => '123abc' } }
11
-
12
- stub_api_request(:post, '/api/v2/users/api_token', :body => fake_body.to_json)
13
-
14
- response = Api::User.api_token('guy@example.com', 'secret')
15
- response[:api_token].must_equal '123abc'
6
+ params = { email: 'guy@example.com', password: 'secret' }
7
+ body = { subdomain: 'fooco', token: '123abc' }
8
+
9
+ stub_request(:post, 'localhost/api/v3/user/token').
10
+ with(body: params.to_json).
11
+ to_return(status: 200, body: body.to_json)
12
+
13
+ EM.run_block {
14
+ Forward::API::User.authenticate(params[:email], params[:password]) do |subdomain, token|
15
+ token.must_equal '123abc'
16
+ end
17
+ }
16
18
  end
17
19
 
18
20
  it "exits with message if authentication fails" do
19
- fake_body = { :type => 'api_error' }
21
+ params = { email: 'guy@example.com', password: 'secret' }
20
22
 
21
- stub_api_request(:post, '/api/v2/users/api_token', :body => fake_body.to_json, :status => [ 401, 'Authentication Failed' ])
23
+ stub_request(:post, 'localhost/api/v3/user/token').
24
+ with(body: params.to_json).
25
+ to_return(status: 401)
22
26
 
23
27
  out, err = capture_io do
24
28
  begin
25
- Api::User.api_token('guy@example.com', 'secret')
29
+ EM.run_block {
30
+ Forward::API::User.authenticate(params[:email], params[:password]) {}
31
+ }
26
32
  rescue SystemExit; end
27
33
  end
28
- out.must_match /unable to authenticate/i
34
+ out.must_match /Unable to authenticate/i
29
35
  end
30
36
 
31
37
  it "exits with message if response has errors" do
32
- fake_body = { :type => 'api_error' }
38
+ params = { email: 'guy@example.com', password: 'secret' }
33
39
 
34
- stub_api_request(:post, '/api/v2/users/api_token', :body => fake_body.to_json, :status => [ 422, 'Unprocessable Entity' ])
40
+ stub_request(:post, 'localhost/api/v3/user/token').
41
+ with(body: params.to_json).
42
+ to_return(status: 422)
35
43
 
36
- lambda {
37
- dev_null { Api::User.api_token('guy@example.com', 'secret') }
38
- }.must_raise SystemExit
44
+ out, err = capture_io do
45
+ begin
46
+ EM.run_block {
47
+ Forward::API::User.authenticate(params[:email], params[:password]) {}
48
+ }
49
+ rescue SystemExit; end
50
+ end
51
+ out.must_match /Unable to authenticate/i
39
52
  end
40
53
 
41
54
  end
data/test/cli_test.rb CHANGED
@@ -2,130 +2,4 @@ require 'test_helper'
2
2
 
3
3
  describe Forward::CLI do
4
4
 
5
- it 'parses a forwarded port' do
6
- forwarded = Forward::CLI.parse_forwarded('600')
7
- forwarded.has_key?(:port).must_equal true
8
- forwarded[:port].must_equal 600
9
- end
10
-
11
- it 'parses a forwarded host' do
12
- forwarded = Forward::CLI.parse_forwarded('mysite.dev')
13
- forwarded.has_key?(:host).must_equal true
14
- forwarded[:host].must_equal 'mysite.dev'
15
- end
16
-
17
- it 'parses a forwarded host and port' do
18
- forwarded = Forward::CLI.parse_forwarded('mysite.dev:88')
19
- forwarded.has_key?(:host).must_equal true
20
- forwarded.has_key?(:port).must_equal true
21
- forwarded[:host].must_equal 'mysite.dev'
22
- forwarded[:port].must_equal 88
23
- end
24
-
25
- it 'exits if username is invalid' do
26
- [ 'foo ', ' asdfasdf ', 'fooo bar' ].each do |username|
27
- lambda {
28
- dev_null { Forward::CLI.validate_username(username) }
29
- }.must_raise SystemExit
30
- end
31
- end
32
-
33
- it 'validates a good username' do
34
- [ 'foo', 'asdflkj3r&)(#@#)', 'DF#R::#SFSDF' ].each do |username|
35
- Forward::CLI.validate_username(username).must_be_nil
36
- end
37
- end
38
-
39
- it 'exits if password is invalid' do
40
- [ 'foo ', ' asdfasdf ', 'fooo bar' ].each do |password|
41
- lambda {
42
- dev_null { Forward::CLI.validate_password(password) }
43
- }.must_raise SystemExit
44
- end
45
- end
46
-
47
- it 'validates a good password' do
48
- [ 'foo', 'asdflkj3r&)(#@#)', 'DF#R::#SFSDF' ].each do |password|
49
- Forward::CLI.validate_password(password).must_be_nil
50
- end
51
- end
52
-
53
- it 'parses a Forwardfile' do
54
- hash_to_forwardfile(:auth => 'username:password')
55
-
56
- options = Forward::CLI.parse_forwardfile
57
- options.has_key?(:auth).must_equal true
58
- end
59
-
60
- it 'exits if a Forwardfile does not parse to a Hash' do
61
- hash_to_forwardfile('username:password')
62
-
63
- lambda {
64
- dev_null { Forward::CLI.parse_forwardfile }
65
- }.must_raise SystemExit
66
- end
67
-
68
- it 'overloads Forwardfile options via commandline options' do
69
- hash_to_forwardfile(
70
- :port => 8000,
71
- :username => 'username',
72
- :password => 'password',
73
- :subdomain_prefix => 'foo'
74
- )
75
- args = [ '3000', '-a', 'example:secret', 'bar' ]
76
- options = Forward::CLI.parse_args_and_options(args)
77
-
78
- options[:port].must_equal 3000
79
- options[:username].must_equal 'example'
80
- options[:password].must_equal 'secret'
81
- options[:subdomain_prefix].must_equal 'bar'
82
- end
83
-
84
- it 'doesnt exit on valid ports' do
85
- Forward::CLI.validate_port(69).must_be_nil
86
- Forward::CLI.validate_port(3000).must_be_nil
87
- Forward::CLI.validate_port(65535).must_be_nil
88
- end
89
-
90
- it 'validates port and exits if invalid' do
91
- [ 0, 65536 ].each do |port|
92
- lambda {
93
- dev_null { Forward::CLI.validate_port(port) }
94
- }.must_raise SystemExit
95
- end
96
- end
97
-
98
- it 'doesnt exit on valid cnames' do
99
- [ 'foo.com', 'whatever-foo.com', 'www.foo.com', 'asdf.asdf.asdf.com' ].each do |cname|
100
- Forward::CLI.validate_cname(cname).must_be_nil
101
- end
102
- end
103
-
104
- it 'validates cname and exits if invalid' do
105
- [ 'whatever', 'asdfasdf.', '-asdf', 'adsf#$).com' ].each do |cname|
106
- lambda {
107
- dev_null { Forward::CLI.validate_cname(cname) }
108
- }.must_raise SystemExit
109
- end
110
- end
111
-
112
- it 'doesnt exit on valid subdomains prefix' do
113
- [ 'foo', 'whatever-foo', 'asdf40' ].each do |subdomain|
114
- Forward::CLI.validate_subdomain_prefix(subdomain).must_be_nil
115
- end
116
- end
117
-
118
- it 'validates subdomain prefix and exits if invalid' do
119
- [ '-asdf', 'adsf#$)' ].each do |subdomain|
120
- lambda {
121
- dev_null { Forward::CLI.validate_subdomain_prefix(subdomain) }
122
- }.must_raise SystemExit
123
- end
124
- end
125
-
126
- end
127
-
128
- def hash_to_forwardfile(hash)
129
- yaml = YAML.dump(hash)
130
- File.open('Forwardfile', 'w') { |f| f.write(yaml) }
131
5
  end
@@ -0,0 +1,26 @@
1
+ require 'test_helper'
2
+
3
+ describe Forward::Command::Account do
4
+ before :each do
5
+ FakeFS.activate!
6
+ FileUtils.mkdir_p(ENV['HOME'])
7
+ end
8
+
9
+ after :each do
10
+ FileUtils.rm_rf(ENV['HOME'])
11
+ FakeFS.deactivate!
12
+ end
13
+
14
+ it "logs a client in with a provided email" do
15
+ email = 'foo@example.com'
16
+ password = 'bar'
17
+ command = Forward::Command::Account.new({}, [email])
18
+
19
+ Forward::API::User.expects(:authenticate).with(email, password)
20
+ command.expects(:ask_for_credentials).with(email).returns([email, password])
21
+
22
+ EM.run_block {
23
+ command.login
24
+ }
25
+ end
26
+ end
@@ -0,0 +1,133 @@
1
+ require 'test_helper'
2
+
3
+ describe Forward::Command::Tunnel do
4
+ before :each do
5
+ FakeFS.activate!
6
+ FileUtils.mkdir_p(ENV['HOME'])
7
+ end
8
+
9
+ after :each do
10
+ FileUtils.rm_rf(ENV['HOME'])
11
+ FakeFS.deactivate!
12
+ end
13
+
14
+ it 'parses a forwarded port' do
15
+ command = Forward::Command::Tunnel.new({}, ['600'])
16
+
17
+ command.send(:parse_forwarded)
18
+ command.options[:port].must_equal 600
19
+ end
20
+
21
+ it 'parses a forwarded host' do
22
+ command = Forward::Command::Tunnel.new({}, ['mysite.dev'])
23
+
24
+ command.send(:parse_forwarded)
25
+ command.options[:host].must_equal 'mysite.dev'
26
+ end
27
+
28
+ it 'parses a forwarded host and port' do
29
+ command = Forward::Command::Tunnel.new({}, ['mysite.dev:8888'])
30
+
31
+ command.send(:parse_forwarded)
32
+ command.options[:host].must_equal 'mysite.dev'
33
+ command.options[:port].must_equal 8888
34
+ end
35
+
36
+ it 'exits if auth pair is invalid' do
37
+ [ 'as:df:asdf', 'fooo bar', 'foo oo:bar' ].each do |auth|
38
+ lambda {
39
+ dev_null {
40
+ command = Forward::Command::Tunnel.new(auth: auth)
41
+ command.send(:validate_auth)
42
+ }
43
+ }.must_raise Forward::CLIError
44
+ end
45
+ end
46
+
47
+ it 'validates a auth pair' do
48
+ [ 'foo:asdflkj3r&)', 'tom:#SFSDF' ].each do |auth|
49
+ command = Forward::Command::Tunnel.new(auth: auth)
50
+ command.send(:validate_auth)
51
+ end
52
+ end
53
+
54
+ it 'parses a Forwardfile' do
55
+ hash_to_forwardfile(auth: 'username:password')
56
+
57
+ command = Forward::Command::Tunnel.new
58
+ command.send(:parse_forwardfile)
59
+ command.options[:auth].must_equal 'username:password'
60
+ end
61
+
62
+ it 'exits if a Forwardfile does not parse to a Hash' do
63
+ hash_to_forwardfile('username:password')
64
+
65
+ lambda {
66
+ dev_null { Forward::Command::Tunnel.new.send(:parse_forwardfile) }
67
+ }.must_raise SystemExit
68
+ end
69
+
70
+ it 'overloads Forwardfile options via commandline options' do
71
+ hash_to_forwardfile(
72
+ port: 8000,
73
+ username: 'username',
74
+ password: 'password',
75
+ subdomain_prefix: 'foo'
76
+ )
77
+
78
+ EM.expects(:run)
79
+
80
+ command = Forward::Command::Tunnel.new({auth: 'example:secret'}, ['3000', 'bar'])
81
+ command.start
82
+
83
+ command.options[:port].must_equal 3000
84
+ command.options[:username].must_equal 'example'
85
+ command.options[:password].must_equal 'secret'
86
+ command.options[:subdomain_prefix].must_equal 'bar'
87
+ end
88
+
89
+ it 'validates port and exits if invalid' do
90
+ command = Forward::Command::Tunnel.new
91
+ command.options = { port: 3000 }
92
+
93
+ command.send(:validate_port)
94
+
95
+ [ 0, 65536 ].each do |port|
96
+ lambda {
97
+ dev_null {
98
+ command.options[:port] = port
99
+ command.send(:validate_port) }
100
+ }.must_raise Forward::CLIError
101
+ end
102
+ end
103
+
104
+ it 'validates cname and exits if invalid' do
105
+ command = Forward::Command::Tunnel.new
106
+ command.options = { cname: 'foo.bar.com' }
107
+
108
+ command.send(:validate_cname)
109
+ [ 'whatever', 'asdfasdf.', '-asdf', 'adsf#$).com' ].each do |cname|
110
+ lambda {
111
+ dev_null {
112
+ command.options[:cname] = cname
113
+ command.send(:validate_cname)
114
+ }
115
+ }.must_raise Forward::CLIError
116
+ end
117
+ end
118
+
119
+ it 'validates subdomain prefix and exits if invalid' do
120
+ command = Forward::Command::Tunnel.new
121
+ command.options = { subdomain_prefix: 'foo' }
122
+
123
+ command.send(:validate_subdomain_prefix)
124
+ [ '-asdf', 'adsf#$)' ].each do |subdomain|
125
+ lambda {
126
+ dev_null {
127
+ command.options[:subdomain_prefix] = subdomain
128
+ command.send(:validate_subdomain_prefix)
129
+ }
130
+ }.must_raise Forward::CLIError
131
+ end
132
+ end
133
+ end