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,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