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,225 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Hoodoo::ActiveRecord::Support do
4
+ context '#self.process_to_map' do
5
+ it 'processes as expected' do
6
+ proc1 = Proc.new { puts "hello" }
7
+ proc2 = Proc.new { puts "world" }
8
+
9
+ hash = {
10
+ :foo => nil,
11
+ 'bar' => nil,
12
+ :baz => proc1,
13
+ 'boo' => proc2
14
+ }
15
+
16
+ map = described_class.process_to_map( hash )
17
+
18
+ # Ensure all keys become strings. The 'nil' values should become matcher
19
+ # Procs per Hoodoo::ActiveRecord::Finder::SearchHelper.cs_match, however
20
+ # you can't compare Proc instances; so instead run those Procs and check
21
+ # the expected results.
22
+ #
23
+ # Since a created Proc is supposed to encode the attribute name into the
24
+ # Proc via the key in the input hash, any run-time specification of an
25
+ # attribute name ought to be ignored if the mapping method is working as
26
+ # expected.
27
+
28
+ expect( map[ 'foo' ] ).to be_a( Proc )
29
+ expect( map[ 'foo' ].call( 'ignored', 'val1' ) ).to eq( [ 'foo = ? AND foo IS NOT NULL', 'val1' ] )
30
+
31
+ expect( map[ 'bar' ] ).to be_a( Proc )
32
+ expect( map[ 'bar' ].call( 'ignored', 'val2' ) ).to eq( [ 'bar = ? AND bar IS NOT NULL', 'val2' ] )
33
+
34
+ expect( map[ 'baz' ] ).to eq( proc1 )
35
+
36
+ expect( map[ 'boo' ] ).to eq( proc2 )
37
+ end
38
+ end
39
+
40
+ context '#full_scope_for' do
41
+
42
+ # Here we not only test the full scope generator with all mixins included,
43
+ # and test them all in a *deactivated* state, we also check that inheritance
44
+ # can then override that condition without disturbing the base class to
45
+ # ensure that "class attribute" style code, rather than "@@" class variable
46
+ # style code, is being maintained in the mixins. Then check the reverse -
47
+ # make sure things in the base class do get inherited by subclasses.
48
+
49
+ before :all do
50
+ @thtname1 = 'r_spec_full_scope_for_test_foo_history'
51
+ @thtname2 = 'r_spec_full_scope_for_test_with_directives_foo_history'
52
+
53
+ spec_helper_silence_stdout() do
54
+ ActiveRecord::Migration.create_table( :r_spec_full_scope_for_test_bases ) do | t |
55
+ t.timestamps :null => true
56
+ end
57
+
58
+ ActiveRecord::Migration.create_table( :r_spec_full_scope_for_test_subclasses ) do | t |
59
+ t.string :foo
60
+ t.timestamps :null => true
61
+ end
62
+
63
+ ActiveRecord::Migration.create_table( @thtname1 ) do | t |
64
+ t.string :foo
65
+ t.timestamps :null => true
66
+ end
67
+
68
+ ActiveRecord::Migration.create_table( :rspec_full_scope_for_test_base_with_directives_custom ) do | t |
69
+ t.string :bar
70
+ t.timestamps :null => true
71
+ end
72
+
73
+ ActiveRecord::Migration.create_table( @thtname2 ) do | t |
74
+ t.string :bar
75
+ t.timestamps :null => true
76
+ end
77
+ end
78
+
79
+ # Note inheritance from plain ActiveRecord::Base, important for
80
+ # additional coverage. Module inclusions are thus done manually.
81
+
82
+ class RSpecFullScopeForTestBase < ActiveRecord::Base
83
+ include Hoodoo::ActiveRecord::Secure
84
+ include Hoodoo::ActiveRecord::Dated
85
+ include Hoodoo::ActiveRecord::Translated
86
+ end
87
+
88
+ class RSpecFullScopeForTestSubclass < RSpecFullScopeForTestBase
89
+ TEST_HISTORY_TABLE_NAME = 'r_spec_full_scope_for_test_foo_history'
90
+
91
+ self.table_name = :r_spec_full_scope_for_test_subclasses
92
+ secure_with( :foo => :foo )
93
+ dating_enabled( :history_table_name => TEST_HISTORY_TABLE_NAME )
94
+ end
95
+
96
+ # Note inheritance from Hoodoo::ActiveRecord::Base, important for
97
+ # additional coverage. Module inclusions are automatic.
98
+
99
+ class RSpecFullScopeForTestBaseWithDirectives < Hoodoo::ActiveRecord::Base
100
+ TEST_HISTORY_TABLE_NAME = 'r_spec_full_scope_for_test_with_directives_foo_history'
101
+
102
+ self.table_name = :rspec_full_scope_for_test_base_with_directives_custom
103
+ secure_with( :bar => :foo )
104
+ dating_enabled( :history_table_name => TEST_HISTORY_TABLE_NAME )
105
+ end
106
+
107
+ class RSpecFullScopeForTestBaseSubclassWithoutOverrides < RSpecFullScopeForTestBaseWithDirectives
108
+ # No overrides at all
109
+ end
110
+ end
111
+
112
+ before :each do
113
+
114
+ # Get a good-enough-for-test interaction and context.
115
+
116
+ @interaction = Hoodoo::Services::Middleware::Interaction.new( {}, nil )
117
+ @interaction.context = Hoodoo::Services::Context.new(
118
+ Hoodoo::Services::Session.new,
119
+ @interaction.context.request,
120
+ @interaction.context.response,
121
+ @interaction
122
+ )
123
+
124
+ @context = @interaction.context
125
+ @session = @interaction.context.session
126
+
127
+ # Now set up the data inside that context so that the tests generate
128
+ # predictable SQL output.
129
+
130
+ @test_time_value = DateTime.now
131
+ @context.request.dated_at = @test_time_value
132
+
133
+ @test_scoping_value = 23
134
+ @session.scoping = OpenStruct.new
135
+ @session.scoping.foo = [ @test_scoping_value ]
136
+ end
137
+
138
+ context '(with subclass overriding base class)' do
139
+ it 'prerequisites' do
140
+ expect( RSpecFullScopeForTestBase.all().to_sql() ).to_not eq( RSpecFullScopeForTestSubclass.all().to_sql() )
141
+ end
142
+
143
+ it 'gets "all" scope in the base class' do
144
+
145
+ # There are no module activations in the base class so we expect to
146
+ # get the "all" context, the subclass's activations having not made
147
+ # any difference to it.
148
+
149
+ auto_scope = described_class.full_scope_for( RSpecFullScopeForTestBase, @context ).to_sql()
150
+ manual_scope = RSpecFullScopeForTestBase.all().to_sql()
151
+
152
+ expect( auto_scope ).to eq( manual_scope )
153
+ end
154
+
155
+ context 'gets customised scope in the subclass:' do
156
+
157
+ # These first tests just make sure that the individual modules
158
+ # are generating expected scoping data, so that we aren't accidentally
159
+ # comparing malfunctioning scope inclusions against each other (e.g.
160
+ # empty strings).
161
+
162
+ it 'dated' do
163
+ manual_scope = RSpecFullScopeForTestSubclass.dated( @context ).to_sql()
164
+
165
+ expect( manual_scope ).to include( "FROM #{ @thtname1 }" )
166
+ expect( manual_scope ).to include( "effective_end > #{ RSpecFullScopeForTestSubclass.sanitize( @test_time_value ) }" )
167
+ end
168
+
169
+ it 'secure' do
170
+ manual_scope = RSpecFullScopeForTestSubclass.secure( @context ).to_sql()
171
+
172
+ expect( manual_scope ).to include( "\"r_spec_full_scope_for_test_subclasses\".\"foo\" = '#{ @test_scoping_value }'" )
173
+ end
174
+
175
+ pending 'translated' do
176
+ raise "Scope verification for '\#translated'"
177
+ end
178
+
179
+ # In this last one, we actually check full_scope_for; activations are in
180
+ # the subclass so we drive the non-context versions directly to verify
181
+ # that the context chain worked.
182
+
183
+ it 'everything' do
184
+ auto_scope = described_class.full_scope_for( RSpecFullScopeForTestSubclass, @context ).to_sql()
185
+ manual_scope = RSpecFullScopeForTestSubclass.secure( @context ).dated( @context ).translated( @context ).to_sql()
186
+
187
+ expect( auto_scope ).to eq( manual_scope )
188
+ end
189
+ end
190
+ end
191
+
192
+ context '(with base class definitions used by subclass)' do
193
+ it 'prerequisites' do
194
+ expect( RSpecFullScopeForTestBaseWithDirectives.all().to_sql ).to eq( RSpecFullScopeForTestBaseSubclassWithoutOverrides.all().to_sql() )
195
+ end
196
+
197
+ # As above - some tests to check individual scopes work (paranoia), then
198
+ # the actual #full_scope_for test which puts it all together.
199
+
200
+ it 'dated' do
201
+ manual_scope = RSpecFullScopeForTestBaseSubclassWithoutOverrides.dated( @context ).to_sql()
202
+
203
+ expect( manual_scope ).to include( "FROM #{ @thtname2 }" )
204
+ expect( manual_scope ).to include( "effective_end > #{ RSpecFullScopeForTestBaseSubclassWithoutOverrides.sanitize( @test_time_value ) }" )
205
+ end
206
+
207
+ it 'secure' do
208
+ manual_scope = RSpecFullScopeForTestBaseSubclassWithoutOverrides.secure( @context ).to_sql()
209
+
210
+ expect( manual_scope ).to include( "\"rspec_full_scope_for_test_base_with_directives_custom\".\"bar\" = '#{ @test_scoping_value }'" )
211
+ end
212
+
213
+ pending 'translated' do
214
+ raise "Scope verification for '\#translated'"
215
+ end
216
+
217
+ it 'yields the same SQL' do
218
+ auto_scope = described_class.full_scope_for( RSpecFullScopeForTestBaseSubclassWithoutOverrides, @context ).to_sql()
219
+ manual_scope = RSpecFullScopeForTestBaseSubclassWithoutOverrides.secure( @context ).dated( @context ).translated( @context ).to_sql()
220
+
221
+ expect( auto_scope ).to eq( manual_scope )
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+
4
+ describe Hoodoo::ActiveRecord::Translated do
5
+ pending 'is tested' do
6
+
7
+ # For RCov only
8
+
9
+ class Test
10
+ include Hoodoo::ActiveRecord::Translated
11
+ def self.all; end
12
+ end
13
+
14
+ ignored = true
15
+ Test.translated( ignored )
16
+
17
+ raise "Replace with real test"
18
+ end
19
+ end
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+
4
+ describe Hoodoo::ActiveRecord::UUID do
5
+ before :all do
6
+ spec_helper_silence_stdout() do
7
+ tblname = :r_spec_model_uuid_tests
8
+
9
+ ActiveRecord::Migration.create_table( tblname, :id => false ) do | t |
10
+ t.string( :id, :limit => 32, :null => false )
11
+ end
12
+
13
+ ActiveRecord::Migration.add_index( tblname, :id, :unique => true )
14
+
15
+ class RSpecModelUUIDTest < ActiveRecord::Base
16
+ include Hoodoo::ActiveRecord::UUID
17
+ end
18
+ end
19
+ end
20
+
21
+ it 'should gain a UUID' do
22
+ m = RSpecModelUUIDTest.new
23
+ m.save
24
+
25
+ expect( m.id ).to_not be_nil
26
+ expect( Hoodoo::UUID.valid?( m.id ) ).to eq( true )
27
+ end
28
+
29
+ it 'should complain about a bad UUID' do
30
+ m = RSpecModelUUIDTest.new
31
+ m.id = "hello"
32
+
33
+ expect( m.save ).to eq( false )
34
+ expect( Hoodoo::UUID.valid?( m.id ) ).to eq( false )
35
+ expect( m.errors ).to_not be_empty
36
+ expect( m.errors.messages ).to eq( { :id => [ 'is invalid' ] } )
37
+ end
38
+
39
+ it 'should not overwrite a good UUID' do
40
+ m = RSpecModelUUIDTest.new
41
+ uuid = Hoodoo::UUID.generate()
42
+ m.id = uuid
43
+ m.save
44
+
45
+ expect( m.id ).to eq( uuid )
46
+ expect( Hoodoo::UUID.valid?( m.id ) ).to eq( true )
47
+ end
48
+
49
+ # Accidental attribute assignment of "id" to "nil" on updates can cause
50
+ # really bad consequences if the UUID is then automatically assigned again;
51
+ # the resource primary key would change magically! Never allow this. If a
52
+ # primary key change is ever wanted, a new UUID must be explicitly given.
53
+ #
54
+ it 'should not update with a replacement UUID' do
55
+ m = RSpecModelUUIDTest.new
56
+ m.save!
57
+ id = m.id
58
+
59
+ # So it shouldn't save with a 'nil' ID...
60
+
61
+ m.assign_attributes( :id => nil )
62
+
63
+ expect( m.save ).to eq( false )
64
+ expect( m.errors.messages ).to eq( { :id => [ "can't be blank" ] } )
65
+
66
+ # ...and should be able to reload it with the original ID still.
67
+
68
+ m.id = id
69
+ m.reload
70
+ expect( m.id ).to eq( id )
71
+ end
72
+ end
@@ -0,0 +1,272 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+
4
+ describe Hoodoo::ActiveRecord::Writer do
5
+
6
+ context 'persist_in' do
7
+
8
+ ###########################################################################
9
+ # DATABASE AND MODEL SETUP
10
+ ###########################################################################
11
+
12
+ class RSpecModelWriterTestWithValidation < ActiveRecord::Base
13
+ self.primary_key = :id
14
+ self.table_name = :r_spec_model_writer_tests
15
+
16
+ include Hoodoo::ActiveRecord::Writer
17
+ include Hoodoo::ActiveRecord::ErrorMapping
18
+
19
+ validates :code, uniqueness: true
20
+ end
21
+
22
+ class RSpecModelWriterTestWithoutValidation < ActiveRecord::Base
23
+ self.primary_key = :id
24
+ self.table_name = :r_spec_model_writer_tests
25
+
26
+ include Hoodoo::ActiveRecord::Writer
27
+ include Hoodoo::ActiveRecord::ErrorMapping
28
+ end
29
+
30
+ before( :all ) do
31
+ spec_helper_silence_stdout() do
32
+ ActiveRecord::Migration.create_table(:r_spec_model_writer_tests, :id => false) do | t |
33
+ t.text :id
34
+ t.text :uuid
35
+ t.text :code
36
+ t.text :random_field
37
+
38
+ t.timestamps :null => false
39
+ end
40
+
41
+ ActiveRecord::Migration.add_index(:r_spec_model_writer_tests, :code, unique: true)
42
+ end
43
+ end
44
+
45
+ ###########################################################################
46
+ # TESTS
47
+ ###########################################################################
48
+
49
+ def unique_attributes
50
+ {
51
+ :id => Hoodoo::UUID.generate(),
52
+ :code => Hoodoo::UUID.generate(),
53
+ :random_field => Hoodoo::UUID.generate()
54
+ }
55
+ end
56
+
57
+ before( :each ) do
58
+ @record_with_app_validation = RSpecModelWriterTestWithValidation.create(
59
+ :id => 'one',
60
+ :code => 'unique',
61
+ :random_field => 'sudo random value'
62
+ )
63
+
64
+ @record_without_app_validation = RSpecModelWriterTestWithoutValidation.create(
65
+ :id => 'two',
66
+ :code => 'unique - but only far as the db is concerned',
67
+ :random_field => 'sudo random value'
68
+ )
69
+
70
+ @interaction = Hoodoo::Services::Middleware::Interaction.new( {}, nil )
71
+ @interaction.context = Hoodoo::Services::Context.new(
72
+ Hoodoo::Services::Session.new,
73
+ @interaction.context.request,
74
+ @interaction.context.response,
75
+ @interaction
76
+ )
77
+
78
+ @context = @interaction.context
79
+ @session = @interaction.context.session
80
+ end
81
+
82
+ context 'saves valid records' do
83
+ def expect_no_error( record )
84
+ expect( record.persisted? ).to eq( true )
85
+ @context.response.add_errors( record.platform_errors )
86
+ expect( @context.response.halt_processing? ).to eq( false )
87
+ end
88
+
89
+ shared_examples 'a persist_in-able model' do | klass |
90
+ it 'via class method' do
91
+ expect(
92
+ record = klass.persist_in( @context, unique_attributes() )
93
+ ).to be_a( klass )
94
+
95
+ expect_no_error( record )
96
+ end
97
+
98
+ it 'via instance method' do
99
+ record = klass.new( unique_attributes() )
100
+ result = record.persist_in( @context )
101
+ expect( result ).to eq( :success )
102
+ expect_no_error( record )
103
+ end
104
+ end
105
+
106
+ context 'with AR validations present' do
107
+ it_behaves_like 'a persist_in-able model', RSpecModelWriterTestWithValidation
108
+ end
109
+
110
+ context 'with AR validations missing' do
111
+ it_behaves_like 'a persist_in-able model', RSpecModelWriterTestWithoutValidation
112
+ end
113
+ end
114
+
115
+ context 'adds correct errors for invalid records' do
116
+ def expect_correct_error( record, reference )
117
+ expect( record.persisted? ).to eq( false )
118
+ @context.response.add_errors( record.platform_errors )
119
+ expect( @context.response.errors.errors[ 0 ] ).to eq(
120
+ {
121
+ 'code' => 'generic.invalid_duplication',
122
+ 'message' => 'has already been taken',
123
+ 'reference' => reference
124
+ }
125
+ )
126
+ end
127
+
128
+ shared_examples 'an errant model' do | klass, error_reference |
129
+ it 'via class method' do
130
+ expect(
131
+ record = klass.persist_in( @context, record_to_copy().attributes() )
132
+ ).to be_a( klass )
133
+
134
+ expect_correct_error( record, error_reference )
135
+ end
136
+
137
+ it 'via instance method' do
138
+ record = klass.new( record_to_copy().attributes() )
139
+ result = record.persist_in( @context )
140
+ expect( result ).to eq( :failure )
141
+ expect_correct_error( record, error_reference )
142
+ end
143
+ end
144
+
145
+ context 'with AR validations present' do
146
+
147
+ # Needed due to RSpec / scoping quirks; can't pass an instance variable
148
+ # to a shared example as a parameter; ends up 'nil' inside the example.
149
+ #
150
+ let( :record_to_copy ) { @record_with_app_validation }
151
+
152
+ it_behaves_like 'an errant model',
153
+ RSpecModelWriterTestWithValidation,
154
+ 'code' # This being the name of a known duplication-violating field
155
+ end
156
+
157
+ context 'with AR validations missing' do
158
+ let( :record_to_copy ) { @record_without_app_validation }
159
+ it_behaves_like 'an errant model',
160
+ RSpecModelWriterTestWithoutValidation,
161
+ 'model instance' # This being the default name for an unknown duplication-violation cause
162
+ end
163
+ end
164
+
165
+ # This tries to check that, despite the nested transaction and rollback
166
+ # behaviour inside #persist_in, deep internal exceptions still propagate
167
+ # out correctly. It assumes database rollbacks happened OK (that's up to
168
+ # AR and the DB driver) and just makes sure the exception gets out.
169
+ #
170
+ context 'when internal exceptions occur' do
171
+ it 'reports them correctly' do
172
+ record = RSpecModelWriterTestWithValidation.new( @record_with_app_validation.attributes() )
173
+
174
+ # This is a method called deep inside ActiveRecord in its Transactions
175
+ # mixin. It is private, so this test is fragile.
176
+
177
+ expect( record ).to receive( :add_to_transaction ).and_raise( 'boo!' )
178
+ expect { result = record.persist_in( @context ) }.to raise_error( RuntimeError, 'boo!' )
179
+ end
180
+ end
181
+
182
+ # Prove that we handle the classic ActiveRecord validation of:
183
+ #
184
+ # Thread 1 Thread 2
185
+ # Check for dup? --
186
+ # No dupe Check for dup?
187
+ # => Save record No dupe
188
+ # OK => Save record
189
+ # Fails
190
+ #
191
+ # Two handler threads are set up to run through this scenario and each
192
+ # uses a Ruby Queue to talk to the other when it needs to pause and wait
193
+ # for the other thread to advance, in order to ensure the articial race
194
+ # condition is provoked every time without reliance on dubious 'sleep's.
195
+ #
196
+ context 'with race conditions' do
197
+ shared_examples 'a robust model' do | use_transaction |
198
+ it 'and handles duplicates correctly' do
199
+ attrs = unique_attributes()
200
+
201
+ record_1 = RSpecModelWriterTestWithValidation.new( attrs )
202
+ record_2 = RSpecModelWriterTestWithValidation.new( attrs )
203
+
204
+ queue_1 = Queue.new
205
+ queue_2 = Queue.new
206
+
207
+ thread_1 = Thread.new do
208
+ queue_1.pop() # Wait until thread 2 gets going
209
+
210
+ expect( record_1 ).to receive( :perform_validations ).once do
211
+ queue_2 << :go # Tell thread 2 to run validations
212
+ queue_1.pop() # Wait for thread 2 to run validations
213
+ true # Indicate successful validation, let save happen
214
+ end
215
+
216
+ result = if use_transaction
217
+ record_1.transaction do
218
+ record_1.persist_in( @context )
219
+ end
220
+ else
221
+ record_1.persist_in( @context )
222
+ end
223
+
224
+ expect( result ).to eq( :success )
225
+
226
+ queue_2 << :go # Tell thread 2 to save
227
+ end
228
+
229
+ thread_2 = Thread.new do
230
+ queue_1 << :go
231
+ queue_2.pop() # Wait for thread 1 to run validations, then tell us to go
232
+
233
+ expect( record_2 ).to receive( :perform_validations ).once do
234
+ queue_1 << :go # Now tell thread 1 to save
235
+ queue_2.pop() # Wait for thread 2 to do the same as the above
236
+ true # Indicate successful validation, let save happen
237
+ end
238
+
239
+ # Since the validation above will succeed but then try to save and that
240
+ # will fail, we expect the Writer module to re-query "valid?" and in the
241
+ # end to return ":failure".
242
+
243
+ expect( record_2 ).to receive( :valid? ).once.and_call_original
244
+
245
+ result = if use_transaction
246
+ record_2.transaction do
247
+ record_2.persist_in( @context )
248
+ end
249
+ else
250
+ record_2.persist_in( @context )
251
+ end
252
+
253
+ expect( result ).to eq( :failure )
254
+ end
255
+
256
+ thread_1.join()
257
+ thread_2.join()
258
+ end
259
+ end
260
+
261
+ context 'and no outer transaction' do
262
+ it_behaves_like 'a robust model', false
263
+ end
264
+
265
+ context 'and an outer transaction' do
266
+ it_behaves_like 'a robust model', true
267
+ end
268
+ end
269
+
270
+ end
271
+
272
+ end