substation 0.0.10.beta2 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +7 -3
- data/Changelog.md +99 -24
- data/Gemfile +8 -1
- data/Gemfile.devtools +37 -21
- data/Guardfile +1 -1
- data/README.md +118 -46
- data/TODO.md +1 -0
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/reek.yml +41 -16
- data/config/rubocop.yml +44 -0
- data/config/yardstick.yml +1 -1
- data/lib/substation.rb +41 -5
- data/lib/substation/chain.rb +73 -84
- data/lib/substation/chain/definition.rb +147 -0
- data/lib/substation/chain/dsl.rb +150 -112
- data/lib/substation/chain/dsl/config.rb +55 -0
- data/lib/substation/chain/dsl/module_builder.rb +84 -0
- data/lib/substation/dispatcher.rb +20 -229
- data/lib/substation/dsl/guard.rb +96 -0
- data/lib/substation/dsl/registry.rb +181 -0
- data/lib/substation/environment.rb +126 -23
- data/lib/substation/environment/dsl.rb +31 -12
- data/lib/substation/processor.rb +238 -7
- data/lib/substation/processor/builder.rb +26 -0
- data/lib/substation/processor/config.rb +24 -0
- data/lib/substation/processor/evaluator.rb +66 -42
- data/lib/substation/processor/evaluator/handler.rb +54 -0
- data/lib/substation/processor/evaluator/result.rb +24 -0
- data/lib/substation/processor/executor.rb +46 -0
- data/lib/substation/processor/nest.rb +40 -0
- data/lib/substation/processor/transformer.rb +46 -0
- data/lib/substation/processor/wrapper.rb +16 -5
- data/lib/substation/request.rb +29 -27
- data/lib/substation/response.rb +19 -34
- data/lib/substation/response/api.rb +37 -0
- data/lib/substation/response/exception.rb +20 -0
- data/lib/substation/response/exception/output.rb +59 -0
- data/lib/substation/response/failure.rb +11 -0
- data/lib/substation/response/success.rb +11 -0
- data/lib/substation/version.rb +3 -1
- data/spec/demo/core.rb +64 -0
- data/spec/demo/core/action.rb +49 -0
- data/spec/demo/core/action/create_person.rb +28 -0
- data/spec/demo/core/errors.rb +16 -0
- data/spec/demo/core/facade.rb +38 -0
- data/spec/demo/core/handler.rb +21 -0
- data/spec/demo/core/handler/acceptor.rb +47 -0
- data/spec/demo/core/handler/authenticator.rb +36 -0
- data/spec/demo/core/handler/authorizer.rb +38 -0
- data/spec/demo/core/input.rb +15 -0
- data/spec/demo/core/observers.rb +10 -0
- data/spec/demo/core/validator.rb +21 -0
- data/spec/demo/domain/actor.rb +29 -0
- data/spec/demo/domain/dto/person.rb +18 -0
- data/spec/demo/domain/environment.rb +55 -0
- data/spec/demo/domain/storage.rb +49 -0
- data/spec/demo/web.rb +26 -0
- data/spec/demo/web/errors.rb +9 -0
- data/spec/demo/web/facade.rb +60 -0
- data/spec/demo/web/handler/deserializer.rb +36 -0
- data/spec/demo/web/presenter.rb +38 -0
- data/spec/demo/web/presenter/person.rb +19 -0
- data/spec/demo/web/renderer.rb +45 -0
- data/spec/demo/web/sanitizer.rb +35 -0
- data/spec/demo/web/sanitizer/person.rb +20 -0
- data/spec/demo/web/views.rb +28 -0
- data/spec/integration/demo/core_spec.rb +97 -0
- data/spec/integration/demo/web_spec.rb +114 -0
- data/spec/shared/context/integration/demo.rb +33 -0
- data/spec/shared/context/unit/chain.rb +13 -0
- data/spec/shared/context/unit/processor.rb +58 -0
- data/spec/shared/context/unit/request.rb +8 -0
- data/spec/shared/examples/integration/demo.rb +35 -0
- data/spec/shared/examples/unit/processor.rb +72 -0
- data/spec/spec_helper.rb +52 -23
- data/spec/unit/substation/chain/definition_spec.rb +141 -0
- data/spec/unit/substation/chain/dsl/config/dsl_module_spec.rb +13 -0
- data/spec/unit/substation/chain/dsl/config/registry_spec.rb +13 -0
- data/spec/unit/substation/chain/dsl/config_spec.rb +18 -0
- data/spec/unit/substation/chain/dsl/module_builder_spec.rb +77 -0
- data/spec/unit/substation/chain/dsl_spec.rb +175 -0
- data/spec/unit/substation/chain_spec.rb +303 -0
- data/spec/unit/substation/dispatcher_spec.rb +68 -0
- data/spec/unit/substation/dsl/guard_spec.rb +72 -0
- data/spec/unit/substation/dsl/registry_spec.rb +181 -0
- data/spec/unit/substation/environment/dsl_spec.rb +156 -0
- data/spec/unit/substation/environment_spec.rb +259 -0
- data/spec/unit/substation/processor/builder_spec.rb +21 -0
- data/spec/unit/substation/processor/config_spec.rb +40 -0
- data/spec/unit/substation/processor/evaluator/handler_spec.rb +20 -0
- data/spec/unit/substation/processor/evaluator/pivot_spec.rb +42 -0
- data/spec/unit/substation/processor/evaluator/request_spec.rb +11 -0
- data/spec/unit/substation/processor/evaluator/result/failure_spec.rb +14 -0
- data/spec/unit/substation/processor/evaluator/result/success_spec.rb +14 -0
- data/spec/unit/substation/processor/evaluator/result_spec.rb +13 -0
- data/spec/unit/substation/processor/evaluator_spec.rb +18 -0
- data/spec/unit/substation/processor/executor/null_spec.rb +25 -0
- data/spec/unit/substation/processor/executor_spec.rb +32 -0
- data/spec/unit/substation/processor/fallible_spec.rb +24 -0
- data/spec/unit/substation/processor/incoming_spec.rb +17 -0
- data/spec/unit/substation/processor/nest/incoming_spec.rb +56 -0
- data/spec/unit/substation/processor/nest_spec.rb +6 -0
- data/spec/unit/substation/processor/outgoing_spec.rb +47 -0
- data/spec/unit/substation/processor/transformer/incoming_spec.rb +17 -0
- data/spec/unit/substation/processor/transformer/outgoing_spec.rb +17 -0
- data/spec/unit/substation/processor/wrapper/incoming_spec.rb +15 -0
- data/spec/unit/substation/processor/wrapper/outgoing_spec.rb +15 -0
- data/spec/unit/substation/processor/wrapper_spec.rb +24 -0
- data/spec/unit/substation/processor_spec.rb +68 -0
- data/spec/unit/substation/request_spec.rb +70 -0
- data/spec/unit/substation/response/api_spec.rb +22 -0
- data/spec/unit/substation/response/exception/output_spec.rb +46 -0
- data/spec/unit/substation/response/exception_spec.rb +25 -0
- data/spec/unit/substation/response/failure_spec.rb +25 -0
- data/spec/unit/substation/response/success_spec.rb +24 -0
- data/spec/unit/substation/response_spec.rb +73 -0
- data/substation.gemspec +7 -6
- metadata +157 -67
- checksums.yaml +0 -7
- data/TODO +0 -0
- data/lib/substation/observer.rb +0 -66
- data/lib/substation/processor/pivot.rb +0 -25
- data/lib/substation/utils.rb +0 -68
- data/spec/integration/substation/dispatcher/call_spec.rb +0 -260
- data/spec/unit/substation/chain/call_spec.rb +0 -63
- data/spec/unit/substation/chain/dsl/builder/class_methods/call_spec.rb +0 -19
- data/spec/unit/substation/chain/dsl/builder/dsl_spec.rb +0 -21
- data/spec/unit/substation/chain/dsl/builder/failure_chain_spec.rb +0 -30
- data/spec/unit/substation/chain/dsl/chain_spec.rb +0 -15
- data/spec/unit/substation/chain/dsl/class_methods/processors_spec.rb +0 -24
- data/spec/unit/substation/chain/dsl/initialize_spec.rb +0 -19
- data/spec/unit/substation/chain/dsl/processors_spec.rb +0 -42
- data/spec/unit/substation/chain/dsl/use_spec.rb +0 -14
- data/spec/unit/substation/chain/each_spec.rb +0 -46
- data/spec/unit/substation/chain/incoming/result_spec.rb +0 -21
- data/spec/unit/substation/chain/outgoing/call_spec.rb +0 -25
- data/spec/unit/substation/chain/outgoing/result_spec.rb +0 -21
- data/spec/unit/substation/dispatcher/action/call_spec.rb +0 -23
- data/spec/unit/substation/dispatcher/action/class_methods/coerce_spec.rb +0 -61
- data/spec/unit/substation/dispatcher/action_names_spec.rb +0 -14
- data/spec/unit/substation/dispatcher/call_spec.rb +0 -47
- data/spec/unit/substation/dispatcher/class_methods/coerce_spec.rb +0 -20
- data/spec/unit/substation/environment/chain_spec.rb +0 -50
- data/spec/unit/substation/environment/class_methods/build_spec.rb +0 -11
- data/spec/unit/substation/environment/dsl/class_methods/registry_spec.rb +0 -18
- data/spec/unit/substation/environment/dsl/register_spec.rb +0 -14
- data/spec/unit/substation/environment/dsl/registry_spec.rb +0 -19
- data/spec/unit/substation/observer/chain/call_spec.rb +0 -26
- data/spec/unit/substation/observer/class_methods/coerce_spec.rb +0 -33
- data/spec/unit/substation/observer/null/call_spec.rb +0 -12
- data/spec/unit/substation/processor/evaluator/call_spec.rb +0 -49
- data/spec/unit/substation/processor/pivot/call_spec.rb +0 -17
- data/spec/unit/substation/processor/wrapper/call_spec.rb +0 -20
- data/spec/unit/substation/request/env_spec.rb +0 -14
- data/spec/unit/substation/request/error_spec.rb +0 -15
- data/spec/unit/substation/request/input_spec.rb +0 -14
- data/spec/unit/substation/request/success_spec.rb +0 -15
- data/spec/unit/substation/response/env_spec.rb +0 -16
- data/spec/unit/substation/response/failure/success_predicate_spec.rb +0 -15
- data/spec/unit/substation/response/input_spec.rb +0 -16
- data/spec/unit/substation/response/output_spec.rb +0 -16
- data/spec/unit/substation/response/request_spec.rb +0 -16
- data/spec/unit/substation/response/success/success_predicate_spec.rb +0 -15
- data/spec/unit/substation/utils/class_methods/coerce_callable_spec.rb +0 -46
- data/spec/unit/substation/utils/class_methods/const_get_spec.rb +0 -46
- data/spec/unit/substation/utils/class_methods/symbolize_keys_spec.rb +0 -20
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Demo
|
4
|
+
module Web
|
5
|
+
module Sanitizer
|
6
|
+
|
7
|
+
NEW_PERSON = Ducktrap.build do
|
8
|
+
primitive(Hash)
|
9
|
+
hash_transform do
|
10
|
+
fetch_key('name') do
|
11
|
+
primitive(String)
|
12
|
+
dump_key(:name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
add(ID_TRAP)
|
16
|
+
anima_load(Domain::DTO::Person)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Demo
|
4
|
+
module Web
|
5
|
+
|
6
|
+
module Views
|
7
|
+
|
8
|
+
module Layout
|
9
|
+
def page_title
|
10
|
+
'substation demo'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Person < Presenter
|
15
|
+
include Layout
|
16
|
+
end
|
17
|
+
|
18
|
+
class SanitizationError
|
19
|
+
include Concord::Public.new(:error)
|
20
|
+
include Layout
|
21
|
+
end
|
22
|
+
|
23
|
+
class InternalError
|
24
|
+
include Concord::Public.new(:data)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
require 'demo/core'
|
6
|
+
|
7
|
+
describe 'a typical substation application' do
|
8
|
+
subject { object.call(name, input) }
|
9
|
+
|
10
|
+
let(:object) { Demo::Core::APP }
|
11
|
+
|
12
|
+
include_context 'demo application'
|
13
|
+
include_context 'with registered chains'
|
14
|
+
|
15
|
+
let(:input) { Demo::Core::Input::Incomplete.new(session_data, person) }
|
16
|
+
|
17
|
+
context 'with valid input' do
|
18
|
+
let(:person_name) { 'John' }
|
19
|
+
let(:account_id) { authorized_id }
|
20
|
+
let(:processed_input) { accepted_input }
|
21
|
+
let(:response) { Substation::Response::Success.new(processed_request, person) }
|
22
|
+
|
23
|
+
it_behaves_like 'an action invocation' do
|
24
|
+
let(:action_response) { response }
|
25
|
+
let(:success_status) { true }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with an input that produces an application error' do
|
30
|
+
let(:person_name) { 'error' }
|
31
|
+
let(:account_id) { authorized_id }
|
32
|
+
let(:processed_input) { accepted_input }
|
33
|
+
let(:error) { Demo::Core::Error::ApplicationError.new(person) }
|
34
|
+
let(:response) { error_response }
|
35
|
+
|
36
|
+
it_behaves_like 'an action invocation' do
|
37
|
+
let(:action_response) { Substation::Response::Failure.new(processed_request, person) }
|
38
|
+
let(:success_status) { false }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'with an input that raises an exception while processing' do
|
43
|
+
let(:person_name) { 'exception' }
|
44
|
+
let(:account_id) { authorized_id }
|
45
|
+
let(:processed_input) { accepted_input }
|
46
|
+
let(:failure_data) { Substation::Response::Exception::Output.new(processed_input, RuntimeError.new) }
|
47
|
+
let(:error) { Demo::Core::Error::InternalError.new(failure_data) }
|
48
|
+
let(:response) { exception_response }
|
49
|
+
|
50
|
+
it_behaves_like 'no action invocation'
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'with invalid input' do
|
54
|
+
let(:person_name) { 'X' }
|
55
|
+
let(:account_id) { authorized_id }
|
56
|
+
let(:processed_input) { incomplete_input }
|
57
|
+
let(:error) { Demo::Core::Error::ValidationError.new(invalid_input) }
|
58
|
+
let(:invalid_input) { Demo::Core::Input::Incomplete.new(session_data, validation_error) }
|
59
|
+
let(:validation_error) { Demo::Domain::DTO::NEW_PERSON_VALIDATOR.call(person).output }
|
60
|
+
let(:response) { error_response }
|
61
|
+
|
62
|
+
it_behaves_like 'no action invocation'
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'with malformed input' do
|
66
|
+
let(:person) { :foo }
|
67
|
+
let(:account_id) { authorized_id }
|
68
|
+
let(:processed_input) { incomplete_input }
|
69
|
+
let(:failure_data) { Substation::Response::Exception::Output.new(processed_input, RuntimeError.new) }
|
70
|
+
let(:error) { Demo::Core::Error::InternalError.new(failure_data) }
|
71
|
+
let(:response) { exception_response }
|
72
|
+
|
73
|
+
pending 'the response is returned is actually correct, but somehow #eql? fails' do
|
74
|
+
it_behaves_like 'no action invocation'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'with input from an unknown user' do
|
79
|
+
let(:person_name) { 'unknown' }
|
80
|
+
let(:account_id) { unknown_id }
|
81
|
+
let(:processed_input) { incomplete_input }
|
82
|
+
let(:error) { Demo::Core::Error::AuthenticationError.new(processed_input) }
|
83
|
+
let(:response) { error_response }
|
84
|
+
|
85
|
+
it_behaves_like 'no action invocation'
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'with input from an unauthorized user' do
|
89
|
+
let(:person_name) { 'forbidden' }
|
90
|
+
let(:account_id) { unauthorized_id }
|
91
|
+
let(:processed_input) { incomplete_input }
|
92
|
+
let(:error) { Demo::Core::Error::AuthorizationError.new(processed_input) }
|
93
|
+
let(:response) { error_response }
|
94
|
+
|
95
|
+
it_behaves_like 'no action invocation'
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
require 'demo/web'
|
6
|
+
|
7
|
+
describe 'a typical substation application' do
|
8
|
+
subject { object.call(name, input) }
|
9
|
+
|
10
|
+
let(:object) { Demo::Web::HTML::APP }
|
11
|
+
|
12
|
+
include_context 'demo application'
|
13
|
+
include_context 'with registered chains'
|
14
|
+
|
15
|
+
let(:input) {
|
16
|
+
{
|
17
|
+
'session' => MultiJson.dump(session_data),
|
18
|
+
'data' => MultiJson.dump(data)
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
let(:data) { { 'name' => person_name } }
|
23
|
+
let(:rendered_error_response) { Substation::Response::Failure.new(processed_request, rendered) }
|
24
|
+
let(:rendered_exception_response) { Substation::Response::Exception.new(processed_request, rendered) }
|
25
|
+
|
26
|
+
context 'with valid input' do
|
27
|
+
let(:person_name) { 'John' }
|
28
|
+
let(:account_id) { authorized_id }
|
29
|
+
let(:processed_input) { accepted_input }
|
30
|
+
let(:presenter) { Demo::Web::Presenter::Person.new(person) }
|
31
|
+
let(:view) { Demo::Web::Views::Person.new(presenter) }
|
32
|
+
let(:response) { Substation::Response::Success.new(processed_request, view) }
|
33
|
+
|
34
|
+
it_behaves_like 'an action invocation' do
|
35
|
+
let(:action_response) { Substation::Response::Success.new(processed_request, person) }
|
36
|
+
let(:success_status) { true }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with an input that produces an application error' do
|
41
|
+
let(:person_name) { 'error' }
|
42
|
+
let(:account_id) { authorized_id }
|
43
|
+
let(:processed_input) { accepted_input }
|
44
|
+
let(:error) { Demo::Core::Error::ApplicationError.new(person) }
|
45
|
+
let(:rendered) { Demo::Web::Renderer::ApplicationError.call(error_response) }
|
46
|
+
let(:response) { rendered_error_response }
|
47
|
+
|
48
|
+
it_behaves_like 'an action invocation' do
|
49
|
+
let(:action_response) { Substation::Response::Failure.new(processed_request, person) }
|
50
|
+
let(:success_status) { false }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with an input that raises an exception while processing' do
|
55
|
+
let(:person_name) { 'exception' }
|
56
|
+
let(:account_id) { authorized_id }
|
57
|
+
let(:processed_input) { accepted_input }
|
58
|
+
let(:failure_data) { Substation::Response::Exception::Output.new(processed_input, RuntimeError.new) }
|
59
|
+
let(:error) { Demo::Core::Error::InternalError.new(failure_data) }
|
60
|
+
let(:view) { Demo::Web::Views::InternalError.new(error) }
|
61
|
+
let(:error_data) { view }
|
62
|
+
let(:rendered) { Demo::Web::Renderer::InternalError.call(error_response) }
|
63
|
+
let(:response) { rendered_exception_response }
|
64
|
+
|
65
|
+
it_behaves_like 'no action invocation'
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'with invalid input' do
|
69
|
+
let(:person_name) { 'X' }
|
70
|
+
let(:account_id) { authorized_id }
|
71
|
+
let(:processed_input) { incomplete_input }
|
72
|
+
let(:error) { Demo::Core::Error::ValidationError.new(person) }
|
73
|
+
let(:rendered) { Demo::Web::Renderer::ValidationError.call(error_response) }
|
74
|
+
let(:response) { rendered_error_response }
|
75
|
+
|
76
|
+
it_behaves_like 'no action invocation'
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'with malformed input' do
|
80
|
+
let(:data) { { 'malformed' => 'input' } }
|
81
|
+
let(:account_id) { authorized_id }
|
82
|
+
let(:processed_input) { incomplete_input }
|
83
|
+
let(:incomplete_data) { data }
|
84
|
+
let(:error) { Demo::Web::Error::SanitizationError.new(processed_input) }
|
85
|
+
let(:view) { Demo::Web::Views::SanitizationError.new(error) }
|
86
|
+
let(:error_data) { view }
|
87
|
+
let(:rendered) { Demo::Web::Renderer::SanitizationError.call(error_response) }
|
88
|
+
let(:response) { rendered_error_response }
|
89
|
+
|
90
|
+
it_behaves_like 'no action invocation'
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'with input from an unknown user' do
|
94
|
+
let(:person_name) { 'unknown' }
|
95
|
+
let(:account_id) { unknown_id }
|
96
|
+
let(:processed_input) { incomplete_input }
|
97
|
+
let(:error) { Demo::Core::Error::AuthenticationError.new(processed_input) }
|
98
|
+
let(:rendered) { Demo::Web::Renderer::AuthenticationError.call(error_response) }
|
99
|
+
let(:response) { rendered_error_response }
|
100
|
+
|
101
|
+
it_behaves_like 'no action invocation'
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'with input from an unauthorized user' do
|
105
|
+
let(:person_name) { 'forbidden' }
|
106
|
+
let(:account_id) { unauthorized_id }
|
107
|
+
let(:processed_input) { incomplete_input }
|
108
|
+
let(:error) { Demo::Core::Error::AuthorizationError.new(processed_input) }
|
109
|
+
let(:rendered) { Demo::Web::Renderer::AuthorizationError.call(error_response) }
|
110
|
+
let(:response) { rendered_error_response }
|
111
|
+
|
112
|
+
it_behaves_like 'no action invocation'
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
shared_context 'demo application' do
|
4
|
+
let(:request) { Substation::Request.new(name, env, input) }
|
5
|
+
let(:name) { :create_person }
|
6
|
+
let(:env) { Demo::APP_ENV }
|
7
|
+
|
8
|
+
let(:session_data) { { 'account_id' => account_id } }
|
9
|
+
|
10
|
+
let(:authorized_id) { 1 }
|
11
|
+
let(:unauthorized_id) { 2 }
|
12
|
+
let(:unknown_id) { -1 }
|
13
|
+
|
14
|
+
let(:processed_request) { Substation::Request.new(name, env, processed_input) }
|
15
|
+
let(:actor) { Demo::Domain::Actor.coerce(session_data, acting_person) }
|
16
|
+
let(:acting_person) { Demo::Domain::DTO::Person.new(:id => account_id, :name => 'Jane') }
|
17
|
+
|
18
|
+
let(:accepted_input) { Demo::Core::Input::Accepted.new(actor, person) }
|
19
|
+
let(:incomplete_input) { Demo::Core::Input::Incomplete.new(session_data, incomplete_data) }
|
20
|
+
let(:incomplete_data) { person }
|
21
|
+
|
22
|
+
let(:person) { Demo::Domain::DTO::Person.new(:id => nil, :name => person_name) }
|
23
|
+
|
24
|
+
let(:error_response) { Substation::Response::Failure.new(processed_request, error_data) }
|
25
|
+
let(:exception_response) { Substation::Response::Exception.new(processed_request, error_data) }
|
26
|
+
let(:error_data) { error }
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
shared_context 'with registered chains' do
|
31
|
+
let(:input) { mock }
|
32
|
+
let(:account_id) { authorized_id }
|
33
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
shared_context 'Chain::DSL#initialize' do
|
4
|
+
let(:chain_dsl) { Chain::DSL.build(registry, definition) }
|
5
|
+
let(:registry) { Environment::DSL.registry(&env_block) }
|
6
|
+
let(:env_block) { ->(_) { register(:test, Spec::Processor) } }
|
7
|
+
let(:processor_name) { :test }
|
8
|
+
|
9
|
+
let(:definition) { Chain::Definition.new(chain_name, processors) }
|
10
|
+
let(:chain_name) { double('chain_name') }
|
11
|
+
let(:processors) { [processor] }
|
12
|
+
let(:processor) { double('processor', :name => :processor) }
|
13
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
shared_context 'Processor::Executor#initialize' do
|
4
|
+
let(:executor) { Processor::Executor.new(decomposer, composer) }
|
5
|
+
let(:decomposer) { double('decomposer') }
|
6
|
+
let(:composer) { double('composer') }
|
7
|
+
let(:decomposed) { double('decomposed') }
|
8
|
+
let(:composed) { double('composed') }
|
9
|
+
let(:handler_result) { double('handler_result') }
|
10
|
+
let(:handler_output) { double('handler_output') }
|
11
|
+
let(:handler_success) { true }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(handler_result).to receive(:success?).with(no_args).and_return(handler_success)
|
15
|
+
allow(handler_result).to receive(:output).with(no_args).and_return(handler_output)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
shared_context 'Processor::Config#initialize' do
|
20
|
+
include_context 'Processor::Executor#initialize'
|
21
|
+
|
22
|
+
let(:processor_config) { Processor::Config.new(executor, failure_chain, observers) }
|
23
|
+
let(:failure_chain) { double('failure_chain') }
|
24
|
+
let(:observers) { double('observers') }
|
25
|
+
let(:observer) { double('observer') }
|
26
|
+
end
|
27
|
+
|
28
|
+
shared_context 'Processor#initialize' do
|
29
|
+
include_context 'Processor::Config#initialize'
|
30
|
+
|
31
|
+
let(:object) { klass.new(processor_name, handler, processor_config) }
|
32
|
+
let(:processor_name) { double('name') }
|
33
|
+
let(:handler) { double('handler') }
|
34
|
+
let(:failure_response) { double('failure_response') }
|
35
|
+
end
|
36
|
+
|
37
|
+
shared_context 'Processor::Call' do
|
38
|
+
|
39
|
+
include_context 'Request#initialize'
|
40
|
+
include_context 'Processor#initialize'
|
41
|
+
|
42
|
+
let(:response) { Response::Success.new(request, original_data) }
|
43
|
+
let(:original_data) { double('original_data') }
|
44
|
+
|
45
|
+
let(:expected) { Response::Success.new(request, expected_data) }
|
46
|
+
let(:expected_data) { composed }
|
47
|
+
end
|
48
|
+
|
49
|
+
shared_context 'Processor::Builder#initialize' do
|
50
|
+
include_context 'Processor::Executor#initialize'
|
51
|
+
|
52
|
+
let(:processor_builder) {
|
53
|
+
Processor::Builder.new(processor_name, processor_klass, executor)
|
54
|
+
}
|
55
|
+
|
56
|
+
let(:processor_name) { :test }
|
57
|
+
let(:processor_klass) { Spec::Processor }
|
58
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
shared_examples_for 'all invocations' do
|
4
|
+
it { should eql(response) }
|
5
|
+
|
6
|
+
it 'indicates success via #success?' do
|
7
|
+
expect(subject.success?).to be(success_status)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'provides the processed input in Response#input' do
|
11
|
+
expect(subject.input).to eql(processed_input)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
shared_examples_for 'an action invocation' do
|
16
|
+
it_behaves_like 'all invocations'
|
17
|
+
|
18
|
+
it 'notifies all observers' do
|
19
|
+
expect(Demo::Core::Observers::LOG_EVENT).to receive(:call).with(action_response).ordered
|
20
|
+
expect(Demo::Core::Observers::SEND_EMAIL).to receive(:call).with(action_response).ordered
|
21
|
+
subject
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
shared_examples_for 'no action invocation' do
|
26
|
+
let(:success_status) { false }
|
27
|
+
|
28
|
+
it_behaves_like 'all invocations'
|
29
|
+
|
30
|
+
it 'does not notify any observers' do
|
31
|
+
expect(Demo::Core::Observers::LOG_EVENT).to_not receive(:call)
|
32
|
+
expect(Demo::Core::Observers::SEND_EMAIL).to_not receive(:call)
|
33
|
+
subject
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
shared_examples_for 'Processor::Call::Outgoing#call' do
|
4
|
+
subject { object.call(response) }
|
5
|
+
|
6
|
+
include_context 'Processor::Call'
|
7
|
+
|
8
|
+
before do
|
9
|
+
expect(decomposer).to receive(:call).with(response).and_return(decomposed)
|
10
|
+
expect(handler_result).to_not receive(:success?)
|
11
|
+
expect(handler_result).to_not receive(:output)
|
12
|
+
expect(composer).to receive(:call).with(response, handler_result).and_return(composed)
|
13
|
+
end
|
14
|
+
|
15
|
+
it { should eql(expected) }
|
16
|
+
end
|
17
|
+
|
18
|
+
shared_examples_for 'Processor::Call::Incoming#call' do
|
19
|
+
subject { object.call(request) }
|
20
|
+
|
21
|
+
include_context 'Processor::Call'
|
22
|
+
|
23
|
+
before do
|
24
|
+
expect(decomposer).to receive(:call).with(request).and_return(decomposed)
|
25
|
+
expect(handler_result).to_not receive(:success?)
|
26
|
+
expect(handler_result).to_not receive(:output)
|
27
|
+
expect(composer).to receive(:call).with(request, handler_result).and_return(composed)
|
28
|
+
end
|
29
|
+
|
30
|
+
it { should eql(expected) }
|
31
|
+
end
|
32
|
+
|
33
|
+
shared_examples_for 'Processor::Evaluator#call' do
|
34
|
+
subject { object.call(request) }
|
35
|
+
|
36
|
+
include_context 'Processor::Call'
|
37
|
+
|
38
|
+
context 'with a successful handler' do
|
39
|
+
let(:handler_success) { true }
|
40
|
+
let(:response) { Response::Success.new(request, composed) }
|
41
|
+
|
42
|
+
before do
|
43
|
+
expect(decomposer).to receive(:call).with(request).and_return(decomposed)
|
44
|
+
expect(handler).to receive(:call).with(decomposed).and_return(handler_result)
|
45
|
+
expect(observers).to receive(:each).with(no_args).and_yield(observer)
|
46
|
+
expect(observer).to receive(:call).with(handler_result)
|
47
|
+
expect(handler_result).to receive(:success?)
|
48
|
+
expect(handler_result).to receive(:output)
|
49
|
+
expect(composer).to receive(:call).with(request, handler_output).and_return(composed)
|
50
|
+
end
|
51
|
+
|
52
|
+
it { should eql(expected) }
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with a failing handler' do
|
56
|
+
let(:handler_success) { false }
|
57
|
+
let(:response) { Response::Failure.new(request, composed) }
|
58
|
+
|
59
|
+
before do
|
60
|
+
expect(decomposer).to receive(:call).with(request).and_return(decomposed)
|
61
|
+
expect(handler).to receive(:call).with(decomposed).and_return(handler_result)
|
62
|
+
expect(observers).to receive(:each).with(no_args).and_yield(observer)
|
63
|
+
expect(observer).to receive(:call).with(handler_result)
|
64
|
+
expect(handler_result).to receive(:success?)
|
65
|
+
expect(handler_result).to receive(:output)
|
66
|
+
expect(composer).to receive(:call).with(request, handler_output).and_return(composed)
|
67
|
+
expect(failure_chain).to receive(:call).with(response).and_return(failure_response)
|
68
|
+
end
|
69
|
+
|
70
|
+
it { should eql(failure_response) }
|
71
|
+
end
|
72
|
+
end
|