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.
- 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,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'raygun4ruby'
|
3
|
+
|
4
|
+
# This doesn't test the Airbrake gem / configuration itself - just check that
|
5
|
+
# the appropriate Airbrake method gets called.
|
6
|
+
|
7
|
+
describe Hoodoo::Services::Middleware::ExceptionReporting::AirbrakeReporter do
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
Hoodoo::Services::Middleware::ExceptionReporting.add( described_class )
|
11
|
+
end
|
12
|
+
|
13
|
+
after :each do
|
14
|
+
Hoodoo::Services::Middleware::ExceptionReporting.wait()
|
15
|
+
Hoodoo::Services::Middleware::ExceptionReporting.remove( described_class )
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'calls Airbrake' do
|
19
|
+
Hoodoo::Services::Middleware::ExceptionReporting.add( described_class )
|
20
|
+
ex = RuntimeError.new( 'A' )
|
21
|
+
expect( Airbrake ).to receive( :notify_or_ignore ).once.with( ex, { :rack_env => nil } )
|
22
|
+
Hoodoo::Services::Middleware::ExceptionReporting.report( ex )
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'airbrake'
|
3
|
+
|
4
|
+
# This doesn't test the Raygun gem / configuration itself - just check that
|
5
|
+
# the appropriate Raygun method gets called.
|
6
|
+
|
7
|
+
describe Hoodoo::Services::Middleware::ExceptionReporting::RaygunReporter do
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
Hoodoo::Services::Middleware::ExceptionReporting.add( described_class )
|
11
|
+
end
|
12
|
+
|
13
|
+
after :each do
|
14
|
+
Hoodoo::Services::Middleware::ExceptionReporting.wait()
|
15
|
+
Hoodoo::Services::Middleware::ExceptionReporting.remove( described_class )
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'calls Raygun' do
|
19
|
+
ex = RuntimeError.new( 'A' )
|
20
|
+
expect( Raygun ).to receive( :track_exception ).once.with( ex, nil )
|
21
|
+
Hoodoo::Services::Middleware::ExceptionReporting.report( ex )
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestCORSImplementation < Hoodoo::Services::Implementation
|
4
|
+
def show( context )
|
5
|
+
context.response.body = { 'show' => 'the thing', 'the_thing' => context.request.ident }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class TestCORSInterface < Hoodoo::Services::Interface
|
10
|
+
interface :TestCORS do
|
11
|
+
endpoint :test_cors, TestCORSImplementation
|
12
|
+
actions :show
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TestCORSService < Hoodoo::Services::Service
|
17
|
+
comprised_of TestCORSInterface
|
18
|
+
end
|
19
|
+
|
20
|
+
describe Hoodoo::Services::Middleware do
|
21
|
+
def app
|
22
|
+
Rack::Builder.new do
|
23
|
+
use Hoodoo::Services::Middleware
|
24
|
+
run TestCORSService.new
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'preflight' do
|
29
|
+
it 'accepts a valid request' do
|
30
|
+
origin = 'http://localhost'
|
31
|
+
|
32
|
+
options '/v1/test_cors/hello', nil, {
|
33
|
+
'HTTP_ORIGIN' => origin,
|
34
|
+
'HTTP_ACCESS_CONTROL_REQUEST_METHOD' => 'GET',
|
35
|
+
'HTTP_ACCESS_CONTROL_REQUEST_HEADERS' => 'Content-Type, X-SESSION_ID'
|
36
|
+
}
|
37
|
+
|
38
|
+
expect(last_response.status).to eq(200)
|
39
|
+
|
40
|
+
expect(last_response.headers['Access-Control-Allow-Origin']).to eq(origin)
|
41
|
+
expect(last_response.headers['Access-Control-Allow-Methods']).to eq(Hoodoo::Services::Middleware::ALLOWED_HTTP_METHODS.to_a.join(', '))
|
42
|
+
expect(last_response.headers['Access-Control-Allow-Headers']).to eq('Content-Type, X-SESSION_ID')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'refuses preflight without an Origin header' do
|
46
|
+
|
47
|
+
# Without an Origin this doesn't look like a CORS request, but in that
|
48
|
+
# case the Content-Type 422 will normally get us. Provide a Content-Type
|
49
|
+
# here just to make sure that the "invalid HTTP method" code gets a
|
50
|
+
# chance to catch it.
|
51
|
+
|
52
|
+
options '/v1/test_cors/hello', nil, {
|
53
|
+
'CONTENT_TYPE' => 'application/json; charset=utf-8',
|
54
|
+
'HTTP_ACCESS_CONTROL_REQUEST_METHOD' => 'GET'
|
55
|
+
}
|
56
|
+
|
57
|
+
expect(last_response.status).to eq(405)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'refuses unsupported methods' do
|
61
|
+
options '/v1/test_cors/hello', nil, {
|
62
|
+
'HTTP_ORIGIN' => 'http://localhost',
|
63
|
+
'HTTP_ACCESS_CONTROL_REQUEST_METHOD' => 'PUT' # We use PATCH not PUT
|
64
|
+
}
|
65
|
+
|
66
|
+
expect(last_response.status).to eq(405)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'other request' do
|
71
|
+
it 'quotes the origin' do
|
72
|
+
origin = 'http://localhost'
|
73
|
+
|
74
|
+
get '/v1/test_cors/hello', nil, {
|
75
|
+
'CONTENT_TYPE' => 'application/json; charset=utf-8',
|
76
|
+
'HTTP_ORIGIN' => origin
|
77
|
+
}
|
78
|
+
|
79
|
+
expect(last_response.status).to eq(200)
|
80
|
+
expect(last_response.headers['Access-Control-Allow-Origin']).to eq(origin)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'understands non-CORS requests' do
|
84
|
+
get '/v1/test_cors/hello', nil, {
|
85
|
+
'CONTENT_TYPE' => 'application/json; charset=utf-8'
|
86
|
+
}
|
87
|
+
|
88
|
+
expect(last_response.status).to eq(200)
|
89
|
+
expect(last_response.headers['Access-Control-Allow-Origin']).to be_nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,489 @@
|
|
1
|
+
# Specific tests for behaviour around the middleware's enforcement of
|
2
|
+
# the to_create/to_update DSL through service interfaces and validation
|
3
|
+
# of inbound payloads.
|
4
|
+
#
|
5
|
+
# Since it relies upon the same test setup, the X-Resource-UUID secured
|
6
|
+
# header is tested here - both secure headers as a mechanism, and that
|
7
|
+
# particular header allowing in a value for a resource ID which gets
|
8
|
+
# passed in as an "id" field in the effective body data.
|
9
|
+
|
10
|
+
require 'spec_helper.rb'
|
11
|
+
|
12
|
+
# Resource "A" describes different to-create/to-update data as a set
|
13
|
+
# of explicit declarations.
|
14
|
+
|
15
|
+
class RSpecToUpdateToCreateTestAImplementation < Hoodoo::Services::Implementation
|
16
|
+
def create( context ); context.response.body = context.request.body; end
|
17
|
+
def update( context ); context.response.body = context.request.body; end
|
18
|
+
end
|
19
|
+
|
20
|
+
class RSpecToUpdateToCreateTestAInterface < Hoodoo::Services::Interface
|
21
|
+
interface :RSpecToUpdateToCreateTestA do
|
22
|
+
endpoint :r_spec_to_update_to_create_test_a, RSpecToUpdateToCreateTestAImplementation
|
23
|
+
|
24
|
+
# Default values in to-create blocks are irrelevant as they're for
|
25
|
+
# rendering only. Expectations in tests for the hashes "seen" by
|
26
|
+
# the mock resource implementations check this because the resources
|
27
|
+
# return "context.response.body" as-is, so if default values were
|
28
|
+
# leaked into that, tests would fail.
|
29
|
+
|
30
|
+
to_create do
|
31
|
+
text :foo
|
32
|
+
integer :bar, :required => true
|
33
|
+
integer :defaulted, :default => 42
|
34
|
+
object :nested do
|
35
|
+
text :thing
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Required values in to-update blocks are ignored. There is explicit
|
40
|
+
# test coverage for this. Default values are handled the same way as
|
41
|
+
# with to-create.
|
42
|
+
|
43
|
+
to_update do
|
44
|
+
boolean :foo, :required => true
|
45
|
+
enum :bar, :from => [ :foo, :bar, :baz ]
|
46
|
+
integer :defaulted, :default => 24
|
47
|
+
object :nested do
|
48
|
+
text :thing
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Resource "B" describes to-create/to-update data in terms of Hoodoo
|
55
|
+
# Resources and Types. Common resource fields like 'id', 'kind' etc.
|
56
|
+
# should still _not_ be permitted for inbound creation or updates.
|
57
|
+
|
58
|
+
class RSpecToUpdateToCreateTestBImplementation < Hoodoo::Services::Implementation
|
59
|
+
def create( context ); context.response.body = context.request.body; end
|
60
|
+
def update( context ); context.response.body = context.request.body; end
|
61
|
+
end
|
62
|
+
|
63
|
+
class RSpecToUpdateToCreateTestBInterface < Hoodoo::Services::Interface
|
64
|
+
interface :RSpecToUpdateToCreateTestB do
|
65
|
+
endpoint :r_spec_to_update_to_create_test_b, RSpecToUpdateToCreateTestBImplementation
|
66
|
+
|
67
|
+
to_create do
|
68
|
+
resource :Errors
|
69
|
+
type :ErrorPrimitive
|
70
|
+
end
|
71
|
+
|
72
|
+
to_update do
|
73
|
+
resource :Session
|
74
|
+
type :Permissions
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Resource "C" describes nothing special, to check that common fields
|
80
|
+
# are still rejected for creations and updates.
|
81
|
+
|
82
|
+
class RSpecToUpdateToCreateTestCImplementation < Hoodoo::Services::Implementation
|
83
|
+
def create( context ); context.response.body = context.request.body; end
|
84
|
+
def update( context ); context.response.body = context.request.body; end
|
85
|
+
end
|
86
|
+
|
87
|
+
class RSpecToUpdateToCreateTestCInterface < Hoodoo::Services::Interface
|
88
|
+
interface :RSpecToUpdateToCreateTestC do
|
89
|
+
endpoint :r_spec_to_update_to_create_test_c, RSpecToUpdateToCreateTestCImplementation
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Put them all in the same service application for simplicity.
|
94
|
+
|
95
|
+
class RSpecToUpdateToCreateTestService < Hoodoo::Services::Service
|
96
|
+
comprised_of RSpecToUpdateToCreateTestAInterface,
|
97
|
+
RSpecToUpdateToCreateTestBInterface
|
98
|
+
|
99
|
+
# Paranoid implicit check that multiple "comprised_of" calls still work :-)
|
100
|
+
|
101
|
+
comprised_of RSpecToUpdateToCreateTestCInterface
|
102
|
+
end
|
103
|
+
|
104
|
+
# Finally, the tests.
|
105
|
+
|
106
|
+
describe Hoodoo::Services::Middleware do
|
107
|
+
def app
|
108
|
+
Rack::Builder.new do
|
109
|
+
use Hoodoo::Services::Middleware
|
110
|
+
run RSpecToUpdateToCreateTestService.new
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def do_post( variant, hash, headers = {} ) # Variant is :a or :b
|
115
|
+
post "/v1/r_spec_to_update_to_create_test_#{ variant }/",
|
116
|
+
hash.nil? ? '' : JSON.generate( hash ),
|
117
|
+
{ 'CONTENT_TYPE' => 'application/json; charset=utf-8' }.merge( headers )
|
118
|
+
|
119
|
+
expectations( hash )
|
120
|
+
end
|
121
|
+
|
122
|
+
def do_patch( variant, hash, headers = {} ) # Variant is :a or :b
|
123
|
+
patch "/v1/r_spec_to_update_to_create_test_#{ variant }/any",
|
124
|
+
hash.nil? ? '' : JSON.generate( hash ),
|
125
|
+
{ 'CONTENT_TYPE' => 'application/json; charset=utf-8' }.merge( headers )
|
126
|
+
|
127
|
+
expectations( hash )
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'accepts valid input' do
|
131
|
+
def expectations( hash )
|
132
|
+
expect( last_response.status ).to eq( 200 )
|
133
|
+
expect( JSON.parse( last_response.body ) ).to eq( hash )
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'with many fields' do
|
137
|
+
do_post( :a, 'foo' => 'hello', 'bar' => 42 )
|
138
|
+
do_patch( :a, 'foo' => true, 'bar' => 'foo' )
|
139
|
+
do_post( :b, 'code' => 'hello', 'message' => 'world', 'reference' => 'baz', 'errors' => [] )
|
140
|
+
do_patch( :b, 'actions' => { 'list' => 'allow' }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'with required fields only' do
|
144
|
+
do_post( :a, 'bar' => 42 )
|
145
|
+
do_patch( :a, 'foo' => true )
|
146
|
+
do_post( :b, 'code' => 'hello', 'message' => 'world', 'errors' => [] )
|
147
|
+
do_patch( :b, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
148
|
+
end
|
149
|
+
|
150
|
+
# Explicit nils should be preserved through rendering and
|
151
|
+
# accepted through the checking engine (for non-required data).
|
152
|
+
#
|
153
|
+
it 'with explicit nils' do
|
154
|
+
do_post( :a, 'foo' => nil, 'bar' => 42 )
|
155
|
+
do_patch( :a, 'foo' => true, 'bar' => nil )
|
156
|
+
do_post( :b, 'code' => 'hello', 'message' => 'world', 'reference' => nil, 'errors' => [] )
|
157
|
+
do_patch( :b, 'actions' => nil, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
158
|
+
do_patch( :b, 'actions' => { 'list' => nil }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'rejects unknown data' do
|
163
|
+
def expectations( hash )
|
164
|
+
expect( last_response.status ).to eq( 422 )
|
165
|
+
parsed = JSON.parse( last_response.body )
|
166
|
+
expect( parsed[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Body data contains unrecognised or prohibited fields' )
|
167
|
+
expect( parsed[ 'errors' ][ 0 ][ 'reference' ] ).to eq( 'random' )
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'with many fields' do
|
171
|
+
do_post( :a, 'foo' => 'hello', 'bar' => 42, 'random' => true )
|
172
|
+
do_patch( :a, 'foo' => true, 'bar' => 'foo', 'random' => true )
|
173
|
+
do_post( :b, 'code' => 'hello', 'random' => true, 'message' => 'world', 'reference' => 'baz', 'errors' => [] )
|
174
|
+
do_patch( :b, 'actions' => { 'list' => 'allow' }, 'random' => true, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'with required fields only' do
|
178
|
+
do_post( :a, 'bar' => 42, 'random' => true )
|
179
|
+
do_patch( :a, 'foo' => true, 'random' => true )
|
180
|
+
do_post( :b, 'code' => 'hello', 'random' => true, 'message' => 'world', 'errors' => [] )
|
181
|
+
do_patch( :b, 'random' => true, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'with explicit nils' do
|
185
|
+
do_post( :a, 'foo' => nil, 'bar' => 42, 'random' => true )
|
186
|
+
do_patch( :a, 'foo' => true, 'bar' => nil, 'random' => true )
|
187
|
+
do_post( :b, 'code' => 'hello', 'random' => true, 'message' => 'world', 'reference' => nil, 'errors' => [] )
|
188
|
+
do_patch( :b, 'actions' => nil, 'random' => true, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
189
|
+
do_patch( :b, 'actions' => { 'list' => nil }, 'random' => true, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'rejects unknown data across many fields (flat top level)' do
|
193
|
+
def expectations( hash )
|
194
|
+
expect( last_response.status ).to eq( 422 )
|
195
|
+
parsed = JSON.parse( last_response.body )
|
196
|
+
expect( parsed[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Body data contains unrecognised or prohibited fields' )
|
197
|
+
expect( parsed[ 'errors' ][ 0 ][ 'reference' ] ).to eq( 'random\\, more' )
|
198
|
+
end
|
199
|
+
|
200
|
+
do_post( :a, 'foo' => 'hello', 'bar' => 42, 'random' => { 'foo' => true, 'bar' => true }, 'more' => 42 )
|
201
|
+
do_patch( :a, 'foo' => true, 'bar' => 'foo', 'random' => { 'foo' => true, 'bar' => true }, 'more' => 42 )
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'rejects unknown data across many fields (nested top level)' do
|
205
|
+
def expectations( hash )
|
206
|
+
expect( last_response.status ).to eq( 422 )
|
207
|
+
parsed = JSON.parse( last_response.body )
|
208
|
+
expect( parsed[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Body data contains unrecognised or prohibited fields' )
|
209
|
+
expect( parsed[ 'errors' ][ 0 ][ 'reference' ] ).to eq( 'nested.foo\\, nested.bar\\, more' )
|
210
|
+
end
|
211
|
+
|
212
|
+
do_post( :a, 'foo' => 'hello', 'bar' => 42, 'nested' => { 'foo' => true, 'bar' => true }, 'more' => 42 )
|
213
|
+
do_patch( :a, 'foo' => true, 'bar' => 'foo', 'nested' => { 'foo' => true, 'bar' => true }, 'more' => 42 )
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'rejects known but prohibited fields' do
|
218
|
+
def expectations( hash )
|
219
|
+
expect( last_response.status ).to eq( 422 )
|
220
|
+
expect( JSON.parse( last_response.body )[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Body data contains unrecognised or prohibited fields' )
|
221
|
+
end
|
222
|
+
|
223
|
+
# Paranoia check to ensure rejection when attempting to specify just an ID,
|
224
|
+
# with an otherwise entirely valid payload.
|
225
|
+
#
|
226
|
+
it 'for just "id"' do
|
227
|
+
do_post( :a, { 'id' => Hoodoo::UUID.generate, 'foo' => 'hello', 'bar' => 42 } )
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'with many fields' do
|
231
|
+
do_post( :b, 'id' => Hoodoo::UUID.generate, 'code' => 'hello', 'message' => 'world', 'reference' => 'baz', 'errors' => [] )
|
232
|
+
do_patch( :b, 'id' => Hoodoo::UUID.generate, 'actions' => { 'list' => 'allow' }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
233
|
+
do_post( :b, 'kind' => 'Foo', 'code' => 'hello', 'message' => 'world', 'reference' => 'baz', 'errors' => [] )
|
234
|
+
do_patch( :b, 'kind' => 'Foo', 'actions' => { 'list' => 'allow' }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
235
|
+
do_post( :b, 'created_at' => Time.now.iso8601, 'code' => 'hello', 'message' => 'world', 'reference' => 'baz', 'errors' => [] )
|
236
|
+
do_patch( :b, 'created_at' => Time.now.iso8601, 'actions' => { 'list' => 'allow' }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
237
|
+
do_post( :b, 'language' => 'fr', 'code' => 'hello', 'message' => 'world', 'reference' => 'baz', 'errors' => [] )
|
238
|
+
do_patch( :b, 'language' => 'fr', 'actions' => { 'list' => 'allow' }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'with required fields only' do
|
242
|
+
do_post( :b, 'id' => Hoodoo::UUID.generate, 'code' => 'hello', 'message' => 'world', 'errors' => [] )
|
243
|
+
do_patch( :b, 'id' => Hoodoo::UUID.generate, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
244
|
+
do_post( :b, 'kind' => 'Foo', 'code' => 'hello', 'message' => 'world', 'errors' => [] )
|
245
|
+
do_patch( :b, 'kind' => 'Foo', 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
246
|
+
do_post( :b, 'created_at' => Time.now.iso8601, 'code' => 'hello', 'message' => 'world', 'errors' => [] )
|
247
|
+
do_patch( :b, 'created_at' => Time.now.iso8601, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
248
|
+
do_post( :b, 'language' => 'fr', 'code' => 'hello', 'message' => 'world', 'errors' => [] )
|
249
|
+
do_patch( :b, 'language' => 'fr', 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'with explicit nils' do
|
253
|
+
do_post( :b, 'id' => Hoodoo::UUID.generate, 'code' => 'hello', 'message' => 'world', 'reference' => nil, 'errors' => [] )
|
254
|
+
do_patch( :b, 'id' => Hoodoo::UUID.generate, 'actions' => nil, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
255
|
+
do_patch( :b, 'id' => Hoodoo::UUID.generate, 'actions' => { 'list' => nil }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
256
|
+
do_post( :b, 'kind' => 'Foo', 'code' => 'hello', 'message' => 'world', 'reference' => nil, 'errors' => [] )
|
257
|
+
do_patch( :b, 'kind' => 'Foo', 'actions' => nil, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
258
|
+
do_patch( :b, 'kind' => 'Foo', 'actions' => { 'list' => nil }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
259
|
+
do_post( :b, 'created_at' => Time.now.iso8601, 'code' => 'hello', 'message' => 'world', 'reference' => nil, 'errors' => [] )
|
260
|
+
do_patch( :b, 'created_at' => Time.now.iso8601, 'actions' => nil, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
261
|
+
do_patch( :b, 'created_at' => Time.now.iso8601, 'actions' => { 'list' => nil }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
262
|
+
do_post( :b, 'language' => 'fr', 'code' => 'hello', 'message' => 'world', 'reference' => nil, 'errors' => [] )
|
263
|
+
do_patch( :b, 'language' => 'fr', 'actions' => nil, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
264
|
+
do_patch( :b, 'language' => 'fr', 'actions' => { 'list' => nil }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# A more complete range of tests for the DSL is elsewhere, but it's easy
|
269
|
+
# to add a bit more coverage here explicitly for the to-create/update code.
|
270
|
+
|
271
|
+
context 'rejects known fields with incorrect types' do
|
272
|
+
def expectations( hash )
|
273
|
+
expect( last_response.status ).to eq( 422 )
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'with many fields' do
|
277
|
+
do_post( :a, 'foo' => 'hello', 'bar' => 'not an integer' )
|
278
|
+
do_patch( :a, 'foo' => 'not boolean', 'bar' => 'foo' )
|
279
|
+
do_post( :b, 'code' => 22, 'message' => 'world', 'reference' => 'baz', 'errors' => [] )
|
280
|
+
do_patch( :b, 'actions' => 'not an object', 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'with required fields only' do
|
284
|
+
do_post( :a, 'bar' => 'still not an integer' )
|
285
|
+
do_patch( :a, 'foo' => 'still not a boolean' )
|
286
|
+
do_post( :b, 'code' => 'hello', 'message' => 'world', 'errors' => 'not an array' )
|
287
|
+
do_patch( :b, 'caller_id' => 'not a uuid', 'expires_at' => Time.now.iso8601 )
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'with explicit nils' do
|
291
|
+
do_post( :a, 'foo' => nil, 'bar' => 'still not an integer' )
|
292
|
+
do_patch( :a, 'foo' => 'still not a boolean', 'bar' => nil )
|
293
|
+
do_post( :b, 'code' => 'hello', 'message' => 'world', 'reference' => nil, 'errors' => 'still not an array' )
|
294
|
+
do_patch( :b, 'actions' => nil, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => 'not a time' )
|
295
|
+
do_patch( :b, 'actions' => { 'list' => nil }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => 'not a time' )
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# Reject common fields where there's no create/update block.
|
300
|
+
|
301
|
+
context 'rejects common fields without schema' do
|
302
|
+
def expectations( ignore )
|
303
|
+
expect( last_response.status ).to eq( 422 )
|
304
|
+
parsed = JSON.parse( last_response.body )
|
305
|
+
expect( parsed[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Body data contains unrecognised or prohibited fields' )
|
306
|
+
expect( parsed[ 'errors' ][ 0 ][ 'reference' ] ).to eq( 'id\\, created_at\\, kind\\, language' )
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'rejects bad creations' do
|
310
|
+
do_post( :c, 'created_at' => 'now', 'id' => '234',
|
311
|
+
'kind' => 'FortyTwo', 'language' => 'bleat',
|
312
|
+
'random' => 'field' )
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'rejects bad updates' do
|
316
|
+
do_patch( :c, 'created_at' => 'now', 'id' => '234',
|
317
|
+
'kind' => 'FortyTwo', 'language' => 'bleat',
|
318
|
+
'random' => 'field' )
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# There's coverage for ":required => true" elsewhere too, but again,
|
323
|
+
# may as well add extra coverage for to-create/to-update here.
|
324
|
+
#
|
325
|
+
# The creation blocks expect to fault the missing required fields with
|
326
|
+
# a 422. The update blocks expect this to be OK (omitted field just
|
327
|
+
# means "don't change the value").
|
328
|
+
#
|
329
|
+
context 'required fields' do
|
330
|
+
context 'are required for to_create' do
|
331
|
+
def expectations( hash )
|
332
|
+
expect( last_response.status ).to eq( 422 )
|
333
|
+
expect( JSON.parse( last_response.body )[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'generic.required_field_missing' )
|
334
|
+
end
|
335
|
+
|
336
|
+
it 'with many fields' do
|
337
|
+
do_post( :a, 'foo' => 'hello' )
|
338
|
+
do_post( :b, 'message' => 'world', 'reference' => 'baz', 'errors' => [] )
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'with empty data' do
|
342
|
+
do_post( :a, {} )
|
343
|
+
do_post( :b, {} )
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'with explicit nils' do
|
347
|
+
do_post( :a, 'bar' => nil )
|
348
|
+
do_post( :b, 'code' => nil, 'message' => 'world', 'reference' => nil, 'errors' => [] )
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
context 'are irrelevant for to_update' do
|
353
|
+
def expectations( hash )
|
354
|
+
expect( last_response.status ).to eq( 200 )
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'with many fields' do
|
358
|
+
do_patch( :a, 'bar' => 'foo' )
|
359
|
+
do_patch( :b, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'with empty data' do
|
363
|
+
do_patch( :a, {} )
|
364
|
+
do_patch( :b, {} )
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'with explicit nils' do
|
368
|
+
do_patch( :a, 'foo' => nil )
|
369
|
+
do_patch( :b, 'actions' => nil, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
370
|
+
do_patch( :b, 'actions' => { 'list' => nil }, 'caller_id' => Hoodoo::UUID.generate, 'expires_at' => Time.now.iso8601 )
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
# Tests for the X-Resource-UUID header / authorised headers generally.
|
376
|
+
|
377
|
+
context 'with X-Resource-UUID' do
|
378
|
+
before :each do
|
379
|
+
@test_uuid = Hoodoo::UUID.generate()
|
380
|
+
@old_test_session = Hoodoo::Services::Middleware.test_session()
|
381
|
+
@test_session = @old_test_session.dup
|
382
|
+
permissions = Hoodoo::Services::Permissions.new # (this is "default-else-deny")
|
383
|
+
permissions.set_default_fallback( Hoodoo::Services::Permissions::ALLOW )
|
384
|
+
@test_session.permissions = permissions
|
385
|
+
@test_session.scoping = @test_session.scoping.dup
|
386
|
+
@test_session.scoping.authorised_http_headers = [] # (no secured headers allowed to start with)
|
387
|
+
Hoodoo::Services::Middleware.set_test_session( @test_session )
|
388
|
+
end
|
389
|
+
|
390
|
+
after :each do
|
391
|
+
Hoodoo::Services::Middleware.set_test_session( @old_test_session )
|
392
|
+
end
|
393
|
+
|
394
|
+
it 'accepts session-authorised and valid IDs' do
|
395
|
+
def expectations( hash )
|
396
|
+
expect( last_response.status ).to eq( 200 )
|
397
|
+
expect( JSON.parse( last_response.body ) ).to eq( hash.merge( 'id' => @test_uuid ) )
|
398
|
+
end
|
399
|
+
|
400
|
+
@test_session.scoping.authorised_http_headers = [ 'HTTP_X_RESOURCE_UUID' ]
|
401
|
+
do_post( :a, { 'foo' => 'hello', 'bar' => 42 }, { 'HTTP_X_RESOURCE_UUID' => @test_uuid } )
|
402
|
+
end
|
403
|
+
|
404
|
+
# Don't expect any errors for non-POST uses of X-Resource-UUID; just
|
405
|
+
# don't expect the body data to contain the 'id' field.
|
406
|
+
#
|
407
|
+
it 'rejects session-authorised and valid IDs for PATCH' do
|
408
|
+
def expectations( hash )
|
409
|
+
expect( last_response.status ).to eq( 200 )
|
410
|
+
expect( JSON.parse( last_response.body ) ).to eq( { 'foo' => true } )
|
411
|
+
end
|
412
|
+
|
413
|
+
@test_session.scoping.authorised_http_headers = [ 'HTTP_X_RESOURCE_UUID' ]
|
414
|
+
do_patch( :a, { 'foo' => true }, { 'HTTP_X_RESOURCE_UUID' => Hoodoo::UUID.generate() } )
|
415
|
+
end
|
416
|
+
|
417
|
+
it 'rejects session-authorised but invalid IDs' do
|
418
|
+
def expectations( hash )
|
419
|
+
expect( last_response.status ).to eq( 422 )
|
420
|
+
expect( JSON.parse( last_response.body )[ 'errors' ][ 0 ][ 'reference' ] ).to eq( 'X-Resource-UUID' )
|
421
|
+
end
|
422
|
+
|
423
|
+
@test_session.scoping.authorised_http_headers = [ 'HTTP_X_RESOURCE_UUID' ]
|
424
|
+
do_post( :a, { 'foo' => 'hello', 'bar' => 42 }, { 'HTTP_X_RESOURCE_UUID' => 'not a valid UUID' } )
|
425
|
+
end
|
426
|
+
|
427
|
+
it 'rejects requests with no authorised headers' do
|
428
|
+
def expectations( hash )
|
429
|
+
expect( last_response.status ).to eq( 403 )
|
430
|
+
|
431
|
+
# Check for a *generic* platform.forbidden message. It isn't even very
|
432
|
+
# accurate but the whole point is that we don't reveal the exact nature
|
433
|
+
# of the authorisation failure (use of a secure header without session
|
434
|
+
# permissions) because that would be an information disclosure bug.
|
435
|
+
#
|
436
|
+
expect( JSON.parse( last_response.body )[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Action not authorized' )
|
437
|
+
end
|
438
|
+
|
439
|
+
do_post( :a, { 'foo' => 'hello', 'bar' => 42 }, { 'HTTP_X_RESOURCE_UUID' => Hoodoo::UUID.generate } )
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'rejects requests with malformed (nil) authorised header collection' do
|
443
|
+
def expectations( hash )
|
444
|
+
expect( last_response.status ).to eq( 403 )
|
445
|
+
expect( JSON.parse( last_response.body )[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Action not authorized' )
|
446
|
+
end
|
447
|
+
|
448
|
+
@test_session.scoping.authorised_http_headers = nil
|
449
|
+
do_post( :a, { 'foo' => 'hello', 'bar' => 42 }, { 'HTTP_X_RESOURCE_UUID' => Hoodoo::UUID.generate } )
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'rejects requests with an explicitly empty authorised header collection' do
|
453
|
+
def expectations( hash )
|
454
|
+
expect( last_response.status ).to eq( 403 )
|
455
|
+
expect( JSON.parse( last_response.body )[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Action not authorized' )
|
456
|
+
end
|
457
|
+
|
458
|
+
@test_session.scoping.authorised_http_headers = []
|
459
|
+
do_post( :a, { 'foo' => 'hello', 'bar' => 42 }, { 'HTTP_X_RESOURCE_UUID' => Hoodoo::UUID.generate } )
|
460
|
+
end
|
461
|
+
|
462
|
+
it 'rejects requests with mismatched authorised headers' do
|
463
|
+
def expectations( hash )
|
464
|
+
expect( last_response.status ).to eq( 403 )
|
465
|
+
expect( JSON.parse( last_response.body )[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Action not authorized' )
|
466
|
+
end
|
467
|
+
|
468
|
+
@test_session.scoping.authorised_http_headers = [ 'HTTP_NOT_X_RESOURCE_UUID' ]
|
469
|
+
do_post( :a, { 'foo' => 'hello', 'bar' => 42 }, { 'HTTP_X_RESOURCE_UUID' => Hoodoo::UUID.generate } )
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# There's coverage for nil payloads elsewhere as well, but once more,
|
474
|
+
# add extra coverage here.
|
475
|
+
|
476
|
+
context 'edge cases:' do
|
477
|
+
def expectations( hash )
|
478
|
+
expect( last_response.status ).to eq( 422 )
|
479
|
+
expect( JSON.parse( last_response.body )[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'generic.malformed' )
|
480
|
+
end
|
481
|
+
|
482
|
+
it 'no body data' do
|
483
|
+
do_post( :a, nil )
|
484
|
+
do_patch( :a, nil )
|
485
|
+
do_post( :b, nil )
|
486
|
+
do_patch( :b, nil )
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|