servactory 2.5.0.rc1 → 2.5.0.rc3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/servactory/install_generator.rb +21 -0
- data/lib/generators/servactory/rspec_generator.rb +88 -0
- data/lib/generators/servactory/service_generator.rb +49 -0
- data/lib/generators/servactory/templates/services/application_service/base.rb +60 -0
- data/lib/generators/servactory/templates/services/application_service/exceptions.rb +11 -0
- data/lib/generators/servactory/templates/services/application_service/result.rb +5 -0
- data/lib/servactory/context/workspace/internals.rb +1 -1
- data/lib/servactory/info/dsl.rb +56 -4
- data/lib/servactory/maintenance/attributes/options/registrar.rb +0 -3
- data/lib/servactory/result.rb +1 -1
- data/lib/servactory/test_kit/rspec/helpers.rb +95 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/consists_of_matcher.rb +121 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/inclusion_matcher.rb +70 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/must_matcher.rb +61 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/types_matcher.rb +72 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb +203 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/default_matcher.rb +67 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/optional_matcher.rb +63 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/required_matcher.rb +78 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/valid_with_matcher.rb +233 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb +148 -0
- data/lib/servactory/test_kit/rspec/matchers.rb +295 -0
- data/lib/servactory/test_kit/utils/faker.rb +78 -0
- data/lib/servactory/version.rb +1 -1
- data/lib/servactory.rb +1 -0
- metadata +21 -2
@@ -0,0 +1,233 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module TestKit
|
5
|
+
module Rspec
|
6
|
+
module Matchers
|
7
|
+
module HaveServiceInputMatchers
|
8
|
+
class ValidWithMatcher # rubocop:disable Metrics/ClassLength
|
9
|
+
attr_reader :missing_option
|
10
|
+
|
11
|
+
def initialize(described_class, attribute_type, attribute_name, attributes)
|
12
|
+
@described_class = described_class
|
13
|
+
@attribute_type = attribute_type
|
14
|
+
@attribute_type_plural = attribute_type.to_s.pluralize.to_sym
|
15
|
+
@attribute_name = attribute_name
|
16
|
+
@attributes = attributes
|
17
|
+
|
18
|
+
@attribute_data = described_class.info.public_send(attribute_type_plural).fetch(attribute_name)
|
19
|
+
|
20
|
+
@missing_option = ""
|
21
|
+
end
|
22
|
+
|
23
|
+
def description
|
24
|
+
"valid_with attribute checking"
|
25
|
+
end
|
26
|
+
|
27
|
+
def matches?(subject)
|
28
|
+
if attributes.is_a?(FalseClass) || submatcher_passes?(subject)
|
29
|
+
true
|
30
|
+
else
|
31
|
+
@missing_option = build_missing_option
|
32
|
+
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :described_class,
|
40
|
+
:attribute_type,
|
41
|
+
:attribute_type_plural,
|
42
|
+
:attribute_name,
|
43
|
+
:attributes,
|
44
|
+
:attribute_data
|
45
|
+
|
46
|
+
def submatcher_passes?(_subject) # rubocop:disable Metrics/CyclomaticComplexity
|
47
|
+
success_passes? &&
|
48
|
+
failure_type_passes? &&
|
49
|
+
failure_required_passes? &&
|
50
|
+
failure_optional_passes? &&
|
51
|
+
failure_consists_of_passes? &&
|
52
|
+
failure_format_passes? &&
|
53
|
+
failure_inclusion_passes? &&
|
54
|
+
failure_must_passes?
|
55
|
+
end
|
56
|
+
|
57
|
+
def success_passes?
|
58
|
+
expect_success_with!(attributes)
|
59
|
+
end
|
60
|
+
|
61
|
+
def failure_type_passes? # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
62
|
+
option_types = attribute_data.fetch(:types)
|
63
|
+
input_first_type = option_types.first
|
64
|
+
input_required = attribute_data.fetch(:required).fetch(:is)
|
65
|
+
attribute_consists_of_types = Array(attribute_data.fetch(:consists_of).fetch(:type))
|
66
|
+
attribute_consists_of_first_type = attribute_consists_of_types.first
|
67
|
+
|
68
|
+
prepared_attributes = attributes.dup
|
69
|
+
prepared_attributes[attribute_name] = Servactory::TestKit::FakeType.new
|
70
|
+
|
71
|
+
input_required_message =
|
72
|
+
if described_class.config.collection_mode_class_names.include?(input_first_type) &&
|
73
|
+
attribute_consists_of_first_type != false
|
74
|
+
if input_required
|
75
|
+
I18n.t(
|
76
|
+
"servactory.#{attribute_type_plural}.validations.required.default_error.for_collection",
|
77
|
+
service_class_name: described_class.name,
|
78
|
+
"#{attribute_type}_name": attribute_name
|
79
|
+
)
|
80
|
+
else
|
81
|
+
I18n.t(
|
82
|
+
"servactory.#{attribute_type_plural}.validations.type.default_error.for_collection.wrong_type",
|
83
|
+
service_class_name: described_class.name,
|
84
|
+
"#{attribute_type}_name": attribute_name,
|
85
|
+
expected_type: option_types.join(", "),
|
86
|
+
given_type: Servactory::TestKit::FakeType.new.class.name
|
87
|
+
)
|
88
|
+
end
|
89
|
+
else
|
90
|
+
I18n.t(
|
91
|
+
"servactory.#{attribute_type_plural}.validations.type.default_error.default",
|
92
|
+
service_class_name: described_class.name,
|
93
|
+
"#{attribute_type}_name": attribute_name,
|
94
|
+
expected_type: option_types.join(", "),
|
95
|
+
given_type: Servactory::TestKit::FakeType.new.class.name
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
expect_failure_with!(prepared_attributes, input_required_message)
|
100
|
+
end
|
101
|
+
|
102
|
+
def failure_required_passes? # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
103
|
+
input_required = attribute_data.fetch(:required).fetch(:is)
|
104
|
+
|
105
|
+
return true unless input_required
|
106
|
+
|
107
|
+
prepared_attributes = attributes.dup
|
108
|
+
prepared_attributes[attribute_name] = nil
|
109
|
+
|
110
|
+
input_required_message = attribute_data.fetch(:required).fetch(:message)
|
111
|
+
|
112
|
+
if input_required_message.nil?
|
113
|
+
input_required_message = I18n.t(
|
114
|
+
"servactory.#{attribute_type_plural}.validations.required.default_error.default",
|
115
|
+
service_class_name: described_class.name,
|
116
|
+
"#{attribute_type}_name": attribute_name
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
120
|
+
expect_failure_with!(prepared_attributes, input_required_message)
|
121
|
+
end
|
122
|
+
|
123
|
+
def failure_optional_passes?
|
124
|
+
input_required = attribute_data.fetch(:required).fetch(:is)
|
125
|
+
|
126
|
+
return true if input_required
|
127
|
+
|
128
|
+
prepared_attributes = attributes.dup
|
129
|
+
prepared_attributes[attribute_name] = nil
|
130
|
+
|
131
|
+
expect_failure_with!(prepared_attributes, nil)
|
132
|
+
end
|
133
|
+
|
134
|
+
def failure_format_passes?
|
135
|
+
# NOTE: Checking for negative cases is not implemented for `format`
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
def failure_consists_of_passes? # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
140
|
+
option_types = attribute_data.fetch(:types)
|
141
|
+
input_first_type = option_types.first
|
142
|
+
|
143
|
+
return true unless described_class.config.collection_mode_class_names.include?(input_first_type)
|
144
|
+
|
145
|
+
prepared_attributes = attributes.dup
|
146
|
+
prepared_attributes[attribute_name] = input_first_type[Servactory::TestKit::FakeType.new]
|
147
|
+
|
148
|
+
attribute_consists_of_types = Array(attribute_data.fetch(:consists_of).fetch(:type))
|
149
|
+
attribute_consists_of_first_type = attribute_consists_of_types.first
|
150
|
+
|
151
|
+
return true if attribute_consists_of_first_type == false
|
152
|
+
|
153
|
+
attribute_consists_of_message = attribute_data.fetch(:consists_of).fetch(:message)
|
154
|
+
|
155
|
+
if attribute_consists_of_message.nil?
|
156
|
+
attribute_consists_of_message = I18n.t(
|
157
|
+
"servactory.#{attribute_type_plural}.validations.type.default_error.for_collection.wrong_element_type", # rubocop:disable Layout/LineLength
|
158
|
+
service_class_name: described_class.name,
|
159
|
+
"#{attribute_type}_name": attribute_name,
|
160
|
+
expected_type: attribute_consists_of_types.join(", "),
|
161
|
+
given_type: prepared_attributes[attribute_name].map { _1.class.name }.join(", ")
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
expect_failure_with!(prepared_attributes, attribute_consists_of_message)
|
166
|
+
end
|
167
|
+
|
168
|
+
def failure_inclusion_passes? # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
169
|
+
input_inclusion_in = attribute_data.fetch(:inclusion).fetch(:in)
|
170
|
+
|
171
|
+
return true if input_inclusion_in.blank?
|
172
|
+
|
173
|
+
wrong_value = Servactory::TestKit::Utils::Faker.fetch_value_for(input_inclusion_in.first.class)
|
174
|
+
|
175
|
+
prepared_attributes = attributes.dup
|
176
|
+
prepared_attributes[attribute_name] = wrong_value
|
177
|
+
|
178
|
+
input_required_message = attribute_data.fetch(:inclusion).fetch(:message)
|
179
|
+
|
180
|
+
if input_required_message.nil?
|
181
|
+
input_required_message = I18n.t(
|
182
|
+
"servactory.#{attribute_type_plural}.validations.inclusion.default_error",
|
183
|
+
service_class_name: described_class.name,
|
184
|
+
"#{attribute_type}_name": attribute_name,
|
185
|
+
"#{attribute_type}_inclusion": input_inclusion_in,
|
186
|
+
value: wrong_value
|
187
|
+
)
|
188
|
+
elsif input_required_message.is_a?(Proc)
|
189
|
+
input_work = attribute_data.fetch(:work)
|
190
|
+
|
191
|
+
input_required_message = input_required_message.call(
|
192
|
+
service_class_name: described_class.name,
|
193
|
+
input: input_work,
|
194
|
+
value: wrong_value
|
195
|
+
)
|
196
|
+
end
|
197
|
+
|
198
|
+
expect_failure_with!(prepared_attributes, input_required_message)
|
199
|
+
end
|
200
|
+
|
201
|
+
def failure_must_passes?
|
202
|
+
# NOTE: Checking for negative cases is not implemented for `must`
|
203
|
+
true
|
204
|
+
end
|
205
|
+
|
206
|
+
def expect_success_with!(prepared_attributes)
|
207
|
+
described_class.call!(prepared_attributes).success?
|
208
|
+
rescue Servactory::Exceptions::Input
|
209
|
+
false
|
210
|
+
rescue StandardError
|
211
|
+
true
|
212
|
+
end
|
213
|
+
|
214
|
+
def expect_failure_with!(prepared_attributes, expected_message)
|
215
|
+
described_class.call!(prepared_attributes).success?
|
216
|
+
rescue Servactory::Exceptions::Input => e
|
217
|
+
return false if expected_message.blank?
|
218
|
+
|
219
|
+
expected_message.casecmp(e.message).zero?
|
220
|
+
rescue Servactory::Exceptions::Internal, Servactory::Exceptions::Output
|
221
|
+
# NOTE: Skips the fall of validations inside the service, which are not important in this place.
|
222
|
+
true
|
223
|
+
end
|
224
|
+
|
225
|
+
def build_missing_option
|
226
|
+
"should work as expected on the specified attributes based on its options"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module TestKit
|
5
|
+
module Rspec
|
6
|
+
module Matchers
|
7
|
+
class HaveServiceInternalMatcher # rubocop:disable Metrics/ClassLength
|
8
|
+
attr_reader :described_class, :internal_name, :options
|
9
|
+
|
10
|
+
def initialize(described_class, internal_name)
|
11
|
+
@described_class = described_class
|
12
|
+
@internal_name = internal_name
|
13
|
+
|
14
|
+
@options = {}
|
15
|
+
@submatchers = []
|
16
|
+
|
17
|
+
@missing = ""
|
18
|
+
end
|
19
|
+
|
20
|
+
def supports_block_expectations?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def type(type)
|
25
|
+
@option_types = Array(type)
|
26
|
+
add_submatcher(
|
27
|
+
HaveServiceAttributeMatchers::TypesMatcher,
|
28
|
+
described_class,
|
29
|
+
:internal,
|
30
|
+
internal_name,
|
31
|
+
@option_types
|
32
|
+
)
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def types(*types)
|
37
|
+
@option_types = types
|
38
|
+
add_submatcher(
|
39
|
+
HaveServiceAttributeMatchers::TypesMatcher,
|
40
|
+
described_class,
|
41
|
+
:internal,
|
42
|
+
internal_name,
|
43
|
+
@option_types
|
44
|
+
)
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def consists_of(*types) # rubocop:disable Metrics/MethodLength
|
49
|
+
message = block_given? ? yield : nil
|
50
|
+
|
51
|
+
add_submatcher(
|
52
|
+
HaveServiceAttributeMatchers::ConsistsOfMatcher,
|
53
|
+
described_class,
|
54
|
+
:internal,
|
55
|
+
internal_name,
|
56
|
+
@option_types,
|
57
|
+
Array(types),
|
58
|
+
message
|
59
|
+
)
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def inclusion(values)
|
64
|
+
add_submatcher(
|
65
|
+
HaveServiceAttributeMatchers::InclusionMatcher,
|
66
|
+
described_class,
|
67
|
+
:internal,
|
68
|
+
internal_name,
|
69
|
+
Array(values)
|
70
|
+
)
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def must(*must_names)
|
75
|
+
add_submatcher(
|
76
|
+
HaveServiceAttributeMatchers::MustMatcher,
|
77
|
+
described_class,
|
78
|
+
:internal,
|
79
|
+
internal_name,
|
80
|
+
Array(must_names)
|
81
|
+
)
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def description
|
86
|
+
"#{internal_name} with #{submatchers.map(&:description).join(', ')}"
|
87
|
+
end
|
88
|
+
|
89
|
+
def failure_message
|
90
|
+
"Expected #{expectation}, which #{missing_options}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def failure_message_when_negated
|
94
|
+
"Did not expect #{expectation} with specified options"
|
95
|
+
end
|
96
|
+
|
97
|
+
def matches?(subject)
|
98
|
+
@subject = subject
|
99
|
+
|
100
|
+
submatchers_match?
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
|
105
|
+
attr_reader :submatchers, :missing, :subject
|
106
|
+
|
107
|
+
def add_submatcher(matcher_class, *args)
|
108
|
+
remove_submatcher(matcher_class)
|
109
|
+
submatchers << matcher_class.new(*args)
|
110
|
+
end
|
111
|
+
|
112
|
+
def remove_submatcher(matcher_class)
|
113
|
+
submatchers.delete_if do |submatcher|
|
114
|
+
submatcher.is_a?(matcher_class)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def expectation
|
119
|
+
"#{described_class.name} to have a service internal attribute named #{internal_name}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def missing_options
|
123
|
+
missing_options = [missing, missing_options_for_failing_submatchers]
|
124
|
+
missing_options.flatten.select(&:present?).join(", ")
|
125
|
+
end
|
126
|
+
|
127
|
+
def failing_submatchers
|
128
|
+
@failing_submatchers ||= submatchers.reject do |matcher|
|
129
|
+
matcher.matches?(subject)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def missing_options_for_failing_submatchers
|
134
|
+
if defined?(failing_submatchers)
|
135
|
+
failing_submatchers.map(&:missing_option)
|
136
|
+
else
|
137
|
+
[]
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def submatchers_match?
|
142
|
+
failing_submatchers.empty?
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,295 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module TestKit
|
5
|
+
module Rspec
|
6
|
+
module Matchers # rubocop:disable Metrics/ModuleLength
|
7
|
+
def have_service_input(input_name) # rubocop:disable Naming/PredicateName
|
8
|
+
HaveServiceInputMatcher.new(described_class, input_name)
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec::Matchers.alias_matcher :have_input, :have_service_input
|
12
|
+
|
13
|
+
def have_service_internal(internal_name) # rubocop:disable Naming/PredicateName
|
14
|
+
HaveServiceInternalMatcher.new(described_class, internal_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec::Matchers.alias_matcher :have_internal, :have_service_internal
|
18
|
+
|
19
|
+
########################################################################
|
20
|
+
########################################################################
|
21
|
+
########################################################################
|
22
|
+
|
23
|
+
RSpec::Matchers.define :have_service_output do |output_name| # rubocop:disable Metrics/BlockLength
|
24
|
+
description { "service output" }
|
25
|
+
|
26
|
+
match do |actual|
|
27
|
+
match_for(actual, output_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
chain :instance_of do |class_or_name|
|
31
|
+
@instance_of = Servactory::Utils.constantize_class(class_or_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
chain :nested do |*values|
|
35
|
+
@nested = values
|
36
|
+
end
|
37
|
+
|
38
|
+
chain :with do |value|
|
39
|
+
@value = value
|
40
|
+
end
|
41
|
+
|
42
|
+
failure_message do |actual|
|
43
|
+
match_for(actual, output_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
47
|
+
def match_for(actual, output_name)
|
48
|
+
given_value = actual.public_send(output_name)
|
49
|
+
|
50
|
+
if defined?(@nested) && @nested.present?
|
51
|
+
@nested.each do |method_name|
|
52
|
+
next unless given_value.respond_to?(method_name)
|
53
|
+
|
54
|
+
given_value = given_value.public_send(method_name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
expect(given_value).to(
|
59
|
+
if defined?(@instance_of)
|
60
|
+
RSpec::Matchers::BuiltIn::BeAnInstanceOf.new(@instance_of)
|
61
|
+
elsif @value.is_a?(Array)
|
62
|
+
RSpec::Matchers::BuiltIn::ContainExactly.new(@value)
|
63
|
+
elsif @value.is_a?(Hash)
|
64
|
+
RSpec::Matchers::BuiltIn::Match.new(@value)
|
65
|
+
elsif @value.is_a?(TrueClass) || @value.is_a?(FalseClass)
|
66
|
+
RSpec::Matchers::BuiltIn::Equal.new(@value)
|
67
|
+
elsif @value.is_a?(NilClass)
|
68
|
+
RSpec::Matchers::BuiltIn::BeNil.new(@value)
|
69
|
+
else
|
70
|
+
RSpec::Matchers::BuiltIn::Eq.new(@value)
|
71
|
+
end
|
72
|
+
)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
76
|
+
|
77
|
+
RSpec::Matchers.alias_matcher :have_output, :have_service_output
|
78
|
+
|
79
|
+
RSpec::Matchers.define :be_success_service do # rubocop:disable Metrics/BlockLength
|
80
|
+
description { "service success" }
|
81
|
+
|
82
|
+
def expected_data
|
83
|
+
@expected_data ||= {}
|
84
|
+
end
|
85
|
+
|
86
|
+
match do |actual|
|
87
|
+
matched = actual.is_a?(Servactory::Result)
|
88
|
+
matched &&= actual.success?
|
89
|
+
matched &&= !actual.failure?
|
90
|
+
|
91
|
+
if defined?(expected_data)
|
92
|
+
matched &&= expected_data.all? do |key, value|
|
93
|
+
if actual.respond_to?(key)
|
94
|
+
actual.public_send(key) == value
|
95
|
+
else
|
96
|
+
false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
matched
|
102
|
+
end
|
103
|
+
|
104
|
+
chain :with_output do |key, value|
|
105
|
+
expected_data[key] = value
|
106
|
+
end
|
107
|
+
|
108
|
+
chain :with_outputs do |attributes|
|
109
|
+
attributes.each do |key, value|
|
110
|
+
expected_data[key] = value
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
failure_message do |actual| # rubocop:disable Metrics/BlockLength
|
115
|
+
unless actual.instance_of?(Servactory::Result)
|
116
|
+
break <<~MESSAGE
|
117
|
+
Incorrect service result:
|
118
|
+
|
119
|
+
expected Servactory::Result
|
120
|
+
got #{actual.class.name}
|
121
|
+
MESSAGE
|
122
|
+
end
|
123
|
+
|
124
|
+
if actual.failure?
|
125
|
+
break <<~MESSAGE
|
126
|
+
Incorrect service result:
|
127
|
+
|
128
|
+
expected success
|
129
|
+
got failure
|
130
|
+
MESSAGE
|
131
|
+
end
|
132
|
+
|
133
|
+
if defined?(expected_data)
|
134
|
+
message = expected_data.each do |key, value|
|
135
|
+
unless actual.respond_to?(key)
|
136
|
+
break <<~MESSAGE
|
137
|
+
Non-existent value key in result:
|
138
|
+
|
139
|
+
expected #{actual.inspect}
|
140
|
+
got #{key}
|
141
|
+
MESSAGE
|
142
|
+
end
|
143
|
+
|
144
|
+
expected_value = actual.public_send(key)
|
145
|
+
next if actual.public_send(key) == value
|
146
|
+
|
147
|
+
break <<~MESSAGE
|
148
|
+
Incorrect result value for #{key}:
|
149
|
+
|
150
|
+
expected #{expected_value.inspect}
|
151
|
+
got #{value.inspect}
|
152
|
+
MESSAGE
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
break message if message.present?
|
157
|
+
|
158
|
+
<<~MESSAGE
|
159
|
+
Unexpected case when using `be_success_service`.
|
160
|
+
|
161
|
+
Please try to build an example based on the documentation.
|
162
|
+
Or report your problem to us:
|
163
|
+
|
164
|
+
https://github.com/servactory/servactory/issues
|
165
|
+
MESSAGE
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
RSpec::Matchers.define :be_failure_service do # rubocop:disable Metrics/BlockLength
|
170
|
+
description { "service failure" }
|
171
|
+
|
172
|
+
match do |actual|
|
173
|
+
expected_failure_class =
|
174
|
+
defined?(@expected_failure_class) ? @expected_failure_class : Servactory::Exceptions::Failure
|
175
|
+
|
176
|
+
expected_type = defined?(@expected_type) ? @expected_type : :base
|
177
|
+
expected_message = defined?(@expected_message) ? @expected_message : nil
|
178
|
+
expected_meta = defined?(@expected_meta) ? @expected_meta : nil
|
179
|
+
|
180
|
+
matched = actual.is_a?(Servactory::Result)
|
181
|
+
matched &&= !actual.success?
|
182
|
+
matched &&= actual.failure?
|
183
|
+
matched &&= actual.error.is_a?(Servactory::Exceptions::Failure)
|
184
|
+
matched &&= actual.error.instance_of?(expected_failure_class)
|
185
|
+
matched &&= actual.error.type == expected_type
|
186
|
+
matched &&= actual.error.message == expected_message
|
187
|
+
matched &&= actual.error.meta == expected_meta
|
188
|
+
matched
|
189
|
+
end
|
190
|
+
|
191
|
+
chain :with do |expected_failure_class|
|
192
|
+
@expected_failure_class = expected_failure_class
|
193
|
+
end
|
194
|
+
|
195
|
+
chain :type do |expected_type|
|
196
|
+
@expected_type = expected_type
|
197
|
+
end
|
198
|
+
|
199
|
+
chain :message do |expected_message|
|
200
|
+
@expected_message = expected_message
|
201
|
+
end
|
202
|
+
|
203
|
+
chain :meta do |expected_meta|
|
204
|
+
@expected_meta = expected_meta
|
205
|
+
end
|
206
|
+
|
207
|
+
failure_message do |actual| # rubocop:disable Metrics/BlockLength
|
208
|
+
unless actual.instance_of?(Servactory::Result)
|
209
|
+
break <<~MESSAGE
|
210
|
+
Incorrect service result:
|
211
|
+
|
212
|
+
expected Servactory::Result
|
213
|
+
got #{actual.class.name}
|
214
|
+
MESSAGE
|
215
|
+
end
|
216
|
+
|
217
|
+
if actual.success?
|
218
|
+
break <<~MESSAGE
|
219
|
+
Incorrect service result:
|
220
|
+
|
221
|
+
expected failure
|
222
|
+
got success
|
223
|
+
MESSAGE
|
224
|
+
end
|
225
|
+
|
226
|
+
unless actual.error.is_a?(Servactory::Exceptions::Failure)
|
227
|
+
break <<~MESSAGE
|
228
|
+
Incorrect error object:
|
229
|
+
|
230
|
+
expected Servactory::Exceptions::Failure
|
231
|
+
got #{actual.error.class.name}
|
232
|
+
MESSAGE
|
233
|
+
end
|
234
|
+
|
235
|
+
if defined?(@expected_failure_class)
|
236
|
+
unless actual.error.instance_of?(@expected_failure_class)
|
237
|
+
break <<~MESSAGE
|
238
|
+
Incorrect instance error:
|
239
|
+
|
240
|
+
expected #{@expected_failure_class}
|
241
|
+
got #{actual.error.class.name}
|
242
|
+
MESSAGE
|
243
|
+
end
|
244
|
+
else
|
245
|
+
unless actual.error.instance_of?(Servactory::Exceptions::Failure)
|
246
|
+
break <<~MESSAGE
|
247
|
+
Incorrect error object:
|
248
|
+
|
249
|
+
expected Servactory::Exceptions::Failure
|
250
|
+
got #{actual.error.class.name}
|
251
|
+
MESSAGE
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
if defined?(@expected_type) && actual.error.type != @expected_type
|
256
|
+
break <<~MESSAGE
|
257
|
+
Incorrect error type:
|
258
|
+
|
259
|
+
expected #{actual.error.type.inspect}
|
260
|
+
got #{@expected_type.inspect}
|
261
|
+
MESSAGE
|
262
|
+
end
|
263
|
+
|
264
|
+
if defined?(@expected_message) && actual.error.message != @expected_message
|
265
|
+
break <<~MESSAGE
|
266
|
+
Incorrect error message:
|
267
|
+
|
268
|
+
expected #{actual.error.message.inspect}
|
269
|
+
got #{@expected_message.inspect}
|
270
|
+
MESSAGE
|
271
|
+
end
|
272
|
+
|
273
|
+
if defined?(@expected_meta) && actual.error.meta != @expected_meta
|
274
|
+
break <<~MESSAGE
|
275
|
+
Incorrect error meta:
|
276
|
+
|
277
|
+
expected #{actual.error.meta.inspect}
|
278
|
+
got #{@expected_meta.inspect}
|
279
|
+
MESSAGE
|
280
|
+
end
|
281
|
+
|
282
|
+
<<~MESSAGE
|
283
|
+
Unexpected case when using `be_failure_service`.
|
284
|
+
|
285
|
+
Please try to build an example based on the documentation.
|
286
|
+
Or report your problem to us:
|
287
|
+
|
288
|
+
https://github.com/servactory/servactory/issues
|
289
|
+
MESSAGE
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|