hoodoo 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/lib/hoodoo/client/headers.rb +33 -0
- data/lib/hoodoo/services/middleware/middleware.rb +172 -3
- data/lib/hoodoo/version.rb +1 -1
- data/spec/client/client_spec.rb +66 -22
- data/spec/client/headers_spec.rb +29 -0
- data/spec/services/middleware/middleware_assumed_identity_spec.rb +651 -0
- data/spec/services/middleware/middleware_multi_local_spec.rb +145 -13
- data/spec/services/middleware/middleware_multi_remote_spec.rb +145 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzEwZWNjMTA5OTJhODlhN2VkZGQxMDQ0NzNhOGVjZjUxMTM2ZmM1Nw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OTA1MzJjZTZlYzFhY2FiNWUxZWM3MDBiN2QwYzEwNDYyNDM2Y2ZjOA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZDQ4YmVkYmQ3YWEwMWY1NGU1ZDZlYTA2NjAxNzM2NTFiYzk5NjI1NTNmZWE0
|
10
|
+
NGYwYjhlMmIxZmY0YTViZjk4Zjg4ODgzODRjNGQ5MDhmNjEwZjVmZDJhM2Yx
|
11
|
+
ODQzZjliM2NhYTE1NTQ2ZjdjNzk1YTZmNmE3NTM2MGQ2NWFhNjE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODhiYjgzMTFmYTZhOGFhZGM4NDM2OWU3N2MwNjE3OGVhMTk0MGFkNGFhZTg5
|
14
|
+
Y2E5Mzg0YjI3OTBkNzcyNjJhZGI5MjYyN2M5YTYxOWYyYjVjMTgxMGRiM2Ix
|
15
|
+
MGQ4YTBiNWY4MzMzNTI3NWJkODI3ZTE0YWJmYjlkYjI3YWQ4MTg=
|
@@ -35,6 +35,29 @@ module Hoodoo
|
|
35
35
|
#
|
36
36
|
UUID_HEADER_PROC = -> ( value ) { value }
|
37
37
|
|
38
|
+
# Used by HEADER_TO_PROPERTY; this Proc when called with some non-nil
|
39
|
+
# value from an HTTP header containing URL-encoded simple key/value
|
40
|
+
# pair data returns a decoded Hash of key/value pairs. Use URL encoding
|
41
|
+
# in the HTTP header value as per:
|
42
|
+
#
|
43
|
+
# http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
|
44
|
+
#
|
45
|
+
# Invalid input will produce unusual results, e.g. an empty Hash or a
|
46
|
+
# Hash where certain keys may have empty string values.
|
47
|
+
#
|
48
|
+
KVP_PROPERTY_PROC = -> ( value ) {
|
49
|
+
Hash[ URI.decode_www_form( value ) ]
|
50
|
+
}
|
51
|
+
|
52
|
+
# Used by HEADER_TO_PROPERTY; this Proc when called with some non-nested
|
53
|
+
# Hash evaluates to a URL-encoded form data String as per:
|
54
|
+
#
|
55
|
+
# http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
|
56
|
+
#
|
57
|
+
KVP_HEADER_PROC = -> ( value ) {
|
58
|
+
URI.encode_www_form( value )
|
59
|
+
}
|
60
|
+
|
38
61
|
# Used by HEADER_TO_PROPERTY; this Proc when called with some non-nil
|
39
62
|
# value from an HTTP header representing a Date/Time in a supported
|
40
63
|
# format, evaluates to either a parsed DateTime instance or +nil+ if the
|
@@ -152,6 +175,16 @@ module Hoodoo
|
|
152
175
|
:secured => true,
|
153
176
|
},
|
154
177
|
|
178
|
+
'HTTP_X_ASSUME_IDENTITY_OF' => {
|
179
|
+
:property => :assume_identity_of,
|
180
|
+
:property_proc => KVP_PROPERTY_PROC,
|
181
|
+
:header => 'X-Assume-Identity-Of',
|
182
|
+
:header_proc => KVP_HEADER_PROC,
|
183
|
+
|
184
|
+
:secured => true,
|
185
|
+
:auto_transfer => true,
|
186
|
+
},
|
187
|
+
|
155
188
|
'HTTP_X_DATED_AT' => {
|
156
189
|
:property => :dated_at,
|
157
190
|
:property_proc => DATETIME_IN_PAST_ONLY_PROPERTY_PROC,
|
@@ -758,6 +758,10 @@ module Hoodoo; module Services
|
|
758
758
|
|
759
759
|
return add_local_errors.call() if local_response.halt_processing?
|
760
760
|
|
761
|
+
deal_with_x_assume_identity_of( local_interaction )
|
762
|
+
|
763
|
+
return add_local_errors.call() if local_response.halt_processing?
|
764
|
+
|
761
765
|
# Construct the local request details.
|
762
766
|
|
763
767
|
local_request.uri_path_components = upc
|
@@ -1186,7 +1190,7 @@ module Hoodoo; module Services
|
|
1186
1190
|
end
|
1187
1191
|
|
1188
1192
|
if secure == false || level == :error
|
1189
|
-
body =
|
1193
|
+
body = String.new
|
1190
1194
|
rack_data[ 2 ].each { | thing | body << thing.to_s }
|
1191
1195
|
|
1192
1196
|
if interaction.context.response.halt_processing?
|
@@ -1547,10 +1551,13 @@ module Hoodoo; module Services
|
|
1547
1551
|
|
1548
1552
|
# Load the session and then, in the context of a loaded session, process
|
1549
1553
|
# any remaining extension ("X-...") HTTP headers, checking up on secured
|
1550
|
-
# headers in passing.
|
1554
|
+
# headers in passing. There's special handling for X-Assume-Identity-Of,
|
1555
|
+
# which may update the session data loaded into 'interaction' with new
|
1556
|
+
# identity information.
|
1551
1557
|
|
1552
1558
|
load_session_into( interaction )
|
1553
1559
|
deal_with_x_headers( interaction )
|
1560
|
+
deal_with_x_assume_identity_of( interaction )
|
1554
1561
|
|
1555
1562
|
return nil
|
1556
1563
|
end
|
@@ -1934,6 +1941,167 @@ module Hoodoo; module Services
|
|
1934
1941
|
end
|
1935
1942
|
end
|
1936
1943
|
|
1944
|
+
# The X-Assume-Identity-Of secured HTTP header allows a caller to specify
|
1945
|
+
# values for parts of their session's "identity" section, based upon
|
1946
|
+
# permitted values described in their session's "scoping" section. This
|
1947
|
+
# method assumes that the permission to use the header in the first place
|
1948
|
+
# has already been established by #deal_with_x_headers and, as a result,
|
1949
|
+
# relevant property information has been written into the request object.
|
1950
|
+
#
|
1951
|
+
# The header's value is parsed and checked against the session scoping
|
1952
|
+
# data. If everything looks good, the loaded session's identity is
|
1953
|
+
# updated accordingly. If there are any problems, one or more errors will
|
1954
|
+
# be added to the interaction's context's response object.
|
1955
|
+
#
|
1956
|
+
# +interaction+:: Hoodoo::Services::Middleware::Interaction instance
|
1957
|
+
# describing the current interaction. Updated on exit.
|
1958
|
+
#
|
1959
|
+
def deal_with_x_assume_identity_of( interaction )
|
1960
|
+
|
1961
|
+
# Header not in use? Exit now.
|
1962
|
+
#
|
1963
|
+
return if interaction.context.request.assume_identity_of.nil?
|
1964
|
+
|
1965
|
+
input_hash = interaction.context.request.assume_identity_of
|
1966
|
+
rules_hash = interaction.context.session.scoping.authorised_identities rescue {}
|
1967
|
+
|
1968
|
+
if ( input_hash.empty? )
|
1969
|
+
interaction.context.response.errors.add_error(
|
1970
|
+
'generic.malformed',
|
1971
|
+
{
|
1972
|
+
:message => "X-Assume-Identity-Of header value is malformed",
|
1973
|
+
:reference => { :header_value => ( interaction.context.request.assume_identity_of rescue 'unknown' ) }
|
1974
|
+
}
|
1975
|
+
)
|
1976
|
+
end
|
1977
|
+
|
1978
|
+
return if interaction.context.response.halt_processing?
|
1979
|
+
|
1980
|
+
identity_overrides = validate_x_assume_identity_of( interaction, input_hash, rules_hash )
|
1981
|
+
|
1982
|
+
return if interaction.context.response.halt_processing?
|
1983
|
+
|
1984
|
+
identity_overrides.each do | key, value |
|
1985
|
+
interaction.context.session.identity.send( "#{ key }=", value )
|
1986
|
+
end
|
1987
|
+
end
|
1988
|
+
|
1989
|
+
# Back-end to #deal_with_x_assume_identity_of which recursively processes
|
1990
|
+
# a rule set against a value from the X-Assume-Identity-Of HTTP header and
|
1991
|
+
# either updates the interaction's context's response object with error
|
1992
|
+
# details if anything is wrong, or returns a flat Hash of keys and values
|
1993
|
+
# to (over-)write in the session's identity section.
|
1994
|
+
#
|
1995
|
+
# +interaction+:: Hoodoo::Services::Middleware::Interaction instance
|
1996
|
+
# describing the current interaction. Will be updated on
|
1997
|
+
# exit if errors occur.
|
1998
|
+
#
|
1999
|
+
# +input_hash+:: Header value for X-Assume-Identity-Of processed into a
|
2000
|
+
# flat Hash of String keys and String values.
|
2001
|
+
#
|
2002
|
+
# +rules_hash+:: Rules Hash from the session scoping data - usually its
|
2003
|
+
# "authorised_identities" key - or a sub-hash from nested
|
2004
|
+
# data during recursive calls.
|
2005
|
+
#
|
2006
|
+
# +recursive+:: Top-level callers MUST omit this parameter. Internal
|
2007
|
+
# recursive callers MUST set this to +true+.
|
2008
|
+
#
|
2009
|
+
def validate_x_assume_identity_of( interaction, input_hash, rules_hash, recursive = false )
|
2010
|
+
identity_overrides = {}
|
2011
|
+
|
2012
|
+
unless rules_hash.is_a?( Hash )
|
2013
|
+
interaction.context.response.errors.add_error(
|
2014
|
+
'generic.malformed',
|
2015
|
+
:message => "X-Assume-Identity-Of header cannot be processed because of malformed scoping rules in Session's associated Caller",
|
2016
|
+
)
|
2017
|
+
|
2018
|
+
return nil
|
2019
|
+
end
|
2020
|
+
|
2021
|
+
rules_hash.each do | rules_key, rules_value |
|
2022
|
+
|
2023
|
+
next unless input_hash.has_key?( rules_key )
|
2024
|
+
input_value = input_hash[ rules_key ]
|
2025
|
+
|
2026
|
+
unless input_value.is_a?( String )
|
2027
|
+
raise "Internal error - internal validation input value for X-Assume-Identity-Of is not a String"
|
2028
|
+
end
|
2029
|
+
|
2030
|
+
if rules_value.is_a?( Array )
|
2031
|
+
if rules_value.include?( input_value )
|
2032
|
+
identity_overrides[ rules_key ] = input_value
|
2033
|
+
else
|
2034
|
+
interaction.context.response.errors.add_error(
|
2035
|
+
'platform.forbidden',
|
2036
|
+
{
|
2037
|
+
:message => "X-Assume-Identity-Of header value requests a prohibited identity quantity",
|
2038
|
+
:reference =>
|
2039
|
+
{
|
2040
|
+
:name => rules_key,
|
2041
|
+
:value => input_value
|
2042
|
+
}
|
2043
|
+
}
|
2044
|
+
)
|
2045
|
+
return nil
|
2046
|
+
end
|
2047
|
+
|
2048
|
+
elsif rules_value.is_a?( Hash )
|
2049
|
+
if rules_value.has_key?( input_value )
|
2050
|
+
identity_overrides[ rules_key ] = input_value
|
2051
|
+
|
2052
|
+
nested_identity_overrides = validate_x_assume_identity_of(
|
2053
|
+
interaction,
|
2054
|
+
input_hash,
|
2055
|
+
rules_value[ input_value ],
|
2056
|
+
true
|
2057
|
+
)
|
2058
|
+
|
2059
|
+
return if nested_identity_overrides.nil?
|
2060
|
+
identity_overrides.merge!( nested_identity_overrides )
|
2061
|
+
|
2062
|
+
else
|
2063
|
+
interaction.context.response.errors.add_error(
|
2064
|
+
'platform.forbidden',
|
2065
|
+
{
|
2066
|
+
:message => "X-Assume-Identity-Of header value requests a prohibited identity quantity",
|
2067
|
+
:reference =>
|
2068
|
+
{
|
2069
|
+
:name => rules_key,
|
2070
|
+
:value => input_value
|
2071
|
+
}
|
2072
|
+
}
|
2073
|
+
)
|
2074
|
+
return nil
|
2075
|
+
|
2076
|
+
end
|
2077
|
+
|
2078
|
+
else
|
2079
|
+
interaction.context.response.errors.add_error(
|
2080
|
+
'generic.malformed',
|
2081
|
+
:message => "X-Assume-Identity-Of header cannot be processed because of malformed scoping rules in Session's associated Caller",
|
2082
|
+
)
|
2083
|
+
return nil
|
2084
|
+
|
2085
|
+
end
|
2086
|
+
end
|
2087
|
+
|
2088
|
+
unless recursive || ( input_hash.keys - identity_overrides.keys ).empty?
|
2089
|
+
interaction.context.response.errors.add_error(
|
2090
|
+
'platform.forbidden',
|
2091
|
+
{
|
2092
|
+
:message => "X-Assume-Identity-Of header value requests prohibited identity name(s)",
|
2093
|
+
:reference =>
|
2094
|
+
{
|
2095
|
+
:names => ( input_hash.keys - identity_overrides.keys ).sort().join( ',' )
|
2096
|
+
}
|
2097
|
+
}
|
2098
|
+
)
|
2099
|
+
return nil
|
2100
|
+
end
|
2101
|
+
|
2102
|
+
return identity_overrides
|
2103
|
+
end
|
2104
|
+
|
1937
2105
|
# Preprocessing stage that sets up common headers required in any response.
|
1938
2106
|
# May vary according to inbound content type requested. If processing was
|
1939
2107
|
# aborted early (e.g. missing inbound Content-Type) we may fall to defaults.
|
@@ -1941,7 +2109,8 @@ module Hoodoo; module Services
|
|
1941
2109
|
# (At the time of writing, platform documentations say we're JSON only - but
|
1942
2110
|
# there's an strong chance of e.g. XML representation being demanded later).
|
1943
2111
|
#
|
1944
|
-
# +
|
2112
|
+
# +interaction+:: Hoodoo::Services::Middleware::Interaction instance
|
2113
|
+
# describing the current interaction. Updated on exit.
|
1945
2114
|
#
|
1946
2115
|
def set_common_response_headers( interaction )
|
1947
2116
|
interaction.context.response.add_header( 'X-Interaction-ID', interaction.interaction_id )
|
data/lib/hoodoo/version.rb
CHANGED
data/spec/client/client_spec.rb
CHANGED
@@ -146,20 +146,22 @@ class RSpecClientTestTargetImplementation < Hoodoo::Services::Implementation
|
|
146
146
|
# if adding things.
|
147
147
|
|
148
148
|
{
|
149
|
-
'id'
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
'created_at'
|
154
|
-
'kind'
|
155
|
-
'language'
|
156
|
-
|
157
|
-
'embeds'
|
158
|
-
'body_hash'
|
159
|
-
'dated_at'
|
160
|
-
'dated_from'
|
161
|
-
'resource_uuid'
|
162
|
-
'deja_vu'
|
149
|
+
'id' => context.request.ident ||
|
150
|
+
context.request.body.try( :[], 'id' ) ||
|
151
|
+
Hoodoo::UUID.generate(),
|
152
|
+
|
153
|
+
'created_at' => Time.now.utc.iso8601,
|
154
|
+
'kind' => 'RSpecClientTestTarget',
|
155
|
+
'language' => context.request.locale,
|
156
|
+
|
157
|
+
'embeds' => context.request.embeds,
|
158
|
+
'body_hash' => context.request.body,
|
159
|
+
'dated_at' => context.request.dated_at.nil? ? nil : Hoodoo::Utilities.nanosecond_iso8601( context.request.dated_at ),
|
160
|
+
'dated_from' => context.request.dated_from.nil? ? nil : Hoodoo::Utilities.nanosecond_iso8601( context.request.dated_from ),
|
161
|
+
'resource_uuid' => context.request.resource_uuid,
|
162
|
+
'deja_vu' => context.request.deja_vu,
|
163
|
+
'assume_identity_of' => context.request.assume_identity_of,
|
164
|
+
'actual_identity' => ( context.session.identity.to_h rescue nil ),
|
163
165
|
}
|
164
166
|
end
|
165
167
|
end
|
@@ -213,6 +215,8 @@ describe Hoodoo::Client do
|
|
213
215
|
@old_test_session = Hoodoo::Services::Middleware.test_session()
|
214
216
|
@port = spec_helper_start_svc_app_in_thread_for( RSpecClientTestService )
|
215
217
|
@https_port = spec_helper_start_svc_app_in_thread_for( RSpecClientTestService, true )
|
218
|
+
@authorised_identities = { 'member_id' => [ '23', '24' ] }
|
219
|
+
@example_authorised_identity = { 'member_id' => '23' }
|
216
220
|
end
|
217
221
|
|
218
222
|
after :all do
|
@@ -249,15 +253,17 @@ describe Hoodoo::Client do
|
|
249
253
|
# "def option_based_expectations" later in this file. Be careful
|
250
254
|
# to follow the naming convention evident below if adding things.
|
251
255
|
|
252
|
-
@expected_dated_at
|
253
|
-
@expected_dated_from
|
254
|
-
@expected_resource_uuid
|
255
|
-
@
|
256
|
+
@expected_dated_at = @dated_at.nil? ? nil : Hoodoo::Utilities.nanosecond_iso8601( @dated_at )
|
257
|
+
@expected_dated_from = @dated_from.nil? ? nil : Hoodoo::Utilities.nanosecond_iso8601( @dated_from )
|
258
|
+
@expected_resource_uuid = @resource_uuid
|
259
|
+
@expected_assume_identity_of = @assume_identity_of
|
260
|
+
@expected_deja_vu = @deja_vu != true ? nil : true
|
256
261
|
|
257
|
-
endpoint_opts[ :dated_at
|
258
|
-
endpoint_opts[ :dated_from
|
259
|
-
endpoint_opts[ :resource_uuid
|
260
|
-
endpoint_opts[ :
|
262
|
+
endpoint_opts[ :dated_at ] = @dated_at unless @dated_at.nil?
|
263
|
+
endpoint_opts[ :dated_from ] = @dated_from unless @dated_from.nil?
|
264
|
+
endpoint_opts[ :resource_uuid ] = @resource_uuid unless @resource_uuid.nil?
|
265
|
+
endpoint_opts[ :assume_identity_of ] = @assume_identity_of unless @assume_identity_of.nil?
|
266
|
+
endpoint_opts[ :deja_vu ] = @deja_vu if @deja_vu == true
|
261
267
|
|
262
268
|
if rand( 2 ) == 0
|
263
269
|
override_locale = SecureRandom.urlsafe_base64( 2 )
|
@@ -508,6 +514,8 @@ describe Hoodoo::Client do
|
|
508
514
|
case property
|
509
515
|
when :resource_uuid
|
510
516
|
@resource_uuid = Hoodoo::UUID.generate
|
517
|
+
when :assume_identity_of
|
518
|
+
@assume_identity_of = @example_authorised_identity
|
511
519
|
else
|
512
520
|
raise "Update client_spec.rb with new secured properties for test"
|
513
521
|
end
|
@@ -725,8 +733,10 @@ describe Hoodoo::Client do
|
|
725
733
|
context 'and with secured option' do
|
726
734
|
before :each do
|
727
735
|
test_session = @old_test_session.dup
|
736
|
+
test_session.identity = OpenStruct.new
|
728
737
|
test_session.scoping = @old_test_session.scoping.dup
|
729
738
|
test_session.scoping.authorised_http_headers = []
|
739
|
+
test_session.scoping.authorised_identities = @authorised_identities
|
730
740
|
|
731
741
|
Hoodoo::Client::Headers::HEADER_TO_PROPERTY.each do | rack_header, description |
|
732
742
|
next unless description[ :secured ] == true
|
@@ -750,6 +760,8 @@ describe Hoodoo::Client do
|
|
750
760
|
case property
|
751
761
|
when :resource_uuid
|
752
762
|
@resource_uuid = Hoodoo::UUID.generate
|
763
|
+
when :assume_identity_of
|
764
|
+
@assume_identity_of = @example_authorised_identity
|
753
765
|
else
|
754
766
|
raise "Update client_spec.rb with new secured properties for test"
|
755
767
|
end
|
@@ -763,6 +775,8 @@ describe Hoodoo::Client do
|
|
763
775
|
result = @endpoint.show( mock_ident )
|
764
776
|
|
765
777
|
expect( result.platform_errors.has_errors? ).to eq( false )
|
778
|
+
|
779
|
+
option_based_expectations( result )
|
766
780
|
end
|
767
781
|
end
|
768
782
|
|
@@ -781,6 +795,36 @@ describe Hoodoo::Client do
|
|
781
795
|
expect( result.platform_errors.has_errors? ).to eq( false )
|
782
796
|
expect( result[ 'id' ] ).to eq( @resource_uuid )
|
783
797
|
end
|
798
|
+
|
799
|
+
context "'assume_identity_of' in use" do
|
800
|
+
it 'but invalid' do
|
801
|
+
@assume_identity_of = { 'invalid' => 'Hoodoo::UUID.generate' }
|
802
|
+
|
803
|
+
set_vars_for(
|
804
|
+
base_uri: "http://localhost:#{ @port }",
|
805
|
+
auto_session: false
|
806
|
+
)
|
807
|
+
|
808
|
+
result = @endpoint.create( { 'hello' => 'world' } )
|
809
|
+
|
810
|
+
expect( result.platform_errors.has_errors? ).to eq( true )
|
811
|
+
expect( result.platform_errors.errors[ 0 ][ 'code' ] ).to eq( 'platform.forbidden' )
|
812
|
+
end
|
813
|
+
|
814
|
+
it 'and valid' do
|
815
|
+
@assume_identity_of = @example_authorised_identity
|
816
|
+
|
817
|
+
set_vars_for(
|
818
|
+
base_uri: "http://localhost:#{ @port }",
|
819
|
+
auto_session: false
|
820
|
+
)
|
821
|
+
|
822
|
+
result = @endpoint.create( { 'hello' => 'world' } )
|
823
|
+
|
824
|
+
expect( result.platform_errors.has_errors? ).to eq( false )
|
825
|
+
expect( result[ 'actual_identity' ] ).to eq( @example_authorised_identity )
|
826
|
+
end
|
827
|
+
end
|
784
828
|
end
|
785
829
|
end
|
786
830
|
|
data/spec/client/headers_spec.rb
CHANGED
@@ -23,6 +23,35 @@ describe Hoodoo::Client::Headers do
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
context 'for URL encoded data' do
|
27
|
+
before :each do
|
28
|
+
@test_hash =
|
29
|
+
{
|
30
|
+
'foo' => "hello, world; this & that = foo! \r\t",
|
31
|
+
'bar' => "foo;bar=baz & this + UTF-8 / emoji 😀"
|
32
|
+
}
|
33
|
+
|
34
|
+
@test_string = URI::encode_www_form( @test_hash )
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'KVP_PROPERTY_PROC' do
|
38
|
+
it 'converts valid values' do
|
39
|
+
expect( described_class::KVP_PROPERTY_PROC.call( @test_string ) ).to eq( @test_hash )
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does not raise exceptions for invalid values' do
|
43
|
+
expect( described_class::KVP_PROPERTY_PROC.call( '' ) ).to eq( {} )
|
44
|
+
expect( described_class::KVP_PROPERTY_PROC.call( 'hello' ) ).to eq( { 'hello' => '' } )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'KVP_HEADER_PROC' do
|
49
|
+
it 'converts values' do
|
50
|
+
expect( described_class::KVP_HEADER_PROC.call( @test_hash ) ).to eq( @test_string )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
26
55
|
context 'DATETIME_IN_PAST_ONLY_PROPERTY_PROC' do
|
27
56
|
it 'converts valid values' do
|
28
57
|
date_time = DateTime.now - 10.seconds
|