hoodoo 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/hoodoo +5 -0
- data/lib/hoodoo.rb +27 -0
- data/lib/hoodoo/active.rb +32 -0
- data/lib/hoodoo/active/active_model/uuid_validator.rb +45 -0
- data/lib/hoodoo/active/active_record/base.rb +81 -0
- data/lib/hoodoo/active/active_record/creator.rb +134 -0
- data/lib/hoodoo/active/active_record/dated.rb +343 -0
- data/lib/hoodoo/active/active_record/error_mapping.rb +351 -0
- data/lib/hoodoo/active/active_record/finder.rb +606 -0
- data/lib/hoodoo/active/active_record/search_helper.rb +189 -0
- data/lib/hoodoo/active/active_record/secure.rb +431 -0
- data/lib/hoodoo/active/active_record/support.rb +106 -0
- data/lib/hoodoo/active/active_record/translated.rb +87 -0
- data/lib/hoodoo/active/active_record/uuid.rb +80 -0
- data/lib/hoodoo/active/active_record/writer.rb +321 -0
- data/lib/hoodoo/client.rb +23 -0
- data/lib/hoodoo/client/augmented_array.rb +29 -0
- data/lib/hoodoo/client/augmented_base.rb +168 -0
- data/lib/hoodoo/client/augmented_hash.rb +23 -0
- data/lib/hoodoo/client/client.rb +354 -0
- data/lib/hoodoo/client/endpoint/endpoint.rb +427 -0
- data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +180 -0
- data/lib/hoodoo/client/endpoint/endpoints/auto_session.rb +194 -0
- data/lib/hoodoo/client/endpoint/endpoints/http.rb +203 -0
- data/lib/hoodoo/client/endpoint/endpoints/http_based.rb +367 -0
- data/lib/hoodoo/client/endpoint/endpoints/not_found.rb +59 -0
- data/lib/hoodoo/client/headers.rb +269 -0
- data/lib/hoodoo/communicators.rb +23 -0
- data/lib/hoodoo/communicators/fast.rb +44 -0
- data/lib/hoodoo/communicators/pool.rb +601 -0
- data/lib/hoodoo/communicators/slow.rb +84 -0
- data/lib/hoodoo/data.rb +51 -0
- data/lib/hoodoo/data/resources/caller.rb +39 -0
- data/lib/hoodoo/data/resources/errors.rb +28 -0
- data/lib/hoodoo/data/resources/log.rb +31 -0
- data/lib/hoodoo/data/resources/session.rb +26 -0
- data/lib/hoodoo/data/types/error_primitive.rb +27 -0
- data/lib/hoodoo/data/types/permissions.rb +40 -0
- data/lib/hoodoo/data/types/permissions_defaults.rb +32 -0
- data/lib/hoodoo/data/types/permissions_full.rb +28 -0
- data/lib/hoodoo/data/types/permissions_resources.rb +31 -0
- data/lib/hoodoo/discovery.rb +20 -0
- data/lib/hoodoo/errors.rb +19 -0
- data/lib/hoodoo/errors/error_descriptions.rb +229 -0
- data/lib/hoodoo/errors/errors.rb +322 -0
- data/lib/hoodoo/generator.rb +139 -0
- data/lib/hoodoo/logger.rb +23 -0
- data/lib/hoodoo/logger/fast_writer.rb +27 -0
- data/lib/hoodoo/logger/flattener_mixin.rb +36 -0
- data/lib/hoodoo/logger/logger.rb +387 -0
- data/lib/hoodoo/logger/slow_writer.rb +49 -0
- data/lib/hoodoo/logger/writer_mixin.rb +52 -0
- data/lib/hoodoo/logger/writers/file_writer.rb +45 -0
- data/lib/hoodoo/logger/writers/log_entries_dot_com_writer.rb +64 -0
- data/lib/hoodoo/logger/writers/stream_writer.rb +43 -0
- data/lib/hoodoo/middleware.rb +33 -0
- data/lib/hoodoo/presenters.rb +45 -0
- data/lib/hoodoo/presenters/base.rb +281 -0
- data/lib/hoodoo/presenters/base_dsl.rb +519 -0
- data/lib/hoodoo/presenters/common_resource_fields.rb +31 -0
- data/lib/hoodoo/presenters/embedding.rb +232 -0
- data/lib/hoodoo/presenters/types/array.rb +118 -0
- data/lib/hoodoo/presenters/types/boolean.rb +26 -0
- data/lib/hoodoo/presenters/types/date.rb +26 -0
- data/lib/hoodoo/presenters/types/date_time.rb +26 -0
- data/lib/hoodoo/presenters/types/decimal.rb +47 -0
- data/lib/hoodoo/presenters/types/enum.rb +55 -0
- data/lib/hoodoo/presenters/types/field.rb +158 -0
- data/lib/hoodoo/presenters/types/float.rb +26 -0
- data/lib/hoodoo/presenters/types/hash.rb +361 -0
- data/lib/hoodoo/presenters/types/integer.rb +26 -0
- data/lib/hoodoo/presenters/types/object.rb +117 -0
- data/lib/hoodoo/presenters/types/string.rb +53 -0
- data/lib/hoodoo/presenters/types/tags.rb +24 -0
- data/lib/hoodoo/presenters/types/text.rb +26 -0
- data/lib/hoodoo/presenters/types/uuid.rb +54 -0
- data/lib/hoodoo/services.rb +34 -0
- data/lib/hoodoo/services/discovery/discoverers/by_consul.rb +66 -0
- data/lib/hoodoo/services/discovery/discoverers/by_convention.rb +173 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +195 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server.rb +166 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server_start.rb +37 -0
- data/lib/hoodoo/services/discovery/discovery.rb +186 -0
- data/lib/hoodoo/services/discovery/results/for_amqp.rb +58 -0
- data/lib/hoodoo/services/discovery/results/for_http.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_local.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_remote.rb +57 -0
- data/lib/hoodoo/services/middleware/amqp_log_message.rb +186 -0
- data/lib/hoodoo/services/middleware/amqp_log_writer.rb +119 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_local.rb +130 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_remote.rb +202 -0
- data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +105 -0
- data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +115 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +64 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +63 -0
- data/lib/hoodoo/services/middleware/interaction.rb +127 -0
- data/lib/hoodoo/services/middleware/middleware.rb +2705 -0
- data/lib/hoodoo/services/middleware/rack_monkey_patch.rb +73 -0
- data/lib/hoodoo/services/services/context.rb +153 -0
- data/lib/hoodoo/services/services/implementation.rb +132 -0
- data/lib/hoodoo/services/services/interface.rb +934 -0
- data/lib/hoodoo/services/services/permissions.rb +250 -0
- data/lib/hoodoo/services/services/request.rb +189 -0
- data/lib/hoodoo/services/services/response.rb +316 -0
- data/lib/hoodoo/services/services/service.rb +141 -0
- data/lib/hoodoo/services/services/session.rb +729 -0
- data/lib/hoodoo/utilities.rb +12 -0
- data/lib/hoodoo/utilities/string_inquirer.rb +54 -0
- data/lib/hoodoo/utilities/utilities.rb +380 -0
- data/lib/hoodoo/utilities/uuid.rb +44 -0
- data/lib/hoodoo/version.rb +17 -0
- data/spec/active/active_record/base_spec.rb +57 -0
- data/spec/active/active_record/creator_spec.rb +88 -0
- data/spec/active/active_record/dated_spec.rb +248 -0
- data/spec/active/active_record/error_mapping_spec.rb +360 -0
- data/spec/active/active_record/finder_spec.rb +744 -0
- data/spec/active/active_record/search_helper_spec.rb +384 -0
- data/spec/active/active_record/secure_spec.rb +435 -0
- data/spec/active/active_record/support_spec.rb +225 -0
- data/spec/active/active_record/translated_spec.rb +19 -0
- data/spec/active/active_record/uuid_spec.rb +72 -0
- data/spec/active/active_record/writer_spec.rb +272 -0
- data/spec/alchemy/alchemy-amq.rb +33 -0
- data/spec/client/augmented_array_spec.rb +15 -0
- data/spec/client/augmented_base_spec.rb +50 -0
- data/spec/client/augmented_hash_spec.rb +15 -0
- data/spec/client/client_spec.rb +955 -0
- data/spec/client/endpoint/endpoint_spec.rb +70 -0
- data/spec/client/endpoint/endpoints/amqp_spec.rb +16 -0
- data/spec/client/endpoint/endpoints/auto_session_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_based_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_spec.rb +103 -0
- data/spec/client/endpoint/endpoints/not_found_spec.rb +35 -0
- data/spec/client/headers_spec.rb +172 -0
- data/spec/communicators/fast_spec.rb +9 -0
- data/spec/communicators/pool_spec.rb +339 -0
- data/spec/communicators/slow_spec.rb +15 -0
- data/spec/data/resources/caller_spec.rb +156 -0
- data/spec/data/resources/errors_spec.rb +22 -0
- data/spec/data/resources/log_spec.rb +20 -0
- data/spec/data/resources/session_spec.rb +15 -0
- data/spec/data/types/error_primitive_spec.rb +15 -0
- data/spec/data/types/permissions_defaults_spec.rb +25 -0
- data/spec/data/types/permissions_full_spec.rb +44 -0
- data/spec/data/types/permissions_resources_spec.rb +34 -0
- data/spec/data/types/permissions_spec.rb +37 -0
- data/spec/errors/error_descriptions_spec.rb +98 -0
- data/spec/errors/errors_spec.rb +346 -0
- data/spec/integration/service_actions_spec.rb +112 -0
- data/spec/logger/fast_writer_spec.rb +18 -0
- data/spec/logger/logger_spec.rb +259 -0
- data/spec/logger/slow_writer_spec.rb +144 -0
- data/spec/logger/writers/file_writer_spec.rb +37 -0
- data/spec/logger/writers/log_entries_dot_com_writer_spec.rb +29 -0
- data/spec/logger/writers/stream_writer_spec.rb +38 -0
- data/spec/presenters/base_dsl_spec.rb +111 -0
- data/spec/presenters/base_spec.rb +871 -0
- data/spec/presenters/common_resource_fields_spec.rb +30 -0
- data/spec/presenters/embedding_spec.rb +87 -0
- data/spec/presenters/types/array_spec.rb +249 -0
- data/spec/presenters/types/boolean_spec.rb +51 -0
- data/spec/presenters/types/date_spec.rb +57 -0
- data/spec/presenters/types/date_time_spec.rb +59 -0
- data/spec/presenters/types/decimal_spec.rb +58 -0
- data/spec/presenters/types/enum_spec.rb +71 -0
- data/spec/presenters/types/field_spec.rb +77 -0
- data/spec/presenters/types/float_spec.rb +50 -0
- data/spec/presenters/types/hash_spec.rb +1069 -0
- data/spec/presenters/types/integer_spec.rb +50 -0
- data/spec/presenters/types/object_spec.rb +177 -0
- data/spec/presenters/types/string_spec.rb +65 -0
- data/spec/presenters/types/tags_spec.rb +56 -0
- data/spec/presenters/types/text_spec.rb +50 -0
- data/spec/presenters/types/uuid_spec.rb +46 -0
- data/spec/presenters/walk_spec.rb +198 -0
- data/spec/services/discovery/discoverers/by_consul_spec.rb +29 -0
- data/spec/services/discovery/discoverers/by_convention_spec.rb +67 -0
- data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +80 -0
- data/spec/services/discovery/discoverers/by_drb/drb_server_spec.rb +205 -0
- data/spec/services/discovery/discovery_spec.rb +73 -0
- data/spec/services/discovery/results/for_amqp_spec.rb +17 -0
- data/spec/services/discovery/results/for_http_spec.rb +37 -0
- data/spec/services/discovery/results/for_local_spec.rb +21 -0
- data/spec/services/discovery/results/for_remote_spec.rb +15 -0
- data/spec/services/middleware/amqp_log_message_spec.rb +60 -0
- data/spec/services/middleware/amqp_log_writer_spec.rb +95 -0
- data/spec/services/middleware/endpoints/inter_resource_local_spec.rb +9 -0
- data/spec/services/middleware/endpoints/inter_resource_remote_spec.rb +9 -0
- data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +16 -0
- data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +92 -0
- data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +24 -0
- data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +23 -0
- data/spec/services/middleware/middleware_cors_spec.rb +93 -0
- data/spec/services/middleware/middleware_create_update_spec.rb +489 -0
- data/spec/services/middleware/middleware_dated_at_spec.rb +186 -0
- data/spec/services/middleware/middleware_exotic_communication_spec.rb +560 -0
- data/spec/services/middleware/middleware_logging_spec.rb +356 -0
- data/spec/services/middleware/middleware_multi_local_spec.rb +1094 -0
- data/spec/services/middleware/middleware_multi_remote_spec.rb +1440 -0
- data/spec/services/middleware/middleware_permissions_spec.rb +1014 -0
- data/spec/services/middleware/middleware_public_spec.rb +238 -0
- data/spec/services/middleware/middleware_spec.rb +1569 -0
- data/spec/services/middleware/string_inquirer_spec.rb +30 -0
- data/spec/services/services/application_spec.rb +74 -0
- data/spec/services/services/context_spec.rb +48 -0
- data/spec/services/services/implementation_spec.rb +45 -0
- data/spec/services/services/interface_spec.rb +262 -0
- data/spec/services/services/permissions_spec.rb +249 -0
- data/spec/services/services/request_spec.rb +95 -0
- data/spec/services/services/response_spec.rb +250 -0
- data/spec/services/services/session_spec.rb +432 -0
- data/spec/spec_helper.rb +298 -0
- data/spec/utilities/utilities_spec.rb +537 -0
- data/spec/utilities/uuid_spec.rb +20 -0
- 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
|