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,33 @@
1
+ # Hoodoo is going open source before AlchemyAMQ, but the test suite has
2
+ # Alchemy dependencies. The Gemfile even used to require Alchemy in test
3
+ # mode directly from the private GitHub repository.
4
+ #
5
+ # That doesn't work for a public gem, so this file is a temporary mock
6
+ # of relevant Alchemy namespaces to get tests to pass until Alchemy AMQ
7
+ # is released.
8
+ #
9
+ # Without being able to see bits of Alchemy code that this minimally
10
+ # mocks, neither this file nor the tests that run through it will make
11
+ # much sense unfortunately.
12
+
13
+ require 'ostruct'
14
+ require 'msgpack'
15
+
16
+ module AlchemyAMQ
17
+ class HTTPResponse < OpenStruct
18
+ end
19
+
20
+ class Message < OpenStruct
21
+ def serialize
22
+ self.payload = MessagePack.pack(@content)
23
+ end
24
+
25
+ def deserialize
26
+ @content = MessagePack.unpack(self.payload, :symbolize_keys => true)
27
+ end
28
+
29
+ def send_message; end
30
+
31
+ def self.register_type( *args ); end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Client::AugmentedArray do
4
+
5
+ # However much the class arrangement might change in future, ultimately the
6
+ # class must inherit from Array and support a basic interface expected by
7
+ # any AugmentedBase "client".
8
+ #
9
+ it 'instantiates correctly' do
10
+ expect( described_class.new ).to be_a( Array )
11
+ expect( described_class.new.respond_to?( :adds_errors_to? ) ).to eq( true )
12
+ expect( described_class.new.respond_to?( :set_platform_errors ) ).to eq( true )
13
+ expect( described_class.new.respond_to?( :platform_errors ) ).to eq( true )
14
+ end
15
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Client::AugmentedBase do
4
+
5
+ class AugmentedBaseTest
6
+ include Hoodoo::Client::AugmentedBase
7
+ end
8
+
9
+ before :each do
10
+ @abt = AugmentedBaseTest.new
11
+ @fof = Hoodoo::Errors.new
12
+ @fof.add_error( 'platform.timeout' )
13
+ end
14
+
15
+ context 'without externally set collection' do
16
+ it 'generates its own collection' do
17
+ e = @abt.platform_errors
18
+ expect( e ).to be_a( Hoodoo::Errors )
19
+ expect( e.has_errors? ).to eq( false )
20
+ end
21
+
22
+ it 'merges properly' do
23
+ # There are no errors inside @abt to add to @fof, so should return 'false'
24
+ expect( @abt.adds_errors_to?( @fof ) ).to eq( false )
25
+ data = @fof.render( Hoodoo::UUID.generate() )
26
+ expect( data[ 'errors' ] ).to be_a( Array )
27
+ expect( data[ 'errors' ].size ).to eq( 1 )
28
+ expect( data[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.timeout' )
29
+ end
30
+ end
31
+
32
+ context 'with externally set collection' do
33
+
34
+ it 'sets and returns errors collections' do
35
+ @abt.set_platform_errors( @fof )
36
+ expect( @abt.platform_errors ).to eq( @fof )
37
+ expect( @abt.platform_errors.has_errors? ).to eq( true )
38
+ end
39
+
40
+ it 'merges properly' do
41
+ col = Hoodoo::Errors.new
42
+ @abt.set_platform_errors( @fof )
43
+ expect( @abt.adds_errors_to?( col ) ).to eq( true )
44
+ data = col.render( Hoodoo::UUID.generate() )
45
+ expect( data[ 'errors' ] ).to be_a( Array )
46
+ expect( data[ 'errors' ].size ).to eq( 1 )
47
+ expect( data[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.timeout' )
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hoodoo::Client::AugmentedHash do
4
+
5
+ # However much the class arrangement might change in future, ultimately the
6
+ # class must inherit from Hash and support a basic interface expected by
7
+ # any AugmentedBase "client".
8
+ #
9
+ it 'instantiates correctly' do
10
+ expect( described_class.new ).to be_a( Hash )
11
+ expect( described_class.new.respond_to?( :adds_errors_to? ) ).to eq( true )
12
+ expect( described_class.new.respond_to?( :set_platform_errors ) ).to eq( true )
13
+ expect( described_class.new.respond_to?( :platform_errors ) ).to eq( true )
14
+ end
15
+ end
@@ -0,0 +1,955 @@
1
+ # So this is heavy... We need:
2
+ #
3
+ # - A real resource endpoint that implements the Session API
4
+ # - Another endpoint with public actions to test without a session
5
+ # - Some of that enpoint which requires a session
6
+ # - The endpoint to be available by convention or DRb
7
+ #
8
+ # In passing we necessarily end up with quite a lot of test coverage for
9
+ # some of the endpoint and discovery code.
10
+
11
+ require 'securerandom'
12
+ require 'spec_helper.rb'
13
+
14
+ ##############################################################################
15
+ # Session resource
16
+ ##############################################################################
17
+
18
+ class RSpecClientTestSessionImplementation < Hoodoo::Services::Implementation
19
+ def create( context )
20
+
21
+ session_id = Hoodoo::UUID.generate()
22
+ test_session = Hoodoo::Services::Middleware::DEFAULT_TEST_SESSION.dup
23
+ test_session.session_id = session_id
24
+ Hoodoo::Services::Middleware.set_test_session( test_session )
25
+
26
+ # All the session reading code really cares about is 'id', but let's
27
+ # return something that looks very like a documented Session anyway.
28
+ #
29
+ context.response.set_resource(
30
+ 'id' => session_id,
31
+ 'created_at' => Time.now.utc.iso8601,
32
+ 'kind' => 'RSpecClientTestSession',
33
+ 'caller_id' => context.request.body[ 'caller_id' ],
34
+ 'expires_at' => ( Time.now.utc + 24*60*60 ).iso8601
35
+ )
36
+ end
37
+ end
38
+
39
+ class RSpecClientTestSessionInterface < Hoodoo::Services::Interface
40
+ interface :RSpecClientTestSession do
41
+ endpoint :r_spec_client_test_sessions, RSpecClientTestSessionImplementation
42
+ public_actions :create
43
+ end
44
+ end
45
+
46
+ ##############################################################################
47
+ # Target resource
48
+ ##############################################################################
49
+
50
+ # This resource 'echoes back' some of the query and body data in its
51
+ # responses so we can check to see if things seem to be passed through
52
+ # the layers properly. It's not exhaustive.
53
+ #
54
+ class RSpecClientTestTargetImplementation < Hoodoo::Services::Implementation
55
+
56
+ public
57
+
58
+ # This will be a public action
59
+
60
+ def show( context )
61
+
62
+ # Deliberate error generation hook.
63
+ #
64
+ if context.request.ident == 'return_error'
65
+ context.response.add_error( 'platform.malformed' )
66
+ return
67
+ end
68
+
69
+ context.response.set_resource( mock( context ) )
70
+ end
71
+
72
+ # The rest will be protected actions
73
+
74
+ def list( context )
75
+
76
+ # Deliberate error generation hook.
77
+ #
78
+ if context.request.list.offset == 42
79
+ context.response.add_error( 'platform.malformed' )
80
+ return
81
+ end
82
+
83
+ context.response.set_resources(
84
+ [ mock( context ), mock( context ), mock( context ) ],
85
+ 3
86
+ )
87
+ end
88
+
89
+ def create( context )
90
+
91
+ # Deliberate error generation hook.
92
+ #
93
+ if context.request.body.has_key?( 'return_error' )
94
+ context.response.add_error( 'platform.malformed' )
95
+ return
96
+ end
97
+
98
+ # If expecting deja-vu, cause deja-vu.
99
+ #
100
+ if context.request.deja_vu
101
+ context.response.add_error(
102
+ 'generic.invalid_duplication',
103
+ :reference => { :field_name => 'hello' }
104
+ )
105
+
106
+ else
107
+ context.response.set_resource( mock( context ) )
108
+
109
+ end
110
+ end
111
+
112
+ def update( context )
113
+
114
+ # Deliberate error generation hook.
115
+ #
116
+ if context.request.ident == 'return_error'
117
+ context.response.add_error( 'platform.malformed' )
118
+ return
119
+ end
120
+
121
+ context.response.set_resource( mock( context ) )
122
+ context.response.add_header( 'X-Example-Header', 'example' )
123
+ end
124
+
125
+ def delete( context )
126
+
127
+ # Deliberate error generation hook.
128
+ #
129
+ if context.request.ident == 'return_error'
130
+ context.response.add_error( 'platform.malformed' )
131
+ return
132
+ end
133
+
134
+ context.response.set_resource( mock( context ) )
135
+ end
136
+
137
+ private
138
+ def mock( context )
139
+
140
+ # Part of this could be automatic via HEADER_TO_PROPERTY but then
141
+ # any errors in that mapping which don't match our requirements in
142
+ # the test would be hidden, by the test using the same broken map.
143
+ #
144
+ # See also "def set_vars_for" and "def option_based_expectations"
145
+ # below. Be careful to follow the naming convention evident below
146
+ # if adding things.
147
+
148
+ {
149
+ 'id' => context.request.ident ||
150
+ context.request.body.try( :[], 'id' ) ||
151
+ Hoodoo::UUID.generate(),
152
+
153
+ 'created_at' => Time.now.utc.iso8601,
154
+ 'kind' => 'RSpecClientTestTarget',
155
+ 'language' => context.request.locale,
156
+
157
+ 'embeds' => context.request.embeds,
158
+ 'body_hash' => context.request.body,
159
+ 'dated_at' => context.request.dated_at.nil? ? nil : Hoodoo::Utilities.nanosecond_iso8601( context.request.dated_at ),
160
+ 'dated_from' => context.request.dated_from.nil? ? nil : Hoodoo::Utilities.nanosecond_iso8601( context.request.dated_from ),
161
+ 'resource_uuid' => context.request.resource_uuid,
162
+ 'deja_vu' => context.request.deja_vu,
163
+ }
164
+ end
165
+ end
166
+
167
+ class RSpecClientTestTargetInterface < Hoodoo::Services::Interface
168
+ interface :RSpecClientTestTarget do
169
+ endpoint :r_spec_client_test_targets, RSpecClientTestTargetImplementation
170
+ public_actions :show
171
+ actions :list, :create, :update, :delete
172
+ embeds :foo, :bar, :baz
173
+ end
174
+ end
175
+
176
+ # This resource is just here for the custom routing tests.
177
+ #
178
+ class RSpecClientTestTargetCustomRoutingImplementation < Hoodoo::Services::Implementation
179
+ def show( context )
180
+ context.response.set_resource( { 'id' => context.request.ident } )
181
+ end
182
+ end
183
+
184
+ class RSpecClientTestTargetCustomRoutingInterface < Hoodoo::Services::Interface
185
+ interface :RSpecClientTestTargetCustomRouting do
186
+ endpoint :r_spec_client_test_targets_with_custom_route, RSpecClientTestTargetCustomRoutingImplementation
187
+ public_actions :show
188
+ end
189
+ end
190
+
191
+ ##############################################################################
192
+ # One endpoint for both resources
193
+ #
194
+ # When we use by-convention discovery, a single base host is assumed. If we
195
+ # were to try and start multiple resource endpoints in their own HTTP threads
196
+ # on localhost, you'd have different port numbers and thus a different base
197
+ # URI, which by-convention discovery can't find (even though by-DRb can).
198
+ ##############################################################################
199
+
200
+ class RSpecClientTestService < Hoodoo::Services::Service
201
+ comprised_of RSpecClientTestSessionInterface,
202
+ RSpecClientTestTargetInterface,
203
+ RSpecClientTestTargetCustomRoutingInterface
204
+ end
205
+
206
+ ##############################################################################
207
+ # Tests
208
+ ##############################################################################
209
+
210
+ describe Hoodoo::Client do
211
+
212
+ before :all do
213
+ @old_test_session = Hoodoo::Services::Middleware.test_session()
214
+ @port = spec_helper_start_svc_app_in_thread_for( RSpecClientTestService )
215
+ @https_port = spec_helper_start_svc_app_in_thread_for( RSpecClientTestService, true )
216
+ end
217
+
218
+ after :all do
219
+ Hoodoo::Services::Middleware.set_test_session( @old_test_session )
220
+ end
221
+
222
+ # Set @locale, @expected_locale (latter for 'to eq(...)' matching),
223
+ # @dated_at/from, @expected_data_at/from (same deal), @client and callable
224
+ # @endpoint up, passing the given options to the Hoodoo::Client constructor,
225
+ # with randomised locale merged in. Conversion to lower case is checked
226
+ # implicitly, with a mixed case locale generated (potentially) by random
227
+ # but the 'downcase' version being used for 'expect's.
228
+ #
229
+ # Similar instance variables are used for UUID and déjà-vu specifiers. This
230
+ # method will probably get expanded over time with other properties derived
231
+ # from Hoodoo::Client::Headers' HEADER_TO_PROPERTY map too.
232
+ #
233
+ # Through the use of random switches, we (eventually!) get test coverage
234
+ # on locale specified (or not) given to the Client, plus override locale
235
+ # (or not) and a dated-at date/time (or not) given to the Endpoint.
236
+ #
237
+ def set_vars_for( opts )
238
+ @locale = rand( 2 ) == 0 ? nil : SecureRandom.urlsafe_base64(2)
239
+ @expected_locale = @locale.nil? ? 'en-nz' : @locale.downcase
240
+ @client = Hoodoo::Client.new( opts.merge( :locale => @locale ) )
241
+
242
+ endpoint_opts = {}
243
+
244
+ # Part of this could be automatic via HEADER_TO_PROPERTY but then
245
+ # any errors in that mapping which don't match our requirements in
246
+ # the test would be hidden, by the test using the same broken map.
247
+ #
248
+ # See also "def mock" earlier in this file and
249
+ # "def option_based_expectations" later in this file. Be careful
250
+ # to follow the naming convention evident below if adding things.
251
+
252
+ @expected_dated_at = @dated_at.nil? ? nil : Hoodoo::Utilities.nanosecond_iso8601( @dated_at )
253
+ @expected_dated_from = @dated_from.nil? ? nil : Hoodoo::Utilities.nanosecond_iso8601( @dated_from )
254
+ @expected_resource_uuid = @resource_uuid
255
+ @expected_deja_vu = @deja_vu != true ? nil : true
256
+
257
+ endpoint_opts[ :dated_at ] = @dated_at unless @dated_at.nil?
258
+ endpoint_opts[ :dated_from ] = @dated_from unless @dated_from.nil?
259
+ endpoint_opts[ :resource_uuid ] = @resource_uuid unless @resource_uuid.nil?
260
+ endpoint_opts[ :deja_vu ] = @deja_vu if @deja_vu == true
261
+
262
+ if rand( 2 ) == 0
263
+ override_locale = SecureRandom.urlsafe_base64( 2 )
264
+ endpoint_opts[ :locale ] = override_locale
265
+ @expected_locale = override_locale.downcase
266
+ end
267
+
268
+ @endpoint = @client.resource( :RSpecClientTestTarget, 1, endpoint_opts )
269
+ end
270
+
271
+ # Automatic expectations based on HEADER_TO_PROPERTY are fine here as they
272
+ # are just working off the properties and the associated variable naming
273
+ # conventions within this test file.
274
+ #
275
+ # See also "def mock" and "def set_vars_for" earlier in this file.
276
+ #
277
+ def option_based_expectations( result )
278
+
279
+ if result.is_a?( Hoodoo::Client::AugmentedArray )
280
+ resource = result[ 0 ]
281
+ else
282
+ resource = result
283
+ end
284
+
285
+ Hoodoo::Client::Headers::HEADER_TO_PROPERTY.each do | rack_header, description |
286
+ property = description[ :property ]
287
+ expect( resource[ property.to_s ] ).to eq( instance_variable_get( "@expected_#{ property }" ) )
288
+ end
289
+
290
+ # We also always expect some standard response headers to have been included
291
+ # in the result out-of-band options payload.
292
+
293
+ expect( result.response_options[ 'interaction_id' ] ).to be_present
294
+ expect( Hoodoo::UUID.valid?( result.response_options[ 'interaction_id' ] ) ).to eq( true )
295
+ expect( result.response_options[ 'service_response_time' ] ).to be_present
296
+
297
+ end
298
+
299
+ ##############################################################################
300
+ # No automatic session acquisition and no session
301
+ ##############################################################################
302
+
303
+ context 'without any session' do
304
+ shared_examples Hoodoo::Client do
305
+ it '(public actions allowed)' do
306
+ mock_ident = Hoodoo::UUID.generate()
307
+ embeds = [ 'foo', 'baz' ]
308
+ query_hash = { '_embed' => embeds }
309
+
310
+ result = @endpoint.show( mock_ident )
311
+ expect( result.platform_errors.has_errors? ).to eq( false )
312
+
313
+ expect( result[ 'id' ] ).to eq( mock_ident )
314
+ expect( result[ 'embeds' ] ).to eq( [] )
315
+ expect( result[ 'language' ] ).to eq( @expected_locale )
316
+
317
+ option_based_expectations( result )
318
+
319
+ result = @endpoint.show( mock_ident, query_hash )
320
+ expect( result.platform_errors.has_errors? ).to eq( false )
321
+
322
+ expect( result[ 'id' ] ).to eq( mock_ident )
323
+ expect( result[ 'embeds' ] ).to eq( embeds )
324
+ expect( result[ 'language' ] ).to eq( @expected_locale )
325
+
326
+ option_based_expectations( result )
327
+ end
328
+
329
+ it '(protected actions prohibited)' do
330
+ result = @endpoint.list()
331
+ expect( result.platform_errors.has_errors? ).to eq( true )
332
+ expect( result.platform_errors.errors[ 0 ][ 'code' ] ).to eq( 'platform.invalid_session' )
333
+ end
334
+ end
335
+
336
+ before :each do
337
+ Hoodoo::Services::Middleware.set_test_session( nil )
338
+ end
339
+
340
+ context 'and by DRb' do
341
+ before :each do
342
+ set_vars_for(
343
+ drb_port: URI.parse( Hoodoo::Services::Discovery::ByDRb::DRbServer.uri() ).port,
344
+ auto_session: false
345
+ )
346
+ end
347
+
348
+ it_behaves_like Hoodoo::Client
349
+ end
350
+
351
+ context 'and by DRb with custom discoverer' do
352
+ before :each do
353
+ drb_port = URI.parse( Hoodoo::Services::Discovery::ByDRb::DRbServer.uri() ).port
354
+ discoverer = Hoodoo::Services::Discovery::ByDRb.new( drb_port: drb_port )
355
+
356
+ expect( discoverer ).to receive( :discover ).at_least( :once ).and_call_original
357
+
358
+ set_vars_for(
359
+ auto_session: false,
360
+ discoverer: discoverer
361
+ )
362
+ end
363
+
364
+ it_behaves_like Hoodoo::Client
365
+ end
366
+
367
+ context 'and by convention' do
368
+ before :each do
369
+ set_vars_for(
370
+ base_uri: "http://localhost:#{ @port }",
371
+ auto_session: false
372
+ )
373
+ end
374
+
375
+ it_behaves_like Hoodoo::Client
376
+ end
377
+
378
+ context 'and by convention with custom discoverer' do
379
+ before :each do
380
+ base_uri = "http://localhost:#{ @port }"
381
+ discoverer = Hoodoo::Services::Discovery::ByConvention.new( base_uri: base_uri )
382
+
383
+ expect( discoverer ).to receive( :discover ).at_least( :once ).and_call_original
384
+
385
+ set_vars_for(
386
+ base_uri: base_uri,
387
+ auto_session: false,
388
+ session_id: @old_test_session.session_id,
389
+ discoverer: discoverer
390
+ )
391
+ end
392
+
393
+ it_behaves_like Hoodoo::Client
394
+ end
395
+
396
+ context 'and with an HTTP proxy via custom discoverer' do
397
+ before :each do
398
+ base_uri = "http://localhost:#{ @port }"
399
+ proxy_uri = 'http://foo:bar@proxyhost:1234'
400
+ discoverer = Hoodoo::Services::Discovery::ByConvention.new(
401
+ base_uri: base_uri,
402
+ proxy_uri: proxy_uri
403
+ )
404
+
405
+ original_new = Net::HTTP.method( :new )
406
+
407
+ expect( Net::HTTP ).to receive( :new ).at_least( :once ) do | host, port, proxy_host, proxy_port, proxy_user, proxy_pass |
408
+ expect( host ).to eq( 'localhost' )
409
+ expect( port ).to eq( @port )
410
+ expect( proxy_host ).to eq( 'proxyhost' )
411
+ expect( proxy_port ).to eq( 1234 )
412
+ expect( proxy_user ).to eq( 'foo' )
413
+ expect( proxy_pass ).to eq( 'bar' )
414
+
415
+ original_new.call( host, port )
416
+ end
417
+
418
+ set_vars_for(
419
+ auto_session: false,
420
+ session_id: @old_test_session.session_id,
421
+ discoverer: discoverer
422
+ )
423
+ end
424
+
425
+ it_behaves_like Hoodoo::Client
426
+ end
427
+
428
+ context 'and with a custom ca_file' do
429
+ before :each do
430
+ # Note: the ssl-cert is for 127.0.0.1, localhost doesn't match it
431
+ base_uri = "https://127.0.0.1:#{ @https_port }"
432
+ ca_file = 'spec/files/ca/ca-cert.pem'
433
+ discoverer = Hoodoo::Services::Discovery::ByConvention.new(
434
+ base_uri: base_uri,
435
+ ca_file: ca_file
436
+ )
437
+
438
+ set_vars_for(
439
+ base_uri: base_uri,
440
+ auto_session: false,
441
+ session_id: @old_test_session.session_id,
442
+ discoverer: discoverer
443
+ )
444
+ end
445
+
446
+ it_behaves_like Hoodoo::Client
447
+ end
448
+
449
+ context 'and with a custom HTTP read timeout' do
450
+ before :each do
451
+ base_uri = "http://localhost:#{ @port }"
452
+ discoverer = Hoodoo::Services::Discovery::ByConvention.new(
453
+ base_uri: base_uri,
454
+ http_timeout: 0.0000001
455
+ )
456
+
457
+ set_vars_for(
458
+ base_uri: base_uri,
459
+ auto_session: false,
460
+ session_id: @old_test_session.session_id,
461
+ discoverer: discoverer
462
+ )
463
+ end
464
+
465
+ it 'times out elegantly' do
466
+ mock_ident = Hoodoo::UUID.generate()
467
+ result = @endpoint.show( mock_ident )
468
+
469
+ expect( result.platform_errors.has_errors? ).to eq( true )
470
+ expect( result.platform_errors.errors[ 0 ][ 'code' ] ).to eq( 'platform.timeout' )
471
+ end
472
+ end
473
+
474
+ context 'and with custom routing' do
475
+ it 'obeys the routes' do
476
+ base_uri = "http://localhost:#{ @port }"
477
+ discoverer = Hoodoo::Services::Discovery::ByConvention.new(
478
+ :base_uri => base_uri,
479
+ :routing => {
480
+ :RSpecClientTestTargetCustomRouting => {
481
+ 1 => '/v1/r_spec_client_test_targets_with_custom_route'
482
+ }
483
+ }
484
+ )
485
+
486
+ set_vars_for(
487
+ base_uri: base_uri,
488
+ auto_session: false,
489
+ session_id: @old_test_session.session_id,
490
+ discoverer: discoverer
491
+ )
492
+
493
+ mock_ident = Hoodoo::UUID.generate()
494
+ result = @client.resource( :RSpecClientTestTargetCustomRouting, 1 ).show( mock_ident )
495
+ expect( result.platform_errors.has_errors? ).to eq( false )
496
+ expect( result[ 'id' ] ).to eq( mock_ident )
497
+ end
498
+ end
499
+
500
+ context 'rejects a request with secured option' do
501
+ Hoodoo::Client::Headers::HEADER_TO_PROPERTY.each do | rack_header, description |
502
+ property = description[ :property ]
503
+ secured = description[ :secured ]
504
+
505
+ next unless secured == true
506
+
507
+ it "'#{ property }' present" do
508
+ case property
509
+ when :resource_uuid
510
+ @resource_uuid = Hoodoo::UUID.generate
511
+ else
512
+ raise "Update client_spec.rb with new secured properties for test"
513
+ end
514
+
515
+ set_vars_for(
516
+ base_uri: "http://localhost:#{ @port }",
517
+ auto_session: false
518
+ )
519
+
520
+ mock_ident = Hoodoo::UUID.generate()
521
+ result = @endpoint.show( mock_ident )
522
+
523
+ expect( result.platform_errors.has_errors? ).to eq( true )
524
+ expect( result.platform_errors.errors[ 0 ][ 'code' ] ).to eq( 'platform.forbidden' )
525
+ end
526
+ end
527
+ end
528
+
529
+ context 'accepts a request with non-secured option' do
530
+ Hoodoo::Client::Headers::HEADER_TO_PROPERTY.each do | rack_header, description |
531
+ property = description[ :property ]
532
+ secured = description[ :secured ]
533
+
534
+ next if secured == true
535
+
536
+ context "'#{ property }' present" do
537
+ before :each do
538
+ case property
539
+ when :dated_at
540
+ @dated_at = Time.now - 1.year
541
+ when :dated_from
542
+ @dated_from = Time.now - 2.years
543
+ when :deja_vu
544
+ @deja_vu = true
545
+ else
546
+ raise "Update client_spec.rb with new non-secured properties for test"
547
+ end
548
+
549
+ set_vars_for(
550
+ base_uri: "http://localhost:#{ @port }",
551
+ auto_session: false
552
+ )
553
+ end
554
+
555
+ it_behaves_like Hoodoo::Client
556
+ end
557
+ end
558
+ end
559
+ end
560
+
561
+ ##############################################################################
562
+ # No automatic session acquisition with manual session
563
+ ##############################################################################
564
+
565
+ context 'with a manual session' do
566
+ shared_examples Hoodoo::Client do
567
+ it '(public actions allowed)' do
568
+ mock_ident = Hoodoo::UUID.generate()
569
+
570
+ result = @endpoint.show( mock_ident )
571
+ expect( result.platform_errors.has_errors? ).to eq( false )
572
+ expect( result[ 'id' ] ).to eq( mock_ident )
573
+ end
574
+
575
+ it '(protected actions prohibited)' do
576
+ mock_ident = Hoodoo::UUID.generate()
577
+ embeds = [ 'bar' ]
578
+ query_hash = { '_embed' => embeds }
579
+
580
+ result = @endpoint.list( query_hash )
581
+ expect( result.platform_errors.has_errors? ).to eq( false )
582
+ expect( result.dataset_size ).to eq( result.size )
583
+
584
+ expect( result[ 0 ][ 'embeds' ] ).to eq( embeds )
585
+ expect( result[ 0 ][ 'language' ] ).to eq( @expected_locale )
586
+
587
+ option_based_expectations( result )
588
+
589
+ embeds = [ 'baz' ]
590
+ query_hash = { '_embed' => embeds }
591
+ body_hash = { 'hello' => 'world' }
592
+
593
+ result = @endpoint.create( body_hash, query_hash )
594
+ expect( result.platform_errors.has_errors? ).to eq( false )
595
+
596
+ expect( result[ 'body_hash' ] ).to eq( body_hash )
597
+ expect( result[ 'embeds' ] ).to eq( embeds )
598
+ expect( result[ 'language' ] ).to eq( @expected_locale )
599
+
600
+ option_based_expectations( result )
601
+
602
+ mock_ident = Hoodoo::UUID.generate()
603
+ embeds = [ 'foo' ]
604
+ query_hash = { '_embed' => embeds }
605
+ body_hash = { 'left' => 'right' }
606
+
607
+ result = @endpoint.update( mock_ident, body_hash, query_hash )
608
+ expect( result.platform_errors.has_errors? ).to eq( false )
609
+ expect( result.response_options[ 'example_header' ] ).to eq( 'example' )
610
+
611
+ expect( result[ 'id' ] ).to eq( mock_ident )
612
+ expect( result[ 'body_hash' ] ).to eq( body_hash )
613
+ expect( result[ 'embeds' ] ).to eq( embeds )
614
+ expect( result[ 'language' ] ).to eq( @expected_locale )
615
+
616
+ option_based_expectations( result )
617
+
618
+ mock_ident = Hoodoo::UUID.generate()
619
+ embeds = [ 'baz', 'bar' ]
620
+ query_hash = { '_embed' => embeds }
621
+
622
+ result = @endpoint.delete( mock_ident, query_hash )
623
+ expect( result.platform_errors.has_errors? ).to eq( false )
624
+
625
+ expect( result[ 'id' ] ).to eq( mock_ident )
626
+ expect( result[ 'embeds' ] ).to eq( embeds )
627
+ expect( result[ 'language' ] ).to eq( @expected_locale )
628
+
629
+ option_based_expectations( result )
630
+ end
631
+ end
632
+
633
+ before :each do
634
+ Hoodoo::Services::Middleware.set_test_session( @old_test_session )
635
+ end
636
+
637
+ context 'and by DRb' do
638
+ before :each do
639
+ set_vars_for(
640
+ drb_port: URI.parse( Hoodoo::Services::Discovery::ByDRb::DRbServer.uri() ).port,
641
+ auto_session: false,
642
+ session_id: @old_test_session.session_id
643
+ )
644
+ end
645
+
646
+ it_behaves_like Hoodoo::Client
647
+ end
648
+
649
+ context 'and by convention' do
650
+ before :each do
651
+ set_vars_for(
652
+ base_uri: "http://localhost:#{ @port }",
653
+ auto_session: false,
654
+ session_id: @old_test_session.session_id
655
+ )
656
+ end
657
+
658
+ it_behaves_like Hoodoo::Client
659
+ end
660
+
661
+ context 'and when endpoints return errors' do
662
+ before :each do
663
+ set_vars_for(
664
+ base_uri: "http://localhost:#{ @port }",
665
+ auto_session: false,
666
+ session_id: @old_test_session.session_id
667
+ )
668
+ end
669
+
670
+ it 'returns an AugmentedArray for #list' do
671
+ result = @endpoint.list( { :offset => 42 } ) # 42 -> magic -> service adds error
672
+
673
+ expect( result ).to be_a( Hoodoo::Client::AugmentedArray )
674
+ expect( result.platform_errors.has_errors? ).to eq( true )
675
+ end
676
+
677
+ it 'returns an AugmentedHash for #show' do
678
+ result = @endpoint.show( 'return_error' )
679
+
680
+ expect( result ).to be_a( Hoodoo::Client::AugmentedHash )
681
+ expect( result.platform_errors.has_errors? ).to eq( true )
682
+ end
683
+
684
+ it 'returns an AugmentedHash for #create' do
685
+ result = @endpoint.create( { 'return_error' => true } )
686
+
687
+ expect( result ).to be_a( Hoodoo::Client::AugmentedHash )
688
+ expect( result.platform_errors.has_errors? ).to eq( true )
689
+ end
690
+
691
+ it 'returns an AugmentedHash for #update' do
692
+ result = @endpoint.update( 'return_error', {} )
693
+
694
+ expect( result ).to be_a( Hoodoo::Client::AugmentedHash )
695
+ expect( result.platform_errors.has_errors? ).to eq( true )
696
+ end
697
+
698
+ it 'returns an AugmentedHash for #delete' do
699
+ result = @endpoint.delete( 'return_error' )
700
+
701
+ expect( result ).to be_a( Hoodoo::Client::AugmentedHash )
702
+ expect( result.platform_errors.has_errors? ).to eq( true )
703
+ end
704
+ end
705
+
706
+ context 'and with non-secured option' do
707
+
708
+ # Specific functional tests
709
+
710
+ it "'deja_vu' in use" do
711
+ @deja_vu = true
712
+
713
+ set_vars_for(
714
+ base_uri: "http://localhost:#{ @port }",
715
+ auto_session: false
716
+ )
717
+
718
+ result = @endpoint.create( { 'hello' => 'world' } )
719
+
720
+ expect( result.platform_errors.has_errors? ).to eq( false )
721
+ expect( result.response_options[ 'deja_vu' ] ).to eq( 'confirmed' )
722
+ end
723
+ end
724
+
725
+ context 'and with secured option' do
726
+ before :each do
727
+ test_session = @old_test_session.dup
728
+ test_session.scoping = @old_test_session.scoping.dup
729
+ test_session.scoping.authorised_http_headers = []
730
+
731
+ Hoodoo::Client::Headers::HEADER_TO_PROPERTY.each do | rack_header, description |
732
+ next unless description[ :secured ] == true
733
+ test_session.scoping.authorised_http_headers << rack_header
734
+ end
735
+
736
+ Hoodoo::Services::Middleware.set_test_session( test_session )
737
+ end
738
+
739
+ after :each do
740
+ Hoodoo::Services::Middleware.set_test_session( @old_test_session )
741
+ end
742
+
743
+ Hoodoo::Client::Headers::HEADER_TO_PROPERTY.each do | rack_header, description |
744
+ property = description[ :property ]
745
+ secured = description[ :secured ]
746
+
747
+ next unless secured == true
748
+
749
+ it "'#{ property }' present" do
750
+ case property
751
+ when :resource_uuid
752
+ @resource_uuid = Hoodoo::UUID.generate
753
+ else
754
+ raise "Update client_spec.rb with new secured properties for test"
755
+ end
756
+
757
+ set_vars_for(
758
+ base_uri: "http://localhost:#{ @port }",
759
+ auto_session: false
760
+ )
761
+
762
+ mock_ident = Hoodoo::UUID.generate()
763
+ result = @endpoint.show( mock_ident )
764
+
765
+ expect( result.platform_errors.has_errors? ).to eq( false )
766
+ end
767
+ end
768
+
769
+ # Specific functional tests
770
+
771
+ it "'resource_uuid' in use" do
772
+ @resource_uuid = Hoodoo::UUID.generate
773
+
774
+ set_vars_for(
775
+ base_uri: "http://localhost:#{ @port }",
776
+ auto_session: false
777
+ )
778
+
779
+ result = @endpoint.create( { 'hello' => 'world' } )
780
+
781
+ expect( result.platform_errors.has_errors? ).to eq( false )
782
+ expect( result[ 'id' ] ).to eq( @resource_uuid )
783
+ end
784
+ end
785
+ end
786
+
787
+ ##############################################################################
788
+ # Automatic session acquisition
789
+ ##############################################################################
790
+
791
+ context 'with auto-session' do
792
+ shared_examples Hoodoo::Client do
793
+ it '(public actions allowed)' do
794
+ mock_ident = Hoodoo::UUID.generate()
795
+
796
+ result = @endpoint.show( mock_ident )
797
+ expect( result.platform_errors.has_errors? ).to eq( false )
798
+ expect( result[ 'id' ] ).to eq( mock_ident )
799
+ end
800
+
801
+ it '(protected actions allowed)' do
802
+ mock_ident = Hoodoo::UUID.generate()
803
+ embeds = [ 'bar' ]
804
+ query_hash = { '_embed' => embeds }
805
+
806
+ result = @endpoint.list( query_hash )
807
+ expect( result.platform_errors.has_errors? ).to eq( false )
808
+ expect( result.dataset_size ).to eq( result.size )
809
+
810
+ expect( result[ 0 ][ 'embeds' ] ).to eq( embeds )
811
+ expect( result[ 0 ][ 'language' ] ).to eq( @expected_locale )
812
+
813
+ option_based_expectations( result )
814
+
815
+ embeds = [ 'baz' ]
816
+ query_hash = { '_embed' => embeds }
817
+ body_hash = { 'hello' => 'world' }
818
+
819
+ result = @endpoint.create( body_hash, query_hash )
820
+ expect( result.platform_errors.has_errors? ).to eq( false )
821
+
822
+ expect( result[ 'body_hash' ] ).to eq( body_hash )
823
+ expect( result[ 'embeds' ] ).to eq( embeds )
824
+ expect( result[ 'language' ] ).to eq( @expected_locale )
825
+
826
+ option_based_expectations( result )
827
+
828
+ mock_ident = Hoodoo::UUID.generate()
829
+ embeds = [ 'foo' ]
830
+ query_hash = { '_embed' => embeds }
831
+ body_hash = { 'left' => 'right' }
832
+
833
+ result = @endpoint.update( mock_ident, body_hash, query_hash )
834
+ expect( result.platform_errors.has_errors? ).to eq( false )
835
+ expect( result.response_options[ 'example_header' ] ).to eq( 'example' )
836
+
837
+ expect( result[ 'id' ] ).to eq( mock_ident )
838
+ expect( result[ 'body_hash' ] ).to eq( body_hash )
839
+ expect( result[ 'embeds' ] ).to eq( embeds )
840
+ expect( result[ 'language' ] ).to eq( @expected_locale )
841
+
842
+ option_based_expectations( result )
843
+
844
+ mock_ident = Hoodoo::UUID.generate()
845
+ embeds = [ 'baz', 'bar' ]
846
+ query_hash = { '_embed' => embeds }
847
+
848
+ result = @endpoint.delete( mock_ident, query_hash )
849
+ expect( result.platform_errors.has_errors? ).to eq( false )
850
+
851
+ expect( result[ 'id' ] ).to eq( mock_ident )
852
+ expect( result[ 'embeds' ] ).to eq( embeds )
853
+ expect( result[ 'language' ] ).to eq( @expected_locale )
854
+
855
+ option_based_expectations( result )
856
+ end
857
+
858
+ it 'automatically retries' do
859
+ result = @endpoint.list()
860
+ expect( result.platform_errors.has_errors? ).to eq( false )
861
+
862
+ expect_any_instance_of( RSpecClientTestSessionImplementation ).to receive( :create ).once.and_call_original
863
+ Hoodoo::Services::Middleware.set_test_session( nil )
864
+
865
+ result = @endpoint.list()
866
+ expect( result.platform_errors.has_errors? ).to eq( false )
867
+ end
868
+
869
+ it 'handles errors from the session resource' do
870
+ expect_any_instance_of( RSpecClientTestSessionImplementation ).to receive( :create ).and_raise( "boo!" )
871
+
872
+ Hoodoo::Services::Middleware.set_test_session( nil )
873
+
874
+ result = @endpoint.list()
875
+ expect( result.platform_errors.has_errors? ).to eq( true )
876
+ expect( result.platform_errors.errors[ 0 ][ 'code' ] ).to eq( 'platform.fault' )
877
+ end
878
+
879
+ it 'handles malformed sessions' do
880
+ expect_any_instance_of( RSpecClientTestSessionImplementation ).to receive( :create ) { | ignored, context |
881
+ context.response.body = { 'not' => 'a session' }
882
+ }
883
+
884
+ Hoodoo::Services::Middleware.set_test_session( nil )
885
+
886
+ result = @endpoint.list()
887
+ expect( result.platform_errors.has_errors? ).to eq( true )
888
+ expect( result.platform_errors.errors[ 0 ][ 'code' ] ).to eq( 'generic.malformed' )
889
+ end
890
+
891
+ # This is a fragile test due to the way it is forced to simulate a
892
+ # connection fault; see similar test in middleware_multi_spec.rb
893
+ # for details.
894
+ #
895
+ it 'handles explicit connection errors as 404' do
896
+ expect_any_instance_of( Net::HTTP ).to receive( :request ).once.and_raise( Errno::ECONNREFUSED )
897
+ result = @endpoint.list()
898
+ expect( result.platform_errors.has_errors? ).to eq( true )
899
+ expect( result.platform_errors.errors[ 0 ][ 'code' ] ).to eq( 'platform.not_found' )
900
+ end
901
+
902
+ # As above, this is a fragile test.
903
+ #
904
+ it 'handles arbitrary communication errors as 500' do
905
+ expect_any_instance_of( Net::HTTP ).to receive( :request ).once.and_raise( 'some connection error' )
906
+ result = @endpoint.list()
907
+ expect( result.platform_errors.has_errors? ).to eq( true )
908
+ expect( result.platform_errors.errors[ 0 ][ 'code' ] ).to eq( 'platform.fault' )
909
+ expect( result.platform_errors.errors[ 0 ][ 'reference' ] ).to include( 'some connection error' )
910
+ end
911
+ end
912
+
913
+ before :each do
914
+ Hoodoo::Services::Middleware.set_test_session( nil )
915
+ end
916
+
917
+ context 'and by DRb' do
918
+ before :each do
919
+ set_vars_for(
920
+ drb_port: URI.parse( Hoodoo::Services::Discovery::ByDRb::DRbServer.uri() ).port,
921
+ caller_id: Hoodoo::UUID.generate,
922
+ caller_secret: Hoodoo::UUID.generate,
923
+ auto_session_resource: 'RSpecClientTestSession'
924
+ )
925
+ end
926
+
927
+ it_behaves_like Hoodoo::Client
928
+ end
929
+
930
+ context 'and by convention' do
931
+ before :each do
932
+ set_vars_for(
933
+ base_uri: "http://localhost:#{ @port }",
934
+ caller_id: Hoodoo::UUID.generate,
935
+ caller_secret: Hoodoo::UUID.generate,
936
+ auto_session_resource: 'RSpecClientTestSession'
937
+ )
938
+ end
939
+
940
+ it_behaves_like Hoodoo::Client
941
+ end
942
+ end
943
+
944
+ ##############################################################################
945
+ # Code coverage
946
+ ##############################################################################
947
+
948
+ context 'code coverage' do
949
+ it 'complains about missing options in the constructor' do
950
+ expect {
951
+ Hoodoo::Client.new
952
+ }.to raise_error( RuntimeError, 'Hoodoo::Client: Please pass one of the "discoverer", "base_uri", "drb_uri" or "drb_port" parameters.' )
953
+ end
954
+ end
955
+ end