sanford 0.4.0 → 0.6.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.
@@ -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