hoodoo 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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,186 @@
1
+ require 'spec_helper'
2
+
3
+ # Most coverage for 'dated-at' is done in passing through all the various
4
+ # integration, inter-resource local and inter-resource remote tests in other
5
+ # files. This particular test file was originally created to check the
6
+ # following scenario:
7
+ #
8
+ # * Top-level incoming call hits #create in resource A
9
+ # * The payload indicates some sort of backdating is in use
10
+ # * Resource A calls resource B with a dated-at specifier
11
+ # * Resource B calls resource C without an explicit specifier
12
+ # * Resource C should inherit the dated-at context from B.
13
+ #
14
+ # Since the corresponding "dated-from" feature is *not* automaticalled passed
15
+ # through, there's no additional coverage for that here.
16
+
17
+ ###############################################################################
18
+
19
+ # A#create calls B#show calls C#show.
20
+
21
+ class RSpecDatedAtAImplementation < Hoodoo::Services::Implementation
22
+ def create( context )
23
+ created_at = Time.now
24
+ backdated_to = context.request.body[ 'backdated_to' ]
25
+ dated_at = Hoodoo::Utilities.rationalise_datetime( backdated_to || created_at )
26
+
27
+ if context.request.body[ 'use_resource_options' ]
28
+ endpoint = context.resource( :RSpecDatedAtB, 1, { :dated_at => dated_at } )
29
+ else
30
+ endpoint = context.resource( :RSpecDatedAtB )
31
+ endpoint.dated_at = dated_at
32
+ end
33
+
34
+ result = endpoint.show( context.request.body[ 'thing_to_show_in_b' ] )
35
+ context.response.set_resource( result ) unless result.platform_errors.has_errors?
36
+ end
37
+ end
38
+
39
+ class RSpecDatedAtBImplementation < Hoodoo::Services::Implementation
40
+ def show( context )
41
+ endpoint = context.resource( :RSpecDatedAtC )
42
+ result = endpoint.show( context.request.ident + '_to_c' )
43
+ context.response.set_resource( result ) unless result.platform_errors.has_errors?
44
+ end
45
+ end
46
+
47
+ class RSpecDatedAtCImplementation < Hoodoo::Services::Implementation
48
+ def show( context )
49
+ context.response.set_resource( {
50
+ 'id' => context.request.ident,
51
+ 'dated_at' => context.request.dated_at.to_s
52
+ } )
53
+ end
54
+ end
55
+
56
+ class RSpecDatedAtAInterface < Hoodoo::Services::Interface
57
+ interface :RSpecDatedAtA do
58
+ endpoint :rspec_dated_at_a, RSpecDatedAtAImplementation
59
+ end
60
+ end
61
+
62
+ class RSpecDatedAtBInterface < Hoodoo::Services::Interface
63
+ interface :RSpecDatedAtB do
64
+ endpoint :rspec_dated_at_b, RSpecDatedAtBImplementation
65
+ end
66
+ end
67
+
68
+ class RSpecDatedAtCInterface < Hoodoo::Services::Interface
69
+ interface :RSpecDatedAtC do
70
+ endpoint :rspec_dated_at_c, RSpecDatedAtCImplementation
71
+ end
72
+ end
73
+
74
+ # Three services to stand up individually for a remote inter-resource
75
+ # call check, one service to stand up on its own for a local call check.
76
+
77
+ class RSpecDatedAtAService < Hoodoo::Services::Service
78
+ comprised_of RSpecDatedAtAInterface
79
+ end
80
+
81
+ class RSpecDatedAtBService < Hoodoo::Services::Service
82
+ comprised_of RSpecDatedAtBInterface
83
+ end
84
+
85
+ class RSpecDatedAtCService < Hoodoo::Services::Service
86
+ comprised_of RSpecDatedAtCInterface
87
+ end
88
+
89
+ class RSpecDatedAtAllService < Hoodoo::Services::Service
90
+ comprised_of RSpecDatedAtAInterface,
91
+ RSpecDatedAtBInterface,
92
+ RSpecDatedAtCInterface
93
+ end
94
+
95
+ ###############################################################################
96
+
97
+ describe Hoodoo::Services::Middleware do
98
+ context 'deep local calls' do
99
+ after :all do
100
+ Hoodoo::Services::Middleware.flush_services_for_test()
101
+ end
102
+
103
+ def app
104
+ Rack::Builder.new do
105
+ use Hoodoo::Services::Middleware
106
+ run RSpecDatedAtAllService.new
107
+ end
108
+ end
109
+
110
+ def run_test_with_resource_options_boolean_of( value )
111
+ now = DateTime.now
112
+
113
+ data = {
114
+ :backdated_to => Hoodoo::Utilities.nanosecond_iso8601( now ),
115
+ :use_resource_options => value,
116
+ :thing_to_show_in_b => 'hello'
117
+ }
118
+
119
+ post '/v1/rspec_dated_at_a',
120
+ data.to_json,
121
+ { 'CONTENT_TYPE' => 'application/json; charset=utf-8' }
122
+
123
+ expect( last_response.status ).to eq( 200 )
124
+ result = JSON.parse( last_response.body )
125
+
126
+ expect( result ).to eq( {
127
+ 'id' => 'hello_to_c',
128
+ 'dated_at' => now.to_s
129
+ } )
130
+ end
131
+
132
+ it 'passes dates through properly via context options' do
133
+ run_test_with_resource_options_boolean_of( true )
134
+ end
135
+
136
+ it 'passes dates through properly via endpoint options' do
137
+ run_test_with_resource_options_boolean_of( false )
138
+ end
139
+ end
140
+
141
+ context 'deep remote calls' do
142
+ after :all do
143
+ Hoodoo::Services::Middleware.flush_services_for_test()
144
+ end
145
+
146
+ before :all do
147
+ @port = spec_helper_start_svc_app_in_thread_for( RSpecDatedAtAService )
148
+ spec_helper_start_svc_app_in_thread_for( RSpecDatedAtBService )
149
+ spec_helper_start_svc_app_in_thread_for( RSpecDatedAtCService )
150
+ end
151
+
152
+ def run_test_with_resource_options_boolean_of( value )
153
+ now = DateTime.now
154
+
155
+ data = {
156
+ :backdated_to => Hoodoo::Utilities.nanosecond_iso8601( now ),
157
+ :use_resource_options => value,
158
+ :thing_to_show_in_b => 'hello'
159
+ }
160
+
161
+ response = spec_helper_http(
162
+ klass: Net::HTTP::Post,
163
+ port: @port,
164
+ path: '/v1/rspec_dated_at_a',
165
+ body: data.to_json,
166
+ headers: { 'Content-Type' => 'application/json; charset=utf-8' }
167
+ )
168
+
169
+ expect( response.code ).to eq( '200' )
170
+ result = JSON.parse( response.body )
171
+
172
+ expect( result ).to eq( {
173
+ 'id' => 'hello_to_c',
174
+ 'dated_at' => now.to_s
175
+ } )
176
+ end
177
+
178
+ it 'passes dates through properly via context options' do
179
+ run_test_with_resource_options_boolean_of( true )
180
+ end
181
+
182
+ it 'passes dates through properly via endpoint options' do
183
+ run_test_with_resource_options_boolean_of( false )
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,560 @@
1
+ # Test coverage (mostly hypothetical, just to ensure no typing errors etc. via
2
+ # full code coverage) for esoteric/exotic communcation methods such as on-queue
3
+ # endpoints or HTTPS transport for local machine inter-resource calls.
4
+
5
+ require 'spec_helper'
6
+
7
+ describe Hoodoo::Services::Middleware do
8
+
9
+ class RSpecTestServiceExoticStubImplementation < Hoodoo::Services::Implementation
10
+ def list( context )
11
+ context.response.set_resources( [], 99 )
12
+ end
13
+ end
14
+
15
+ class RSpecTestServiceExoticStubInterface < Hoodoo::Services::Interface
16
+ interface :Version do
17
+ endpoint :version, RSpecTestServiceExoticStubImplementation
18
+ version 2
19
+ end
20
+ end
21
+
22
+ class RSpecTestServiceExoticStub < Hoodoo::Services::Service
23
+ comprised_of RSpecTestServiceExoticStubInterface
24
+ end
25
+
26
+ context 'on queue' do
27
+ before :each do
28
+ @old_queue = ENV[ 'AMQ_ENDPOINT' ]
29
+ ENV[ 'AMQ_ENDPOINT' ] = 'amqp://test:test@127.0.0.1'
30
+ @mw = Hoodoo::Services::Middleware.new( RSpecTestServiceExoticStub.new )
31
+
32
+ @cvar = false
33
+ if Hoodoo::Services::Middleware.class_variable_defined?( '@@alchemy' )
34
+ @cvar = true
35
+ @cvar_val = Hoodoo::Services::Middleware.class_variable_get( '@@alchemy' )
36
+ end
37
+
38
+ # Need to blow away the discoverer's local cache or the simulation will
39
+ # never attempt to run on queue.
40
+
41
+ discoverer = @mw.instance_variable_get( '@discoverer' )
42
+ discoverer.instance_variable_set( '@known_local_resources', {} ) # Hack for test!
43
+ end
44
+
45
+ after :each do
46
+ ENV[ 'AMQ_ENDPOINT' ] = @old_queue
47
+
48
+ if Hoodoo::Services::Middleware.class_variable_defined?( '@@alchemy' )
49
+ if @cvar == true
50
+ Hoodoo::Services::Middleware.class_variable_set( '@@alchemy', @cvar_val )
51
+ else
52
+ Hoodoo::Services::Middleware.remove_class_variable( '@@alchemy' )
53
+ end
54
+ end
55
+ end
56
+
57
+ it 'knows it is on-queue' do
58
+ expect( Hoodoo::Services::Middleware.on_queue? ).to eq( true )
59
+ end
60
+
61
+ # TODO: Weak test! Assumes static mappings. Will need modification
62
+ # for real Consul discoverer when implemented.
63
+ #
64
+ it 'returns known queue endpoint locations' do
65
+ location = @mw.instance_variable_get( '@discoverer' ).discover( :Version, 2 )
66
+ expect( location ).to be_a( Hoodoo::Services::Discovery::ForAMQP )
67
+ expect( location.queue_name ).to eq( 'service.version' )
68
+ expect( location.equivalent_path ).to eq( '/v2/versions' )
69
+ end
70
+
71
+ # TODO: Update for real Consul discoverer when implemented.
72
+ #
73
+ # it 'returns "nil" for unknown queue endpoint locations' do
74
+ # location = @mw.instance_variable_get( '@discoverer' ).discover( :NotAKnownResource, 2 )
75
+ # expect( location ).to be_nil
76
+ # end
77
+
78
+ context 'calling Alchemy' do
79
+ before :each do
80
+ mw = Hoodoo::Services::Middleware.new( RSpecTestServiceExoticStub.new )
81
+ @interaction = Hoodoo::Services::Middleware::Interaction.new(
82
+ {},
83
+ mw,
84
+ Hoodoo::Services::Middleware.test_session()
85
+ )
86
+ @interaction.target_interface = OpenStruct.new
87
+ @interaction.context.request.locale = 'fr'
88
+
89
+ @mock_alchemy = OpenStruct.new
90
+ Hoodoo::Services::Middleware.class_variable_set( '@@alchemy', @mock_alchemy )
91
+ end
92
+
93
+ def run_expectations( action, mock_queue, full_path, mock_method, mock_query, mock_remote, mock_response )
94
+ expect_any_instance_of( Hoodoo::Services::Discovery::ByConsul ).to receive(
95
+ :discover_remote
96
+ ).and_return(
97
+ mock_remote
98
+ )
99
+
100
+ if mock_query
101
+ mock_query = Hoodoo::Utilities.stringify( mock_query )
102
+ mock_query[ 'search' ] = URI.encode_www_form( mock_query[ 'search' ] ) if ( mock_query[ 'search' ].is_a?( ::Hash ) )
103
+ mock_query[ 'filter' ] = URI.encode_www_form( mock_query[ 'filter' ] ) if ( mock_query[ 'filter' ].is_a?( ::Hash ) )
104
+ end
105
+
106
+ expect( @mock_alchemy ).to receive( :http_request ).once do | queue, method, path, opts |
107
+ expect( queue ).to eq( mock_queue )
108
+ expect( method ).to eq( mock_method )
109
+ expect( path ).to eq( full_path )
110
+ expect( opts ).to eq(
111
+ {
112
+ :session_id => @interaction.context.session.session_id,
113
+ :host => 'localhost',
114
+ :port => 80,
115
+ :body => action == :create || action == :update ? '{}' : '',
116
+ :query => mock_query,
117
+ :headers => {
118
+ 'Content-Type' => 'application/json; charset=utf-8',
119
+ 'Content-Language' => 'fr',
120
+ 'Accept-Language' => 'fr',
121
+ 'X-Interaction-ID' => @interaction.interaction_id,
122
+ 'X-Session-ID' => @interaction.context.session.session_id
123
+ }
124
+ }
125
+ )
126
+ end.and_return( mock_response )
127
+ end
128
+
129
+ # All the other tests in this section run with @mw having no
130
+ # local services "as far as it is concerned" via the before-each
131
+ # code above. We want to make sure that service announcement when
132
+ # on-queue *does* announce locally (because at one point it did
133
+ # not, which was a bug) so this test provides that coverage.
134
+ #
135
+ it 'runs local discovery unless that is knocked out' do
136
+
137
+ # @mw has local discovery knocked out, so build a new
138
+ # one that doesn't. This will have the local discovery data
139
+ # available still.
140
+ #
141
+ @mw = Hoodoo::Services::Middleware.new( RSpecTestServiceExoticStub.new )
142
+
143
+ mock_queue = 'service.version'
144
+ mock_path = '/v2/version/'
145
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
146
+ resource: 'Version',
147
+ version: 2,
148
+ queue_name: mock_queue,
149
+ equivalent_path: mock_path
150
+ )
151
+
152
+ # We expect local discovery, so no discover_remote call.
153
+
154
+ expect_any_instance_of( Hoodoo::Services::Discovery::ByConsul ).to_not receive( :discover_remote )
155
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
156
+
157
+ # The endpoint should've been called locally; the implementation at
158
+ # the top of this file sets an empty array with dataset size 99.
159
+
160
+ mock_result = endpoint.list()
161
+ expect( mock_result ).to be_empty
162
+ expect( mock_result.dataset_size ).to eq( 99 )
163
+ end
164
+
165
+ it 'complains about a missing Alchemy instance' do
166
+ mock_queue = 'service.version'
167
+ mock_path = '/v2/version/'
168
+
169
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
170
+ resource: 'Version',
171
+ version: 2,
172
+ queue_name: mock_queue,
173
+ equivalent_path: mock_path
174
+ )
175
+
176
+ expect_any_instance_of( Hoodoo::Services::Discovery::ByConsul ).to receive(
177
+ :discover_remote
178
+ ).and_return(
179
+ mock_remote
180
+ )
181
+
182
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
183
+
184
+ # Fragile! Hack for test only.
185
+ endpoint.instance_variable_get( '@wrapped_endpoint' ).alchemy = nil
186
+
187
+ mock_response = AlchemyAMQ::HTTPResponse.new(
188
+ :status_code => 200,
189
+ :body => '{"_data":[]}'
190
+ )
191
+
192
+ expect {
193
+ mock_result = endpoint.list()
194
+ }.to raise_error( RuntimeError, 'Hoodoo::Client::Endpoint::AMQP cannot be used unless an Alchemy instance has been provided' )
195
+ end
196
+
197
+ it 'calls #list over Alchemy and handles 200' do
198
+ mock_queue = 'service.version'
199
+ mock_path = '/v2/version'
200
+ mock_method = 'GET'
201
+ mock_query = { :search => { :foo => :bar } }
202
+
203
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
204
+ resource: 'Version',
205
+ version: 2,
206
+ queue_name: mock_queue,
207
+ equivalent_path: mock_path
208
+ )
209
+
210
+ mock_response = AlchemyAMQ::HTTPResponse.new(
211
+ :status_code => 200,
212
+ :body => '{"_data":[]}'
213
+ )
214
+
215
+ run_expectations( :list, mock_queue, mock_path + '/', mock_method, mock_query, mock_remote, mock_response )
216
+
217
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
218
+ mock_result = endpoint.list( mock_query )
219
+
220
+ expect( mock_result ).to eq( Hoodoo::Client::AugmentedArray.new )
221
+ end
222
+
223
+ it 'calls #show over Alchemy and handles 200' do
224
+ mock_queue = 'service.version'
225
+ mock_path = '/v2/version'
226
+ mock_method = 'GET'
227
+ mock_query = { :search => { :foo => :bar } }
228
+
229
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
230
+ resource: 'Version',
231
+ version: 2,
232
+ queue_name: mock_queue,
233
+ equivalent_path: mock_path
234
+ )
235
+
236
+ mock_response = AlchemyAMQ::HTTPResponse.new(
237
+ :status_code => 200,
238
+ :body => '{}'
239
+ )
240
+
241
+ run_expectations( :show, mock_queue, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
242
+
243
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
244
+ mock_result = endpoint.show( 'ident', mock_query )
245
+
246
+ expect( mock_result ).to eq( Hoodoo::Client::AugmentedHash.new )
247
+ end
248
+
249
+ it 'calls #create over Alchemy and handles 200' do
250
+ mock_queue = 'service.version'
251
+ mock_path = '/v2/version'
252
+ mock_method = 'POST'
253
+ mock_query = { :search => { :foo => :bar } }
254
+
255
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
256
+ resource: 'Version',
257
+ version: 2,
258
+ queue_name: mock_queue,
259
+ equivalent_path: mock_path
260
+ )
261
+
262
+ mock_response = AlchemyAMQ::HTTPResponse.new(
263
+ :status_code => 200,
264
+ :body => '{}'
265
+ )
266
+
267
+ run_expectations( :create, mock_queue, mock_path + '/', mock_method, mock_query, mock_remote, mock_response )
268
+
269
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
270
+ mock_result = endpoint.create( {}, mock_query )
271
+
272
+ expect( mock_result ).to eq( Hoodoo::Client::AugmentedHash.new )
273
+ end
274
+
275
+ it 'calls #update over Alchemy and handles 200' do
276
+ mock_queue = 'service.version'
277
+ mock_path = '/v2/version'
278
+ mock_method = 'PATCH'
279
+ mock_query = { :search => { :foo => :bar } }
280
+
281
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
282
+ resource: 'Version',
283
+ version: 2,
284
+ queue_name: mock_queue,
285
+ equivalent_path: mock_path
286
+ )
287
+
288
+ mock_response = AlchemyAMQ::HTTPResponse.new(
289
+ :status_code => 200,
290
+ :body => '{}'
291
+ )
292
+
293
+ run_expectations( :update, mock_queue, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
294
+
295
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
296
+ mock_result = endpoint.update( 'ident', {}, mock_query )
297
+
298
+ expect( mock_result ).to eq( Hoodoo::Client::AugmentedHash.new )
299
+ end
300
+
301
+ it 'calls #delete over Alchemy and handles 200' do
302
+ mock_queue = 'service.version'
303
+ mock_path = '/v2/version'
304
+ mock_method = 'DELETE'
305
+ mock_query = { :search => { :foo => :bar } }
306
+
307
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
308
+ resource: 'Version',
309
+ version: 2,
310
+ queue_name: mock_queue,
311
+ equivalent_path: mock_path
312
+ )
313
+
314
+ mock_response = AlchemyAMQ::HTTPResponse.new(
315
+ :status_code => 200,
316
+ :body => '{}'
317
+ )
318
+
319
+ run_expectations( :delete, mock_queue, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
320
+
321
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
322
+ mock_result = endpoint.delete( 'ident', mock_query )
323
+
324
+ expect( mock_result ).to eq( Hoodoo::Client::AugmentedHash.new )
325
+ end
326
+
327
+ it 'calls #show over Alchemy and handles 408' do
328
+ mock_queue = 'service.version'
329
+ mock_path = '/v2/version'
330
+ mock_method = 'GET'
331
+ mock_query = { :search => { :foo => :bar } }
332
+
333
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
334
+ resource: 'Version',
335
+ version: 2,
336
+ queue_name: mock_queue,
337
+ equivalent_path: mock_path
338
+ )
339
+
340
+ mock_response = AlchemyAMQ::HTTPResponse.new(
341
+ :status_code => 408,
342
+ :body => '408 Timeout'
343
+ )
344
+
345
+ run_expectations( :show, mock_queue, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
346
+
347
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
348
+ mock_result = endpoint.show( 'ident', mock_query )
349
+
350
+ expect( mock_result ).to be_a( Hoodoo::Client::AugmentedHash )
351
+ expect( mock_result.platform_errors ).to_not be_nil
352
+
353
+ errors = mock_result.platform_errors.render( Hoodoo::UUID.generate )
354
+
355
+ expect( errors ).to have_key( 'errors' )
356
+ expect( errors[ 'errors' ] ).to be_a( Array )
357
+ expect( errors[ 'errors' ][ 0 ] ).to have_key( 'code' )
358
+ expect( errors[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.timeout' )
359
+ end
360
+
361
+ it 'calls #show over Alchemy and handles 404' do
362
+ mock_queue = 'service.version'
363
+ mock_path = '/v2/version'
364
+ mock_method = 'GET'
365
+ mock_query = { :search => { :foo => :bar } }
366
+
367
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
368
+ resource: 'Version',
369
+ version: 2,
370
+ queue_name: mock_queue,
371
+ equivalent_path: mock_path
372
+ )
373
+
374
+ mock_response = AlchemyAMQ::HTTPResponse.new(
375
+ :status_code => 404,
376
+ :body => '404 Not Found'
377
+ )
378
+
379
+ run_expectations( :show, mock_queue, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
380
+
381
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
382
+ mock_result = endpoint.show( 'ident', mock_query )
383
+
384
+ expect( mock_result ).to be_a( Hoodoo::Client::AugmentedHash )
385
+ expect( mock_result.platform_errors ).to_not be_nil
386
+
387
+ errors = mock_result.platform_errors.render( Hoodoo::UUID.generate )
388
+
389
+ expect( errors ).to have_key( 'errors' )
390
+ expect( errors[ 'errors' ] ).to be_a( Array )
391
+ expect( errors[ 'errors' ][ 0 ] ).to have_key( 'code' )
392
+ expect( errors[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.not_found' )
393
+ end
394
+
395
+ it 'calls #show over Alchemy and handles "unexpected" status codes' do
396
+ mock_queue = 'service.version'
397
+ mock_path = '/v2/version'
398
+ mock_method = 'GET'
399
+ mock_query = { :search => { :foo => :bar } }
400
+
401
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
402
+ resource: 'Version',
403
+ version: 2,
404
+ queue_name: mock_queue,
405
+ equivalent_path: mock_path
406
+ )
407
+
408
+ bad_body_data = '499 Invented'
409
+ mock_response = AlchemyAMQ::HTTPResponse.new(
410
+ :status_code => 499,
411
+ :body => bad_body_data
412
+ )
413
+
414
+ run_expectations( :show, mock_queue, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
415
+
416
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
417
+ mock_result = endpoint.show( 'ident', mock_query )
418
+
419
+ expect( mock_result ).to be_a( Hoodoo::Client::AugmentedHash )
420
+ expect( mock_result.platform_errors ).to_not be_nil
421
+
422
+ errors = mock_result.platform_errors.render( Hoodoo::UUID.generate )
423
+
424
+ expect( errors ).to have_key( 'errors' )
425
+ expect( errors[ 'errors' ] ).to be_a( Array )
426
+ expect( errors[ 'errors' ][ 0 ] ).to_not be_nil
427
+
428
+ expect( errors[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.fault' )
429
+ expect( errors[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Unexpected raw HTTP status code 499 with non-JSON response' )
430
+ expect( errors[ 'errors' ][ 0 ][ 'reference' ] ).to eq( "#{ bad_body_data }" )
431
+ end
432
+
433
+ it 'calls #show over Alchemy and handles 200 status but bad JSON' do
434
+ mock_queue = 'service.version'
435
+ mock_path = '/v2/version'
436
+ mock_method = 'GET'
437
+ mock_query = { :search => { :foo => :bar } }
438
+
439
+ mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
440
+ resource: 'Version',
441
+ version: 2,
442
+ queue_name: mock_queue,
443
+ equivalent_path: mock_path
444
+ )
445
+
446
+ bad_body_data = 'Not JSON'
447
+ mock_response = AlchemyAMQ::HTTPResponse.new(
448
+ :status_code => 200,
449
+ :body => bad_body_data
450
+ )
451
+
452
+ run_expectations( :show, mock_queue, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
453
+
454
+ endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
455
+ mock_result = endpoint.show( 'ident', mock_query )
456
+
457
+ expect( mock_result ).to be_a( Hoodoo::Client::AugmentedHash )
458
+ expect( mock_result.platform_errors ).to_not be_nil
459
+
460
+ errors = mock_result.platform_errors.render( Hoodoo::UUID.generate )
461
+
462
+ expect( errors ).to have_key( 'errors' )
463
+ expect( errors[ 'errors' ] ).to be_a( Array )
464
+ expect( errors[ 'errors' ][ 0 ] ).to_not be_nil
465
+
466
+ expect( errors[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.fault' )
467
+ expect( errors[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Could not parse body data returned from inter-resource call despite receiving HTTP status code 200' )
468
+ expect( errors[ 'errors' ][ 0 ][ 'reference' ] ).to eq( "#{ bad_body_data }" )
469
+ end
470
+ end
471
+ end
472
+
473
+ context 'over HTTPS' do
474
+ before :all do
475
+ @port = spec_helper_start_svc_app_in_thread_for(
476
+ RSpecTestServiceExoticStub,
477
+ true # Use SSL
478
+ )
479
+ end
480
+
481
+ # In random order the test after this might work first, but the idea here
482
+ # is just to make a normal Net::HTTP request over SSL to the service using
483
+ # the appropriate spec_helper.rb support method, just to see if it's awake.
484
+ # If not, we don't expect the middleware's internal routines to manage it!
485
+ #
486
+ it 'demonstrates working HTTPS generally' do
487
+ response = spec_helper_http(
488
+ port: @port,
489
+ path: '/v2/version',
490
+ ssl: true
491
+ )
492
+
493
+ expect( response.code ).to eq( '200' )
494
+ end
495
+
496
+ # This is the *real* internal test, though done via calling down into the
497
+ # middleware's private implementation rather than worrying about standing
498
+ # up two services and making a 'real' inter-resource call across them.
499
+ # That's done elsewhere over HTTP. We just want to know if HTTPS functions
500
+ # at all from that same chunk of code here.
501
+ #
502
+ it 'attempts HTTPS communication' do
503
+
504
+ # Set up a middleware instance and mock interaction
505
+
506
+ mw = Hoodoo::Services::Middleware.new( RSpecTestServiceExoticStub.new )
507
+ interaction = Hoodoo::Services::Middleware::Interaction.new(
508
+ {},
509
+ mw,
510
+ Hoodoo::Services::Middleware.test_session()
511
+ )
512
+ interaction.target_interface = OpenStruct.new
513
+
514
+ # Synthesise an HTTP(S) discovery result for 'Version' / v2 and use
515
+ # it to build an HTTP(S) endpoint.
516
+
517
+ mock_wrapped_discovery_result = Hoodoo::Services::Discovery::ForHTTP.new(
518
+ resource: 'Version',
519
+ version: 2,
520
+ endpoint_uri: URI.parse( "https://127.0.0.1:#{ @port }/v2/version" ),
521
+ ca_file: 'spec/files/ca/ca-cert.pem'
522
+ )
523
+
524
+ mock_wrapped_endpoint = Hoodoo::Client::Endpoint::HTTP.new(
525
+ 'Version',
526
+ 2,
527
+ :session => Hoodoo::Services::Middleware.test_session(),
528
+ :discovery_result => mock_wrapped_discovery_result
529
+ )
530
+
531
+ # Synthesise a remote resource discovery result for the HTTP(S) endpoint
532
+ # built above and use that to make a remote call endpoint.
533
+
534
+ discovery_result = Hoodoo::Services::Discovery::ForRemote.new(
535
+ :resource => 'Version',
536
+ :version => 2,
537
+ :wrapped_endpoint => mock_wrapped_endpoint
538
+ )
539
+
540
+ endpoint = Hoodoo::Services::Middleware::InterResourceRemote.new(
541
+ 'Version',
542
+ 2,
543
+ {
544
+ :interaction => interaction,
545
+ :discovery_result => discovery_result
546
+ }
547
+ )
548
+
549
+ # Use the endpoint.
550
+
551
+ mock_result = endpoint.list()
552
+
553
+ # Expect an empty *array* back, with dataset size. A Hash implies an error.
554
+
555
+ expect( mock_result ).to eq( Hoodoo::Client::AugmentedArray.new )
556
+ expect( mock_result.dataset_size ).to eq(99)
557
+ expect( mock_result.platform_errors.has_errors? ).to eq( false )
558
+ end
559
+ end
560
+ end