switchtower 0.9.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.
- data/bin/switchtower +11 -0
- data/examples/sample.rb +113 -0
- data/lib/switchtower.rb +1 -0
- data/lib/switchtower/actor.rb +350 -0
- data/lib/switchtower/cli.rb +220 -0
- data/lib/switchtower/command.rb +85 -0
- data/lib/switchtower/configuration.rb +193 -0
- data/lib/switchtower/gateway.rb +106 -0
- data/lib/switchtower/generators/rails/deployment/deployment_generator.rb +25 -0
- data/lib/switchtower/generators/rails/deployment/templates/deploy.rb +116 -0
- data/lib/switchtower/generators/rails/deployment/templates/switchtower.rake +33 -0
- data/lib/switchtower/generators/rails/loader.rb +20 -0
- data/lib/switchtower/logger.rb +56 -0
- data/lib/switchtower/recipes/standard.rb +175 -0
- data/lib/switchtower/recipes/templates/maintenance.rhtml +53 -0
- data/lib/switchtower/scm/base.rb +43 -0
- data/lib/switchtower/scm/cvs.rb +73 -0
- data/lib/switchtower/scm/darcs.rb +27 -0
- data/lib/switchtower/scm/subversion.rb +104 -0
- data/lib/switchtower/ssh.rb +30 -0
- data/lib/switchtower/version.rb +9 -0
- data/test/actor_test.rb +261 -0
- data/test/command_test.rb +43 -0
- data/test/configuration_test.rb +210 -0
- data/test/fixtures/config.rb +5 -0
- data/test/scm/cvs_test.rb +164 -0
- data/test/scm/subversion_test.rb +100 -0
- data/test/ssh_test.rb +104 -0
- data/test/utils.rb +41 -0
- metadata +88 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
|
2
|
+
|
|
3
|
+
require 'stringio'
|
|
4
|
+
require 'test/unit'
|
|
5
|
+
require 'switchtower/command'
|
|
6
|
+
|
|
7
|
+
class CommandTest < Test::Unit::TestCase
|
|
8
|
+
class MockSession
|
|
9
|
+
def open_channel
|
|
10
|
+
{ :closed => true, :status => 0 }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class MockActor
|
|
15
|
+
attr_reader :sessions
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@sessions = Hash.new { |h,k| h[k] = MockSession.new }
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def setup
|
|
23
|
+
@actor = MockActor.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def test_command_executes_on_all_servers
|
|
27
|
+
command = SwitchTower::Command.new(%w(server1 server2 server3),
|
|
28
|
+
"hello", nil, {}, @actor)
|
|
29
|
+
assert_equal %w(server1 server2 server3), @actor.sessions.keys.sort
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_command_with_newlines
|
|
33
|
+
command = SwitchTower::Command.new(%w(server1), "hello\nworld", nil, {},
|
|
34
|
+
@actor)
|
|
35
|
+
assert_equal "hello\\\nworld", command.command
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_command_with_windows_newlines
|
|
39
|
+
command = SwitchTower::Command.new(%w(server1), "hello\r\nworld", nil, {},
|
|
40
|
+
@actor)
|
|
41
|
+
assert_equal "hello\\\nworld", command.command
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
|
2
|
+
|
|
3
|
+
require 'test/unit'
|
|
4
|
+
require 'switchtower/configuration'
|
|
5
|
+
require 'flexmock'
|
|
6
|
+
|
|
7
|
+
class ConfigurationTest < Test::Unit::TestCase
|
|
8
|
+
class MockActor
|
|
9
|
+
attr_reader :tasks
|
|
10
|
+
|
|
11
|
+
def initialize(config)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def define_task(*args, &block)
|
|
15
|
+
(@tasks ||= []).push [args, block].flatten
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class MockSCM
|
|
20
|
+
attr_reader :configuration
|
|
21
|
+
|
|
22
|
+
def initialize(config)
|
|
23
|
+
@configuration = config
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def setup
|
|
28
|
+
@config = SwitchTower::Configuration.new(MockActor)
|
|
29
|
+
@config.set :scm, MockSCM
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_version_dir_default
|
|
33
|
+
assert "releases", @config.version_dir
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_current_dir_default
|
|
37
|
+
assert "current", @config.current_dir
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_shared_dir_default
|
|
41
|
+
assert "shared", @config.shared_dir
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_set_repository
|
|
45
|
+
@config.set :repository, "/foo/bar/baz"
|
|
46
|
+
assert_equal "/foo/bar/baz", @config.repository
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_set_user
|
|
50
|
+
@config.set :user, "flippy"
|
|
51
|
+
assert_equal "flippy", @config.user
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_define_single_role
|
|
55
|
+
@config.role :app, "somewhere.example.com"
|
|
56
|
+
assert_equal 1, @config.roles[:app].length
|
|
57
|
+
assert_equal "somewhere.example.com", @config.roles[:app].first.host
|
|
58
|
+
assert_equal Hash.new, @config.roles[:app].first.options
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_define_single_role_with_options
|
|
62
|
+
@config.role :app, "somewhere.example.com", :primary => true
|
|
63
|
+
assert_equal 1, @config.roles[:app].length
|
|
64
|
+
assert_equal "somewhere.example.com", @config.roles[:app].first.host
|
|
65
|
+
assert_equal({:primary => true}, @config.roles[:app].first.options)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_define_multi_role
|
|
69
|
+
@config.role :app, "somewhere.example.com", "else.example.com"
|
|
70
|
+
assert_equal 2, @config.roles[:app].length
|
|
71
|
+
assert_equal "somewhere.example.com", @config.roles[:app].first.host
|
|
72
|
+
assert_equal "else.example.com", @config.roles[:app].last.host
|
|
73
|
+
assert_equal({}, @config.roles[:app].first.options)
|
|
74
|
+
assert_equal({}, @config.roles[:app].last.options)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_define_multi_role_with_options
|
|
78
|
+
@config.role :app, "somewhere.example.com", "else.example.com", :primary => true
|
|
79
|
+
assert_equal 2, @config.roles[:app].length
|
|
80
|
+
assert_equal "somewhere.example.com", @config.roles[:app].first.host
|
|
81
|
+
assert_equal "else.example.com", @config.roles[:app].last.host
|
|
82
|
+
assert_equal({:primary => true}, @config.roles[:app].first.options)
|
|
83
|
+
assert_equal({:primary => true}, @config.roles[:app].last.options)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_load_string_unnamed
|
|
87
|
+
@config.load :string => "set :repository, __FILE__"
|
|
88
|
+
assert_equal "<eval>", @config.repository
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def test_load_string_named
|
|
92
|
+
@config.load :string => "set :repository, __FILE__", :name => "test.rb"
|
|
93
|
+
assert_equal "test.rb", @config.repository
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def test_load
|
|
97
|
+
file = File.dirname(__FILE__) + "/fixtures/config.rb"
|
|
98
|
+
@config.load file
|
|
99
|
+
assert_equal "1/2/foo", @config.repository
|
|
100
|
+
assert_equal "./#{file}.example.com", @config.gateway
|
|
101
|
+
assert_equal 1, @config.roles[:web].length
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def test_load_explicit_name
|
|
105
|
+
file = File.dirname(__FILE__) + "/fixtures/config.rb"
|
|
106
|
+
@config.load file, :name => "config"
|
|
107
|
+
assert_equal "1/2/foo", @config.repository
|
|
108
|
+
assert_equal "config.example.com", @config.gateway
|
|
109
|
+
assert_equal 1, @config.roles[:web].length
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_load_file_implied_name
|
|
113
|
+
file = File.dirname(__FILE__) + "/fixtures/config.rb"
|
|
114
|
+
@config.load :file => file
|
|
115
|
+
assert_equal "1/2/foo", @config.repository
|
|
116
|
+
assert_equal "./#{file}.example.com", @config.gateway
|
|
117
|
+
assert_equal 1, @config.roles[:web].length
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def test_load_file_explicit_name
|
|
121
|
+
file = File.dirname(__FILE__) + "/fixtures/config.rb"
|
|
122
|
+
@config.load :file => file, :name => "config"
|
|
123
|
+
assert_equal "1/2/foo", @config.repository
|
|
124
|
+
assert_equal "config.example.com", @config.gateway
|
|
125
|
+
assert_equal 1, @config.roles[:web].length
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def test_task_without_options
|
|
129
|
+
block = Proc.new { }
|
|
130
|
+
@config.task :hello, &block
|
|
131
|
+
assert_equal 1, @config.actor.tasks.length
|
|
132
|
+
assert_equal :hello, @config.actor.tasks[0][0]
|
|
133
|
+
assert_equal({}, @config.actor.tasks[0][1])
|
|
134
|
+
assert_equal block, @config.actor.tasks[0][2]
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def test_task_with_options
|
|
138
|
+
block = Proc.new { }
|
|
139
|
+
@config.task :hello, :roles => :app, &block
|
|
140
|
+
assert_equal 1, @config.actor.tasks.length
|
|
141
|
+
assert_equal :hello, @config.actor.tasks[0][0]
|
|
142
|
+
assert_equal({:roles => :app}, @config.actor.tasks[0][1])
|
|
143
|
+
assert_equal block, @config.actor.tasks[0][2]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def test_source
|
|
147
|
+
@config.set :repository, "/foo/bar/baz"
|
|
148
|
+
assert_equal "/foo/bar/baz", @config.source.configuration.repository
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def test_releases_path_default
|
|
152
|
+
@config.set :deploy_to, "/start/of/path"
|
|
153
|
+
assert_equal "/start/of/path/releases", @config.releases_path
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def test_releases_path_custom
|
|
157
|
+
@config.set :deploy_to, "/start/of/path"
|
|
158
|
+
@config.set :version_dir, "right/here"
|
|
159
|
+
assert_equal "/start/of/path/right/here", @config.releases_path
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def test_current_path_default
|
|
163
|
+
@config.set :deploy_to, "/start/of/path"
|
|
164
|
+
assert_equal "/start/of/path/current", @config.current_path
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def test_current_path_custom
|
|
168
|
+
@config.set :deploy_to, "/start/of/path"
|
|
169
|
+
@config.set :current_dir, "right/here"
|
|
170
|
+
assert_equal "/start/of/path/right/here", @config.current_path
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def test_shared_path_default
|
|
174
|
+
@config.set :deploy_to, "/start/of/path"
|
|
175
|
+
assert_equal "/start/of/path/shared", @config.shared_path
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def test_shared_path_custom
|
|
179
|
+
@config.set :deploy_to, "/start/of/path"
|
|
180
|
+
@config.set :shared_dir, "right/here"
|
|
181
|
+
assert_equal "/start/of/path/right/here", @config.shared_path
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def test_release_path_implicit
|
|
185
|
+
@config.set :deploy_to, "/start/of/path"
|
|
186
|
+
assert_equal "/start/of/path/releases/#{@config.now.strftime("%Y%m%d%H%M%S")}", @config.release_path
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def test_release_path_explicit
|
|
190
|
+
@config.set :deploy_to, "/start/of/path"
|
|
191
|
+
assert_equal "/start/of/path/releases/silly", @config.release_path("silly")
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def test_task_description
|
|
195
|
+
block = Proc.new { }
|
|
196
|
+
@config.desc "A sample task"
|
|
197
|
+
@config.task :hello, &block
|
|
198
|
+
assert_equal "A sample task", @config.actor.tasks[0][1][:desc]
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def test_set_scm_to_darcs
|
|
202
|
+
@config.set :scm, :darcs
|
|
203
|
+
assert_equal "SwitchTower::SCM::Darcs", @config.source.class.name
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def test_set_scm_to_subversion
|
|
207
|
+
@config.set :scm, :subversion
|
|
208
|
+
assert_equal "SwitchTower::SCM::Subversion", @config.source.class.name
|
|
209
|
+
end
|
|
210
|
+
end
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + "/../../lib"
|
|
2
|
+
|
|
3
|
+
require File.dirname(__FILE__) + "/../utils"
|
|
4
|
+
require 'test/unit'
|
|
5
|
+
require 'switchtower/scm/cvs'
|
|
6
|
+
|
|
7
|
+
class ScmCvsTest < Test::Unit::TestCase
|
|
8
|
+
class CvsTest < SwitchTower::SCM::Cvs
|
|
9
|
+
attr_accessor :story
|
|
10
|
+
attr_reader :last_path
|
|
11
|
+
|
|
12
|
+
def cvs_log(path)
|
|
13
|
+
@last_path = path
|
|
14
|
+
story.shift
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class MockChannel
|
|
19
|
+
attr_reader :sent_data
|
|
20
|
+
|
|
21
|
+
def send_data(data)
|
|
22
|
+
@sent_data ||= []
|
|
23
|
+
@sent_data << data
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def [](name)
|
|
27
|
+
"value"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class MockActor
|
|
32
|
+
attr_reader :command
|
|
33
|
+
attr_reader :channels
|
|
34
|
+
attr_accessor :story
|
|
35
|
+
|
|
36
|
+
def initialize(config)
|
|
37
|
+
@config = config
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def run(command)
|
|
41
|
+
@command = command
|
|
42
|
+
@channels ||= []
|
|
43
|
+
@channels << MockChannel.new
|
|
44
|
+
story.each { |stream, line| yield @channels.last, stream, line }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def release_path
|
|
48
|
+
(@config[:now] || Time.now.utc).strftime("%Y%m%d%H%M%S")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def method_missing(sym, *args)
|
|
52
|
+
@config.send(sym, *args)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def setup
|
|
57
|
+
@config = MockConfiguration.new
|
|
58
|
+
@config[:repository] = ":ext:joetester@rubyforge.org:/hello/world"
|
|
59
|
+
@config[:local] = "/hello/world"
|
|
60
|
+
@config[:cvs] = "/path/to/cvs"
|
|
61
|
+
@config[:password] = "chocolatebrownies"
|
|
62
|
+
@config[:now] = Time.utc(2005,8,24,12,0,0)
|
|
63
|
+
@scm = CvsTest.new(@config)
|
|
64
|
+
@actor = MockActor.new(@config)
|
|
65
|
+
@log_msg = <<MSG.strip
|
|
66
|
+
RCS file: /var/cvs/copland/copland/LICENSE,v
|
|
67
|
+
Working file: LICENSE
|
|
68
|
+
head: 1.1
|
|
69
|
+
branch:
|
|
70
|
+
locks: strict
|
|
71
|
+
access list:
|
|
72
|
+
keyword substitution: kv
|
|
73
|
+
total revisions: 1; selected revisions: 1
|
|
74
|
+
description:
|
|
75
|
+
----------------------------
|
|
76
|
+
revision 1.1
|
|
77
|
+
date: 2004/08/29 04:23:36; author: minam; state: Exp;
|
|
78
|
+
New implementation.
|
|
79
|
+
=============================================================================
|
|
80
|
+
|
|
81
|
+
RCS file: /var/cvs/copland/copland/Rakefile,v
|
|
82
|
+
Working file: Rakefile
|
|
83
|
+
head: 1.7
|
|
84
|
+
branch:
|
|
85
|
+
locks: strict
|
|
86
|
+
access list:
|
|
87
|
+
keyword substitution: kv
|
|
88
|
+
total revisions: 7; selected revisions: 1
|
|
89
|
+
description:
|
|
90
|
+
----------------------------
|
|
91
|
+
revision 1.7
|
|
92
|
+
date: 2004/09/15 16:35:01; author: minam; state: Exp; lines: +2 -1
|
|
93
|
+
Rakefile now publishes package documentation from doc/packages instead of
|
|
94
|
+
doc/packrat. Updated "latest updates" in manual.
|
|
95
|
+
=============================================================================
|
|
96
|
+
|
|
97
|
+
RCS file: /var/cvs/copland/copland/TODO,v
|
|
98
|
+
Working file: TODO
|
|
99
|
+
head: 1.18
|
|
100
|
+
branch:
|
|
101
|
+
locks: strict
|
|
102
|
+
access list:
|
|
103
|
+
keyword substitution: kv
|
|
104
|
+
total revisions: 18; selected revisions: 1
|
|
105
|
+
description:
|
|
106
|
+
----------------------------
|
|
107
|
+
revision 1.18
|
|
108
|
+
date: 2004/10/12 02:21:02; author: minam; state: Exp; lines: +4 -1
|
|
109
|
+
Added RubyConf 2004 presentation.
|
|
110
|
+
=============================================================================
|
|
111
|
+
|
|
112
|
+
RCS file: /var/cvs/copland/copland/Attic/build-gemspec.rb,v
|
|
113
|
+
Working file: build-gemspec.rb
|
|
114
|
+
head: 1.5
|
|
115
|
+
branch:
|
|
116
|
+
locks: strict
|
|
117
|
+
access list:
|
|
118
|
+
keyword substitution: kv
|
|
119
|
+
total revisions: 5; selected revisions: 1
|
|
120
|
+
description:
|
|
121
|
+
----------------------------
|
|
122
|
+
revision 1.5
|
|
123
|
+
date: 2004/08/29 04:10:17; author: minam; state: dead; lines: +0 -0
|
|
124
|
+
Here we go -- point of no return. Deleting existing implementation to make
|
|
125
|
+
way for new implementation.
|
|
126
|
+
=============================================================================
|
|
127
|
+
|
|
128
|
+
RCS file: /var/cvs/copland/copland/copland.gemspec,v
|
|
129
|
+
Working file: copland.gemspec
|
|
130
|
+
head: 1.12
|
|
131
|
+
branch:
|
|
132
|
+
locks: strict
|
|
133
|
+
access list:
|
|
134
|
+
keyword substitution: kv
|
|
135
|
+
total revisions: 13; selected revisions: 1
|
|
136
|
+
description:
|
|
137
|
+
----------------------------
|
|
138
|
+
revision 1.12
|
|
139
|
+
date: 2004/09/11 21:45:58; author: minam; state: Exp; lines: +4 -4
|
|
140
|
+
Minor change in how version is communicated to gemspec.
|
|
141
|
+
=============================================================================
|
|
142
|
+
MSG
|
|
143
|
+
@scm.story = [ @log_msg ]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def test_latest_revision
|
|
147
|
+
@scm.story = [ @log_msg ]
|
|
148
|
+
assert_equal "2004-10-12 02:21:02", @scm.latest_revision
|
|
149
|
+
assert_equal "/hello/world", @scm.last_path
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def test_checkout
|
|
153
|
+
@actor.story = []
|
|
154
|
+
assert_nothing_raised { @scm.checkout(@actor) }
|
|
155
|
+
assert_nil @actor.channels.last.sent_data
|
|
156
|
+
assert_match %r{/path/to/cvs}, @actor.command
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def test_checkout_needs_ssh_password
|
|
160
|
+
@actor.story = [[:out, "joetester@rubyforge.org's password: "]]
|
|
161
|
+
assert_nothing_raised { @scm.checkout(@actor) }
|
|
162
|
+
assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + "/../../lib"
|
|
2
|
+
|
|
3
|
+
require File.dirname(__FILE__) + "/../utils"
|
|
4
|
+
require 'test/unit'
|
|
5
|
+
require 'switchtower/scm/subversion'
|
|
6
|
+
|
|
7
|
+
class ScmSubversionTest < Test::Unit::TestCase
|
|
8
|
+
class SubversionTest < SwitchTower::SCM::Subversion
|
|
9
|
+
attr_accessor :story
|
|
10
|
+
attr_reader :last_path
|
|
11
|
+
|
|
12
|
+
def svn_log(path)
|
|
13
|
+
@last_path = path
|
|
14
|
+
story.shift
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class MockChannel
|
|
19
|
+
attr_reader :sent_data
|
|
20
|
+
|
|
21
|
+
def send_data(data)
|
|
22
|
+
@sent_data ||= []
|
|
23
|
+
@sent_data << data
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def [](name)
|
|
27
|
+
"value"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class MockActor
|
|
32
|
+
attr_reader :command
|
|
33
|
+
attr_reader :channels
|
|
34
|
+
attr_accessor :story
|
|
35
|
+
|
|
36
|
+
def initialize(config)
|
|
37
|
+
@config = config
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def run(command)
|
|
41
|
+
@command = command
|
|
42
|
+
@channels ||= []
|
|
43
|
+
@channels << MockChannel.new
|
|
44
|
+
story.each { |stream, line| yield @channels.last, stream, line }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def method_missing(sym, *args)
|
|
48
|
+
@config.send(sym, *args)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def setup
|
|
53
|
+
@config = MockConfiguration.new
|
|
54
|
+
@config[:repository] = "/hello/world"
|
|
55
|
+
@config[:svn] = "/path/to/svn"
|
|
56
|
+
@config[:password] = "chocolatebrownies"
|
|
57
|
+
@scm = SubversionTest.new(@config)
|
|
58
|
+
@actor = MockActor.new(@config)
|
|
59
|
+
@log_msg = <<MSG.strip
|
|
60
|
+
------------------------------------------------------------------------
|
|
61
|
+
r1967 | minam | 2005-08-03 06:59:03 -0600 (Wed, 03 Aug 2005) | 2 lines
|
|
62
|
+
|
|
63
|
+
Initial commit of the new switchtower utility
|
|
64
|
+
|
|
65
|
+
------------------------------------------------------------------------
|
|
66
|
+
MSG
|
|
67
|
+
@scm.story = [ @log_msg ]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_latest_revision
|
|
71
|
+
@scm.story = [ @log_msg ]
|
|
72
|
+
assert_equal "1967", @scm.latest_revision
|
|
73
|
+
assert_equal "/hello/world", @scm.last_path
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_latest_revision_searching_upwards
|
|
77
|
+
@scm.story = [ "-----------------------------\n", @log_msg ]
|
|
78
|
+
assert_equal "1967", @scm.latest_revision
|
|
79
|
+
assert_equal "/hello", @scm.last_path
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def test_checkout
|
|
83
|
+
@actor.story = []
|
|
84
|
+
assert_nothing_raised { @scm.checkout(@actor) }
|
|
85
|
+
assert_nil @actor.channels.last.sent_data
|
|
86
|
+
assert_match %r{/path/to/svn}, @actor.command
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_checkout_needs_ssh_password
|
|
90
|
+
@actor.story = [[:out, "Password: "]]
|
|
91
|
+
assert_nothing_raised { @scm.checkout(@actor) }
|
|
92
|
+
assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def test_checkout_needs_http_password
|
|
96
|
+
@actor.story = [[:out, "Password for (something): "]]
|
|
97
|
+
assert_nothing_raised { @scm.checkout(@actor) }
|
|
98
|
+
assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
|
|
99
|
+
end
|
|
100
|
+
end
|