sanford 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/bench/report.txt +34 -4
  2. data/bench/runner.rb +122 -1
  3. data/bench/services.rb +5 -2
  4. data/lib/sanford/error_handler.rb +60 -0
  5. data/lib/sanford/exceptions.rb +11 -10
  6. data/lib/sanford/host.rb +79 -101
  7. data/lib/sanford/host_data.rb +55 -0
  8. data/lib/sanford/logger.rb +23 -0
  9. data/lib/sanford/manager.rb +13 -22
  10. data/lib/sanford/rake.rb +1 -0
  11. data/lib/sanford/runner.rb +50 -0
  12. data/lib/sanford/server.rb +31 -15
  13. data/lib/sanford/service_handler.rb +34 -43
  14. data/lib/sanford/test_runner.rb +47 -0
  15. data/lib/sanford/version.rb +1 -1
  16. data/lib/sanford/worker.rb +124 -0
  17. data/lib/sanford.rb +49 -6
  18. data/sanford.gemspec +1 -1
  19. data/test/helper.rb +1 -0
  20. data/test/support/fake_connection.rb +18 -0
  21. data/test/support/helpers.rb +6 -10
  22. data/test/support/service_handlers.rb +56 -68
  23. data/test/support/services.rb +55 -10
  24. data/test/system/managing_test.rb +18 -18
  25. data/test/system/request_handling_test.rb +10 -100
  26. data/test/unit/config_test.rb +1 -43
  27. data/test/unit/error_handler_test.rb +133 -0
  28. data/test/unit/host_configuration_test.rb +41 -0
  29. data/test/unit/host_data_test.rb +65 -0
  30. data/test/unit/host_test.rb +20 -112
  31. data/test/unit/{host/version_group_test.rb → host_version_group_test.rb} +0 -0
  32. data/test/unit/hosts_test.rb +56 -0
  33. data/test/unit/manager_test.rb +3 -3
  34. data/test/unit/runner_test.rb +26 -0
  35. data/test/unit/server_test.rb +10 -2
  36. data/test/unit/service_handler_test.rb +126 -115
  37. data/test/unit/worker_test.rb +195 -0
  38. metadata +28 -16
  39. data/lib/sanford/config.rb +0 -33
  40. data/lib/sanford/connection.rb +0 -70
  41. data/lib/sanford/exception_handler.rb +0 -43
  42. data/test/unit/connection_test.rb +0 -23
  43. data/test/unit/exception_handler_test.rb +0 -69
@@ -1,115 +1,103 @@
1
- # A bunch of service handler examples. These are defined to implement certain
2
- # edge cases and are for specific tests within the test suite.
3
- #
4
-
5
- class StaticServiceHandler
1
+ class TestServiceHandler
6
2
  include Sanford::ServiceHandler
7
3
 
8
- # builds with the same request and logger always, just for convenience
9
-
10
- def initialize(logger = nil, request = nil)
11
- request ||= Sanford::Protocol::Request.new('v1', 'name', {})
12
- super(logger || Sanford::NullLogger.new, request)
13
- end
14
-
15
4
  end
16
5
 
17
- class ManualThrowServiceHandler < StaticServiceHandler
6
+ class BasicServiceHandler
7
+ include Sanford::ServiceHandler
18
8
 
19
9
  def run!
20
- throw(:halt, 'halted!')
10
+ { 'name' => 'Joe Test', 'email' => "joe.test@example.com" }
21
11
  end
22
12
 
23
13
  end
24
14
 
25
- class HaltWithServiceHandler < StaticServiceHandler
15
+ class FlagServiceHandler
16
+ include Sanford::ServiceHandler
26
17
 
27
- def initialize(halt_with)
28
- request = Sanford::Protocol::Request.new('v1', 'name', {
29
- 'halt_with' => halt_with.dup
30
- })
31
- super(Sanford::NullLogger.new, request)
32
- end
18
+ attr_reader :before_init_called, :init_bang_called, :after_init_called,
19
+ :before_run_called, :run_bang_called, :after_run_called
33
20
 
34
- def run!
35
- params['halt_with'].tap do |halt_with|
36
- halt(halt_with.delete('code'), halt_with)
37
- end
21
+ def before_init
22
+ @before_init_called = true
38
23
  end
39
24
 
40
- end
25
+ def init!
26
+ @init_bang_called = true
27
+ end
41
28
 
42
- class NoopServiceHandler < StaticServiceHandler
29
+ def after_init
30
+ @after_init_called = true
31
+ end
43
32
 
44
- # simply overwrites the default `run!` so it doesn't error
33
+ def before_run
34
+ @before_run_called = true
35
+ end
45
36
 
46
37
  def run!
47
- # nothing!
38
+ @run_bang_called = true
39
+ end
40
+
41
+ def after_run
42
+ @after_run_called = true
48
43
  end
49
44
 
50
45
  end
51
46
 
52
- class FlaggedServiceHandler < NoopServiceHandler
47
+ class HaltServiceHandler
48
+ include Sanford::ServiceHandler
53
49
 
54
- # flags a bunch of methods as they are run by setting instance variables
50
+ def run!
51
+ halt params['code'], :message => params['message'], :data => params['data']
52
+ end
55
53
 
56
- FLAGGED_METHODS = {
57
- 'init' => :init_called,
58
- 'init!' => :init_bang_called,
59
- 'run!' => :run_bang_called,
60
- 'before_run' => :before_run_called,
61
- 'after_run' => :after_run_called
62
- }
63
- FLAGS = FLAGGED_METHODS.values
54
+ end
64
55
 
65
- attr_reader *FLAGS
56
+ class HaltingBehaviorServiceHandler < FlagServiceHandler
66
57
 
67
- def initialize(*passed)
58
+ def before_init
68
59
  super
69
- FLAGS.each{|name| self.instance_variable_set("@#{name}", false) }
60
+ halt_when('before_init')
70
61
  end
71
62
 
72
- FLAGGED_METHODS.each do |method_name, instance_variable_name|
73
-
74
- # def before_run
75
- # super
76
- # @before_run_called = true
77
- # end
78
- define_method(method_name) do
79
- super
80
- self.instance_variable_set("@#{instance_variable_name}", true)
81
- end
82
-
63
+ def init!
64
+ super
65
+ halt_when('init!')
83
66
  end
84
67
 
85
- end
86
-
87
- class ConfigurableServiceHandler < FlaggedServiceHandler
88
-
89
- def initialize(options = {})
90
- @options = options
68
+ def after_init
91
69
  super
70
+ halt_when('after_init')
92
71
  end
93
72
 
94
73
  def before_run
95
74
  super
96
- if @options[:before_run]
97
- self.instance_eval(&@options[:before_run])
98
- end
75
+ halt_when('before_run')
99
76
  end
100
77
 
101
78
  def run!
102
79
  super
103
- if @options[:run!]
104
- self.instance_eval(&@options[:run!])
105
- end
80
+ halt_when('run!')
106
81
  end
107
82
 
108
83
  def after_run
109
84
  super
110
- if @options[:after_run]
111
- self.instance_eval(&@options[:after_run])
112
- end
85
+ halt_when('after_run')
86
+ end
87
+
88
+ def halt_when(method_name)
89
+ return if ![*params['when']].include?(method_name)
90
+ halt(200, {
91
+ :message => "#{method_name} halting",
92
+ :data => {
93
+ :before_init_called => @before_init_called,
94
+ :init_bang_called => @init_bang_called,
95
+ :after_init_called => @after_init_called,
96
+ :before_run_called => @before_run_called,
97
+ :run_bang_called => @run_bang_called,
98
+ :after_run_called => @after_run_called
99
+ }
100
+ })
113
101
  end
114
102
 
115
103
  end
@@ -1,23 +1,32 @@
1
1
  require 'logger'
2
2
 
3
- class DummyHost
3
+ class TestHost
4
4
  include Sanford::Host
5
5
 
6
6
  ip 'localhost'
7
7
  port 12000
8
8
  pid_dir File.expand_path('../../../tmp/', __FILE__)
9
9
 
10
- logger = Logger.new(File.expand_path("../../../log/test.log", __FILE__))
11
- logger.level = Logger::DEBUG
10
+ logger(Logger.new(File.expand_path("../../../log/test.log", __FILE__)).tap do |logger|
11
+ logger.level = Logger::DEBUG
12
+ end)
13
+ verbose_logging true
12
14
 
13
- version 'v1' do
14
- service_handler_ns 'DummyHost'
15
+ error do |exception, host_data, request|
16
+ if exception.kind_of?(::MyCustomError)
17
+ Sanford::Protocol::Response.new([ 987, 'custom error!' ])
18
+ end
19
+ end
15
20
 
16
- service 'echo', 'Echo'
17
- service 'bad', 'Bad'
18
- service 'multiply', 'Multiply'
19
- service 'halt_it', '::DummyHost::HaltIt'
20
- service 'authorized', 'Authorized'
21
+ version 'v1' do
22
+ service_handler_ns 'TestHost'
23
+
24
+ service 'echo', 'Echo'
25
+ service 'bad', 'Bad'
26
+ service 'multiply', 'Multiply'
27
+ service 'halt_it', '::TestHost::HaltIt'
28
+ service 'authorized', 'Authorized'
29
+ service 'custom_error', 'CustomError'
21
30
  end
22
31
 
23
32
  class Echo
@@ -69,4 +78,40 @@ class DummyHost
69
78
 
70
79
  end
71
80
 
81
+ ::MyCustomError = Class.new(RuntimeError)
82
+
83
+ class CustomError
84
+ include Sanford::ServiceHandler
85
+
86
+ def run!
87
+ raise ::MyCustomError
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
94
+ class MyHost
95
+ include Sanford::Host
96
+
97
+ name 'my_host'
98
+ ip 'my.local'
99
+ end
100
+
101
+ class InvalidHost
102
+ include Sanford::Host
103
+
104
+ name 'invalid_host'
105
+ end
106
+
107
+ class UndefinedHandlersHost
108
+ include Sanford::Host
109
+
110
+ version 'v1' do
111
+ service 'undefined', 'ThisIsNotDefined'
112
+ end
113
+ end
114
+
115
+ class EmptyHost
116
+ include Sanford::Host
72
117
  end
@@ -1,5 +1,7 @@
1
1
  require 'assert'
2
2
 
3
+ require 'sanford/manager'
4
+
3
5
  class ManagingTest < Assert::Context
4
6
  desc "Using Sanford's manager"
5
7
  setup do
@@ -15,7 +17,7 @@ class ManagingTest < Assert::Context
15
17
  include Test::ForkManagerHelper
16
18
 
17
19
  setup do
18
- Sanford.config.hosts.add(DummyHost)
20
+ Sanford.register(TestHost)
19
21
  end
20
22
  end
21
23
 
@@ -23,11 +25,9 @@ class ManagingTest < Assert::Context
23
25
  desc "to run a service host"
24
26
 
25
27
  should "start a sanford server for the only service host that is configured" do
26
- host = Sanford.config.hosts.first
27
-
28
28
  self.call_sanford_manager(:run) do
29
- assert_nothing_raised{ self.open_socket(host.config.ip, host.config.port) }
30
- assert File.exists?(self.expected_pid_file(host, host.config.ip, host.config.port))
29
+ assert_nothing_raised{ self.open_socket(TestHost.ip, TestHost.port) }
30
+ assert File.exists?(self.expected_pid_file(TestHost, TestHost.ip, TestHost.port))
31
31
  end
32
32
  end
33
33
  end
@@ -35,17 +35,17 @@ class ManagingTest < Assert::Context
35
35
  class RunWithOptionsTest < CallTest
36
36
  desc "to run a service host and passing options"
37
37
  setup do
38
- # make sure that DummyHost isn't the only 'host'
39
- Sanford.config.hosts.add(Class.new)
38
+ # make sure that TestHost isn't the only 'host'
39
+ Sanford.register(Class.new)
40
40
  end
41
41
 
42
42
  should "start a sanford server for the specified service host and " \
43
43
  "use the passed options to override it's configuration" do
44
- host = Sanford.config.find_host('DummyHost')
44
+ host = Sanford.hosts.find('TestHost')
45
45
 
46
- self.call_sanford_manager(:run, { :host => 'DummyHost', :port => 12345 }) do
47
- assert_nothing_raised{ self.open_socket(host.config.ip, 12345) }
48
- assert File.exists?(self.expected_pid_file(host, host.config.ip, 12345))
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
49
  end
50
50
  end
51
51
  end
@@ -54,10 +54,10 @@ class ManagingTest < Assert::Context
54
54
  desc "to run a service host and setting env vars"
55
55
  setup do
56
56
  @current = ENV.delete('SANFORD_HOST'), ENV.delete('SANFORD_IP'), ENV.delete('SANFORD_PORT')
57
- ENV['SANFORD_HOST'] = 'DummyHost'
57
+ ENV['SANFORD_HOST'] = 'TestHost'
58
58
  ENV['SANFORD_IP'], ENV['SANFORD_PORT'] = 'localhost', '54321'
59
- # make sure that DummyHost isn't the only 'host'
60
- Sanford.config.hosts.add(Class.new)
59
+ # make sure that TestHost isn't the only 'host'
60
+ Sanford.register(Class.new)
61
61
  end
62
62
  teardown do
63
63
  ENV['SANFORD_HOST'], ENV['SANFORD_IP'], ENV['SANFORD_PORT'] = @current
@@ -65,7 +65,7 @@ class ManagingTest < Assert::Context
65
65
 
66
66
  should "start a sanford server for the specified service host and " \
67
67
  "use the env vars to override it's configuration" do
68
- host = Sanford.config.find_host(ENV['SANFORD_HOST'])
68
+ host = Sanford.hosts.find(ENV['SANFORD_HOST'])
69
69
  port = ENV['SANFORD_PORT'].to_i
70
70
 
71
71
  self.call_sanford_manager(:run) do
@@ -78,8 +78,8 @@ class ManagingTest < Assert::Context
78
78
  class BadHostTest < ManagingTest
79
79
  desc "with a bad host name"
80
80
  setup do
81
- Sanford.config.hosts.clear
82
- Sanford.config.hosts.add(Class.new)
81
+ Sanford.hosts.clear
82
+ Sanford.register(Class.new)
83
83
  end
84
84
 
85
85
  should "raise an exception when a service host can't be found" do
@@ -92,7 +92,7 @@ class ManagingTest < Assert::Context
92
92
  class NoHostsTest < ManagingTest
93
93
  desc "with no hosts"
94
94
  setup do
95
- Sanford.config.hosts.clear
95
+ Sanford.hosts.clear
96
96
  end
97
97
 
98
98
  should "raise an exception when there aren't any service hosts" do
@@ -1,16 +1,18 @@
1
1
  # This test is intended as a high level test against Sanford's server. This will
2
2
  # use multiple request scenarios to test out Sanford's behavior and how it
3
- # responds.
3
+ # responds. These tests depend on a socket (or the protocol's connection) and
4
+ # thus are a system level test.
4
5
  #
5
6
  require 'assert'
6
7
 
8
+ require 'sanford/manager'
9
+
7
10
  class RequestHandlingTest < Assert::Context
8
11
  include Test::ForkServerHelper
9
12
 
10
13
  desc "Sanford's handling of requests"
11
14
  setup do
12
- @service_host = DummyHost.new
13
- @server = Sanford::Server.new(@service_host, { :ready_timeout => 0 })
15
+ @server = Sanford::Server.new(TestHost, { :ready_timeout => 0 })
14
16
  end
15
17
 
16
18
  # Simple service test that echos back the params sent to it
@@ -19,7 +21,7 @@ class RequestHandlingTest < Assert::Context
19
21
 
20
22
  should "return a successful response and echo the params sent to it" do
21
23
  self.start_server(@server) do
22
- response = SimpleClient.call_with_request(@service_host, 'v1', 'echo', {
24
+ response = SimpleClient.call_with_request(TestHost, 'v1', 'echo', {
23
25
  :message => 'test'
24
26
  })
25
27
 
@@ -47,7 +49,7 @@ class RequestHandlingTest < Assert::Context
47
49
  should "return a bad request response with an error message" do
48
50
  self.start_server(@server) do
49
51
  bytes = [ Sanford::Protocol.msg_version, "\000" ].join
50
- response = SimpleClient.call_with(@service_host, bytes)
52
+ response = SimpleClient.call_with(TestHost, bytes)
51
53
 
52
54
  assert_equal 400, response.status.code
53
55
  assert_match "size", response.status.message
@@ -63,7 +65,7 @@ class RequestHandlingTest < Assert::Context
63
65
  should "return a bad request response with an error message" do
64
66
  self.start_server(@server) do
65
67
  bytes = [ Sanford::Protocol.msg_version, "\000" ].join
66
- response = SimpleClient.call_with_msg_body(@service_host, {}, nil, "\000")
68
+ response = SimpleClient.call_with_msg_body(TestHost, {}, nil, "\000")
67
69
 
68
70
  assert_equal 400, response.status.code
69
71
  assert_match "Protocol version", response.status.message
@@ -77,7 +79,7 @@ class RequestHandlingTest < Assert::Context
77
79
  desc "when sent a request with an invalid body"
78
80
  should "return a bad request response with an error message" do
79
81
  self.start_server(@server) do
80
- response = SimpleClient.call_with_encoded_msg_body(@service_host, "\000\001\010\011" * 2)
82
+ response = SimpleClient.call_with_encoded_msg_body(TestHost, "\000\001\010\011" * 2)
81
83
 
82
84
  assert_equal 400, response.status.code
83
85
  assert_match "body", response.status.message
@@ -86,98 +88,6 @@ class RequestHandlingTest < Assert::Context
86
88
  end
87
89
  end
88
90
 
89
- class MissingServiceNameTest < ErroringRequestTest
90
- desc "when sent a request with no service name"
91
-
92
- should "return a bad request response" do
93
- self.start_server(@server) do
94
- request_hash = Sanford::Protocol::Request.new('v1', 'what', {}).to_hash
95
- request_hash.delete('name')
96
- response = SimpleClient.call_with_msg_body(@service_host, request_hash)
97
-
98
- assert_equal 400, response.status.code
99
- assert_match "request", response.status.message
100
- assert_match "name", response.status.message
101
- assert_equal nil, response.data
102
- end
103
- end
104
- end
105
-
106
- class MissingServiceVersionTest < ErroringRequestTest
107
- desc "when sent a request with no service version"
108
-
109
- should "return a bad request response" do
110
- self.start_server(@server) do
111
- request_hash = Sanford::Protocol::Request.new('v1', 'what', {}).to_hash
112
- request_hash.delete('version')
113
- response = SimpleClient.call_with_msg_body(@service_host, request_hash)
114
-
115
- assert_equal 400, response.status.code
116
- assert_match "request", response.status.message
117
- assert_match "version", response.status.message
118
- assert_equal nil, response.data
119
- end
120
- end
121
- end
122
-
123
- # Requesting a service that is not defined
124
- class NotFoundServiceTest < ErroringRequestTest
125
- desc "when sent a request with no matching service name"
126
-
127
- should "return a bad request response" do
128
- self.start_server(@server) do
129
- response = SimpleClient.call_with_request(@service_host, 'v1', 'what', {})
130
-
131
- assert_equal 404, response.status.code
132
- assert_equal nil, response.status.message
133
- assert_equal nil, response.data
134
- end
135
- end
136
- end
137
-
138
- # Hitting a service that throws an exception
139
- class ErrorServiceTest < ErroringRequestTest
140
- desc "when sent a request that errors on the server"
141
-
142
- should "return a bad request response" do
143
- self.start_server(@server) do
144
- response = SimpleClient.call_with_request(@service_host, 'v1', 'bad', {})
145
-
146
- assert_equal 500, response.status.code
147
- assert_match "error", response.status.message
148
- assert_equal nil, response.data
149
- end
150
- end
151
- end
152
-
153
- class HaltTest < RequestHandlingTest
154
- desc "when sent a request that halts"
155
-
156
- should "return the response that was halted" do
157
- self.start_server(@server) do
158
- response = SimpleClient.call_with_request(@service_host, 'v1', 'halt_it', {})
159
-
160
- assert_equal 728, response.status.code
161
- assert_equal "I do what I want", response.status.message
162
- assert_equal [ 1, true, 'yes' ], response.data
163
- end
164
- end
165
- end
166
-
167
- class AuthorizeRequestTest < RequestHandlingTest
168
- desc "when sent a request that halts in a callback"
169
-
170
- should "return the response that was halted" do
171
- self.start_server(@server) do
172
- response = SimpleClient.call_with_request(@service_host, 'v1', 'authorized', {})
173
-
174
- assert_equal 401, response.status.code
175
- assert_equal "Not authorized", response.status.message
176
- assert_equal nil, response.data
177
- end
178
- end
179
- end
180
-
181
91
  class HangingRequestTest < ErroringRequestTest
182
92
  desc "when a client connects but doesn't send anything"
183
93
  setup do
@@ -189,7 +99,7 @@ class RequestHandlingTest < Assert::Context
189
99
 
190
100
  should "timeout" do
191
101
  self.start_server(@server) do
192
- client = SimpleClient.new(@service_host, :with_delay => 0.2)
102
+ client = SimpleClient.new(TestHost, :with_delay => 0.2)
193
103
  response = client.call_with_request('v1', 'echo', { :message => 'test' })
194
104
 
195
105
  assert_equal 408, response.status.code
@@ -6,49 +6,7 @@ module Sanford::Config
6
6
  desc "Sanford::Config"
7
7
  subject{ Sanford::Config }
8
8
 
9
- should have_instance_methods :hosts, :services_config, :find_host
10
- end
11
-
12
- class FindHostTest < BaseTest
13
- desc "find_host"
14
- setup do
15
- Test::Environment.store_and_clear_hosts
16
- Sanford::Config.hosts.add(NotNamedHost)
17
- Sanford::Config.hosts.add(NamedHost)
18
- Sanford::Config.hosts.add(BadlyNamedHost)
19
- end
20
- teardown do
21
- Test::Environment.restore_hosts
22
- end
23
-
24
- should "allow finding hosts by their class name or configured name" do
25
- assert_includes NotNamedHost, subject.hosts
26
- assert_includes NamedHost, subject.hosts
27
- assert_equal NotNamedHost, subject.find_host('NotNamedHost')
28
- assert_equal NamedHost, subject.find_host('NamedHost')
29
- assert_equal NamedHost, subject.find_host('named_host')
30
- end
31
- should "check class name before configured name" do
32
- assert_includes BadlyNamedHost, subject.hosts
33
- assert_equal NotNamedHost, subject.find_host('NotNamedHost')
34
- end
35
- end
36
-
37
- # Using this syntax because these classes need to be defined as top-level
38
- # constants for ease in using their class names in the tests
39
-
40
- ::NotNamedHost = Class.new do
41
- include Sanford::Host
42
- end
43
-
44
- ::NamedHost = Class.new do
45
- include Sanford::Host
46
- name 'named_host'
47
- end
48
-
49
- ::BadlyNamedHost = Class.new do
50
- include Sanford::Host
51
- name 'NotNamedHost'
9
+ should have_instance_methods :services_config
52
10
  end
53
11
 
54
12
  end