servactory 2.5.0.rc2 → 2.5.0.rc4

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.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/generators/servactory/install_generator.rb +21 -0
  4. data/lib/generators/servactory/rspec_generator.rb +88 -0
  5. data/lib/generators/servactory/service_generator.rb +49 -0
  6. data/lib/generators/servactory/templates/services/application_service/base.rb +60 -0
  7. data/lib/generators/servactory/templates/services/application_service/exceptions.rb +11 -0
  8. data/lib/generators/servactory/templates/services/application_service/result.rb +5 -0
  9. data/lib/servactory/context/workspace/internals.rb +1 -1
  10. data/lib/servactory/info/dsl.rb +56 -4
  11. data/lib/servactory/maintenance/attributes/options/registrar.rb +1 -1
  12. data/lib/servactory/result.rb +1 -1
  13. data/lib/servactory/test_kit/rspec/helpers.rb +95 -0
  14. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/consists_of_matcher.rb +121 -0
  15. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/inclusion_matcher.rb +70 -0
  16. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/must_matcher.rb +61 -0
  17. data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/types_matcher.rb +72 -0
  18. data/lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb +203 -0
  19. data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/default_matcher.rb +67 -0
  20. data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/optional_matcher.rb +63 -0
  21. data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/required_matcher.rb +78 -0
  22. data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/valid_with_matcher.rb +233 -0
  23. data/lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb +148 -0
  24. data/lib/servactory/test_kit/rspec/matchers.rb +295 -0
  25. data/lib/servactory/test_kit/utils/faker.rb +78 -0
  26. data/lib/servactory/tool_kit/dynamic_options/format.rb +16 -12
  27. data/lib/servactory/version.rb +1 -1
  28. data/lib/servactory.rb +1 -0
  29. metadata +26 -7
@@ -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