sanford 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/Gemfile +1 -1
  2. data/README.md +41 -56
  3. data/Rakefile +0 -1
  4. data/bench/client.rb +8 -3
  5. data/bench/{services.rb → config.sanford} +11 -6
  6. data/bench/{runner.rb → report.rb} +2 -2
  7. data/bench/report.txt +32 -32
  8. data/lib/sanford/cli.rb +42 -28
  9. data/lib/sanford/config_file.rb +79 -0
  10. data/lib/sanford/{worker.rb → connection_handler.rb} +28 -20
  11. data/lib/sanford/error_handler.rb +7 -7
  12. data/lib/sanford/pid_file.rb +42 -0
  13. data/lib/sanford/process.rb +136 -0
  14. data/lib/sanford/process_signal.rb +20 -0
  15. data/lib/sanford/route.rb +48 -0
  16. data/lib/sanford/router.rb +36 -0
  17. data/lib/sanford/runner.rb +30 -58
  18. data/lib/sanford/sanford_runner.rb +19 -9
  19. data/lib/sanford/server.rb +211 -42
  20. data/lib/sanford/server_data.rb +47 -0
  21. data/lib/sanford/service_handler.rb +8 -46
  22. data/lib/sanford/template_source.rb +19 -2
  23. data/lib/sanford/test_runner.rb +27 -28
  24. data/lib/sanford/version.rb +1 -1
  25. data/lib/sanford.rb +1 -23
  26. data/sanford.gemspec +4 -5
  27. data/test/helper.rb +3 -20
  28. data/test/support/app_server.rb +142 -0
  29. data/test/support/config.sanford +7 -0
  30. data/test/support/config_invalid_run.sanford +3 -0
  31. data/test/support/config_no_run.sanford +0 -0
  32. data/test/support/fake_server_connection.rb +58 -0
  33. data/test/support/pid_file_spy.rb +19 -0
  34. data/test/support/template.erb +1 -0
  35. data/test/system/server_tests.rb +378 -0
  36. data/test/system/service_handler_tests.rb +224 -0
  37. data/test/unit/cli_tests.rb +187 -0
  38. data/test/unit/config_file_tests.rb +59 -0
  39. data/test/unit/connection_handler_tests.rb +254 -0
  40. data/test/unit/error_handler_tests.rb +30 -35
  41. data/test/unit/pid_file_tests.rb +70 -0
  42. data/test/unit/process_signal_tests.rb +61 -0
  43. data/test/unit/process_tests.rb +428 -0
  44. data/test/unit/route_tests.rb +92 -0
  45. data/test/unit/router_tests.rb +65 -0
  46. data/test/unit/runner_tests.rb +61 -15
  47. data/test/unit/sanford_runner_tests.rb +162 -28
  48. data/test/unit/sanford_tests.rb +0 -8
  49. data/test/unit/server_data_tests.rb +87 -0
  50. data/test/unit/server_tests.rb +502 -21
  51. data/test/unit/service_handler_tests.rb +114 -219
  52. data/test/unit/template_engine_tests.rb +1 -1
  53. data/test/unit/template_source_tests.rb +56 -16
  54. data/test/unit/test_runner_tests.rb +206 -0
  55. metadata +67 -67
  56. data/bench/tasks.rb +0 -41
  57. data/lib/sanford/config.rb +0 -28
  58. data/lib/sanford/host.rb +0 -129
  59. data/lib/sanford/host_data.rb +0 -65
  60. data/lib/sanford/hosts.rb +0 -38
  61. data/lib/sanford/manager.rb +0 -275
  62. data/test/support/fake_connection.rb +0 -36
  63. data/test/support/helpers.rb +0 -17
  64. data/test/support/service_handlers.rb +0 -154
  65. data/test/support/services.rb +0 -123
  66. data/test/support/simple_client.rb +0 -62
  67. data/test/system/request_handling_tests.rb +0 -306
  68. data/test/unit/config_tests.rb +0 -56
  69. data/test/unit/host_data_tests.rb +0 -71
  70. data/test/unit/host_tests.rb +0 -141
  71. data/test/unit/hosts_tests.rb +0 -50
  72. data/test/unit/manager_tests.rb +0 -195
  73. data/test/unit/worker_tests.rb +0 -24
@@ -0,0 +1,378 @@
1
+ require 'assert'
2
+ require 'sanford/server'
3
+
4
+ require 'sanford-protocol/fake_socket'
5
+ require 'test/support/app_server'
6
+
7
+ module Sanford::Server
8
+
9
+ class SystemTests < Assert::Context
10
+ desc "Sanford::Server"
11
+ setup do
12
+ # Turn off sanford-protocol debugging, this is turned on in
13
+ # `test/helper.rb`. We don't want to debug the protocol, we want it to
14
+ # behave how it will in a production setting. If protocol debugging is
15
+ # turned on, then it won't wrap exceptions that occur within the protocol
16
+ # and we will get unexpected results.
17
+ @current_sanford_protocol_debug = ENV['SANFORD_PROTOCOL_DEBUG']
18
+ ENV.delete('SANFORD_PROTOCOL_DEBUG')
19
+
20
+ # Turn off sanford debugging. For the same reasons as above, we want
21
+ # Sanford behave how it will in a production setting. If debugging is
22
+ # turned on, the server will raise exceptions and we may get unexpected
23
+ # results
24
+ @current_sanford_debug = ENV['SANFORD_DEBUG']
25
+ ENV.delete('SANFORD_DEBUG')
26
+
27
+ @server = AppServer.new
28
+ @client = TestClient.new(AppServer.ip, AppServer.port)
29
+ @server_runner = ServerRunner.new(@server).tap(&:start)
30
+ end
31
+ teardown do
32
+ @server_runner.stop
33
+ ENV['SANFORD_DEBUG'] = @current_sanford_debug
34
+ ENV['SANFORD_PROTOCOL_DEBUG'] = @current_sanford_protocol_debug
35
+ end
36
+ subject{ @response }
37
+
38
+ end
39
+
40
+ class SuccessTests < SystemTests
41
+ desc "calling a service"
42
+ setup do
43
+ @message = Factory.string
44
+ @client.set_request('echo', :message => @message)
45
+ @response = @client.call
46
+ end
47
+
48
+ should "return a success response" do
49
+ assert_equal 200, subject.code
50
+ assert_nil subject.status.message
51
+ assert_equal @message, subject.data
52
+ end
53
+
54
+ end
55
+
56
+ class BadProtocolVersionTests < SystemTests
57
+ desc "calling a server with an invalid protocol version"
58
+ setup do
59
+ @client.bytes = "\000"
60
+ @response = @client.call
61
+ end
62
+
63
+ should "return a client error response" do
64
+ assert_equal 400, subject.code
65
+ assert_equal "Protocol version mismatch", subject.status.message
66
+ assert_nil subject.data
67
+ end
68
+
69
+ end
70
+
71
+ class BadMessageTests < SystemTests
72
+ desc "calling a server with an invalid message size"
73
+ setup do
74
+ @client.bytes = [ Sanford::Protocol.msg_version, "\000" ].join
75
+ @response = @client.call
76
+ end
77
+
78
+ should "return a client error response" do
79
+ assert_equal 400, subject.code
80
+ assert_equal "Empty message size", subject.status.message
81
+ assert_nil subject.data
82
+ end
83
+
84
+ end
85
+
86
+ class BadBodyTests < SystemTests
87
+ desc "calling a server with an invalid message body"
88
+ setup do
89
+ # these are a special set of bytes that cause BSON to throw an exception
90
+ @client.set_encoded_msg_body("\000\001\010\011" * 2)
91
+ @response = @client.call
92
+ end
93
+
94
+ should "return a client error response" do
95
+ assert_equal 400, subject.code
96
+ assert_equal "Error reading message body.", subject.status.message
97
+ assert_nil subject.data
98
+ end
99
+
100
+ end
101
+
102
+ class NotFoundTests < SystemTests
103
+ desc "with a request for a service the server doesn't provide"
104
+ setup do
105
+ @client.set_request('doesnt_exist')
106
+ @response = @client.call
107
+ end
108
+
109
+ should "return a not found error response" do
110
+ assert_equal 404, subject.code
111
+ assert_nil subject.status.message
112
+ assert_nil subject.data
113
+ end
114
+
115
+ end
116
+
117
+ class ServerErrorTests < SystemTests
118
+ desc "with a request that causes a server error"
119
+ setup do
120
+ @client.set_request('raise')
121
+ @response = @client.call
122
+ end
123
+
124
+ should "return a server error response" do
125
+ assert_equal 500, subject.code
126
+ assert_equal "An unexpected error occurred.", subject.status.message
127
+ assert_nil subject.data
128
+ end
129
+
130
+ end
131
+
132
+ class BadRequestTests < SystemTests
133
+ desc "calling a server with an invalid request"
134
+ setup do
135
+ @client.set_msg_body({})
136
+ @response = @client.call
137
+ end
138
+
139
+ should "return a client error response" do
140
+ assert_equal 400, subject.code
141
+ assert_equal "The request doesn't contain a name.", subject.status.message
142
+ assert_nil subject.data
143
+ end
144
+
145
+ end
146
+
147
+ class BadResponseTests < SystemTests
148
+ desc "calling a service that builds an invalid response"
149
+ setup do
150
+ @client.set_request('bad_response')
151
+ @response = @client.call
152
+ end
153
+
154
+ should "return a server error response" do
155
+ assert_equal 500, subject.code
156
+ assert_equal "An unexpected error occurred.", subject.status.message
157
+ assert_nil subject.data
158
+ end
159
+
160
+ end
161
+
162
+ class TemplateTests < SystemTests
163
+ desc "calling a service that renders a template"
164
+ setup do
165
+ @message = Factory.text
166
+ @client.set_request('template', :message => @message)
167
+ @response = @client.call
168
+ end
169
+
170
+ should "return a success response with the rendered data" do
171
+ assert_equal 200, subject.code
172
+ assert_nil subject.status.message
173
+ assert_equal "ERB Template Message: #{@message}\n", subject.data
174
+ end
175
+
176
+ end
177
+
178
+ class KeepAliveTests < SystemTests
179
+ desc "receiving a keep-alive connection"
180
+ setup do
181
+ # no bytes means our client won't write any, this mimics a keep-alive
182
+ # connection that connects and immediately disconnects
183
+ @client.bytes = nil
184
+ @response = @client.call_keep_alive
185
+ end
186
+
187
+ should "not write a response" do
188
+ assert_equal "", subject
189
+ end
190
+
191
+ end
192
+
193
+ class TimeoutErrorTests < SystemTests
194
+ desc "with a client that connects but doesn't send anything"
195
+ setup do
196
+ @current_sanford_timeout = ENV['SANFORD_TIMEOUT']
197
+ ENV['SANFORD_TIMEOUT'] = '0.1'
198
+
199
+ # keep-alive messes up testing this, so we disable it for this test
200
+ Assert.stub(@server.server_data, :receives_keep_alive){ false }
201
+
202
+ @client.delay = 0.5
203
+ @client.set_request('echo', :message => Factory.string)
204
+ @response = @client.call
205
+ end
206
+ teardown do
207
+ ENV['SANFORD_TIMEOUT'] = @current_sanford_timeout
208
+ end
209
+
210
+ should "return a timeout error response" do
211
+ assert_equal 408, subject.code
212
+ assert_nil subject.status.message
213
+ assert_nil subject.data
214
+ end
215
+
216
+ end
217
+
218
+ class HaltTests < SystemTests
219
+
220
+ should "allow halting in a before callback" do
221
+ @client.set_request('halt', :when => 'before')
222
+ @response = @client.call
223
+
224
+ assert_equal 200, subject.code
225
+ assert_equal 'in before', subject.status.message
226
+ assert_nil subject.data
227
+ end
228
+
229
+ should "allow halting in a before init callback" do
230
+ @client.set_request('halt', :when => 'before_init')
231
+ @response = @client.call
232
+
233
+ assert_equal 200, subject.code
234
+ assert_equal 'in before init', subject.status.message
235
+ assert_nil subject.data
236
+ end
237
+
238
+ should "allow halting in init" do
239
+ @client.set_request('halt', :when => 'init')
240
+ @response = @client.call
241
+
242
+ assert_equal 200, subject.code
243
+ assert_equal 'in init', subject.status.message
244
+ assert_nil subject.data
245
+ end
246
+
247
+ should "allow halting in an after init callback" do
248
+ @client.set_request('halt', :when => 'after_init')
249
+ @response = @client.call
250
+
251
+ assert_equal 200, subject.code
252
+ assert_equal 'in after init', subject.status.message
253
+ assert_nil subject.data
254
+ end
255
+
256
+ should "allow halting in a before run callback" do
257
+ @client.set_request('halt', :when => 'before_run')
258
+ @response = @client.call
259
+
260
+ assert_equal 200, subject.code
261
+ assert_equal 'in before run', subject.status.message
262
+ assert_nil subject.data
263
+ end
264
+
265
+ should "allow halting in run" do
266
+ @client.set_request('halt', :when => 'run')
267
+ @response = @client.call
268
+
269
+ assert_equal 200, subject.code
270
+ assert_equal 'in run', subject.status.message
271
+ assert_nil subject.data
272
+ end
273
+
274
+ should "allow halting in an after run callback" do
275
+ @client.set_request('halt', :when => 'after_run')
276
+ @response = @client.call
277
+
278
+ assert_equal 200, subject.code
279
+ assert_equal 'in after run', subject.status.message
280
+ assert_nil subject.data
281
+ end
282
+
283
+ should "allow halting in an after callback" do
284
+ @client.set_request('halt', :when => 'after')
285
+ @response = @client.call
286
+
287
+ assert_equal 200, subject.code
288
+ assert_equal 'in after', subject.status.message
289
+ assert_nil subject.data
290
+ end
291
+
292
+ end
293
+
294
+ class ErrorHandlerResponseTests < SystemTests
295
+ desc "calling a service that triggers an error handler"
296
+ setup do
297
+ @client.set_request('custom_error')
298
+ @response = @client.call
299
+ end
300
+
301
+ should "return a response generated by the error handler" do
302
+ assert_equal 200, subject.code
303
+ assert_nil subject.status.message
304
+ expected = "The server on #{AppServer.ip}:#{AppServer.port} " \
305
+ "threw a StandardError."
306
+ assert_equal expected, subject.data
307
+ end
308
+
309
+ end
310
+
311
+ class TestClient
312
+ attr_accessor :delay, :bytes
313
+
314
+ def initialize(ip, port)
315
+ @ip = ip
316
+ @port = port
317
+ @delay = nil
318
+ @bytes = nil
319
+ end
320
+
321
+ def set_request(name, params = nil)
322
+ params ||= {}
323
+ fake_socket = Sanford::Protocol::FakeSocket.with_request(name, params)
324
+ @bytes = fake_socket.in
325
+ end
326
+
327
+ def set_msg_body(hash)
328
+ fake_socket = Sanford::Protocol::FakeSocket.with_msg_body(hash)
329
+ @bytes = fake_socket.in
330
+ end
331
+
332
+ def set_encoded_msg_body(bytes)
333
+ fake_socket = Sanford::Protocol::FakeSocket.with_encoded_msg_body(bytes)
334
+ @bytes = fake_socket.in
335
+ end
336
+
337
+ def call
338
+ socket = TCPSocket.new(@ip, @port)
339
+ socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true)
340
+ connection = Sanford::Protocol::Connection.new(socket)
341
+ sleep(@delay) if @delay
342
+ socket.send(@bytes, 0)
343
+ socket.close_write
344
+ Sanford::Protocol::Response.parse(connection.read)
345
+ ensure
346
+ socket.close rescue false
347
+ end
348
+
349
+ def call_keep_alive
350
+ socket = TCPSocket.new(@ip, @port)
351
+ socket.read
352
+ ensure
353
+ socket.close rescue false
354
+ end
355
+ end
356
+
357
+ class ServerRunner
358
+ def initialize(server)
359
+ @server = server
360
+ @thread = nil
361
+ end
362
+
363
+ def start
364
+ @server.listen
365
+ @thread = @server.start
366
+ end
367
+
368
+ def wakeup
369
+ @thread.wakeup
370
+ end
371
+
372
+ def stop
373
+ @server.halt
374
+ @thread.join if @thread
375
+ end
376
+ end
377
+
378
+ end
@@ -0,0 +1,224 @@
1
+ require 'assert'
2
+ require 'sanford/service_handler'
3
+
4
+ require 'sanford/test_helpers'
5
+ require 'test/support/app_server'
6
+
7
+ module Sanford::ServiceHandler
8
+
9
+ class SystemTests < Assert::Context
10
+ include Sanford::TestHelpers
11
+
12
+ desc "Sanford::ServiceHandler"
13
+
14
+ end
15
+
16
+ class EchoHandlerTests < SystemTests
17
+ desc "AppHandlers::Echo"
18
+ setup do
19
+ @params = { 'message' => Factory.text }
20
+ @runner = test_runner(AppHandlers::Echo, :params => @params)
21
+ @handler = @runner.handler
22
+ end
23
+ subject{ @handler }
24
+
25
+ should "return a 200 response using the message param when run" do
26
+ response = @runner.run
27
+ assert_equal 200, response.code
28
+ assert_equal @params['message'], response.data
29
+ end
30
+
31
+ end
32
+
33
+ class RaiseHandlerTests < SystemTests
34
+ desc "AppHandlers::Raise"
35
+ setup do
36
+ @runner = test_runner(AppHandlers::Raise)
37
+ @handler = @runner.handler
38
+ end
39
+ subject{ @handler }
40
+
41
+ should "raise a runtime error when run" do
42
+ assert_raises(RuntimeError){ @runner.run }
43
+ end
44
+
45
+ end
46
+
47
+ class BadResponseHandlerTests < SystemTests
48
+ desc "AppHandlers::BadResponse"
49
+ setup do
50
+ @runner = test_runner(AppHandlers::BadResponse)
51
+ @handler = @runner.handler
52
+ end
53
+ subject{ @handler }
54
+
55
+ should "raise a invalid response error when run" do
56
+ assert_raises(BSON::InvalidDocument){ @runner.run }
57
+ end
58
+
59
+ end
60
+
61
+ class TemplateHandlerTests < SystemTests
62
+ desc "AppHandlers::Template"
63
+ setup do
64
+ @params = { 'message' => Factory.text }
65
+ @runner = test_runner(AppHandlers::Template, {
66
+ :params => @params,
67
+ :template_source => AppServer.configuration.template_source
68
+ })
69
+ @handler = @runner.handler
70
+ end
71
+ subject{ @handler }
72
+
73
+ should have_readers :message
74
+
75
+ should "know its message" do
76
+ assert_equal @params['message'], subject.message
77
+ end
78
+
79
+ should "return a 200 response and render the template when run" do
80
+ response = @runner.run
81
+ assert_equal 200, response.code
82
+ expected = "ERB Template Message: #{@params['message']}\n"
83
+ assert_equal expected, response.data
84
+ end
85
+
86
+ end
87
+
88
+ class HaltHandlerTests < SystemTests
89
+ desc "AppHandlers::Halt"
90
+ setup do
91
+ @handler_class = AppHandlers::Halt
92
+ end
93
+
94
+ end
95
+
96
+ class RunHaltBeforeTests < HaltHandlerTests
97
+ desc "run when halting in a before callback"
98
+ setup do
99
+ @params = { 'when' => 'before' }
100
+ @runner = test_runner(@handler_class, :params => @params)
101
+ @response = @runner.run
102
+ end
103
+ subject{ @response }
104
+
105
+ should "not have halted because before callbacks aren't called" do
106
+ assert_equal 200, subject.code
107
+ assert_equal false, subject.data
108
+ end
109
+
110
+ end
111
+
112
+ class RunHaltBeforeInitTests < HaltHandlerTests
113
+ desc "run when halting in a before init callback"
114
+ setup do
115
+ @params = { 'when' => 'before_init' }
116
+ @runner = test_runner(@handler_class, :params => @params)
117
+ @response = @runner.run
118
+ end
119
+ subject{ @response }
120
+
121
+ should "return the resposne from halting in a before init callback" do
122
+ assert_equal 200, subject.code
123
+ assert_equal 'in before init', subject.status.message
124
+ end
125
+
126
+ end
127
+
128
+ class RunHaltInitTests < HaltHandlerTests
129
+ desc "run when halting in init"
130
+ setup do
131
+ @params = { 'when' => 'init' }
132
+ @runner = test_runner(@handler_class, :params => @params)
133
+ @response = @runner.run
134
+ end
135
+ subject{ @response }
136
+
137
+ should "return the resposne from halting in init" do
138
+ assert_equal 200, subject.code
139
+ assert_equal 'in init', subject.status.message
140
+ end
141
+
142
+ end
143
+
144
+ class RunHaltAfterInitTests < HaltHandlerTests
145
+ desc "run when halting in a after init callback"
146
+ setup do
147
+ @params = { 'when' => 'after_init' }
148
+ @runner = test_runner(@handler_class, :params => @params)
149
+ @response = @runner.run
150
+ end
151
+ subject{ @response }
152
+
153
+ should "return the resposne from halting in a after init callback" do
154
+ assert_equal 200, subject.code
155
+ assert_equal 'in after init', subject.status.message
156
+ end
157
+
158
+ end
159
+
160
+ class RunHaltBeforeRunTests < HaltHandlerTests
161
+ desc "run when halting in a before run callback"
162
+ setup do
163
+ @params = { 'when' => 'before_run' }
164
+ @runner = test_runner(@handler_class, :params => @params)
165
+ @response = @runner.run
166
+ end
167
+ subject{ @response }
168
+
169
+ should "return the resposne from halting in a before run callback" do
170
+ assert_equal 200, subject.code
171
+ assert_equal 'in before run', subject.status.message
172
+ end
173
+
174
+ end
175
+
176
+ class RunHaltRunTests < HaltHandlerTests
177
+ desc "run when halting in run"
178
+ setup do
179
+ @params = { 'when' => 'run' }
180
+ @runner = test_runner(@handler_class, :params => @params)
181
+ @response = @runner.run
182
+ end
183
+ subject{ @response }
184
+
185
+ should "return the resposne from halting in run" do
186
+ assert_equal 200, subject.code
187
+ assert_equal 'in run', subject.status.message
188
+ end
189
+
190
+ end
191
+
192
+ class RunHaltAfterRunTests < HaltHandlerTests
193
+ desc "run when halting in a after run callback"
194
+ setup do
195
+ @params = { 'when' => 'after_run' }
196
+ @runner = test_runner(@handler_class, :params => @params)
197
+ @response = @runner.run
198
+ end
199
+ subject{ @response }
200
+
201
+ should "return the resposne from halting in a after run callback" do
202
+ assert_equal 200, subject.code
203
+ assert_equal 'in after run', subject.status.message
204
+ end
205
+
206
+ end
207
+
208
+ class RunHaltAfterTests < HaltHandlerTests
209
+ desc "run when halting in a after callback"
210
+ setup do
211
+ @params = { 'when' => 'after' }
212
+ @runner = test_runner(@handler_class, :params => @params)
213
+ @response = @runner.run
214
+ end
215
+ subject{ @response }
216
+
217
+ should "not have halted because after callbacks aren't called" do
218
+ assert_equal 200, subject.code
219
+ assert_equal false, subject.data
220
+ end
221
+
222
+ end
223
+
224
+ end