1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
|
$:.unshift File.dirname(__FILE__) + "/../lib"
require 'stringio'
require 'test/unit'
require 'switchtower/actor'
require 'switchtower/logger'
class ActorTest < Test::Unit::TestCase
class TestingConnectionFactory
def initialize(config)
end
def connect_to(server)
server
end
end
class GatewayConnectionFactory
def connect_to(server)
server
end
end
class TestingCommand
def self.invoked!
@invoked = true
end
def self.invoked?
@invoked
end
def self.reset!
@invoked = nil
end
def initialize(*args)
end
def process!
self.class.invoked!
end
end
class TestActor < SwitchTower::Actor
attr_reader :factory
self.connection_factory = TestingConnectionFactory
self.command_factory = TestingCommand
def establish_gateway
GatewayConnectionFactory.new
end
end
class MockConfiguration
Role = Struct.new(:host, :options)
attr_accessor :gateway, :pretend
def delegated_method
"result of method"
end
ROLES = { :db => [ Role.new("01.example.com", :primary => true),
Role.new("02.example.com", {}),
Role.new("all.example.com", {})],
:web => [ Role.new("03.example.com", {}),
Role.new("04.example.com", {}),
Role.new("all.example.com", {})],
:app => [ Role.new("05.example.com", {}),
Role.new("06.example.com", {}),
Role.new("07.example.com", {}),
Role.new("all.example.com", {})] }
def roles
ROLES
end
def logger
@logger ||= SwitchTower::Logger.new(:output => StringIO.new)
end
end
def setup
TestingCommand.reset!
@actor = TestActor.new(MockConfiguration.new)
end
def test_define_task_creates_method
@actor.define_task :hello do
"result"
end
assert @actor.respond_to?(:hello)
assert_equal "result", @actor.hello
end
def test_define_task_with_successful_transaction
class << @actor
attr_reader :rolled_back
attr_reader :history
end
@actor.define_task :hello do
(@history ||= []) << :hello
on_rollback { @rolled_back = true }
"hello"
end
@actor.define_task :goodbye do
(@history ||= []) << :goodbye
transaction do
hello
end
"goodbye"
end
assert_nothing_raised { @actor.goodbye }
assert !@actor.rolled_back
assert_equal [:goodbye, :hello], @actor.history
end
def test_define_task_with_failed_transaction
class << @actor
attr_reader :rolled_back
attr_reader :history
end
@actor.define_task :hello do
(@history ||= []) << :hello
on_rollback { @rolled_back = true }
"hello"
end
@actor.define_task :goodbye do
(@history ||= []) << :goodbye
transaction do
hello
raise "ouch"
end
"goodbye"
end
assert_raise(RuntimeError) do
@actor.goodbye
end
assert @actor.rolled_back
assert_equal [:goodbye, :hello], @actor.history
end
def test_delegates_to_configuration
@actor.define_task :hello do
delegated_method
end
assert_equal "result of method", @actor.hello
end
def test_task_servers_with_duplicates
@actor.define_task :foo do
run "do this"
end
assert_equal %w(01.example.com 02.example.com 03.example.com 04.example.com 05.example.com 06.example.com 07.example.com all.example.com), @actor.tasks[:foo].servers(@actor.configuration).sort
end
def test_run_in_task_without_explicit_roles_selects_all_roles
@actor.define_task :foo do
run "do this"
end
@actor.foo
assert_equal %w(01.example.com 02.example.com 03.example.com 04.example.com 05.example.com 06.example.com 07.example.com all.example.com), @actor.sessions.keys.sort
end
def test_run_in_task_with_single_role_selects_that_role
@actor.define_task :foo, :roles => :db do
run "do this"
end
@actor.foo
assert_equal %w(01.example.com 02.example.com all.example.com), @actor.sessions.keys.sort
end
def test_run_in_task_with_multiple_roles_selects_those_roles
@actor.define_task :foo, :roles => [:db, :web] do
run "do this"
end
@actor.foo
assert_equal %w(01.example.com 02.example.com 03.example.com 04.example.com all.example.com), @actor.sessions.keys.sort
end
def test_run_in_task_with_only_restricts_selected_roles
@actor.define_task :foo, :roles => :db, :only => { :primary => true } do
run "do this"
end
@actor.foo
assert_equal %w(01.example.com), @actor.sessions.keys.sort
end
def test_establish_connection_uses_gateway_if_specified
@actor.configuration.gateway = "10.example.com"
@actor.define_task :foo, :roles => :db do
run "do this"
end
@actor.foo
assert_instance_of GatewayConnectionFactory, @actor.factory
end
def test_run_when_not_pretend
@actor.define_task :foo do
run "do this"
end
@actor.configuration.pretend = false
@actor.foo
assert TestingCommand.invoked?
end
def test_run_when_pretend
@actor.define_task :foo do
run "do this"
end
@actor.configuration.pretend = true
@actor.foo
assert !TestingCommand.invoked?
end
def test_task_before_hook
history = []
@actor.define_task :foo do
history << "foo"
end
@actor.define_task :before_foo do
history << "before_foo"
end
@actor.foo
assert_equal %w(before_foo foo), history
end
def test_task_after_hook
history = []
@actor.define_task :foo do
history << "foo"
end
@actor.define_task :after_foo do
history << "after_foo"
end
@actor.foo
assert_equal %w(foo after_foo), history
end
end
|