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.
Files changed (216) hide show
  1. checksums.yaml +7 -0
  2. data/bin/hoodoo +5 -0
  3. data/lib/hoodoo.rb +27 -0
  4. data/lib/hoodoo/active.rb +32 -0
  5. data/lib/hoodoo/active/active_model/uuid_validator.rb +45 -0
  6. data/lib/hoodoo/active/active_record/base.rb +81 -0
  7. data/lib/hoodoo/active/active_record/creator.rb +134 -0
  8. data/lib/hoodoo/active/active_record/dated.rb +343 -0
  9. data/lib/hoodoo/active/active_record/error_mapping.rb +351 -0
  10. data/lib/hoodoo/active/active_record/finder.rb +606 -0
  11. data/lib/hoodoo/active/active_record/search_helper.rb +189 -0
  12. data/lib/hoodoo/active/active_record/secure.rb +431 -0
  13. data/lib/hoodoo/active/active_record/support.rb +106 -0
  14. data/lib/hoodoo/active/active_record/translated.rb +87 -0
  15. data/lib/hoodoo/active/active_record/uuid.rb +80 -0
  16. data/lib/hoodoo/active/active_record/writer.rb +321 -0
  17. data/lib/hoodoo/client.rb +23 -0
  18. data/lib/hoodoo/client/augmented_array.rb +29 -0
  19. data/lib/hoodoo/client/augmented_base.rb +168 -0
  20. data/lib/hoodoo/client/augmented_hash.rb +23 -0
  21. data/lib/hoodoo/client/client.rb +354 -0
  22. data/lib/hoodoo/client/endpoint/endpoint.rb +427 -0
  23. data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +180 -0
  24. data/lib/hoodoo/client/endpoint/endpoints/auto_session.rb +194 -0
  25. data/lib/hoodoo/client/endpoint/endpoints/http.rb +203 -0
  26. data/lib/hoodoo/client/endpoint/endpoints/http_based.rb +367 -0
  27. data/lib/hoodoo/client/endpoint/endpoints/not_found.rb +59 -0
  28. data/lib/hoodoo/client/headers.rb +269 -0
  29. data/lib/hoodoo/communicators.rb +23 -0
  30. data/lib/hoodoo/communicators/fast.rb +44 -0
  31. data/lib/hoodoo/communicators/pool.rb +601 -0
  32. data/lib/hoodoo/communicators/slow.rb +84 -0
  33. data/lib/hoodoo/data.rb +51 -0
  34. data/lib/hoodoo/data/resources/caller.rb +39 -0
  35. data/lib/hoodoo/data/resources/errors.rb +28 -0
  36. data/lib/hoodoo/data/resources/log.rb +31 -0
  37. data/lib/hoodoo/data/resources/session.rb +26 -0
  38. data/lib/hoodoo/data/types/error_primitive.rb +27 -0
  39. data/lib/hoodoo/data/types/permissions.rb +40 -0
  40. data/lib/hoodoo/data/types/permissions_defaults.rb +32 -0
  41. data/lib/hoodoo/data/types/permissions_full.rb +28 -0
  42. data/lib/hoodoo/data/types/permissions_resources.rb +31 -0
  43. data/lib/hoodoo/discovery.rb +20 -0
  44. data/lib/hoodoo/errors.rb +19 -0
  45. data/lib/hoodoo/errors/error_descriptions.rb +229 -0
  46. data/lib/hoodoo/errors/errors.rb +322 -0
  47. data/lib/hoodoo/generator.rb +139 -0
  48. data/lib/hoodoo/logger.rb +23 -0
  49. data/lib/hoodoo/logger/fast_writer.rb +27 -0
  50. data/lib/hoodoo/logger/flattener_mixin.rb +36 -0
  51. data/lib/hoodoo/logger/logger.rb +387 -0
  52. data/lib/hoodoo/logger/slow_writer.rb +49 -0
  53. data/lib/hoodoo/logger/writer_mixin.rb +52 -0
  54. data/lib/hoodoo/logger/writers/file_writer.rb +45 -0
  55. data/lib/hoodoo/logger/writers/log_entries_dot_com_writer.rb +64 -0
  56. data/lib/hoodoo/logger/writers/stream_writer.rb +43 -0
  57. data/lib/hoodoo/middleware.rb +33 -0
  58. data/lib/hoodoo/presenters.rb +45 -0
  59. data/lib/hoodoo/presenters/base.rb +281 -0
  60. data/lib/hoodoo/presenters/base_dsl.rb +519 -0
  61. data/lib/hoodoo/presenters/common_resource_fields.rb +31 -0
  62. data/lib/hoodoo/presenters/embedding.rb +232 -0
  63. data/lib/hoodoo/presenters/types/array.rb +118 -0
  64. data/lib/hoodoo/presenters/types/boolean.rb +26 -0
  65. data/lib/hoodoo/presenters/types/date.rb +26 -0
  66. data/lib/hoodoo/presenters/types/date_time.rb +26 -0
  67. data/lib/hoodoo/presenters/types/decimal.rb +47 -0
  68. data/lib/hoodoo/presenters/types/enum.rb +55 -0
  69. data/lib/hoodoo/presenters/types/field.rb +158 -0
  70. data/lib/hoodoo/presenters/types/float.rb +26 -0
  71. data/lib/hoodoo/presenters/types/hash.rb +361 -0
  72. data/lib/hoodoo/presenters/types/integer.rb +26 -0
  73. data/lib/hoodoo/presenters/types/object.rb +117 -0
  74. data/lib/hoodoo/presenters/types/string.rb +53 -0
  75. data/lib/hoodoo/presenters/types/tags.rb +24 -0
  76. data/lib/hoodoo/presenters/types/text.rb +26 -0
  77. data/lib/hoodoo/presenters/types/uuid.rb +54 -0
  78. data/lib/hoodoo/services.rb +34 -0
  79. data/lib/hoodoo/services/discovery/discoverers/by_consul.rb +66 -0
  80. data/lib/hoodoo/services/discovery/discoverers/by_convention.rb +173 -0
  81. data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +195 -0
  82. data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server.rb +166 -0
  83. data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server_start.rb +37 -0
  84. data/lib/hoodoo/services/discovery/discovery.rb +186 -0
  85. data/lib/hoodoo/services/discovery/results/for_amqp.rb +58 -0
  86. data/lib/hoodoo/services/discovery/results/for_http.rb +85 -0
  87. data/lib/hoodoo/services/discovery/results/for_local.rb +85 -0
  88. data/lib/hoodoo/services/discovery/results/for_remote.rb +57 -0
  89. data/lib/hoodoo/services/middleware/amqp_log_message.rb +186 -0
  90. data/lib/hoodoo/services/middleware/amqp_log_writer.rb +119 -0
  91. data/lib/hoodoo/services/middleware/endpoints/inter_resource_local.rb +130 -0
  92. data/lib/hoodoo/services/middleware/endpoints/inter_resource_remote.rb +202 -0
  93. data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +105 -0
  94. data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +115 -0
  95. data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +64 -0
  96. data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +63 -0
  97. data/lib/hoodoo/services/middleware/interaction.rb +127 -0
  98. data/lib/hoodoo/services/middleware/middleware.rb +2705 -0
  99. data/lib/hoodoo/services/middleware/rack_monkey_patch.rb +73 -0
  100. data/lib/hoodoo/services/services/context.rb +153 -0
  101. data/lib/hoodoo/services/services/implementation.rb +132 -0
  102. data/lib/hoodoo/services/services/interface.rb +934 -0
  103. data/lib/hoodoo/services/services/permissions.rb +250 -0
  104. data/lib/hoodoo/services/services/request.rb +189 -0
  105. data/lib/hoodoo/services/services/response.rb +316 -0
  106. data/lib/hoodoo/services/services/service.rb +141 -0
  107. data/lib/hoodoo/services/services/session.rb +729 -0
  108. data/lib/hoodoo/utilities.rb +12 -0
  109. data/lib/hoodoo/utilities/string_inquirer.rb +54 -0
  110. data/lib/hoodoo/utilities/utilities.rb +380 -0
  111. data/lib/hoodoo/utilities/uuid.rb +44 -0
  112. data/lib/hoodoo/version.rb +17 -0
  113. data/spec/active/active_record/base_spec.rb +57 -0
  114. data/spec/active/active_record/creator_spec.rb +88 -0
  115. data/spec/active/active_record/dated_spec.rb +248 -0
  116. data/spec/active/active_record/error_mapping_spec.rb +360 -0
  117. data/spec/active/active_record/finder_spec.rb +744 -0
  118. data/spec/active/active_record/search_helper_spec.rb +384 -0
  119. data/spec/active/active_record/secure_spec.rb +435 -0
  120. data/spec/active/active_record/support_spec.rb +225 -0
  121. data/spec/active/active_record/translated_spec.rb +19 -0
  122. data/spec/active/active_record/uuid_spec.rb +72 -0
  123. data/spec/active/active_record/writer_spec.rb +272 -0
  124. data/spec/alchemy/alchemy-amq.rb +33 -0
  125. data/spec/client/augmented_array_spec.rb +15 -0
  126. data/spec/client/augmented_base_spec.rb +50 -0
  127. data/spec/client/augmented_hash_spec.rb +15 -0
  128. data/spec/client/client_spec.rb +955 -0
  129. data/spec/client/endpoint/endpoint_spec.rb +70 -0
  130. data/spec/client/endpoint/endpoints/amqp_spec.rb +16 -0
  131. data/spec/client/endpoint/endpoints/auto_session_spec.rb +9 -0
  132. data/spec/client/endpoint/endpoints/http_based_spec.rb +9 -0
  133. data/spec/client/endpoint/endpoints/http_spec.rb +103 -0
  134. data/spec/client/endpoint/endpoints/not_found_spec.rb +35 -0
  135. data/spec/client/headers_spec.rb +172 -0
  136. data/spec/communicators/fast_spec.rb +9 -0
  137. data/spec/communicators/pool_spec.rb +339 -0
  138. data/spec/communicators/slow_spec.rb +15 -0
  139. data/spec/data/resources/caller_spec.rb +156 -0
  140. data/spec/data/resources/errors_spec.rb +22 -0
  141. data/spec/data/resources/log_spec.rb +20 -0
  142. data/spec/data/resources/session_spec.rb +15 -0
  143. data/spec/data/types/error_primitive_spec.rb +15 -0
  144. data/spec/data/types/permissions_defaults_spec.rb +25 -0
  145. data/spec/data/types/permissions_full_spec.rb +44 -0
  146. data/spec/data/types/permissions_resources_spec.rb +34 -0
  147. data/spec/data/types/permissions_spec.rb +37 -0
  148. data/spec/errors/error_descriptions_spec.rb +98 -0
  149. data/spec/errors/errors_spec.rb +346 -0
  150. data/spec/integration/service_actions_spec.rb +112 -0
  151. data/spec/logger/fast_writer_spec.rb +18 -0
  152. data/spec/logger/logger_spec.rb +259 -0
  153. data/spec/logger/slow_writer_spec.rb +144 -0
  154. data/spec/logger/writers/file_writer_spec.rb +37 -0
  155. data/spec/logger/writers/log_entries_dot_com_writer_spec.rb +29 -0
  156. data/spec/logger/writers/stream_writer_spec.rb +38 -0
  157. data/spec/presenters/base_dsl_spec.rb +111 -0
  158. data/spec/presenters/base_spec.rb +871 -0
  159. data/spec/presenters/common_resource_fields_spec.rb +30 -0
  160. data/spec/presenters/embedding_spec.rb +87 -0
  161. data/spec/presenters/types/array_spec.rb +249 -0
  162. data/spec/presenters/types/boolean_spec.rb +51 -0
  163. data/spec/presenters/types/date_spec.rb +57 -0
  164. data/spec/presenters/types/date_time_spec.rb +59 -0
  165. data/spec/presenters/types/decimal_spec.rb +58 -0
  166. data/spec/presenters/types/enum_spec.rb +71 -0
  167. data/spec/presenters/types/field_spec.rb +77 -0
  168. data/spec/presenters/types/float_spec.rb +50 -0
  169. data/spec/presenters/types/hash_spec.rb +1069 -0
  170. data/spec/presenters/types/integer_spec.rb +50 -0
  171. data/spec/presenters/types/object_spec.rb +177 -0
  172. data/spec/presenters/types/string_spec.rb +65 -0
  173. data/spec/presenters/types/tags_spec.rb +56 -0
  174. data/spec/presenters/types/text_spec.rb +50 -0
  175. data/spec/presenters/types/uuid_spec.rb +46 -0
  176. data/spec/presenters/walk_spec.rb +198 -0
  177. data/spec/services/discovery/discoverers/by_consul_spec.rb +29 -0
  178. data/spec/services/discovery/discoverers/by_convention_spec.rb +67 -0
  179. data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +80 -0
  180. data/spec/services/discovery/discoverers/by_drb/drb_server_spec.rb +205 -0
  181. data/spec/services/discovery/discovery_spec.rb +73 -0
  182. data/spec/services/discovery/results/for_amqp_spec.rb +17 -0
  183. data/spec/services/discovery/results/for_http_spec.rb +37 -0
  184. data/spec/services/discovery/results/for_local_spec.rb +21 -0
  185. data/spec/services/discovery/results/for_remote_spec.rb +15 -0
  186. data/spec/services/middleware/amqp_log_message_spec.rb +60 -0
  187. data/spec/services/middleware/amqp_log_writer_spec.rb +95 -0
  188. data/spec/services/middleware/endpoints/inter_resource_local_spec.rb +9 -0
  189. data/spec/services/middleware/endpoints/inter_resource_remote_spec.rb +9 -0
  190. data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +16 -0
  191. data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +92 -0
  192. data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +24 -0
  193. data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +23 -0
  194. data/spec/services/middleware/middleware_cors_spec.rb +93 -0
  195. data/spec/services/middleware/middleware_create_update_spec.rb +489 -0
  196. data/spec/services/middleware/middleware_dated_at_spec.rb +186 -0
  197. data/spec/services/middleware/middleware_exotic_communication_spec.rb +560 -0
  198. data/spec/services/middleware/middleware_logging_spec.rb +356 -0
  199. data/spec/services/middleware/middleware_multi_local_spec.rb +1094 -0
  200. data/spec/services/middleware/middleware_multi_remote_spec.rb +1440 -0
  201. data/spec/services/middleware/middleware_permissions_spec.rb +1014 -0
  202. data/spec/services/middleware/middleware_public_spec.rb +238 -0
  203. data/spec/services/middleware/middleware_spec.rb +1569 -0
  204. data/spec/services/middleware/string_inquirer_spec.rb +30 -0
  205. data/spec/services/services/application_spec.rb +74 -0
  206. data/spec/services/services/context_spec.rb +48 -0
  207. data/spec/services/services/implementation_spec.rb +45 -0
  208. data/spec/services/services/interface_spec.rb +262 -0
  209. data/spec/services/services/permissions_spec.rb +249 -0
  210. data/spec/services/services/request_spec.rb +95 -0
  211. data/spec/services/services/response_spec.rb +250 -0
  212. data/spec/services/services/session_spec.rb +432 -0
  213. data/spec/spec_helper.rb +298 -0
  214. data/spec/utilities/utilities_spec.rb +537 -0
  215. data/spec/utilities/uuid_spec.rb +20 -0
  216. 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,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Client::Endpoint::AutoSession do
4
+
5
+ # No tests here yet. Full coverage is achieved by other tests running
6
+ # through the HTTP and AMQP endpoints (amongst other stuff), since
7
+ # those inherit from this code.
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Client::Endpoint::HTTPBased do
4
+
5
+ # No tests here yet. Full coverage is achieved by other tests running
6
+ # through the HTTP and AMQP endpoints (amongst other stuff), since
7
+ # those inherit from this code.
8
+
9
+ 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,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Communicators::Fast do
4
+ it 'complains if called directly' do
5
+ expect {
6
+ Hoodoo::Communicators::Fast.new.communicate( {} )
7
+ }.to raise_error( RuntimeError )
8
+ end
9
+ 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