hoodoo 1.0.2
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.
- checksums.yaml +7 -0
- data/bin/hoodoo +5 -0
- data/lib/hoodoo.rb +27 -0
- data/lib/hoodoo/active.rb +32 -0
- data/lib/hoodoo/active/active_model/uuid_validator.rb +45 -0
- data/lib/hoodoo/active/active_record/base.rb +81 -0
- data/lib/hoodoo/active/active_record/creator.rb +134 -0
- data/lib/hoodoo/active/active_record/dated.rb +343 -0
- data/lib/hoodoo/active/active_record/error_mapping.rb +351 -0
- data/lib/hoodoo/active/active_record/finder.rb +606 -0
- data/lib/hoodoo/active/active_record/search_helper.rb +189 -0
- data/lib/hoodoo/active/active_record/secure.rb +431 -0
- data/lib/hoodoo/active/active_record/support.rb +106 -0
- data/lib/hoodoo/active/active_record/translated.rb +87 -0
- data/lib/hoodoo/active/active_record/uuid.rb +80 -0
- data/lib/hoodoo/active/active_record/writer.rb +321 -0
- data/lib/hoodoo/client.rb +23 -0
- data/lib/hoodoo/client/augmented_array.rb +29 -0
- data/lib/hoodoo/client/augmented_base.rb +168 -0
- data/lib/hoodoo/client/augmented_hash.rb +23 -0
- data/lib/hoodoo/client/client.rb +354 -0
- data/lib/hoodoo/client/endpoint/endpoint.rb +427 -0
- data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +180 -0
- data/lib/hoodoo/client/endpoint/endpoints/auto_session.rb +194 -0
- data/lib/hoodoo/client/endpoint/endpoints/http.rb +203 -0
- data/lib/hoodoo/client/endpoint/endpoints/http_based.rb +367 -0
- data/lib/hoodoo/client/endpoint/endpoints/not_found.rb +59 -0
- data/lib/hoodoo/client/headers.rb +269 -0
- data/lib/hoodoo/communicators.rb +23 -0
- data/lib/hoodoo/communicators/fast.rb +44 -0
- data/lib/hoodoo/communicators/pool.rb +601 -0
- data/lib/hoodoo/communicators/slow.rb +84 -0
- data/lib/hoodoo/data.rb +51 -0
- data/lib/hoodoo/data/resources/caller.rb +39 -0
- data/lib/hoodoo/data/resources/errors.rb +28 -0
- data/lib/hoodoo/data/resources/log.rb +31 -0
- data/lib/hoodoo/data/resources/session.rb +26 -0
- data/lib/hoodoo/data/types/error_primitive.rb +27 -0
- data/lib/hoodoo/data/types/permissions.rb +40 -0
- data/lib/hoodoo/data/types/permissions_defaults.rb +32 -0
- data/lib/hoodoo/data/types/permissions_full.rb +28 -0
- data/lib/hoodoo/data/types/permissions_resources.rb +31 -0
- data/lib/hoodoo/discovery.rb +20 -0
- data/lib/hoodoo/errors.rb +19 -0
- data/lib/hoodoo/errors/error_descriptions.rb +229 -0
- data/lib/hoodoo/errors/errors.rb +322 -0
- data/lib/hoodoo/generator.rb +139 -0
- data/lib/hoodoo/logger.rb +23 -0
- data/lib/hoodoo/logger/fast_writer.rb +27 -0
- data/lib/hoodoo/logger/flattener_mixin.rb +36 -0
- data/lib/hoodoo/logger/logger.rb +387 -0
- data/lib/hoodoo/logger/slow_writer.rb +49 -0
- data/lib/hoodoo/logger/writer_mixin.rb +52 -0
- data/lib/hoodoo/logger/writers/file_writer.rb +45 -0
- data/lib/hoodoo/logger/writers/log_entries_dot_com_writer.rb +64 -0
- data/lib/hoodoo/logger/writers/stream_writer.rb +43 -0
- data/lib/hoodoo/middleware.rb +33 -0
- data/lib/hoodoo/presenters.rb +45 -0
- data/lib/hoodoo/presenters/base.rb +281 -0
- data/lib/hoodoo/presenters/base_dsl.rb +519 -0
- data/lib/hoodoo/presenters/common_resource_fields.rb +31 -0
- data/lib/hoodoo/presenters/embedding.rb +232 -0
- data/lib/hoodoo/presenters/types/array.rb +118 -0
- data/lib/hoodoo/presenters/types/boolean.rb +26 -0
- data/lib/hoodoo/presenters/types/date.rb +26 -0
- data/lib/hoodoo/presenters/types/date_time.rb +26 -0
- data/lib/hoodoo/presenters/types/decimal.rb +47 -0
- data/lib/hoodoo/presenters/types/enum.rb +55 -0
- data/lib/hoodoo/presenters/types/field.rb +158 -0
- data/lib/hoodoo/presenters/types/float.rb +26 -0
- data/lib/hoodoo/presenters/types/hash.rb +361 -0
- data/lib/hoodoo/presenters/types/integer.rb +26 -0
- data/lib/hoodoo/presenters/types/object.rb +117 -0
- data/lib/hoodoo/presenters/types/string.rb +53 -0
- data/lib/hoodoo/presenters/types/tags.rb +24 -0
- data/lib/hoodoo/presenters/types/text.rb +26 -0
- data/lib/hoodoo/presenters/types/uuid.rb +54 -0
- data/lib/hoodoo/services.rb +34 -0
- data/lib/hoodoo/services/discovery/discoverers/by_consul.rb +66 -0
- data/lib/hoodoo/services/discovery/discoverers/by_convention.rb +173 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +195 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server.rb +166 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server_start.rb +37 -0
- data/lib/hoodoo/services/discovery/discovery.rb +186 -0
- data/lib/hoodoo/services/discovery/results/for_amqp.rb +58 -0
- data/lib/hoodoo/services/discovery/results/for_http.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_local.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_remote.rb +57 -0
- data/lib/hoodoo/services/middleware/amqp_log_message.rb +186 -0
- data/lib/hoodoo/services/middleware/amqp_log_writer.rb +119 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_local.rb +130 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_remote.rb +202 -0
- data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +105 -0
- data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +115 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +64 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +63 -0
- data/lib/hoodoo/services/middleware/interaction.rb +127 -0
- data/lib/hoodoo/services/middleware/middleware.rb +2705 -0
- data/lib/hoodoo/services/middleware/rack_monkey_patch.rb +73 -0
- data/lib/hoodoo/services/services/context.rb +153 -0
- data/lib/hoodoo/services/services/implementation.rb +132 -0
- data/lib/hoodoo/services/services/interface.rb +934 -0
- data/lib/hoodoo/services/services/permissions.rb +250 -0
- data/lib/hoodoo/services/services/request.rb +189 -0
- data/lib/hoodoo/services/services/response.rb +316 -0
- data/lib/hoodoo/services/services/service.rb +141 -0
- data/lib/hoodoo/services/services/session.rb +729 -0
- data/lib/hoodoo/utilities.rb +12 -0
- data/lib/hoodoo/utilities/string_inquirer.rb +54 -0
- data/lib/hoodoo/utilities/utilities.rb +380 -0
- data/lib/hoodoo/utilities/uuid.rb +44 -0
- data/lib/hoodoo/version.rb +17 -0
- data/spec/active/active_record/base_spec.rb +57 -0
- data/spec/active/active_record/creator_spec.rb +88 -0
- data/spec/active/active_record/dated_spec.rb +248 -0
- data/spec/active/active_record/error_mapping_spec.rb +360 -0
- data/spec/active/active_record/finder_spec.rb +744 -0
- data/spec/active/active_record/search_helper_spec.rb +384 -0
- data/spec/active/active_record/secure_spec.rb +435 -0
- data/spec/active/active_record/support_spec.rb +225 -0
- data/spec/active/active_record/translated_spec.rb +19 -0
- data/spec/active/active_record/uuid_spec.rb +72 -0
- data/spec/active/active_record/writer_spec.rb +272 -0
- data/spec/alchemy/alchemy-amq.rb +33 -0
- data/spec/client/augmented_array_spec.rb +15 -0
- data/spec/client/augmented_base_spec.rb +50 -0
- data/spec/client/augmented_hash_spec.rb +15 -0
- data/spec/client/client_spec.rb +955 -0
- data/spec/client/endpoint/endpoint_spec.rb +70 -0
- data/spec/client/endpoint/endpoints/amqp_spec.rb +16 -0
- data/spec/client/endpoint/endpoints/auto_session_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_based_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_spec.rb +103 -0
- data/spec/client/endpoint/endpoints/not_found_spec.rb +35 -0
- data/spec/client/headers_spec.rb +172 -0
- data/spec/communicators/fast_spec.rb +9 -0
- data/spec/communicators/pool_spec.rb +339 -0
- data/spec/communicators/slow_spec.rb +15 -0
- data/spec/data/resources/caller_spec.rb +156 -0
- data/spec/data/resources/errors_spec.rb +22 -0
- data/spec/data/resources/log_spec.rb +20 -0
- data/spec/data/resources/session_spec.rb +15 -0
- data/spec/data/types/error_primitive_spec.rb +15 -0
- data/spec/data/types/permissions_defaults_spec.rb +25 -0
- data/spec/data/types/permissions_full_spec.rb +44 -0
- data/spec/data/types/permissions_resources_spec.rb +34 -0
- data/spec/data/types/permissions_spec.rb +37 -0
- data/spec/errors/error_descriptions_spec.rb +98 -0
- data/spec/errors/errors_spec.rb +346 -0
- data/spec/integration/service_actions_spec.rb +112 -0
- data/spec/logger/fast_writer_spec.rb +18 -0
- data/spec/logger/logger_spec.rb +259 -0
- data/spec/logger/slow_writer_spec.rb +144 -0
- data/spec/logger/writers/file_writer_spec.rb +37 -0
- data/spec/logger/writers/log_entries_dot_com_writer_spec.rb +29 -0
- data/spec/logger/writers/stream_writer_spec.rb +38 -0
- data/spec/presenters/base_dsl_spec.rb +111 -0
- data/spec/presenters/base_spec.rb +871 -0
- data/spec/presenters/common_resource_fields_spec.rb +30 -0
- data/spec/presenters/embedding_spec.rb +87 -0
- data/spec/presenters/types/array_spec.rb +249 -0
- data/spec/presenters/types/boolean_spec.rb +51 -0
- data/spec/presenters/types/date_spec.rb +57 -0
- data/spec/presenters/types/date_time_spec.rb +59 -0
- data/spec/presenters/types/decimal_spec.rb +58 -0
- data/spec/presenters/types/enum_spec.rb +71 -0
- data/spec/presenters/types/field_spec.rb +77 -0
- data/spec/presenters/types/float_spec.rb +50 -0
- data/spec/presenters/types/hash_spec.rb +1069 -0
- data/spec/presenters/types/integer_spec.rb +50 -0
- data/spec/presenters/types/object_spec.rb +177 -0
- data/spec/presenters/types/string_spec.rb +65 -0
- data/spec/presenters/types/tags_spec.rb +56 -0
- data/spec/presenters/types/text_spec.rb +50 -0
- data/spec/presenters/types/uuid_spec.rb +46 -0
- data/spec/presenters/walk_spec.rb +198 -0
- data/spec/services/discovery/discoverers/by_consul_spec.rb +29 -0
- data/spec/services/discovery/discoverers/by_convention_spec.rb +67 -0
- data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +80 -0
- data/spec/services/discovery/discoverers/by_drb/drb_server_spec.rb +205 -0
- data/spec/services/discovery/discovery_spec.rb +73 -0
- data/spec/services/discovery/results/for_amqp_spec.rb +17 -0
- data/spec/services/discovery/results/for_http_spec.rb +37 -0
- data/spec/services/discovery/results/for_local_spec.rb +21 -0
- data/spec/services/discovery/results/for_remote_spec.rb +15 -0
- data/spec/services/middleware/amqp_log_message_spec.rb +60 -0
- data/spec/services/middleware/amqp_log_writer_spec.rb +95 -0
- data/spec/services/middleware/endpoints/inter_resource_local_spec.rb +9 -0
- data/spec/services/middleware/endpoints/inter_resource_remote_spec.rb +9 -0
- data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +16 -0
- data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +92 -0
- data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +24 -0
- data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +23 -0
- data/spec/services/middleware/middleware_cors_spec.rb +93 -0
- data/spec/services/middleware/middleware_create_update_spec.rb +489 -0
- data/spec/services/middleware/middleware_dated_at_spec.rb +186 -0
- data/spec/services/middleware/middleware_exotic_communication_spec.rb +560 -0
- data/spec/services/middleware/middleware_logging_spec.rb +356 -0
- data/spec/services/middleware/middleware_multi_local_spec.rb +1094 -0
- data/spec/services/middleware/middleware_multi_remote_spec.rb +1440 -0
- data/spec/services/middleware/middleware_permissions_spec.rb +1014 -0
- data/spec/services/middleware/middleware_public_spec.rb +238 -0
- data/spec/services/middleware/middleware_spec.rb +1569 -0
- data/spec/services/middleware/string_inquirer_spec.rb +30 -0
- data/spec/services/services/application_spec.rb +74 -0
- data/spec/services/services/context_spec.rb +48 -0
- data/spec/services/services/implementation_spec.rb +45 -0
- data/spec/services/services/interface_spec.rb +262 -0
- data/spec/services/services/permissions_spec.rb +249 -0
- data/spec/services/services/request_spec.rb +95 -0
- data/spec/services/services/response_spec.rb +250 -0
- data/spec/services/services/session_spec.rb +432 -0
- data/spec/spec_helper.rb +298 -0
- data/spec/utilities/utilities_spec.rb +537 -0
- data/spec/utilities/uuid_spec.rb +20 -0
- metadata +615 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Much of this class gets nontrivial coverage from middleware tests, which
|
4
|
+
# existed before the refactor of code from there, into endpoints.
|
5
|
+
#
|
6
|
+
# This file just picks up the loose ends.
|
7
|
+
|
8
|
+
describe Hoodoo::Client::Endpoint do
|
9
|
+
it 'complains about unknown discovery results' do
|
10
|
+
mock_discoverer = OpenStruct.new
|
11
|
+
expect( mock_discoverer ).to receive( :discover ).once.and_return( OpenStruct.new )
|
12
|
+
|
13
|
+
expect {
|
14
|
+
described_class.endpoint_for( :Anything, 1, { :discoverer => mock_discoverer } )
|
15
|
+
}.to raise_error( RuntimeError, "Hoodoo::Client::Endpoint::endpoint_for: Unrecognised discoverer result class of 'OpenStruct'" )
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'with subclass mandatory methods' do
|
19
|
+
|
20
|
+
# Create a subclas that must at least implement configure_with, else
|
21
|
+
# instantiation would fail as the constructor calls this.
|
22
|
+
#
|
23
|
+
class RSpecBadEndpointSubclass < described_class
|
24
|
+
def configure_with( a, b, c )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
before :each do
|
29
|
+
@endpoint = RSpecBadEndpointSubclass.new(
|
30
|
+
:Anything,
|
31
|
+
1,
|
32
|
+
{}
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'complains about missing #configure_with' do
|
37
|
+
# Trying to instantiate Endpoint directly leads it to call its own
|
38
|
+
# implementation of #configure_with, which should raise an exception.
|
39
|
+
|
40
|
+
expect {
|
41
|
+
described_class.new(
|
42
|
+
:Anything,
|
43
|
+
1,
|
44
|
+
{}
|
45
|
+
)
|
46
|
+
}.to raise_error( RuntimeError, 'Subclasses must implement Hoodoo::Client::Endpoint#configure_with' )
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'complains about missing #list' do
|
50
|
+
expect { @endpoint.list }.to raise_error( RuntimeError, 'Subclasses must implement Hoodoo::Client::Endpoint#list' )
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'complains about missing #show' do
|
54
|
+
expect { @endpoint.show( 'foo' ) }.to raise_error( RuntimeError, 'Subclasses must implement Hoodoo::Client::Endpoint#show' )
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'complains about missing #create' do
|
58
|
+
expect { @endpoint.create( {} ) }.to raise_error( RuntimeError, 'Subclasses must implement Hoodoo::Client::Endpoint#create' )
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'complains about missing #update' do
|
62
|
+
expect { @endpoint.update( 'foo', {} ) }.to raise_error( RuntimeError, 'Subclasses must implement Hoodoo::Client::Endpoint#update' )
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'complains about missing #delete' do
|
66
|
+
expect { @endpoint.delete( 'foo' ) }.to raise_error( RuntimeError, 'Subclasses must implement Hoodoo::Client::Endpoint#delete' )
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Much of this class gets nontrivial coverage from middleware tests, which
|
4
|
+
# existed before the refactor of code from there, into endpoints.
|
5
|
+
#
|
6
|
+
# This file just picks up the loose ends.
|
7
|
+
|
8
|
+
describe Hoodoo::Client::Endpoint::AMQP do
|
9
|
+
|
10
|
+
it 'complains if instantiated with the wrong discovery result type' do
|
11
|
+
expect {
|
12
|
+
described_class.new( :Anything, 1, { :discovery_result => OpenStruct.new } )
|
13
|
+
}.to raise_error( RuntimeError, "Hoodoo::Client::Endpoint::AMQP must be configured with a Hoodoo::Services::Discovery::ForAMQP instance - got 'OpenStruct'" )
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Much of this class gets nontrivial coverage from middleware tests, which
|
4
|
+
# existed before the refactor of code from there, into Endpoints.
|
5
|
+
#
|
6
|
+
# This file just picks up the loose ends.
|
7
|
+
|
8
|
+
describe Hoodoo::Client::Endpoint::HTTP do
|
9
|
+
|
10
|
+
it 'complains if instantiated with the wrong discovery result type' do
|
11
|
+
expect {
|
12
|
+
described_class.new( :Anything, 1, { :discovery_result => OpenStruct.new } )
|
13
|
+
}.to raise_error( RuntimeError, "Hoodoo::Client::Endpoint::HTTP must be configured with a Hoodoo::Services::Discovery::ForHTTP instance - got 'OpenStruct'" )
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'SSL Cert chain verification' do
|
17
|
+
|
18
|
+
before(:all) do
|
19
|
+
# Start a service listening on https 127.0.0.1 with a self-signed cert.
|
20
|
+
#
|
21
|
+
# Note: we are skipping hoodoo middleware with +skip_hoodoo_middleware+
|
22
|
+
# because this test is about verifying SSL on the client, and we need
|
23
|
+
# to be certain that any errors thrown are from client, and not from
|
24
|
+
# middleware (e.g. server).
|
25
|
+
#
|
26
|
+
# This test effectively ensures we aren't vulnerable to a MiTM attack, and malicious parties
|
27
|
+
# won't be good upstanding Hoodoo::Middleware using citizens.
|
28
|
+
#
|
29
|
+
@https_port = spec_helper_start_svc_app_in_thread_for(SslSelfSignedApp, true, skip_hoodoo_middleware: true)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should successfuly connect with valid certificate chain' do
|
33
|
+
endpoint = connect_to_real_https_endpoint('127.0.0.1', 'spec/files/ca/ca-cert.pem')
|
34
|
+
response = endpoint.list()
|
35
|
+
expect(response["message"]).to eq("This data is a secret")
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should fail when certificate doesn't match the hostname" do
|
39
|
+
endpoint = connect_to_real_https_endpoint('localhost', 'spec/files/ca/ca-cert.pem')
|
40
|
+
response = endpoint.list()
|
41
|
+
expect(response.platform_errors.has_errors?).to eq(true)
|
42
|
+
expect(response.platform_errors.errors.first["code"]).to eq("platform.fault")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should fail when the certificate isn't in the ca_file" do
|
46
|
+
endpoint = connect_to_real_https_endpoint('127.0.0.1', nil)
|
47
|
+
response = endpoint.list()
|
48
|
+
expect(response.platform_errors.has_errors?).to eq(true)
|
49
|
+
expect(response.platform_errors.errors.first["code"]).to eq("platform.fault")
|
50
|
+
end
|
51
|
+
|
52
|
+
class SslSelfSignedApp
|
53
|
+
def call(env)
|
54
|
+
return [200, {'Content-Type' => 'application/json'}, ['{"message": "This data is a secret"}'] ]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def connect_to_real_https_endpoint(hostname, ca_file)
|
59
|
+
mw = SslSelfSignedApp.new
|
60
|
+
interaction = Hoodoo::Services::Middleware::Interaction.new(
|
61
|
+
{},
|
62
|
+
mw,
|
63
|
+
Hoodoo::Services::Middleware.test_session()
|
64
|
+
)
|
65
|
+
interaction.target_interface = OpenStruct.new
|
66
|
+
|
67
|
+
mock_wrapped_discovery_result = Hoodoo::Services::Discovery::ForHTTP.new(
|
68
|
+
resource: 'SecureData',
|
69
|
+
version: 2,
|
70
|
+
endpoint_uri: URI.parse( "https://#{hostname}:#{ @https_port }/v2/secure_data" ),
|
71
|
+
ca_file: ca_file
|
72
|
+
)
|
73
|
+
|
74
|
+
mock_wrapped_endpoint = Hoodoo::Client::Endpoint::HTTP.new(
|
75
|
+
'SecureData',
|
76
|
+
2,
|
77
|
+
:session => Hoodoo::Services::Middleware.test_session(),
|
78
|
+
:discovery_result => mock_wrapped_discovery_result
|
79
|
+
)
|
80
|
+
|
81
|
+
# Synthesise a remote resource discovery result for the HTTP(S) endpoint
|
82
|
+
# built above and use that to make a remote call endpoint.
|
83
|
+
|
84
|
+
discovery_result = Hoodoo::Services::Discovery::ForRemote.new(
|
85
|
+
:resource => 'SecureData',
|
86
|
+
:version => 2,
|
87
|
+
:wrapped_endpoint => mock_wrapped_endpoint
|
88
|
+
)
|
89
|
+
|
90
|
+
endpoint = Hoodoo::Services::Middleware::InterResourceRemote.new(
|
91
|
+
'SecureData',
|
92
|
+
2,
|
93
|
+
{
|
94
|
+
:interaction => interaction,
|
95
|
+
:discovery_result => discovery_result
|
96
|
+
}
|
97
|
+
)
|
98
|
+
return endpoint
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hoodoo::Client::Endpoint::NotFound do
|
4
|
+
before :each do
|
5
|
+
@endpoint = described_class.new( :NotFoundResource, 1, {} )
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'produces "not found" result for' do
|
9
|
+
def run_expectations( result )
|
10
|
+
expect( result.platform_errors.has_errors? ).to eq( true )
|
11
|
+
expect( result.platform_errors.errors.size ).to eq( 1 )
|
12
|
+
expect( result.platform_errors.errors[ 0 ][ 'code' ] ).to eq( 'platform.not_found' )
|
13
|
+
end
|
14
|
+
|
15
|
+
it '#list' do
|
16
|
+
run_expectations( @endpoint.list() )
|
17
|
+
end
|
18
|
+
|
19
|
+
it '#show' do
|
20
|
+
run_expectations( @endpoint.show( 'foo' ) )
|
21
|
+
end
|
22
|
+
|
23
|
+
it '#create' do
|
24
|
+
run_expectations( @endpoint.create( {} ) )
|
25
|
+
end
|
26
|
+
|
27
|
+
it '#update' do
|
28
|
+
run_expectations( @endpoint.update( 'foo', {} ) )
|
29
|
+
end
|
30
|
+
|
31
|
+
it '#delete' do
|
32
|
+
run_expectations( @endpoint.delete( 'foo' ) )
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# Full test coverage already happens "by osmosis" through "client_spec.rb" and
|
4
|
+
# the middleware suite.
|
5
|
+
|
6
|
+
describe Hoodoo::Client::Headers do
|
7
|
+
context 'UUID_PROPERTY_PROC' do
|
8
|
+
it 'converts valid values' do
|
9
|
+
uuid = Hoodoo::UUID.generate()
|
10
|
+
expect( described_class::UUID_PROPERTY_PROC.call( uuid ) ).to eq( uuid )
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'rejects invalid values' do
|
14
|
+
uuid = "not a UUID"
|
15
|
+
expect( described_class::UUID_PROPERTY_PROC.call( uuid ) ).to be_nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'UUID_HEADER_PROC' do
|
20
|
+
it 'converts values' do
|
21
|
+
uuid = Hoodoo::UUID.generate()
|
22
|
+
expect( described_class::UUID_HEADER_PROC.call( uuid ) ).to eq( uuid )
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'DATETIME_IN_PAST_ONLY_PROPERTY_PROC' do
|
27
|
+
it 'converts valid values' do
|
28
|
+
date_time = DateTime.now - 10.seconds
|
29
|
+
date_time_str = Hoodoo::Utilities.nanosecond_iso8601( date_time )
|
30
|
+
expect( described_class::DATETIME_IN_PAST_ONLY_PROPERTY_PROC.call( date_time_str ) ).to eq( date_time )
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'rejects invalid values' do
|
34
|
+
it 'that are not date/times' do
|
35
|
+
date_time_str = 'not a date/time'
|
36
|
+
expect( described_class::DATETIME_IN_PAST_ONLY_PROPERTY_PROC.call( date_time_str ) ).to be_nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'that are Ruby-valid but not in supported ISO 8601 subset format' do
|
40
|
+
date_time_str = Time.now.to_s
|
41
|
+
expect( described_class::DATETIME_IN_PAST_ONLY_PROPERTY_PROC.call( date_time_str ) ).to be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'that are valid but in the future' do
|
45
|
+
date_time = DateTime.now + 1.hour
|
46
|
+
date_time_str = Hoodoo::Utilities.nanosecond_iso8601( date_time )
|
47
|
+
expect( described_class::DATETIME_IN_PAST_ONLY_PROPERTY_PROC.call( date_time_str ) ).to be_nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'DATETIME_WRITER_PROC calls rationalisation method' do
|
53
|
+
now = Time.now
|
54
|
+
expect( Hoodoo::Utilities ).to receive( :rationalise_datetime ).once.with( now ).and_call_original
|
55
|
+
expect( described_class::DATETIME_WRITER_PROC.call( now ) ).to eq( now.to_datetime )
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'DATETIME_HEADER_PROC converts values' do
|
59
|
+
it 'that are DateTime instances' do
|
60
|
+
date_time = DateTime.now - 10.seconds
|
61
|
+
date_time_str = Hoodoo::Utilities.nanosecond_iso8601( date_time )
|
62
|
+
expect( described_class::DATETIME_HEADER_PROC.call( date_time ) ).to eq( date_time_str )
|
63
|
+
end
|
64
|
+
|
65
|
+
# Technically undocumented but highly likely to happen by accident and
|
66
|
+
# the Proc is written using the Hoodoo::Utilities support methods so
|
67
|
+
# this should always work.
|
68
|
+
#
|
69
|
+
it 'that are Time instances' do
|
70
|
+
time = Time.now - 10.seconds
|
71
|
+
time_str = Hoodoo::Utilities.nanosecond_iso8601( time )
|
72
|
+
expect( described_class::DATETIME_HEADER_PROC.call( time ) ).to eq( time_str )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'BOOLEAN_PROPERTY_PROC converts values' do
|
77
|
+
it 'that are yes/no' do
|
78
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( 'yes' ) ).to eq( true )
|
79
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( 'Yes' ) ).to eq( true )
|
80
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( 'YES' ) ).to eq( true )
|
81
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( 'no' ) ).to eq( false )
|
82
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( 'No' ) ).to eq( false )
|
83
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( 'NO' ) ).to eq( false )
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'that are not yes/no' do
|
87
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( nil ) ).to eq( false )
|
88
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( '' ) ).to eq( false )
|
89
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( 'Yess' ) ).to eq( false )
|
90
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( 'oui' ) ).to eq( false )
|
91
|
+
expect( described_class::BOOLEAN_PROPERTY_PROC.call( 'true' ) ).to eq( false )
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'BOOLEAN_HEADER_PROC converts values' do
|
96
|
+
it 'that are true/false' do
|
97
|
+
expect( described_class::BOOLEAN_HEADER_PROC.call( true ) ).to eq( 'yes' )
|
98
|
+
expect( described_class::BOOLEAN_HEADER_PROC.call( false ) ).to eq( 'no' )
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'that are not true/false' do
|
102
|
+
expect( described_class::BOOLEAN_HEADER_PROC.call( nil ) ).to eq( 'no' )
|
103
|
+
expect( described_class::BOOLEAN_HEADER_PROC.call( 'yes' ) ).to eq( 'no' )
|
104
|
+
expect( described_class::BOOLEAN_HEADER_PROC.call( 'true' ) ).to eq( 'no' )
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'HEADER_TO_PROPERTY' do
|
109
|
+
it 'contains the minimum required properties for all descriptions' do
|
110
|
+
expect( described_class::HEADER_TO_PROPERTY.keys.size ).to_not eq( 0 )
|
111
|
+
|
112
|
+
described_class::HEADER_TO_PROPERTY.each do | rack_header, description |
|
113
|
+
expect( rack_header[ 0..6 ] ).to eq( 'HTTP_X_' )
|
114
|
+
|
115
|
+
expect( description[ :property ] ).to_not be_nil
|
116
|
+
expect( description[ :property_writer ] ).to eq( "#{ description[ :property ] }=" )
|
117
|
+
expect( description[ :property_writer ] ).to_not eq( '=' )
|
118
|
+
expect( description[ :property_proc ] ).to be_a( Proc )
|
119
|
+
expect( description[ :header ] ).to_not be_nil
|
120
|
+
expect( description[ :header_proc ] ).to be_a( Proc )
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Slightly weak but test coverage for the varied bespoke property writers
|
126
|
+
# - which have individual requirements on data types and so-on - gets
|
127
|
+
# brought in by all the other tests which explicitly check that headers
|
128
|
+
# in the HEADER_TO_PROPERTY set are working properly. In the end, it all
|
129
|
+
# gets proper coverage.
|
130
|
+
#
|
131
|
+
context '#define_accessors_for_header_equivalents' do
|
132
|
+
class RSpecDefineAccessorsForHeaderEquivalentsTest
|
133
|
+
end
|
134
|
+
|
135
|
+
before :all do
|
136
|
+
described_class.define_accessors_for_header_equivalents( RSpecDefineAccessorsForHeaderEquivalentsTest )
|
137
|
+
@instance = RSpecDefineAccessorsForHeaderEquivalentsTest.new
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'defines read accessors' do
|
141
|
+
described_class::HEADER_TO_PROPERTY.each do | rack_header, description |
|
142
|
+
method = description[ :property ]
|
143
|
+
expect( @instance ).to respond_to( method )
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'defines write accessors' do
|
148
|
+
described_class::HEADER_TO_PROPERTY.each do | rack_header, description |
|
149
|
+
method = description[ :property_writer ]
|
150
|
+
expect( @instance ).to respond_to( method )
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context '#x_header_to_options' do
|
156
|
+
it 'converts headers' do
|
157
|
+
hash = {
|
158
|
+
'x-interaction-id' => '23',
|
159
|
+
'X-Foo-Bar' => '42',
|
160
|
+
'x_underscored_item' => 'hello world',
|
161
|
+
'X_CAPITAL_UNDERSCORES' => 'yes'
|
162
|
+
}
|
163
|
+
|
164
|
+
options = Hoodoo::Client::Headers.x_header_to_options( hash )
|
165
|
+
|
166
|
+
expect( options[ 'interaction_id' ] ).to eq( '23' )
|
167
|
+
expect( options[ 'foo_bar' ] ).to eq( '42' )
|
168
|
+
expect( options[ 'underscored_item' ] ).to eq( 'hello world' )
|
169
|
+
expect( options[ 'capital_underscores' ] ).to eq( 'yes' )
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,339 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hoodoo::Communicators::Pool do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@pool = Hoodoo::Communicators::Pool.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'general operation' do
|
10
|
+
after :each do
|
11
|
+
@pool.wait()
|
12
|
+
end
|
13
|
+
|
14
|
+
class TestFastCommunicatorA < Hoodoo::Communicators::Fast
|
15
|
+
def communicate( obj )
|
16
|
+
expectable_hook_fast_a( obj )
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class TestFastCommunicatorB < Hoodoo::Communicators::Fast
|
21
|
+
def communicate( obj )
|
22
|
+
expectable_hook_fast_b( obj )
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class TestFastCommunicatorC < Hoodoo::Communicators::Fast
|
27
|
+
def communicate( obj )
|
28
|
+
raise 'I am broken (Fast C)'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class TestSlowCommunicatorA < Hoodoo::Communicators::Slow
|
33
|
+
def communicate( obj )
|
34
|
+
expectable_hook_slow_a( obj )
|
35
|
+
sleep 0.2 # Deliberate delay to make sure #wait() works;
|
36
|
+
# intermittent failures would imply it doesn't.
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class TestSlowCommunicatorB < Hoodoo::Communicators::Slow
|
41
|
+
def communicate( obj )
|
42
|
+
expectable_hook_slow_b( obj )
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class TestSlowCommunicatorC < Hoodoo::Communicators::Slow
|
47
|
+
def communicate( obj )
|
48
|
+
raise 'I am broken (Slow C)'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'lets me add and remove handlers' do
|
53
|
+
c1 = @pool.add( TestFastCommunicatorA.new )
|
54
|
+
c2 = @pool.add( TestFastCommunicatorB.new )
|
55
|
+
c3 = @pool.add( TestSlowCommunicatorA.new )
|
56
|
+
c4 = @pool.add( TestSlowCommunicatorB.new )
|
57
|
+
|
58
|
+
# Should be able to remove them all
|
59
|
+
|
60
|
+
@pool.remove( c1 )
|
61
|
+
@pool.remove( c2 )
|
62
|
+
@pool.remove( c3 )
|
63
|
+
@pool.remove( c4 )
|
64
|
+
|
65
|
+
expect( @pool.instance_variable_get( '@pool' ) ).to be_empty
|
66
|
+
|
67
|
+
# Should be harmess to remove the same thing twice, or remove an instance
|
68
|
+
# not in the pool.
|
69
|
+
|
70
|
+
@pool.remove( c1 )
|
71
|
+
@pool.remove( TestSlowCommunicatorB.new )
|
72
|
+
|
73
|
+
expect( @pool.instance_variable_get( '@pool' ) ).to be_empty
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'calls fast handler A' do
|
77
|
+
c1 = @pool.add( TestFastCommunicatorA.new )
|
78
|
+
ob = { :foo => :bar }
|
79
|
+
expect( c1 ).to receive( :expectable_hook_fast_a ).once.with( ob )
|
80
|
+
@pool.communicate( ob )
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'calls fast handler A and B' do
|
84
|
+
c1 = @pool.add( TestFastCommunicatorA.new )
|
85
|
+
c2 = @pool.add( TestFastCommunicatorB.new )
|
86
|
+
ob = { :foo => :bar }
|
87
|
+
expect( c1 ).to receive( :expectable_hook_fast_a ).once.with( ob )
|
88
|
+
expect( c2 ).to receive( :expectable_hook_fast_b ).once.with( ob )
|
89
|
+
@pool.communicate( ob )
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'calls slow handler A' do
|
93
|
+
c1 = @pool.add( TestSlowCommunicatorA.new )
|
94
|
+
ob = { :bar => :baz }
|
95
|
+
expect( c1 ).to receive( :expectable_hook_slow_a ).once.with( ob )
|
96
|
+
@pool.communicate( ob )
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'calls slow handler A and B' do
|
100
|
+
c1 = @pool.add( TestSlowCommunicatorA.new )
|
101
|
+
c2 = @pool.add( TestSlowCommunicatorB.new )
|
102
|
+
ob = { :bar => :baz }
|
103
|
+
expect( c1 ).to receive( :expectable_hook_slow_a ).once.with( ob )
|
104
|
+
expect( c2 ).to receive( :expectable_hook_slow_b ).once.with( ob )
|
105
|
+
@pool.communicate( ob )
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'calls all handlers' do
|
109
|
+
c1 = @pool.add( TestFastCommunicatorA.new )
|
110
|
+
c2 = @pool.add( TestFastCommunicatorB.new )
|
111
|
+
c3 = @pool.add( TestSlowCommunicatorA.new )
|
112
|
+
c4 = @pool.add( TestSlowCommunicatorB.new )
|
113
|
+
ob = { :baz => :foo }
|
114
|
+
expect( c1 ).to receive( :expectable_hook_fast_a ).once.with( ob )
|
115
|
+
expect( c2 ).to receive( :expectable_hook_fast_b ).once.with( ob )
|
116
|
+
expect( c3 ).to receive( :expectable_hook_slow_a ).once.with( ob )
|
117
|
+
expect( c4 ).to receive( :expectable_hook_slow_b ).once.with( ob )
|
118
|
+
@pool.communicate( ob )
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'complains about bad additions' do
|
122
|
+
expect {
|
123
|
+
@pool.add( Object )
|
124
|
+
}.to raise_exception( RuntimeError )
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'complains about bad removals' do
|
128
|
+
expect {
|
129
|
+
@pool.remove( Object )
|
130
|
+
}.to raise_exception( RuntimeError )
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'ignores exceptions in reporters' do
|
134
|
+
c1 = @pool.add( TestFastCommunicatorC.new ) # Add exception raisers first
|
135
|
+
c2 = @pool.add( TestSlowCommunicatorC.new )
|
136
|
+
c3 = @pool.add( TestFastCommunicatorA.new ) # Then these after, which should still be called
|
137
|
+
c4 = @pool.add( TestSlowCommunicatorA.new )
|
138
|
+
ob = { :foo => :bar }
|
139
|
+
expect( c3 ).to receive( :expectable_hook_fast_a ).once.with( ob )
|
140
|
+
expect( c4 ).to receive( :expectable_hook_slow_a ).once.with( ob )
|
141
|
+
@pool.communicate( ob )
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'ignores exceptions in exception handler' do
|
145
|
+
c1 = @pool.add( TestFastCommunicatorC.new ) # Add exception raisers first
|
146
|
+
c2 = @pool.add( TestSlowCommunicatorC.new )
|
147
|
+
c3 = @pool.add( TestFastCommunicatorA.new ) # Then these after, which should still be called
|
148
|
+
c4 = @pool.add( TestSlowCommunicatorA.new )
|
149
|
+
ob = { :foo => :bar }
|
150
|
+
|
151
|
+
# Expect a debug report on 'stderr'; force the first one to fail, let
|
152
|
+
# the next one through quietly. Everything should be called as expected
|
153
|
+
# even though 'stderr' failed.
|
154
|
+
|
155
|
+
expect( $stderr ).to receive( :puts ).once do
|
156
|
+
raise 'stderr failure'
|
157
|
+
end
|
158
|
+
expect( $stderr ).to receive( :puts ).once.and_call_original
|
159
|
+
expect( c3 ).to receive( :expectable_hook_fast_a ).once.with( ob )
|
160
|
+
expect( c4 ).to receive( :expectable_hook_slow_a ).once.with( ob )
|
161
|
+
@pool.communicate( ob )
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'message dropping' do
|
166
|
+
before :each do
|
167
|
+
|
168
|
+
# This sync queue is pushed from the running test and popped from the
|
169
|
+
# communicators inside their message handler, so that we can force the
|
170
|
+
# communicators to halt "mid-message" by not pushing until we're ready.
|
171
|
+
|
172
|
+
$sync_queue = Queue.new
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
class TestSlowCommunicatorDrops < Hoodoo::Communicators::Slow
|
177
|
+
def initialize
|
178
|
+
@count = 0
|
179
|
+
end
|
180
|
+
|
181
|
+
attr_reader :count
|
182
|
+
|
183
|
+
def communicate( obj )
|
184
|
+
$sync_queue.pop if @count == 0
|
185
|
+
@count += 1
|
186
|
+
end
|
187
|
+
|
188
|
+
def dropped( number )
|
189
|
+
@count += number
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'reports dropped messages' do
|
194
|
+
c = @pool.add( TestSlowCommunicatorDrops.new )
|
195
|
+
o = { :foo => :bar }
|
196
|
+
|
197
|
+
# Send a message. The only way we have to sync up in this test here
|
198
|
+
# is to busy poll our sync queue until we see the communicator thread
|
199
|
+
# waiting on it. That means it pulled the message of its work queue and
|
200
|
+
# is now blocked in the message handler until we push to the sync queue.
|
201
|
+
|
202
|
+
expect(c).to receive(:communicate).once.with( o ).and_call_original
|
203
|
+
@pool.communicate( o )
|
204
|
+
|
205
|
+
loop do
|
206
|
+
sleep 0.01
|
207
|
+
break if $sync_queue.num_waiting > 0
|
208
|
+
end
|
209
|
+
|
210
|
+
# Now send a full queue of messages. That'll send "limit", none of which
|
211
|
+
# will be processed yet, then queue "additional".
|
212
|
+
|
213
|
+
limit = Hoodoo::Communicators::Pool::MAX_SLOW_QUEUE_SIZE
|
214
|
+
additional = 10
|
215
|
+
|
216
|
+
1.upto( limit + additional ) do | i |
|
217
|
+
@pool.communicate( o )
|
218
|
+
sleep 1 if i == 1
|
219
|
+
end
|
220
|
+
|
221
|
+
# The communicator's still waiting on our sync queue. We expect it to get
|
222
|
+
# the "limit" queued calls; let it start processing by pushing something
|
223
|
+
# to the sync queue.
|
224
|
+
|
225
|
+
expect(c).to receive(:communicate).exactly( limit ).times.with( o ).and_call_original
|
226
|
+
$sync_queue << :go!
|
227
|
+
|
228
|
+
# Wait for the communicator to finish processing. Implicit test -
|
229
|
+
# variant of "wait" that takes a specific communicator.
|
230
|
+
|
231
|
+
@pool.wait( communicator: c )
|
232
|
+
|
233
|
+
# Now send one final message. This will prompt the 'dropped' call first,
|
234
|
+
# saying "<x> were dropped between the last communication and this one",
|
235
|
+
# then send in the recent message.
|
236
|
+
|
237
|
+
expect(c).to receive(:dropped).once.with( additional ).and_call_original
|
238
|
+
expect(c).to receive(:communicate).once.with( o ).and_call_original
|
239
|
+
|
240
|
+
@pool.communicate( o )
|
241
|
+
@pool.wait( communicator: c )
|
242
|
+
|
243
|
+
# The 'count' variable in the communicator records the number of
|
244
|
+
# messages it received, or that were dropped, adding them all up.
|
245
|
+
# There was the first 'sync up' message, 'limit' messages in the
|
246
|
+
# queue, 'additional' counted via the "dropped" call and one more
|
247
|
+
# message that provoked the "dropped" call.
|
248
|
+
|
249
|
+
expect( c.count ).to eq( 1 + limit + additional + 1 )
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
context 'waiting, termination and timeouts' do
|
254
|
+
|
255
|
+
before :each do
|
256
|
+
|
257
|
+
# This sync queue gets pushed from the communicators before they sleep,
|
258
|
+
# and popped by the running test so it knows the communicator threads
|
259
|
+
# received a message and are about to sleep for a while within their
|
260
|
+
# message handler.
|
261
|
+
#
|
262
|
+
$sync_queue = Queue.new
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
class TestSlowCommunicatorSleeps < Hoodoo::Communicators::Slow
|
267
|
+
def communicate( obj )
|
268
|
+
$sync_queue << :sync
|
269
|
+
sleep( obj )
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'times out waiting' do
|
274
|
+
c = @pool.add( TestSlowCommunicatorSleeps.new )
|
275
|
+
c = @pool.add( TestSlowCommunicatorSleeps.new )
|
276
|
+
c = @pool.add( TestSlowCommunicatorSleeps.new )
|
277
|
+
|
278
|
+
expect( @pool.group.list.size ).to eq( 3 )
|
279
|
+
|
280
|
+
# Sleep each for 1 second, wait with a timeout of 0.02 seconds, expect
|
281
|
+
# the test to take less than 0.5 seconds overall. Non-timeout waiting
|
282
|
+
# is already tested elsewhere.
|
283
|
+
|
284
|
+
now = Time.now
|
285
|
+
|
286
|
+
@pool.communicate( 1 )
|
287
|
+
1.upto( 3 ) { $sync_queue.pop() } # Make sure all threads got the message
|
288
|
+
|
289
|
+
@pool.wait( per_instance_timeout: 0.02 )
|
290
|
+
|
291
|
+
expect( Time.now - now ).to be < 0.5
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'times out termination' do
|
295
|
+
c = @pool.add( TestSlowCommunicatorSleeps.new )
|
296
|
+
c = @pool.add( TestSlowCommunicatorSleeps.new )
|
297
|
+
c = @pool.add( TestSlowCommunicatorSleeps.new )
|
298
|
+
|
299
|
+
expect( @pool.group.list.size ).to eq( 3 )
|
300
|
+
|
301
|
+
# Sleep each for 5 seconds, terminate with a 0.02 second timeout.
|
302
|
+
# Expect to exit the termination early, with the pool still intact
|
303
|
+
# (since all threads should've timed out).
|
304
|
+
#
|
305
|
+
# The test comms threads will hang around in the runtime for a while
|
306
|
+
# then expire naturally or, if the test suite exits first, get killed
|
307
|
+
# off along with the running Ruby process.
|
308
|
+
|
309
|
+
now = Time.now
|
310
|
+
|
311
|
+
@pool.communicate( 5 )
|
312
|
+
1.upto( 3 ) { $sync_queue.pop() } # Make sure all threads got the message
|
313
|
+
@pool.terminate( per_instance_timeout: 0.02 ) # Now we know they're all asleep, try a timed-out termination.
|
314
|
+
|
315
|
+
expect( Time.now - now ).to be < 1
|
316
|
+
expect( @pool.group.list.size ).to eq( 3 ) # All threads timed out
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'terminates properly without timeout' do
|
320
|
+
c = @pool.add( TestSlowCommunicatorSleeps.new )
|
321
|
+
c = @pool.add( TestSlowCommunicatorSleeps.new )
|
322
|
+
c = @pool.add( TestSlowCommunicatorSleeps.new )
|
323
|
+
|
324
|
+
expect( @pool.group.list.size ).to eq( 3 )
|
325
|
+
|
326
|
+
# Sleep each communicator for just 0.02 seconds and terminate without
|
327
|
+
# timeouts. Expect it to all succeed, with nothing left in the pool.
|
328
|
+
|
329
|
+
now = Time.now
|
330
|
+
|
331
|
+
@pool.communicate( 0.02 )
|
332
|
+
1.upto( 3 ) { $sync_queue.pop() } # Make sure all threads got the message
|
333
|
+
@pool.terminate()
|
334
|
+
|
335
|
+
expect( Time.now - now ).to be < 1
|
336
|
+
expect( @pool.group.list.size ).to eq( 0 ) # All threads exited
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|