sanford 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -44,6 +44,15 @@ class FlagServiceHandler
44
44
 
45
45
  end
46
46
 
47
+ class RunOtherHandler
48
+ include Sanford::ServiceHandler
49
+
50
+ def run!
51
+ response = run_handler(HaltServiceHandler, 'code' => 200, 'data' => 'RunOtherHandler')
52
+ response.data
53
+ end
54
+ end
55
+
47
56
  class HaltServiceHandler
48
57
  include Sanford::ServiceHandler
49
58
 
@@ -3,15 +3,15 @@ require 'logger'
3
3
  class TestHost
4
4
  include Sanford::Host
5
5
 
6
- attr_accessor :setup_has_been_called
6
+ attr_accessor :init_has_been_called
7
7
 
8
- setup do
9
- self.setup_has_been_called = true
8
+ init do
9
+ self.init_has_been_called = true
10
10
  end
11
11
 
12
- ip 'localhost'
13
- port 12000
14
- pid_dir File.expand_path('../../../tmp/', __FILE__)
12
+ ip 'localhost'
13
+ port 12000
14
+ pid_file File.expand_path('../../../tmp/test_host.pid', __FILE__)
15
15
 
16
16
  logger(Logger.new(File.expand_path("../../../log/test.log", __FILE__)).tap do |logger|
17
17
  logger.level = Logger::DEBUG
@@ -100,8 +100,9 @@ end
100
100
  class MyHost
101
101
  include Sanford::Host
102
102
 
103
- name 'my_host'
104
- ip 'my.local'
103
+ name 'my_host'
104
+ ip 'my.local'
105
+ pid_file File.expand_path('../../../tmp/my_host.pid', __FILE__)
105
106
  end
106
107
 
107
108
  class InvalidHost
@@ -35,6 +35,12 @@ class SimpleClient
35
35
  self.call_using_fake_socket(:with_encoded_msg_body, *args)
36
36
  end
37
37
 
38
+ def call_with_keep_alive
39
+ socket = TCPSocket.new(@host, @port)
40
+ ensure
41
+ socket.close rescue false
42
+ end
43
+
38
44
  def call(bytes)
39
45
  socket = TCPSocket.new(@host, @port)
40
46
  socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
@@ -1,103 +1,92 @@
1
1
  require 'assert'
2
2
 
3
- require 'sanford/manager'
3
+ require 'sanford/cli'
4
4
 
5
5
  class ManagingTest < Assert::Context
6
- desc "Using Sanford's manager"
6
+ include Test::ManagerHelper
7
+ desc "Using Sanford's Manager"
7
8
  setup do
8
- # preserve the global service hosts configuration, no matter how we
9
- # manipulate it
10
- Test::Environment.store_and_clear_hosts
9
+ @start_options = { :host => 'MyHost', :ip => 'localhost', :port => 12345 }
11
10
  end
12
- teardown do
13
- Test::Environment.restore_hosts
14
- end
15
-
16
- class CallTest < ManagingTest
17
- include Test::ForkManagerHelper
18
11
 
12
+ class RunTest < ManagingTest
13
+ desc "to run a server"
19
14
  setup do
20
- Sanford.register(TestHost)
15
+ @proc = proc{ Sanford::Manager.call(:run, @start_options) }
21
16
  end
22
- end
23
-
24
- class RunTest < CallTest
25
- desc "to run a service host"
26
17
 
27
- should "start a sanford server for the only service host that is configured" do
28
- self.call_sanford_manager(:run) do
29
- assert_nothing_raised{ self.open_socket(TestHost.ip, TestHost.port) }
30
- assert File.exists?(self.expected_pid_file(TestHost, TestHost.ip, TestHost.port))
18
+ should "run the server specified and write a PID file" do
19
+ self.fork_and_call(@proc) do
20
+ assert_nothing_raised{ self.open_socket('localhost', 12345) }
21
+ assert File.exists?('tmp/my_host.pid')
31
22
  end
32
23
  end
33
- end
34
-
35
- class RunWithOptionsTest < CallTest
36
- desc "to run a service host and passing options"
37
- setup do
38
- # make sure that TestHost isn't the only 'host'
39
- Sanford.register(Class.new)
40
- end
41
24
 
42
- should "start a sanford server for the specified service host and " \
43
- "use the passed options to override it's configuration" do
44
- host = Sanford.hosts.find('TestHost')
45
-
46
- self.call_sanford_manager(:run, { :host => 'TestHost', :port => 12345 }) do
47
- assert_nothing_raised{ self.open_socket(host.ip, 12345) }
48
- assert File.exists?(self.expected_pid_file(host, host.ip, 12345))
49
- end
50
- end
51
25
  end
52
26
 
53
- class RunWithEnvVarsTest < CallTest
54
- desc "to run a service host and setting env vars"
27
+ class StartTest < ManagingTest
28
+ desc "to start a daemonized server"
55
29
  setup do
56
- @current = ENV.delete('SANFORD_HOST'), ENV.delete('SANFORD_IP'), ENV.delete('SANFORD_PORT')
57
- ENV['SANFORD_HOST'] = 'TestHost'
58
- ENV['SANFORD_IP'], ENV['SANFORD_PORT'] = 'localhost', '54321'
59
- # make sure that TestHost isn't the only 'host'
60
- Sanford.register(Class.new)
30
+ @proc = proc{ Sanford::Manager.call(:start, @start_options) }
61
31
  end
62
32
  teardown do
63
- ENV['SANFORD_HOST'], ENV['SANFORD_IP'], ENV['SANFORD_PORT'] = @current
33
+ Sanford::Manager.call(:stop, @start_options)
64
34
  end
65
35
 
66
- should "start a sanford server for the specified service host and " \
67
- "use the env vars to override it's configuration" do
68
- host = Sanford.hosts.find(ENV['SANFORD_HOST'])
69
- port = ENV['SANFORD_PORT'].to_i
70
-
71
- self.call_sanford_manager(:run) do
72
- assert_nothing_raised{ self.open_socket(ENV['SANFORD_IP'], port) }
73
- assert File.exists?(self.expected_pid_file(host, ENV['SANFORD_IP'], port))
36
+ should "run the server specified and write a PID file" do
37
+ self.fork_and_call(@proc) do
38
+ assert_nothing_raised{ self.open_socket('localhost', 12345) }
39
+ assert File.exists?('tmp/my_host.pid')
74
40
  end
75
41
  end
42
+
76
43
  end
77
44
 
78
- class BadHostTest < ManagingTest
79
- desc "with a bad host name"
45
+ class StopTest < ManagingTest
46
+ desc "to stop a daemonized server"
80
47
  setup do
81
- Sanford.hosts.clear
82
- Sanford.register(Class.new)
48
+ @start_proc = proc{ Sanford::Manager.call(:start, @start_options) }
83
49
  end
84
50
 
85
- should "raise an exception when a service host can't be found" do
86
- assert_raises(Sanford::NoHostError) do
87
- Sanford::Manager.call(:run, :host => 'not_a_real_host')
51
+ should "stop the server specified and remove the PID file" do
52
+ self.fork_and_call(@start_proc) do
53
+ Sanford::Manager.call(:stop, @start_options)
54
+ sleep 1
55
+
56
+ assert_raises{ self.open_socket('localhost', 12345) }
57
+ assert_not File.exists?('tmp/my_host_localhost_12345.pid')
88
58
  end
89
59
  end
60
+
90
61
  end
91
62
 
92
- class NoHostsTest < ManagingTest
93
- desc "with no hosts"
63
+ class RestartTest < ManagingTest
64
+ desc "to restart a daemonized server"
94
65
  setup do
95
- Sanford.hosts.clear
66
+ @start_proc = proc{ Sanford::Manager.call(:start, @start_options) }
96
67
  end
97
68
 
98
- should "raise an exception when there aren't any service hosts" do
99
- assert_raises(Sanford::NoHostError) do
100
- Sanford::Manager.call(:run)
69
+ should "stop the server specified and remove the PID file" do
70
+ self.fork_and_call(@start_proc) do
71
+ exception = nil
72
+ stop = false
73
+ thread = Thread.new do
74
+ while !stop do
75
+ begin
76
+ self.open_socket('localhost', 12345)
77
+ sleep 0.1
78
+ rescue Exception => exception
79
+ end
80
+ end
81
+ end
82
+ Sanford::Manager.call(:restart, @start_options)
83
+ thread.join(1)
84
+
85
+ # make sure we didn't lost the ability to connect
86
+ assert_nil exception
87
+
88
+ stop = true
89
+ thread.join
101
90
  end
102
91
  end
103
92
  end
@@ -1,57 +1,268 @@
1
- # This test is intended as a high level test against Sanford's server. This will
2
- # use multiple request scenarios to test out Sanford's behavior and how it
3
- # responds. These tests depend on a socket (or the protocol's connection) and
4
- # thus are a system level test.
5
- #
1
+ # These tests are intended as a high level test against Sanford's server. They
2
+ # use fake and real connections to test how Sanford behaves.
3
+
6
4
  require 'assert'
7
5
 
8
- require 'sanford/manager'
6
+ require 'sanford-protocol/test/fake_socket'
9
7
 
10
8
  class RequestHandlingTest < Assert::Context
11
- include Test::ForkServerHelper
12
-
13
9
  desc "Sanford's handling of requests"
14
10
  setup do
15
- @server = Sanford::Server.new(TestHost, { :ready_timeout => 0 })
11
+ @env_sanford_protocol_debug = ENV['SANFORD_PROTOCOL_DEBUG']
12
+ @env_sanford_debug = ENV['SANFORD_DEBUG']
13
+ ENV.delete('SANFORD_PROTOCOL_DEBUG')
14
+ ENV['SANFORD_DEBUG'] = '1'
15
+ end
16
+ teardown do
17
+ ENV['SANFORD_DEBUG'] = @env_sanford_debug
18
+ ENV['SANFORD_PROTOCOL_DEBUG'] = @env_sanford_protocol_debug
19
+ end
20
+
21
+ class FakeConnectionTest < RequestHandlingTest
22
+ setup do
23
+ @host_data = Sanford::HostData.new(TestHost)
24
+ end
25
+ end
26
+
27
+ class EchoTest < FakeConnectionTest
28
+ desc "running a request for the echo server"
29
+ setup do
30
+ @connection = FakeConnection.with_request('v1', 'echo', { :message => 'test' })
31
+ @worker = Sanford::Worker.new(@host_data, @connection)
32
+ end
33
+
34
+ should "return a successful response and echo the params sent to it" do
35
+ assert_nothing_raised{ @worker.run }
36
+ response = @connection.response
37
+
38
+ assert_equal 200, response.code
39
+ assert_equal nil, response.status.message
40
+ assert_equal 'test', response.data
41
+ end
42
+
43
+ end
44
+
45
+ class MissingServiceVersionTest < FakeConnectionTest
46
+ desc "running a request with no service version"
47
+ setup do
48
+ request_hash = Sanford::Protocol::Request.new('v1', 'what', {}).to_hash
49
+ request_hash.delete('version')
50
+ @connection = FakeConnection.new(request_hash)
51
+ @worker = Sanford::Worker.new(@host_data, @connection)
52
+ end
53
+
54
+ should "return a bad request response" do
55
+ assert_raises(Sanford::Protocol::BadRequestError) do
56
+ @worker.run
57
+ end
58
+ response = @connection.response
59
+
60
+ assert_equal 400, response.code
61
+ assert_match "request", response.status.message
62
+ assert_match "version", response.status.message
63
+ assert_equal nil, response.data
64
+ end
65
+
66
+ end
67
+
68
+ class MissingServiceNameTest < FakeConnectionTest
69
+ desc "running a request with no service name"
70
+ setup do
71
+ request_hash = Sanford::Protocol::Request.new('v1', 'what', {}).to_hash
72
+ request_hash.delete('name')
73
+ @connection = FakeConnection.new(request_hash)
74
+ @worker = Sanford::Worker.new(@host_data, @connection)
75
+ end
76
+
77
+ should "return a bad request response" do
78
+ assert_raises(Sanford::Protocol::BadRequestError) do
79
+ @worker.run
80
+ end
81
+ response = @connection.response
82
+
83
+ assert_equal 400, response.code
84
+ assert_match "request", response.status.message
85
+ assert_match "name", response.status.message
86
+ assert_equal nil, response.data
87
+ end
88
+
89
+ end
90
+
91
+ class NotFoundServiceTest < FakeConnectionTest
92
+ desc "running a request with no matching service name"
93
+ setup do
94
+ @connection = FakeConnection.with_request('v1', 'what', {})
95
+ @worker = Sanford::Worker.new(@host_data, @connection)
96
+ end
97
+
98
+ should "return a bad request response" do
99
+ assert_raises(Sanford::NotFoundError) do
100
+ @worker.run
101
+ end
102
+ response = @connection.response
103
+
104
+ assert_equal 404, response.code
105
+ assert_equal nil, response.status.message
106
+ assert_equal nil, response.data
107
+ end
108
+
109
+ end
110
+
111
+ class ErrorServiceTest < FakeConnectionTest
112
+ desc "running a request that errors on the server"
113
+ setup do
114
+ @connection = FakeConnection.with_request('v1', 'bad', {})
115
+ @worker = Sanford::Worker.new(@host_data, @connection)
116
+ end
117
+
118
+ should "return a bad request response" do
119
+ assert_raises(RuntimeError) do
120
+ @worker.run
121
+ end
122
+ response = @connection.response
123
+
124
+ assert_equal 500, response.code
125
+ assert_match "error", response.status.message
126
+ assert_equal nil, response.data
127
+ end
128
+
129
+ end
130
+
131
+ class HaltTest < FakeConnectionTest
132
+ desc "running a request that halts"
133
+ setup do
134
+ @connection = FakeConnection.with_request('v1', 'halt_it', {})
135
+ @worker = Sanford::Worker.new(@host_data, @connection)
136
+ end
137
+
138
+ should "return the response that was halted" do
139
+ assert_nothing_raised{ @worker.run }
140
+ response = @connection.response
141
+
142
+ assert_equal 728, response.code
143
+ assert_equal "I do what I want", response.status.message
144
+ assert_equal [ 1, true, 'yes' ], response.data
145
+ end
146
+
147
+ end
148
+
149
+ class AuthorizeRequestTest < FakeConnectionTest
150
+ desc "running a request that halts in a callback"
151
+ setup do
152
+ @connection = FakeConnection.with_request('v1', 'authorized', {})
153
+ @worker = Sanford::Worker.new(@host_data, @connection)
154
+ end
155
+
156
+ should "return the response that was halted" do
157
+ assert_nothing_raised{ @worker.run }
158
+ response = @connection.response
159
+
160
+ assert_equal 401, response.code
161
+ assert_equal "Not authorized", response.status.message
162
+ assert_equal nil, response.data
163
+ end
164
+
165
+ end
166
+
167
+ class WithCustomErrorHandlerTest < FakeConnectionTest
168
+ desc "running a request that triggers our custom error handler"
169
+ setup do
170
+ @connection = FakeConnection.with_request('v1', 'custom_error', {})
171
+ @worker = Sanford::Worker.new(@host_data, @connection)
172
+ end
173
+
174
+ should "return the response that was halted" do
175
+ assert_raises(::MyCustomError){ @worker.run }
176
+ response = @connection.response
177
+
178
+ assert_equal 987, response.code
179
+ assert_equal "custom error!", response.status.message
180
+ assert_equal nil, response.data
181
+ end
182
+
183
+ end
184
+
185
+ class WithBadResponseHashTest < FakeConnectionTest
186
+ desc "running a request that builds an object that can't be encoded"
187
+ setup do
188
+ @connection = FakeConnection.with_request('v1', 'echo', { :message => 'cant encode' }, true)
189
+ @worker = Sanford::Worker.new(@host_data, @connection)
190
+ end
191
+
192
+ should "return the response that was halted" do
193
+ assert_raises(RuntimeError){ @worker.run }
194
+ response = @connection.response
195
+
196
+ assert_equal 500, response.code
197
+ assert_equal "An unexpected error occurred.", response.status.message
198
+ assert_equal nil, response.data
199
+ end
200
+
201
+ end
202
+
203
+ # essentially, don't call `IO.select`
204
+ class FakeProtocolConnection < Sanford::Protocol::Connection
205
+ def wait_for_data(*args)
206
+ true
207
+ end
208
+ end
209
+
210
+ class WithAKeepAliveTest < FakeConnectionTest
211
+ desc "receiving a keep-alive connection"
212
+ setup do
213
+ @server = Sanford::Server.new(TestHost, {
214
+ :ready_timeout => 0.1,
215
+ :keep_alive => true
216
+ })
217
+ @server.on_run
218
+ @socket = Sanford::Protocol::Test::FakeSocket.new
219
+ @fake_connection = FakeProtocolConnection.new(@socket)
220
+ Sanford::Protocol::Connection.stubs(:new).with(@socket).returns(@fake_connection)
221
+ end
222
+ teardown do
223
+ Sanford::Protocol::Connection.unstub(:new)
224
+ end
225
+
226
+ should "not error and nothing should be written" do
227
+ assert_nothing_raised do
228
+ @server.serve(@socket)
229
+ assert_equal "", @socket.out
230
+ end
231
+ end
232
+
233
+ end
234
+
235
+ class ForkedServerTest < RequestHandlingTest
236
+ include Test::ForkServerHelper
16
237
  end
17
238
 
18
239
  # Simple service test that echos back the params sent to it
19
- class EchoTest < RequestHandlingTest
240
+ class EchoServerTest < ForkedServerTest
20
241
  desc "when hitting an echo service"
21
242
 
22
243
  should "return a successful response and echo the params sent to it" do
23
- self.start_server(@server) do
244
+ self.start_server(TestHost) do
24
245
  response = SimpleClient.call_with_request(TestHost, 'v1', 'echo', {
25
246
  :message => 'test'
26
247
  })
27
248
 
28
- assert_equal 200, response.status.code
249
+ assert_equal 200, response.code
29
250
  assert_equal nil, response.status.message
30
251
  assert_equal 'test', response.data
31
252
  end
32
253
  end
33
254
  end
34
255
 
35
- class ErroringRequestTest < RequestHandlingTest
36
- setup do
37
- @env_sanford_protocol_debug = ENV['SANFORD_PROTOCOL_DEBUG']
38
- ENV.delete('SANFORD_PROTOCOL_DEBUG')
39
- end
40
- teardown do
41
- ENV['SANFORD_PROTOCOL_DEBUG'] = @env_sanford_protocol_debug
42
- end
43
- end
44
-
45
256
  # Sending the server a completely wrong stream of bytes
46
- class BadMessageTest < ErroringRequestTest
257
+ class BadMessageTest < ForkedServerTest
47
258
  desc "when sent a invalid request stream"
48
259
 
49
260
  should "return a bad request response with an error message" do
50
- self.start_server(@server) do
261
+ self.start_server(TestHost) do
51
262
  bytes = [ Sanford::Protocol.msg_version, "\000" ].join
52
263
  response = SimpleClient.call_with(TestHost, bytes)
53
264
 
54
- assert_equal 400, response.status.code
265
+ assert_equal 400, response.code
55
266
  assert_match "size", response.status.message
56
267
  assert_equal nil, response.data
57
268
  end
@@ -59,15 +270,15 @@ class RequestHandlingTest < Assert::Context
59
270
  end
60
271
 
61
272
  # Sending the server a protocol version that doesn't match it's version
62
- class WrongProtocolVersionTest < ErroringRequestTest
273
+ class WrongProtocolVersionTest < ForkedServerTest
63
274
  desc "when sent a request with a wrong protocol version"
64
275
 
65
276
  should "return a bad request response with an error message" do
66
- self.start_server(@server) do
277
+ self.start_server(TestHost) do
67
278
  bytes = [ Sanford::Protocol.msg_version, "\000" ].join
68
279
  response = SimpleClient.call_with_msg_body(TestHost, {}, nil, "\000")
69
280
 
70
- assert_equal 400, response.status.code
281
+ assert_equal 400, response.code
71
282
  assert_match "Protocol version", response.status.message
72
283
  assert_equal nil, response.data
73
284
  end
@@ -75,21 +286,22 @@ class RequestHandlingTest < Assert::Context
75
286
  end
76
287
 
77
288
  # Sending the server a body that it can't parse
78
- class BadBodyTest < ErroringRequestTest
289
+ class BadBodyTest < ForkedServerTest
79
290
  desc "when sent a request with an invalid body"
291
+
80
292
  should "return a bad request response with an error message" do
81
- self.start_server(@server) do
293
+ self.start_server(TestHost) do
82
294
  response = SimpleClient.call_with_encoded_msg_body(TestHost, "\000\001\010\011" * 2)
83
295
 
84
- assert_equal 400, response.status.code
296
+ assert_equal 400, response.code
85
297
  assert_match "body", response.status.message
86
298
  assert_equal nil, response.data
87
299
  end
88
300
  end
89
301
  end
90
302
 
91
- class HangingRequestTest < ErroringRequestTest
92
- desc "when a client connects but doesn't send anything"
303
+ class HangingRequestTest < ForkedServerTest
304
+ desc "when a client connects but doesn't send anything for to long"
93
305
  setup do
94
306
  ENV['SANFORD_TIMEOUT'] = '0.1'
95
307
  end
@@ -98,11 +310,11 @@ class RequestHandlingTest < Assert::Context
98
310
  end
99
311
 
100
312
  should "timeout" do
101
- self.start_server(@server) do
102
- client = SimpleClient.new(TestHost, :with_delay => 0.2)
313
+ self.start_server(TestHost) do
314
+ client = SimpleClient.new(TestHost, :with_delay => 1)
103
315
  response = client.call_with_request('v1', 'echo', { :message => 'test' })
104
316
 
105
- assert_equal 408, response.status.code
317
+ assert_equal 408, response.code
106
318
  assert_equal nil, response.status.message
107
319
  assert_equal nil, response.data
108
320
  end