sanford 0.10.1 → 0.11.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.
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