tbm 0.1.2 → 0.2.0.rc1
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.
- data/README.md +46 -3
- data/Rakefile +5 -5
- data/bin/tbm +1 -1
- data/lib/TBM/cli.rb +83 -0
- data/lib/TBM/config.rb +41 -0
- data/lib/TBM/config_parser.rb +221 -0
- data/lib/TBM/machine.rb +59 -0
- data/lib/TBM/meta.rb +6 -0
- data/lib/TBM/target.rb +54 -0
- data/lib/TBM/tunnel.rb +26 -0
- data/lib/tbm.rb +7 -4
- data/spec/cli_spec.rb +165 -0
- data/spec/config_spec.rb +348 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/target_spec.rb +40 -0
- metadata +19 -11
- data/lib/tunnel/cli.rb +0 -69
- data/lib/tunnel/config.rb +0 -94
- data/lib/tunnel/meta.rb +0 -5
- data/lib/tunnel/target.rb +0 -24
data/lib/TBM/meta.rb
ADDED
data/lib/TBM/target.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module TBM
|
2
|
+
|
3
|
+
# A target defines a tunnel or set of tunnels that can be created by invoking one or more names assigned to the target.
|
4
|
+
#
|
5
|
+
# It has a name, a host, a user, and one or more tunnels well as an optional list of aliases.
|
6
|
+
class Target
|
7
|
+
|
8
|
+
attr_reader :name, :host, :username, :tunnels
|
9
|
+
|
10
|
+
def initialize( name, host, username )
|
11
|
+
@name = name
|
12
|
+
@host = host
|
13
|
+
@username = username
|
14
|
+
@tunnels = []
|
15
|
+
@aliases = []
|
16
|
+
end
|
17
|
+
|
18
|
+
# Adds a tunnel to the target.
|
19
|
+
#
|
20
|
+
# @param [Tunnel] tunnel the tunnel to add
|
21
|
+
def add_tunnel( tunnel )
|
22
|
+
@tunnels << tunnel
|
23
|
+
end
|
24
|
+
|
25
|
+
# Adds an alias to the list of recognized aliases supported by the target.
|
26
|
+
#
|
27
|
+
# @param [String] name the alias to add
|
28
|
+
def add_alias( name )
|
29
|
+
@aliases << name
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_name?( name )
|
33
|
+
( @name == name ) || ( @aliases.include? name )
|
34
|
+
end
|
35
|
+
|
36
|
+
def each_tunnel( &block )
|
37
|
+
@tunnels.each { |tunnel| yield tunnel }
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
if @aliases.empty?
|
42
|
+
@name
|
43
|
+
else
|
44
|
+
"#{@name} (#{@aliases.join(', ')})"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_tunnels?
|
49
|
+
!@tunnels.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/TBM/tunnel.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module TBM
|
2
|
+
|
3
|
+
# Represents a particular tunnel, possibly one of many for a given target.
|
4
|
+
class Tunnel
|
5
|
+
attr_reader :port
|
6
|
+
|
7
|
+
def initialize( port, options = {} )
|
8
|
+
@port = port
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def remote_port
|
13
|
+
@options[:remote_port] || port
|
14
|
+
end
|
15
|
+
|
16
|
+
def remote_host_addr
|
17
|
+
@options[:remote_host] || 'localhost'
|
18
|
+
end
|
19
|
+
|
20
|
+
def remote_host
|
21
|
+
@options[:remote_host]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/lib/tbm.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require '
|
1
|
+
require 'TBM/meta'
|
2
|
+
require 'TBM/cli'
|
3
|
+
require 'TBM/config'
|
4
|
+
require 'TBM/config_parser'
|
5
|
+
require 'TBM/machine'
|
6
|
+
require 'TBM/target'
|
7
|
+
require 'TBM/tunnel'
|
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'tbm'
|
2
|
+
|
3
|
+
include TBM
|
4
|
+
|
5
|
+
describe CommandLineInterface do
|
6
|
+
|
7
|
+
let( :config ) { double( TBM::Config ) }
|
8
|
+
subject { CommandLineInterface.new config }
|
9
|
+
|
10
|
+
before do
|
11
|
+
TBM::ConfigParser.stub( :parse ) { config }
|
12
|
+
config.stub( :valid? ) { config_valid }
|
13
|
+
config.stub( :errors ) { config_errors }
|
14
|
+
stub_messages
|
15
|
+
end
|
16
|
+
|
17
|
+
context "without valid config" do
|
18
|
+
let(:config_valid) { false }
|
19
|
+
let(:config_errors) { ["Invalid Config"] }
|
20
|
+
it "should print config errors" do
|
21
|
+
CommandLineInterface.parse_and_run
|
22
|
+
@messages.should include_match(/Cannot parse config/)
|
23
|
+
@messages.should include_match(/Invalid Config/)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with no parameters" do
|
28
|
+
let(:config_valid) { true }
|
29
|
+
|
30
|
+
before do
|
31
|
+
ARGV.clear
|
32
|
+
config.stub(:each_target).and_yield( 'alpha' ).and_yield( 'beta' )
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should print syntax and targets" do
|
36
|
+
CommandLineInterface.parse_and_run
|
37
|
+
@messages.should include_match( /SYNTAX/ )
|
38
|
+
@messages.should include_match( /alpha/ )
|
39
|
+
@messages.should include_match( /beta/ )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "with a single parameter" do
|
44
|
+
let(:config_valid) { true }
|
45
|
+
|
46
|
+
before do
|
47
|
+
ARGV.clear.push( 'target-name' )
|
48
|
+
config.stub(:get_target).with('target-name') { target }
|
49
|
+
end
|
50
|
+
|
51
|
+
context "matching a config target" do
|
52
|
+
let(:target) { double Target }
|
53
|
+
let(:thost) { 'target-host.example.com' }
|
54
|
+
let(:tuser) { 'username' }
|
55
|
+
let(:machine) { double( Machine ) }
|
56
|
+
|
57
|
+
before do
|
58
|
+
target.stub(:host) { thost }
|
59
|
+
target.stub(:username) { tuser }
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should start Tunnel Boring Machine" do
|
63
|
+
Machine.stub(:new) { machine }
|
64
|
+
machine.should_receive(:bore)
|
65
|
+
CommandLineInterface.parse_and_run
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "not matching a config target" do
|
70
|
+
let(:target) { nil }
|
71
|
+
|
72
|
+
before do
|
73
|
+
config.stub(:each_target).and_yield( 'another-target' )
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should say 'Cannot find target'" do
|
77
|
+
CommandLineInterface.parse_and_run
|
78
|
+
@messages.should include_match( /Cannot find target/ )
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should print target list" do
|
82
|
+
CommandLineInterface.parse_and_run
|
83
|
+
@messages.should include_match( /another-target/ )
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "with multiple parameters" do
|
89
|
+
let(:config_valid) { true }
|
90
|
+
|
91
|
+
before do
|
92
|
+
ARGV.clear.push( 'alpha', 'beta' )
|
93
|
+
config.stub(:get_target).with('alpha') { alpha }
|
94
|
+
config.stub(:get_target).with('beta') { beta }
|
95
|
+
end
|
96
|
+
|
97
|
+
context "matching configured targets with same host and user" do
|
98
|
+
let(:alpha) { Target.new( 'alpha', 'host', 'username' ) }
|
99
|
+
let(:beta) { Target.new( 'beta', 'host', 'username' ) }
|
100
|
+
let(:machine) { double( Machine ) }
|
101
|
+
|
102
|
+
it "should start boring machine" do
|
103
|
+
Machine.stub(:new) { machine }
|
104
|
+
machine.should_receive(:bore)
|
105
|
+
CommandLineInterface.parse_and_run
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "matching configured targets with different hosts" do
|
110
|
+
let(:alpha) { double Target }
|
111
|
+
let(:beta) { double Target }
|
112
|
+
|
113
|
+
before do
|
114
|
+
alpha.stub(:host) { 'host1' }
|
115
|
+
alpha.stub(:username) { 'username' }
|
116
|
+
beta.stub(:host) { 'host2' }
|
117
|
+
beta.stub(:username) { 'username' }
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should say 'Can't combine targets'" do
|
121
|
+
CommandLineInterface.parse_and_run
|
122
|
+
@messages.should include_match(/Can't combine targets/)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "matching configured targets with different usernames" do
|
127
|
+
let(:alpha) { double Target }
|
128
|
+
let(:beta) { double Target }
|
129
|
+
|
130
|
+
before do
|
131
|
+
alpha.stub(:host) { 'host' }
|
132
|
+
alpha.stub(:username) { 'username1' }
|
133
|
+
beta.stub(:host) { 'host' }
|
134
|
+
beta.stub(:username) { 'username2' }
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should say 'Can't combine targets'" do
|
138
|
+
CommandLineInterface.parse_and_run
|
139
|
+
@messages.should include_match(/Can't combine targets/)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "not all matching configured targets" do
|
144
|
+
let(:alpha) { nil }
|
145
|
+
let(:beta) { nil }
|
146
|
+
|
147
|
+
before do
|
148
|
+
config.stub(:each_target).and_yield( 'gamma' ).and_yield( 'delta' )
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should say 'Cannot find target'" do
|
152
|
+
CommandLineInterface.parse_and_run
|
153
|
+
@messages.should include_match(/Cannot find target/)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should print target list" do
|
157
|
+
CommandLineInterface.parse_and_run
|
158
|
+
@messages.should include_match( /gamma/ )
|
159
|
+
@messages.should include_match( /delta/ )
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
end
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,348 @@
|
|
1
|
+
require 'TBM/config_parser'
|
2
|
+
require 'TBM/config'
|
3
|
+
require 'TBM/target'
|
4
|
+
require 'rspec'
|
5
|
+
|
6
|
+
include TBM
|
7
|
+
|
8
|
+
describe ConfigParser do
|
9
|
+
let( :path ) { File.expand_path( "~/.tbm" ) }
|
10
|
+
subject { ConfigParser.parse }
|
11
|
+
|
12
|
+
context "w/o config file" do
|
13
|
+
before do
|
14
|
+
stub_messages
|
15
|
+
File.stub( :file? ).with( path ) { false }
|
16
|
+
end
|
17
|
+
|
18
|
+
it { should_not be_valid }
|
19
|
+
specify { subject.errors.should include_match /No configuration file found/ }
|
20
|
+
end
|
21
|
+
|
22
|
+
context "w/ config file" do
|
23
|
+
before do
|
24
|
+
File.stub( :file? ).with( path ) { true }
|
25
|
+
YAML.should_receive( :load_file ).with( path ) { config }
|
26
|
+
end
|
27
|
+
|
28
|
+
context "containing no config" do
|
29
|
+
let(:config) { Hash.new }
|
30
|
+
it { should_not be_valid }
|
31
|
+
specify { subject.errors.should include_match(/No gateways/) }
|
32
|
+
end
|
33
|
+
|
34
|
+
context "containing an array" do
|
35
|
+
let(:config) { Array.new }
|
36
|
+
it { should_not be_valid }
|
37
|
+
specify { subject.errors.should include("Cannot parse TBM configuration of type: Array") }
|
38
|
+
end
|
39
|
+
|
40
|
+
context "containing a Hash" do
|
41
|
+
let(:config) { { gateway => targets } }
|
42
|
+
|
43
|
+
context "keyed by a gateway string" do
|
44
|
+
let(:gateway) { "gateway" }
|
45
|
+
let(:targets) { { 'web' => 80 } }
|
46
|
+
it { should be_valid }
|
47
|
+
|
48
|
+
before do
|
49
|
+
Etc.stub(:getlogin) { 'local-username' }
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should contain a host with the specified gateway name" do
|
53
|
+
subject.get_target('web').host.should eql('gateway')
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should contain a host with the local username" do
|
57
|
+
subject.get_target('web').username.should eql('local-username')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "keyed by a username@gateway string" do
|
62
|
+
let(:gateway) { "remote-username@gateway.example.com" }
|
63
|
+
let(:targets) { { 'web' => 80 } }
|
64
|
+
it { should be_valid }
|
65
|
+
it "should contain a host with the specified gateway name" do
|
66
|
+
subject.get_target('web').host.should eql('gateway.example.com')
|
67
|
+
end
|
68
|
+
it "should contain a host with the specified username" do
|
69
|
+
subject.get_target('web').username.should eql('remote-username')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "keyed by a non-String" do
|
74
|
+
let(:gateway) { Array.new }
|
75
|
+
let(:targets) { Hash.new }
|
76
|
+
specify { subject.errors.should include( "Cannot parse gateway name: [] (Array)" ) }
|
77
|
+
end
|
78
|
+
|
79
|
+
context "with target hash of tunnels" do
|
80
|
+
let(:gateway) { 'user@host' }
|
81
|
+
let(:targets) { { 'target-name' => target } }
|
82
|
+
|
83
|
+
context "with nil tunnel" do
|
84
|
+
let(:target) { nil }
|
85
|
+
specify { subject.errors.should include_match(/No target config/) }
|
86
|
+
end
|
87
|
+
|
88
|
+
context "with target config of 8080" do
|
89
|
+
let(:target) { 8080 }
|
90
|
+
it "should forward port 8080" do
|
91
|
+
subject.should be_valid
|
92
|
+
subject.get_target('target-name').should have_tunnel( :port => 8080, :remote_port => 8080, :remote_host => nil )
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "with target config of 0" do
|
97
|
+
let(:target) { 0 }
|
98
|
+
specify { subject.errors.should include( "Invalid port number: 0" ) }
|
99
|
+
end
|
100
|
+
|
101
|
+
context "with target config of '8443'" do
|
102
|
+
let(:target) { "8443" }
|
103
|
+
it "should forward port 8443" do
|
104
|
+
subject.should be_valid
|
105
|
+
target = subject.get_target('target-name').should have_tunnel( :port => 8443, :remote_port => 8443, :remote_host => nil )
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "with target config of '77777'" do
|
110
|
+
let(:target) { "77777" }
|
111
|
+
specify { subject.errors.should include( "Invalid port number: 77777" ) }
|
112
|
+
end
|
113
|
+
|
114
|
+
context "with target config of '8080:80'" do
|
115
|
+
let(:target) { "8080:80" }
|
116
|
+
it "should map port 8080 to 80" do
|
117
|
+
subject.should be_valid
|
118
|
+
target = subject.get_target('target-name').should have_tunnel( :port => 8080, :remote_port => 80, :remote_host => nil )
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "with target config of '8080:prod:80'" do
|
123
|
+
let(:target) { "8080:prod:80" }
|
124
|
+
it "should map port 8080 to 80 on prod" do
|
125
|
+
subject.should be_valid
|
126
|
+
target = subject.get_target('target-name').should have_tunnel( :port => 8080, :remote_port => 80, :remote_host => 'prod' )
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "with target config of 'staging:8080'" do
|
131
|
+
let(:target) { "staging:8080" }
|
132
|
+
it "should forward port 8080 to staging" do
|
133
|
+
puts subject.errors
|
134
|
+
subject.should be_valid
|
135
|
+
target = subject.get_target('target-name').should have_tunnel( :port => 8080, :remote_port => 8080, :remote_host => 'staging' )
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "with target config of [8080,8443]" do
|
140
|
+
let(:target) { [8080,8443] }
|
141
|
+
it "should forward ports 8080 and 8443" do
|
142
|
+
puts subject.errors
|
143
|
+
subject.should be_valid
|
144
|
+
target = subject.get_target('target-name').should have_tunnels( [ { :port => 8080 }, { :port => 8443 } ] )
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "with target config of [3000,8080:80]" do
|
149
|
+
let(:target) { [3000,"8080:80"] }
|
150
|
+
it "should forward port 3000 and map 8080 to 80" do
|
151
|
+
puts subject.errors
|
152
|
+
subject.should be_valid
|
153
|
+
target = subject.get_target('target-name').should have_tunnels( [ { :port => 3000, :remote_port => 3000 }, { :port => 8080, :remote_port => 80 } ] )
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context "with Hash target config" do
|
158
|
+
let(:target) { Hash.new }
|
159
|
+
|
160
|
+
context "containing nothing" do
|
161
|
+
specify { subject.errors.should include_match(/no tunnels/) }
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should forward port from 'tunnel' key" do
|
165
|
+
target['tunnel'] = 3000
|
166
|
+
subject.get_target('target-name').should have_tunnel( :port => 3000 )
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should add alias from 'alias' key" do
|
170
|
+
target['alias']='aka'
|
171
|
+
subject.get_target('target-name').should have_name('aka')
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should treat any other key as a remote host" do
|
175
|
+
target['staging']=[1111,"2222:3333"]
|
176
|
+
target['prod']=3306
|
177
|
+
subject.get_target('target-name').should have_tunnels( [
|
178
|
+
{ :port => 1111, :remote_port => 1111, :remote_host => 'staging' },
|
179
|
+
{ :port => 2222, :remote_port => 3333, :remote_host => 'staging' },
|
180
|
+
{ :port => 3306, :remote_port => 3306, :remote_host => 'prod' },
|
181
|
+
])
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
context "with a non-Hash value" do
|
187
|
+
let(:gateway) { 'user@host' }
|
188
|
+
let(:targets) { "Targets" }
|
189
|
+
specify { subject.errors.should include( "Cannot parse targets, expected Hash, received: String" ) }
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
# target-details is a string (connection), array (array of connection) or hash (target-hash)
|
195
|
+
# connection is an integer or string, matching any of: <port> or <localport>:<remoteport> or <localport>:host:<remoteport>
|
196
|
+
# target-hash contains: alias, forward, remote-host
|
197
|
+
# alias is string or array of strings containing aliases for the tunnel name.
|
198
|
+
# forward contains a connection or array of connections
|
199
|
+
# remote-host maps to a remote connection or array of remote connections
|
200
|
+
# remote connection is an integer or a string in the following formats: <port>, <local-port>:<remote-port>
|
201
|
+
|
202
|
+
# context "and no forward" do
|
203
|
+
# it { should_not be_valid }
|
204
|
+
# specify { subject.errors.should include_match(/no forward/) }
|
205
|
+
# end
|
206
|
+
|
207
|
+
# context "and a forward" do
|
208
|
+
# let(:target) { { 'host' => 'host', 'username' => 'username', 'forward' => forward } }
|
209
|
+
# let(:targetmock) { double(Tunnel::Target) }
|
210
|
+
|
211
|
+
# before do
|
212
|
+
# Tunnel::Target.stub(:new) { targetmock }
|
213
|
+
# targetmock.stub( :host ) { 'host' }
|
214
|
+
# end
|
215
|
+
|
216
|
+
# context "of 8080" do
|
217
|
+
# let(:forward) { 8080 }
|
218
|
+
# it "should forward port 8080 on localhost" do
|
219
|
+
# targetmock.should_receive( :forward_port ).with( 8080, nil )
|
220
|
+
# subject.should be_valid
|
221
|
+
# end
|
222
|
+
# end
|
223
|
+
|
224
|
+
# context "of [ 8000, 8443 ]" do
|
225
|
+
# let(:forward) { [8000,8443] }
|
226
|
+
# it "should forward ports 8080 and 8443" do
|
227
|
+
# targetmock.should_receive( :forward_port ).with( 8000, nil )
|
228
|
+
# targetmock.should_receive( :forward_port ).with( 8443, nil )
|
229
|
+
# subject.should be_valid
|
230
|
+
# end
|
231
|
+
# end
|
232
|
+
|
233
|
+
# context "of { alpha => 3000, beta => [ 8080, 8443 ] } ]" do
|
234
|
+
# let(:forward) { { 'alpha' => 3000, 'beta' => [ 8080, 8443 ] } }
|
235
|
+
# it "should forward port 3000 to alpha" do
|
236
|
+
# targetmock.should_receive( :forward_port ).with( 3000, 'alpha' )
|
237
|
+
# targetmock.stub( :forward_port ).with( anything(), 'beta' )
|
238
|
+
# subject.should be_valid
|
239
|
+
# end
|
240
|
+
# it "should forward ports 8080, 8443 to alpha" do
|
241
|
+
# targetmock.should_receive( :forward_port ).with( 8080, 'beta' )
|
242
|
+
# targetmock.should_receive( :forward_port ).with( 8443, 'beta' )
|
243
|
+
# targetmock.stub( :forward_port ).with( anything(), 'alpha' )
|
244
|
+
# subject.should be_valid
|
245
|
+
# end
|
246
|
+
# end
|
247
|
+
|
248
|
+
# context "of 8000.5" do
|
249
|
+
# let(:forward) { 8000.5 }
|
250
|
+
# it { should_not be_valid }
|
251
|
+
# specify { subject.errors.should include_match(/Not sure how to handle forward .*: Float/) }
|
252
|
+
# end
|
253
|
+
|
254
|
+
# context "of -8080" do
|
255
|
+
# let(:forward) { -8080 }
|
256
|
+
# it { should_not be_valid }
|
257
|
+
# specify { subject.errors.should include_match(/Invalid port/) }
|
258
|
+
# end
|
259
|
+
|
260
|
+
# context "of 'blueberry'" do
|
261
|
+
# let(:forward) { 'blueberry' }
|
262
|
+
# it { should_not be_valid }
|
263
|
+
# specify { subject.errors.should include_match(/Not sure how to handle forward .*: String/) }
|
264
|
+
# end
|
265
|
+
|
266
|
+
# context "of 'blueberry'" do
|
267
|
+
# let(:forward) { [ 80.80, -443, 'blueberry' ] }
|
268
|
+
# it { should_not be_valid }
|
269
|
+
# specify { subject.errors.should include_match(/Invalid port/) }
|
270
|
+
# end
|
271
|
+
|
272
|
+
# end
|
273
|
+
|
274
|
+
# context "and an alias" do
|
275
|
+
# let(:target) { { 'host' => 'host', 'username' => 'username', 'forward' => 8080 } }
|
276
|
+
# let(:targetmock) { double(Tunnel::Target) }
|
277
|
+
|
278
|
+
# before do
|
279
|
+
# Tunnel::Target.stub(:new) { targetmock }
|
280
|
+
# targetmock.stub(:forward_port)
|
281
|
+
# end
|
282
|
+
|
283
|
+
# it "should treat string as single alias" do
|
284
|
+
# target['alias']= 'mr-smith'
|
285
|
+
# targetmock.should_receive( :alias ).with( 'mr-smith' )
|
286
|
+
# subject.should be_valid
|
287
|
+
# end
|
288
|
+
|
289
|
+
# it "should treat an array as a series of aliases" do
|
290
|
+
# target['alias']= 'mr-smith'
|
291
|
+
# targetmock.should_receive( :alias ).with( 'mr-smith' )
|
292
|
+
# subject.should be_valid
|
293
|
+
# end
|
294
|
+
|
295
|
+
# it "should warn with any other content" do
|
296
|
+
# target['alias'] = Hash.new
|
297
|
+
# subject.should_not be_valid
|
298
|
+
# subject.errors.should include_match( /Cannot parse alias/ )
|
299
|
+
# end
|
300
|
+
# end
|
301
|
+
# end
|
302
|
+
|
303
|
+
# end
|
304
|
+
|
305
|
+
# context "containing a hash of five targets" do
|
306
|
+
# let(:config) { { 'alpha' => { 'host' => 'host', 'forward' => 3001 }, 'beta' => { 'host' => 'host', 'forward' => 3002 }, 'gamma' => { 'host' => 'host', 'forward' => 3003 }, 'delta' => { 'host' => 'host', 'forward' => 3004 },
|
307
|
+
# 'omega' => { 'host' => 'host', 'forward' => 3005 } } }
|
308
|
+
# it "should return all five targets specified" do
|
309
|
+
# subject.should be_valid
|
310
|
+
# subject.get_target( 'alpha' ).should be_instance_of(Tunnel::Target)
|
311
|
+
# subject.get_target( 'beta' ).should be_instance_of(Tunnel::Target)
|
312
|
+
# subject.get_target( 'gamma' ).should be_instance_of(Tunnel::Target)
|
313
|
+
# subject.get_target( 'delta' ).should be_instance_of(Tunnel::Target)
|
314
|
+
# subject.get_target( 'omega' ).should be_instance_of(Tunnel::Target)
|
315
|
+
# end
|
316
|
+
# end
|
317
|
+
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
RSpec::Matchers.define :have_tunnel do |expected|
|
322
|
+
match do |actual|
|
323
|
+
actual.should_not be_nil
|
324
|
+
actual.tunnels.size.should eql(1)
|
325
|
+
tunnel = actual.tunnels[0]
|
326
|
+
expected.each do |k,v|
|
327
|
+
tunnel.send(k).should eql(v)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
description do
|
331
|
+
properties = expected.map{ |k,v| "#{k}=#{v}" }.join( ', ' )
|
332
|
+
"have a tunnel with #{properties}"
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
RSpec::Matchers.define :have_tunnels do |expected|
|
337
|
+
match do |actual|
|
338
|
+
actual.should_not be_nil
|
339
|
+
actual.tunnels.size.should eql(expected.size)
|
340
|
+
(0...expected.size).each do |index|
|
341
|
+
tunnel = actual.tunnels[index]
|
342
|
+
properties = expected[index]
|
343
|
+
properties.each do |k,v|
|
344
|
+
tunnel.send(k).should eql(v)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|