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
@@ -1,48 +1,529 @@
1
1
  require 'assert'
2
2
  require 'sanford/server'
3
3
 
4
- class Sanford::Server
4
+ require 'dat-tcp/server_spy'
5
+ require 'ns-options/assert_macros'
6
+ require 'sanford/route'
7
+ require 'sanford-protocol/fake_connection'
8
+ require 'test/support/fake_server_connection'
9
+
10
+ module Sanford::Server
5
11
 
6
12
  class UnitTests < Assert::Context
7
13
  desc "Sanford::Server"
8
14
  setup do
9
- @server = Sanford::Server.new(TestHost, :keep_alive => true)
15
+ @server_class = Class.new do
16
+ include Sanford::Server
17
+ end
18
+ end
19
+ subject{ @server_class }
20
+
21
+ should have_imeths :configuration
22
+ should have_imeths :name, :ip, :port, :pid_file
23
+ should have_imeths :receives_keep_alive
24
+ should have_imeths :verbose_logging, :logger
25
+ should have_imeths :init, :error
26
+ should have_imeths :router
27
+ should have_imeths :template_source, :build_template_source
28
+
29
+ should "know its configuration" do
30
+ config = subject.configuration
31
+ assert_instance_of Configuration, config
32
+ assert_same config, subject.configuration
33
+ end
34
+
35
+ should "allow reading/writing its configuration name" do
36
+ new_name = Factory.string
37
+ subject.name(new_name)
38
+ assert_equal new_name, subject.configuration.name
39
+ assert_equal new_name, subject.name
40
+ end
41
+
42
+ should "allow reading/writing its configuration ip" do
43
+ new_ip = Factory.string
44
+ subject.ip(new_ip)
45
+ assert_equal new_ip, subject.configuration.ip
46
+ assert_equal new_ip, subject.ip
47
+ end
48
+
49
+ should "allow reading/writing its configuration port" do
50
+ new_port = Factory.integer
51
+ subject.port(new_port)
52
+ assert_equal new_port, subject.configuration.port
53
+ assert_equal new_port, subject.port
54
+ end
55
+
56
+ should "allow reading/writing its configuration pid file" do
57
+ new_pid_file = Factory.string
58
+ subject.pid_file(new_pid_file)
59
+ expected = Pathname.new(new_pid_file)
60
+ assert_equal expected, subject.configuration.pid_file
61
+ assert_equal expected, subject.pid_file
62
+ end
63
+
64
+ should "allow reading/writing its configuration receives keep alive" do
65
+ new_keep_alive = Factory.boolean
66
+ subject.receives_keep_alive(new_keep_alive)
67
+ assert_equal new_keep_alive, subject.configuration.receives_keep_alive
68
+ assert_equal new_keep_alive, subject.receives_keep_alive
69
+ end
70
+
71
+ should "allow reading/writing its configuration verbose logging" do
72
+ new_verbose = Factory.boolean
73
+ subject.verbose_logging(new_verbose)
74
+ assert_equal new_verbose, subject.configuration.verbose_logging
75
+ assert_equal new_verbose, subject.verbose_logging
76
+ end
77
+
78
+ should "allow reading/writing its configuration logger" do
79
+ new_logger = Factory.string
80
+ subject.logger(new_logger)
81
+ assert_equal new_logger, subject.configuration.logger
82
+ assert_equal new_logger, subject.logger
83
+ end
84
+
85
+ should "allow adding init procs to its configuration" do
86
+ new_init_proc = proc{ Factory.string }
87
+ subject.init(&new_init_proc)
88
+ assert_includes new_init_proc, subject.configuration.init_procs
89
+ end
90
+
91
+ should "allow adding error procs to its configuration" do
92
+ new_error_proc = proc{ Factory.string }
93
+ subject.error(&new_error_proc)
94
+ assert_includes new_error_proc, subject.configuration.error_procs
95
+ end
96
+
97
+ should "allow reading/writing its configuration router" do
98
+ new_router = Factory.string
99
+ subject.router(new_router)
100
+ assert_equal new_router, subject.configuration.router
101
+ assert_equal new_router, subject.router
102
+ end
103
+
104
+ should "allow configuring the router by passing a block to `router`" do
105
+ new_router = Factory.string
106
+
107
+ block_scope = nil
108
+ subject.router(new_router){ block_scope = self }
109
+ assert_equal new_router, subject.router
110
+ assert_equal new_router, block_scope
111
+ end
112
+
113
+ should "allow setting the configuration template source" do
114
+ new_template_source = Factory.string
115
+ subject.template_source(new_template_source)
116
+ assert_equal new_template_source, subject.configuration.template_source
117
+ assert_equal new_template_source, subject.template_source
118
+ end
119
+
120
+ should "allow setting its template source with a path and block" do
121
+ new_path = Factory.string
122
+ yielded = nil
123
+ subject.build_template_source(new_path){ |s| yielded = s }
124
+ assert_equal new_path, subject.configuration.template_source.path
125
+ assert_equal subject.configuration.template_source, yielded
126
+ end
127
+
128
+ should "allow setting its template source with only a path" do
129
+ new_path = Factory.string
130
+ subject.build_template_source(new_path)
131
+ assert_equal new_path, subject.configuration.template_source.path
132
+ end
133
+
134
+ end
135
+
136
+ class InitTests < UnitTests
137
+ desc "when init"
138
+ setup do
139
+ @server_class.name Factory.string
140
+ @server_class.ip Factory.string
141
+ @server_class.port Factory.integer
142
+ @server_class.error{ Factory.string }
143
+ @server_class.router do
144
+ service Factory.string, TestHandler.to_s
145
+ end
146
+
147
+ @dat_tcp_server_spy = DatTCP::ServerSpy.new
148
+ Assert.stub(DatTCP::Server, :new) do |&block|
149
+ @dat_tcp_server_spy.serve_proc = block
150
+ @dat_tcp_server_spy
151
+ end
152
+
153
+ @server = @server_class.new
10
154
  end
11
155
  subject{ @server }
12
156
 
13
- should have_readers :sanford_host, :sanford_host_data, :sanford_host_options
14
- should have_imeths :on_run
157
+ should have_readers :server_data, :dat_tcp_server
158
+ should have_imeths :name, :ip, :port
159
+ should have_imeths :file_descriptor, :client_file_descriptors
160
+ should have_imeths :listen, :start, :pause, :stop, :halt
161
+ should have_imeths :paused?
162
+
163
+ should "have validated its configuration" do
164
+ assert_true subject.class.configuration.valid?
165
+ end
166
+
167
+ should "know its server data" do
168
+ configuration = subject.class.configuration
169
+ sd = subject.server_data
170
+
171
+ assert_instance_of Sanford::ServerData, sd
172
+ assert_equal configuration.name, sd.name
173
+ assert_equal configuration.ip, sd.ip
174
+ assert_equal configuration.port, sd.port
175
+ assert_equal configuration.verbose_logging, sd.verbose_logging
176
+ assert_equal configuration.receives_keep_alive, sd.receives_keep_alive
177
+ assert_equal configuration.error_procs, sd.error_procs
178
+ assert_equal configuration.routes, sd.routes.values
179
+ assert_instance_of configuration.logger.class, sd.logger
180
+ end
181
+
182
+ should "know its dat tcp server" do
183
+ assert_equal @dat_tcp_server_spy, subject.dat_tcp_server
184
+ assert_not_nil @dat_tcp_server_spy.serve_proc
185
+ end
186
+
187
+ should "know its name, pid file and logger" do
188
+ assert_equal subject.server_data.name, subject.name
189
+ assert_equal subject.server_data.pid_file, subject.pid_file
190
+ assert_equal subject.server_data.logger, subject.logger
191
+ end
192
+
193
+ should "call listen on its dat tcp server using `listen`" do
194
+ subject.listen
195
+ assert_true @dat_tcp_server_spy.listen_called
196
+ end
197
+
198
+ should "use its configured ip and port by default when listening" do
199
+ subject.listen
200
+ assert_equal subject.server_data.ip, @dat_tcp_server_spy.ip
201
+ assert_equal subject.server_data.port, @dat_tcp_server_spy.port
202
+ end
203
+
204
+ should "pass any args to its dat tcp server using `listen`" do
205
+ ip, port = Factory.string, Factory.integer
206
+ subject.listen(ip, port)
207
+ assert_equal ip, @dat_tcp_server_spy.ip
208
+ assert_equal port, @dat_tcp_server_spy.port
209
+
210
+ file_descriptor = Factory.integer
211
+ subject.listen(file_descriptor)
212
+ assert_equal file_descriptor, @dat_tcp_server_spy.file_descriptor
213
+ end
214
+
215
+ should "know its ip, port and file descriptor" do
216
+ assert_equal @dat_tcp_server_spy.ip, subject.ip
217
+ assert_equal @dat_tcp_server_spy.port, subject.port
218
+ subject.listen
219
+ assert_equal @dat_tcp_server_spy.ip, subject.ip
220
+ assert_equal @dat_tcp_server_spy.port, subject.port
221
+
222
+ assert_equal @dat_tcp_server_spy.file_descriptor, subject.file_descriptor
223
+ subject.listen(Factory.integer)
224
+ assert_equal @dat_tcp_server_spy.file_descriptor, subject.file_descriptor
225
+ end
226
+
227
+ should "call start on its dat tcp server using `start`" do
228
+ client_fds = [ Factory.integer ]
229
+ subject.start(client_fds)
230
+ assert_true @dat_tcp_server_spy.start_called
231
+ assert_equal client_fds, @dat_tcp_server_spy.client_file_descriptors
232
+ end
233
+
234
+ should "know its client file descriptors" do
235
+ expected = @dat_tcp_server_spy.client_file_descriptors
236
+ assert_equal expected, subject.client_file_descriptors
237
+ subject.start([ Factory.integer ])
238
+ expected = @dat_tcp_server_spy.client_file_descriptors
239
+ assert_equal expected, subject.client_file_descriptors
240
+ end
241
+
242
+ should "call pause on its dat tcp server using `pause`" do
243
+ wait = Factory.boolean
244
+ subject.pause(wait)
245
+ assert_true @dat_tcp_server_spy.pause_called
246
+ assert_equal wait, @dat_tcp_server_spy.waiting_for_pause
247
+ end
248
+
249
+ should "call stop on its dat tcp server using `stop`" do
250
+ wait = Factory.boolean
251
+ subject.stop(wait)
252
+ assert_true @dat_tcp_server_spy.stop_called
253
+ assert_equal wait, @dat_tcp_server_spy.waiting_for_stop
254
+ end
255
+
256
+ should "call halt on its dat tcp server using `halt`" do
257
+ wait = Factory.boolean
258
+ subject.halt(wait)
259
+ assert_true @dat_tcp_server_spy.halt_called
260
+ assert_equal wait, @dat_tcp_server_spy.waiting_for_halt
261
+ end
262
+
263
+ should "know if its been paused" do
264
+ assert_false subject.paused?
265
+ subject.listen
266
+ assert_true subject.paused?
267
+ subject.start
268
+ assert_false subject.paused?
269
+ subject.pause
270
+ assert_true subject.paused?
271
+ end
272
+
273
+ end
274
+
275
+ class ConfigureTCPServerTests < InitTests
276
+ desc "configuring its tcp server"
277
+ setup do
278
+ @tcp_server = TCPServerSpy.new
279
+ Assert.stub(@dat_tcp_server_spy, :listen) do |*args, &block|
280
+ @configure_tcp_server_proc = block
281
+ end
282
+ @server.listen
283
+ @configure_tcp_server_proc.call(@tcp_server)
284
+ end
285
+ subject{ @tcp_server }
15
286
 
16
- should "include DatTCP::Server" do
17
- assert_includes DatTCP::Server, subject.class
287
+ should "set the TCP_NODELAY option" do
288
+ expected = [ ::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, true ]
289
+ assert_includes expected, @tcp_server.set_socket_option_calls
18
290
  end
19
291
 
20
- should "save its host and host options but not initialize a host data yet" do
21
- assert_equal TestHost, subject.sanford_host
22
- assert_equal true, subject.sanford_host_options[:receives_keep_alive]
23
- assert_nil subject.sanford_host_data
292
+ end
293
+
294
+ class ServeTests < InitTests
295
+ desc "serve"
296
+ setup do
297
+ @socket = Factory.binary
298
+
299
+ @connection = FakeServerConnection.new
300
+ Assert.stub(Connection, :new).with(@socket){ @connection }
301
+
302
+ @connection_handler_spy = ConnectionHandlerSpy.new
303
+ Assert.stub(Sanford::ConnectionHandler, :new).tap do |s|
304
+ s.with(@server.server_data, @connection){ @connection_handler_spy }
305
+ end
306
+
307
+ @serve_proc = @dat_tcp_server_spy.serve_proc
308
+ end
309
+ subject{ @serve_proc }
310
+
311
+ should "run a connection_handler when called with a socket" do
312
+ Assert.stub(@server.server_data, :receives_keep_alive){ false }
313
+ @connection.read_data = Factory.boolean
314
+ assert_false @connection_handler_spy.run_called
315
+ subject.call(@socket)
316
+ assert_true @connection_handler_spy.run_called
317
+ end
318
+
319
+ should "not run a keep-alive connection when configured to receive them" do
320
+ Assert.stub(@server.server_data, :receives_keep_alive){ true }
321
+ @connection.read_data = nil # nothing to read makes it a keep-alive
322
+ assert_false @connection_handler_spy.run_called
323
+ subject.call(@socket)
324
+ assert_false @connection_handler_spy.run_called
325
+ end
326
+
327
+ should "run a keep-alive connection when configured to receive them" do
328
+ Assert.stub(@server.server_data, :receives_keep_alive){ false }
329
+ @connection.read_data = nil # nothing to read makes it a keep-alive
330
+ assert_false @connection_handler_spy.run_called
331
+ subject.call(@socket)
332
+ assert_true @connection_handler_spy.run_called
24
333
  end
25
334
 
26
335
  end
27
336
 
28
- class RunTests < UnitTests
29
- desc "run"
337
+ class ConnectionTests < UnitTests
338
+ desc "Connection"
30
339
  setup do
31
- @server.listen(TestHost.ip, TestHost.port)
32
- @server.run
340
+ fake_socket = Factory.string
341
+ @protocol_conn = Sanford::Protocol::FakeConnection.new(Factory.binary)
342
+ Assert.stub(Sanford::Protocol::Connection, :new).with(fake_socket) do
343
+ @protocol_conn
344
+ end
345
+ @connection = Connection.new(fake_socket)
33
346
  end
34
- teardown do
35
- @server.stop
347
+ subject{ @connection }
348
+
349
+ should have_imeths :read_data, :write_data, :peek_data
350
+ should have_imeths :close_write
351
+
352
+ should "default its timeout" do
353
+ assert_equal 1.0, subject.timeout
354
+ end
355
+
356
+ should "allowing reading from the protocol connection" do
357
+ result = subject.read_data
358
+ assert_equal @protocol_conn.read_data, result
359
+ assert_equal @protocol_conn.read_timeout, subject.timeout
36
360
  end
37
361
 
38
- should "have initialized a host data instance" do
39
- assert_instance_of Sanford::HostData, subject.sanford_host_data
362
+ should "allowing writing to the protocol connection" do
363
+ data = Factory.binary
364
+ subject.write_data(data)
365
+ assert_equal @protocol_conn.write_data, data
366
+ end
367
+
368
+ should "allowing peeking from the protocol connection" do
369
+ result = subject.peek_data
370
+ assert_equal @protocol_conn.peek_data, result
371
+ assert_equal @protocol_conn.peek_timeout, subject.timeout
372
+ end
373
+
374
+ should "allow closing the write stream on the protocol connection" do
375
+ assert_false @protocol_conn.closed_write
376
+ subject.close_write
377
+ assert_true @protocol_conn.closed_write
40
378
  end
41
379
 
42
380
  end
43
381
 
44
- # Sanford::Server#serve is tested in test/system/request_handling_test.rb,
45
- # it requires multiple parts of Sanford and basically tests a large portion of
46
- # the entire system
382
+ class TCPCorkTests < UnitTests
383
+ desc "TCPCork"
384
+ subject{ TCPCork }
385
+
386
+ should have_imeths :apply, :remove
387
+
388
+ end
389
+
390
+ class ConfigurationTests < UnitTests
391
+ include NsOptions::AssertMacros
392
+
393
+ desc "Configuration"
394
+ setup do
395
+ @configuration = Configuration.new.tap do |c|
396
+ c.name Factory.string
397
+ c.ip Factory.string
398
+ c.port Factory.integer
399
+ end
400
+ end
401
+ subject{ @configuration }
402
+
403
+ should have_options :name, :ip, :port, :pid_file
404
+ should have_options :receives_keep_alive
405
+ should have_options :verbose_logging, :logger
406
+ should have_options :template_source
407
+ should have_accessors :init_procs, :error_procs
408
+ should have_accessors :router
409
+ should have_imeths :routes
410
+ should have_imeths :to_hash
411
+ should have_imeths :valid?, :validate!
412
+
413
+ should "be an ns-options proxy" do
414
+ assert_includes NsOptions::Proxy, subject.class
415
+ end
416
+
417
+ should "default its options" do
418
+ config = Configuration.new
419
+ assert_nil config.name
420
+ assert_equal '0.0.0.0', config.ip
421
+ assert_nil config.port
422
+ assert_nil config.pid_file
423
+
424
+ assert_false config.receives_keep_alive
425
+
426
+ assert_true config.verbose_logging
427
+ assert_instance_of Sanford::NullLogger, config.logger
428
+ assert_instance_of Sanford::NullTemplateSource, config.template_source
429
+
430
+ assert_equal [], config.init_procs
431
+ assert_equal [], config.error_procs
432
+
433
+ assert_instance_of Sanford::Router, config.router
434
+ assert_empty config.router.routes
435
+ end
436
+
437
+ should "not be valid by default" do
438
+ assert_false subject.valid?
439
+ end
440
+
441
+ should "know its routes" do
442
+ assert_equal subject.router.routes, subject.routes
443
+ subject.router.service(Factory.string, TestHandler.to_s)
444
+ assert_equal subject.router.routes, subject.routes
445
+ end
446
+
447
+ should "include its routes, error procs and template source in its hash" do
448
+ config_hash = subject.to_hash
449
+ assert_equal subject.error_procs, config_hash[:error_procs]
450
+ assert_equal subject.routes, config_hash[:routes]
451
+ template_source = subject.template_source
452
+ assert_instance_of template_source.class, config_hash[:template_source]
453
+ assert_equal template_source.path, config_hash[:template_source].path
454
+ end
455
+
456
+ should "call its init procs when validated" do
457
+ called = false
458
+ subject.init_procs << proc{ called = true }
459
+ subject.validate!
460
+ assert_true called
461
+ end
462
+
463
+ should "ensure its required options have been set when validated" do
464
+ subject.name = nil
465
+ assert_raises(InvalidError){ subject.validate! }
466
+ subject.name = Factory.string
467
+
468
+ subject.ip = nil
469
+ assert_raises(InvalidError){ subject.validate! }
470
+ subject.ip = Factory.string
471
+
472
+ subject.port = nil
473
+ assert_raises(InvalidError){ subject.validate! }
474
+ subject.port = Factory.integer
475
+
476
+ assert_nothing_raised{ subject.validate! }
477
+ end
478
+
479
+ should "validate its routes when validated" do
480
+ subject.router.service(Factory.string, TestHandler.to_s)
481
+ subject.routes.each{ |route| assert_nil route.handler_class }
482
+ subject.validate!
483
+ subject.routes.each{ |route| assert_not_nil route.handler_class }
484
+ end
485
+
486
+ should "be valid after being validated" do
487
+ assert_false subject.valid?
488
+ subject.validate!
489
+ assert_true subject.valid?
490
+ end
491
+
492
+ should "only be able to be validated once" do
493
+ called = 0
494
+ subject.init_procs << proc{ called += 1 }
495
+ subject.validate!
496
+ assert_equal 1, called
497
+ subject.validate!
498
+ assert_equal 1, called
499
+ end
500
+
501
+ end
502
+
503
+ TestHandler = Class.new
504
+
505
+ class TCPServerSpy
506
+ attr_reader :set_socket_option_calls
507
+
508
+ def initialize
509
+ @set_socket_option_calls = []
510
+ end
511
+
512
+ def setsockopt(*args)
513
+ @set_socket_option_calls << args
514
+ end
515
+ end
516
+
517
+ class ConnectionHandlerSpy
518
+ attr_reader :run_called
519
+
520
+ def initialize
521
+ @run_called = false
522
+ end
523
+
524
+ def run
525
+ @run_called = true
526
+ end
527
+ end
47
528
 
48
529
  end