servactory 2.16.1 → 3.0.0.rc2
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 +4 -4
- data/README.md +38 -9
- data/config/locales/de.yml +134 -0
- data/config/locales/en.yml +15 -0
- data/config/locales/es.yml +134 -0
- data/config/locales/fr.yml +134 -0
- data/config/locales/it.yml +134 -0
- data/config/locales/ru.yml +15 -0
- data/lib/generators/README.md +45 -0
- data/lib/generators/servactory/base.rb +82 -0
- data/lib/generators/servactory/extension/USAGE +54 -0
- data/lib/generators/servactory/extension/extension_generator.rb +41 -0
- data/lib/generators/servactory/extension/templates/extension.rb.tt +62 -0
- data/lib/generators/servactory/install/USAGE +27 -0
- data/lib/generators/servactory/install/install_generator.rb +94 -0
- data/lib/generators/servactory/{templates/services/application_service/base.rb → install/templates/application_service/base.rb.tt} +29 -19
- data/lib/generators/servactory/{templates/services/application_service/exceptions.rb → install/templates/application_service/exceptions.rb.tt} +1 -1
- data/lib/generators/servactory/{templates/services/application_service/result.rb → install/templates/application_service/result.rb.tt} +1 -1
- data/lib/generators/servactory/rspec/USAGE +46 -0
- data/lib/generators/servactory/rspec/rspec_generator.rb +95 -0
- data/lib/generators/servactory/rspec/templates/service_spec.rb.tt +58 -0
- data/lib/generators/servactory/service/USAGE +51 -0
- data/lib/generators/servactory/service/service_generator.rb +56 -0
- data/lib/generators/servactory/service/templates/service.rb.tt +22 -0
- data/lib/servactory/actions/collection.rb +56 -1
- data/lib/servactory/actions/dsl.rb +11 -11
- data/lib/servactory/actions/tools/rules.rb +1 -1
- data/lib/servactory/actions/tools/runner.rb +111 -28
- data/lib/servactory/base.rb +1 -7
- data/lib/servactory/configuration/actions/aliases/collection.rb +5 -0
- data/lib/servactory/configuration/actions/rescue_handlers/collection.rb +5 -0
- data/lib/servactory/configuration/actions/shortcuts/collection.rb +5 -0
- data/lib/servactory/configuration/collection_mode/class_names_collection.rb +5 -0
- data/lib/servactory/configuration/config.rb +36 -0
- data/lib/servactory/configuration/configurable.rb +95 -0
- data/lib/servactory/configuration/dsl.rb +3 -27
- data/lib/servactory/configuration/factory.rb +20 -20
- data/lib/servactory/configuration/hash_mode/class_names_collection.rb +5 -0
- data/lib/servactory/configuration/option_helpers/option_helpers_collection.rb +5 -0
- data/lib/servactory/context/warehouse/inputs.rb +2 -2
- data/lib/servactory/context/workspace/inputs.rb +2 -2
- data/lib/servactory/context/workspace/internals.rb +2 -2
- data/lib/servactory/context/workspace/outputs.rb +2 -2
- data/lib/servactory/context/workspace.rb +11 -7
- data/lib/servactory/dsl.rb +10 -8
- data/lib/servactory/maintenance/attributes/tools/validation.rb +1 -1
- data/lib/servactory/result.rb +2 -2
- data/lib/servactory/test_kit/fake_type.rb +23 -0
- data/lib/servactory/test_kit/result.rb +45 -0
- data/lib/servactory/test_kit/rspec/helpers/argument_matchers.rb +97 -0
- data/lib/servactory/test_kit/rspec/helpers/concerns/error_messages.rb +179 -0
- data/lib/servactory/test_kit/rspec/helpers/concerns/service_class_validation.rb +74 -0
- data/lib/servactory/test_kit/rspec/helpers/fluent.rb +110 -0
- data/lib/servactory/test_kit/rspec/helpers/input_validator.rb +149 -0
- data/lib/servactory/test_kit/rspec/helpers/legacy.rb +228 -0
- data/lib/servactory/test_kit/rspec/helpers/mock_executor.rb +256 -0
- data/lib/servactory/test_kit/rspec/helpers/output_validator.rb +121 -0
- data/lib/servactory/test_kit/rspec/helpers/service_mock_builder.rb +422 -0
- data/lib/servactory/test_kit/rspec/helpers/service_mock_config.rb +129 -0
- data/lib/servactory/test_kit/rspec/helpers.rb +51 -84
- data/lib/servactory/test_kit/rspec/matchers/base/attribute_matcher.rb +324 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher.rb +133 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher_context.rb +101 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher_registry.rb +205 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/attribute_data_access.rb +100 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/error_message_builder.rb +106 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/value_comparison.rb +97 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb +89 -219
- data/lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb +74 -166
- data/lib/servactory/test_kit/rspec/matchers/have_service_output_matcher.rb +238 -0
- data/lib/servactory/test_kit/rspec/matchers/result/be_failure_service_matcher.rb +257 -0
- data/lib/servactory/test_kit/rspec/matchers/result/be_success_service_matcher.rb +185 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/default_submatcher.rb +81 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/optional_submatcher.rb +62 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/required_submatcher.rb +93 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/valid_with_submatcher.rb +271 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/consists_of_submatcher.rb +85 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/inclusion_submatcher.rb +120 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/message_submatcher.rb +115 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/must_submatcher.rb +82 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/schema_submatcher.rb +102 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/target_submatcher.rb +125 -0
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/types_submatcher.rb +77 -0
- data/lib/servactory/test_kit/rspec/matchers.rb +126 -285
- data/lib/servactory/test_kit/utils/faker.rb +62 -2
- data/lib/servactory/tool_kit/dynamic_options/consists_of.rb +166 -0
- data/lib/servactory/tool_kit/dynamic_options/format.rb +195 -8
- data/lib/servactory/tool_kit/dynamic_options/inclusion.rb +219 -17
- data/lib/servactory/tool_kit/dynamic_options/max.rb +143 -0
- data/lib/servactory/tool_kit/dynamic_options/min.rb +143 -0
- data/lib/servactory/tool_kit/dynamic_options/multiple_of.rb +157 -8
- data/lib/servactory/tool_kit/dynamic_options/must.rb +194 -0
- data/lib/servactory/tool_kit/dynamic_options/schema.rb +226 -2
- data/lib/servactory/tool_kit/dynamic_options/target.rb +248 -0
- data/lib/servactory/version.rb +4 -4
- data/lib/servactory.rb +6 -0
- metadata +57 -48
- data/lib/generators/servactory/install_generator.rb +0 -21
- data/lib/generators/servactory/rspec_generator.rb +0 -88
- data/lib/generators/servactory/service_generator.rb +0 -49
- data/lib/servactory/configuration/setup.rb +0 -97
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/consists_of_matcher.rb +0 -68
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/inclusion_matcher.rb +0 -73
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/message_matcher.rb +0 -91
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/must_matcher.rb +0 -72
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/schema_matcher.rb +0 -92
- data/lib/servactory/test_kit/rspec/matchers/have_service_attribute_matchers/types_matcher.rb +0 -72
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/default_matcher.rb +0 -69
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/optional_matcher.rb +0 -63
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/required_matcher.rb +0 -81
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matchers/valid_with_matcher.rb +0 -199
|
@@ -1,228 +1,98 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "have_service_input_matchers/default_matcher"
|
|
4
|
-
require_relative "have_service_input_matchers/optional_matcher"
|
|
5
|
-
require_relative "have_service_input_matchers/required_matcher"
|
|
6
|
-
require_relative "have_service_input_matchers/valid_with_matcher"
|
|
7
|
-
|
|
8
3
|
module Servactory
|
|
9
4
|
module TestKit
|
|
10
5
|
module Rspec
|
|
11
6
|
module Matchers
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
HaveServiceAttributeMatchers::SchemaMatcher,
|
|
102
|
-
described_class,
|
|
103
|
-
:input,
|
|
104
|
-
input_name,
|
|
105
|
-
@option_types,
|
|
106
|
-
data
|
|
107
|
-
)
|
|
108
|
-
self
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def inclusion(values)
|
|
112
|
-
add_submatcher(
|
|
113
|
-
HaveServiceAttributeMatchers::InclusionMatcher,
|
|
114
|
-
described_class,
|
|
115
|
-
:input,
|
|
116
|
-
input_name,
|
|
117
|
-
Array(values)
|
|
118
|
-
)
|
|
119
|
-
self
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def must(*must_names)
|
|
123
|
-
add_submatcher(
|
|
124
|
-
HaveServiceAttributeMatchers::MustMatcher,
|
|
125
|
-
described_class,
|
|
126
|
-
:input,
|
|
127
|
-
input_name,
|
|
128
|
-
Array(must_names)
|
|
129
|
-
)
|
|
130
|
-
self
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def valid_with(attributes)
|
|
134
|
-
add_submatcher(
|
|
135
|
-
HaveServiceInputMatchers::ValidWithMatcher,
|
|
136
|
-
described_class,
|
|
137
|
-
:input,
|
|
138
|
-
input_name,
|
|
139
|
-
attributes
|
|
140
|
-
)
|
|
141
|
-
self
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def message(message)
|
|
145
|
-
add_submatcher(
|
|
146
|
-
HaveServiceAttributeMatchers::MessageMatcher,
|
|
147
|
-
described_class,
|
|
148
|
-
:input,
|
|
149
|
-
input_name,
|
|
150
|
-
@last_submatcher,
|
|
151
|
-
message
|
|
152
|
-
)
|
|
153
|
-
self
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# NOTE: Used for delayed chain implementation
|
|
157
|
-
# def not_implemented_chain(*description)
|
|
158
|
-
# Kernel.warn <<-MESSAGE.squish
|
|
159
|
-
# This chain has not yet been implemented.
|
|
160
|
-
# This message is for informational purposes only.
|
|
161
|
-
# Description: #{description}
|
|
162
|
-
# MESSAGE
|
|
163
|
-
# self
|
|
164
|
-
# end
|
|
165
|
-
|
|
166
|
-
def description
|
|
167
|
-
"#{input_name} with #{submatchers.map(&:description).join(', ')}"
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def failure_message
|
|
171
|
-
"Expected #{expectation}, which #{missing_options}"
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def failure_message_when_negated
|
|
175
|
-
"Did not expect #{expectation} with specified options"
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def matches?(subject)
|
|
179
|
-
@subject = subject
|
|
180
|
-
|
|
181
|
-
submatchers_match?
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
protected
|
|
185
|
-
|
|
186
|
-
attr_reader :last_submatcher, :submatchers, :missing, :subject
|
|
187
|
-
|
|
188
|
-
def add_submatcher(matcher_class, *args)
|
|
189
|
-
remove_submatcher(matcher_class)
|
|
190
|
-
@last_submatcher = matcher_class.new(*args)
|
|
191
|
-
submatchers << @last_submatcher
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
def remove_submatcher(matcher_class)
|
|
195
|
-
submatchers.delete_if do |submatcher|
|
|
196
|
-
submatcher.is_a?(matcher_class)
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
def expectation
|
|
201
|
-
"#{described_class.name} to have a service input attribute named #{input_name}"
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
def missing_options
|
|
205
|
-
missing_options = [missing, missing_options_for_failing_submatchers]
|
|
206
|
-
missing_options.flatten.select(&:present?).join(", ")
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
def failing_submatchers
|
|
210
|
-
@failing_submatchers ||= submatchers.reject do |matcher|
|
|
211
|
-
matcher.matches?(subject)
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
def missing_options_for_failing_submatchers
|
|
216
|
-
if defined?(failing_submatchers)
|
|
217
|
-
failing_submatchers.map(&:missing_option)
|
|
218
|
-
else
|
|
219
|
-
[]
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
def submatchers_match?
|
|
224
|
-
failing_submatchers.empty?
|
|
225
|
-
end
|
|
7
|
+
# RSpec matcher for validating Servactory service input definitions.
|
|
8
|
+
#
|
|
9
|
+
# ## Purpose
|
|
10
|
+
#
|
|
11
|
+
# Validates that a service class has the expected input attribute with
|
|
12
|
+
# specified type, required status, defaults, and other options.
|
|
13
|
+
#
|
|
14
|
+
# ## Usage
|
|
15
|
+
#
|
|
16
|
+
# ```ruby
|
|
17
|
+
# RSpec.describe MyService, type: :service do
|
|
18
|
+
# it { is_expected.to have_service_input(:user_id).type(Integer).required }
|
|
19
|
+
# it { is_expected.to have_service_input(:name).type(String).optional }
|
|
20
|
+
# it { is_expected.to have_service_input(:age).type(Integer).default(18) }
|
|
21
|
+
# it { is_expected.to have_service_input(:status).inclusion(%w[active inactive]) }
|
|
22
|
+
# end
|
|
23
|
+
# ```
|
|
24
|
+
#
|
|
25
|
+
# ## Chain Methods
|
|
26
|
+
#
|
|
27
|
+
# - `.type(Class)` / `.types(Class, ...)` - expected type(s)
|
|
28
|
+
# - `.required` / `.optional` - required status
|
|
29
|
+
# - `.default(value)` - expected default value
|
|
30
|
+
# - `.consists_of(Class)` - for Array/Hash element types
|
|
31
|
+
# - `.schema(Hash)` - expected schema definition
|
|
32
|
+
# - `.inclusion(Array)` - expected inclusion values
|
|
33
|
+
# - `.must(Array)` - custom validation rules
|
|
34
|
+
# - `.target(value, name:)` - target validation
|
|
35
|
+
# - `.message(String)` - expected error message (after other chain)
|
|
36
|
+
class HaveServiceInputMatcher < Base::AttributeMatcher
|
|
37
|
+
for_attribute_type :input
|
|
38
|
+
|
|
39
|
+
# Shared submatchers
|
|
40
|
+
register_submatcher :types,
|
|
41
|
+
class_name: "Shared::TypesSubmatcher",
|
|
42
|
+
chain_method: :type,
|
|
43
|
+
chain_aliases: [:types],
|
|
44
|
+
transform_args: ->(args, _kwargs = {}) { [Array(args).flatten] },
|
|
45
|
+
stores_option_types: true
|
|
46
|
+
|
|
47
|
+
register_submatcher :consists_of,
|
|
48
|
+
class_name: "Shared::ConsistsOfSubmatcher",
|
|
49
|
+
transform_args: ->(args, _kwargs = {}) { [Array(args).flatten] },
|
|
50
|
+
requires_option_types: true
|
|
51
|
+
|
|
52
|
+
register_submatcher :schema,
|
|
53
|
+
class_name: "Shared::SchemaSubmatcher",
|
|
54
|
+
transform_args: ->(args, _kwargs = {}) { [args.first || {}] },
|
|
55
|
+
requires_option_types: true
|
|
56
|
+
|
|
57
|
+
register_submatcher :inclusion,
|
|
58
|
+
class_name: "Shared::InclusionSubmatcher",
|
|
59
|
+
transform_args: (lambda do |args, _kwargs = {}|
|
|
60
|
+
[args.first.is_a?(Range) ? args.first : Array(args.first)]
|
|
61
|
+
end)
|
|
62
|
+
|
|
63
|
+
register_submatcher :target,
|
|
64
|
+
class_name: "Shared::TargetSubmatcher",
|
|
65
|
+
transform_args: (lambda do |args, kwargs = {}|
|
|
66
|
+
[kwargs.fetch(:name, :target), Array(args.first)]
|
|
67
|
+
end),
|
|
68
|
+
accepts_trailing_options: true
|
|
69
|
+
|
|
70
|
+
register_submatcher :must,
|
|
71
|
+
class_name: "Shared::MustSubmatcher",
|
|
72
|
+
transform_args: ->(args, _kwargs = {}) { [Array(args).flatten] }
|
|
73
|
+
|
|
74
|
+
register_submatcher :message,
|
|
75
|
+
class_name: "Shared::MessageSubmatcher",
|
|
76
|
+
transform_args: ->(args, _kwargs = {}) { [args.first] },
|
|
77
|
+
requires_last_submatcher: true
|
|
78
|
+
|
|
79
|
+
# Input-specific submatchers
|
|
80
|
+
register_submatcher :required,
|
|
81
|
+
class_name: "Input::RequiredSubmatcher",
|
|
82
|
+
transform_args: ->(args, _kwargs = {}) { [args.first] },
|
|
83
|
+
mutually_exclusive_with: [:optional]
|
|
84
|
+
|
|
85
|
+
register_submatcher :optional,
|
|
86
|
+
class_name: "Input::OptionalSubmatcher",
|
|
87
|
+
mutually_exclusive_with: [:required]
|
|
88
|
+
|
|
89
|
+
register_submatcher :default,
|
|
90
|
+
class_name: "Input::DefaultSubmatcher",
|
|
91
|
+
transform_args: ->(args, _kwargs = {}) { [args.first] }
|
|
92
|
+
|
|
93
|
+
register_submatcher :valid_with,
|
|
94
|
+
class_name: "Input::ValidWithSubmatcher",
|
|
95
|
+
transform_args: ->(args, _kwargs = {}) { [args.first] }
|
|
226
96
|
end
|
|
227
97
|
end
|
|
228
98
|
end
|
|
@@ -1,175 +1,83 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "have_service_attribute_matchers/types_matcher"
|
|
4
|
-
require_relative "have_service_attribute_matchers/consists_of_matcher"
|
|
5
|
-
require_relative "have_service_attribute_matchers/schema_matcher"
|
|
6
|
-
require_relative "have_service_attribute_matchers/inclusion_matcher"
|
|
7
|
-
require_relative "have_service_attribute_matchers/must_matcher"
|
|
8
|
-
require_relative "have_service_attribute_matchers/message_matcher"
|
|
9
|
-
|
|
10
3
|
module Servactory
|
|
11
4
|
module TestKit
|
|
12
5
|
module Rspec
|
|
13
6
|
module Matchers
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def must(*must_names)
|
|
91
|
-
add_submatcher(
|
|
92
|
-
HaveServiceAttributeMatchers::MustMatcher,
|
|
93
|
-
described_class,
|
|
94
|
-
:internal,
|
|
95
|
-
internal_name,
|
|
96
|
-
Array(must_names)
|
|
97
|
-
)
|
|
98
|
-
self
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def message(message)
|
|
102
|
-
add_submatcher(
|
|
103
|
-
HaveServiceAttributeMatchers::MessageMatcher,
|
|
104
|
-
described_class,
|
|
105
|
-
:internal,
|
|
106
|
-
internal_name,
|
|
107
|
-
@last_submatcher,
|
|
108
|
-
message
|
|
109
|
-
)
|
|
110
|
-
self
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def description
|
|
114
|
-
"#{internal_name} with #{submatchers.map(&:description).join(', ')}"
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def failure_message
|
|
118
|
-
"Expected #{expectation}, which #{missing_options}"
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
def failure_message_when_negated
|
|
122
|
-
"Did not expect #{expectation} with specified options"
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def matches?(subject)
|
|
126
|
-
@subject = subject
|
|
127
|
-
|
|
128
|
-
submatchers_match?
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
protected
|
|
132
|
-
|
|
133
|
-
attr_reader :last_submatcher, :submatchers, :missing, :subject
|
|
134
|
-
|
|
135
|
-
def add_submatcher(matcher_class, *args)
|
|
136
|
-
remove_submatcher(matcher_class)
|
|
137
|
-
@last_submatcher = matcher_class.new(*args)
|
|
138
|
-
submatchers << @last_submatcher
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def remove_submatcher(matcher_class)
|
|
142
|
-
submatchers.delete_if do |submatcher|
|
|
143
|
-
submatcher.is_a?(matcher_class)
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def expectation
|
|
148
|
-
"#{described_class.name} to have a service internal attribute named #{internal_name}"
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def missing_options
|
|
152
|
-
missing_options = [missing, missing_options_for_failing_submatchers]
|
|
153
|
-
missing_options.flatten.select(&:present?).join(", ")
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def failing_submatchers
|
|
157
|
-
@failing_submatchers ||= submatchers.reject do |matcher|
|
|
158
|
-
matcher.matches?(subject)
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def missing_options_for_failing_submatchers
|
|
163
|
-
if defined?(failing_submatchers)
|
|
164
|
-
failing_submatchers.map(&:missing_option)
|
|
165
|
-
else
|
|
166
|
-
[]
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def submatchers_match?
|
|
171
|
-
failing_submatchers.empty?
|
|
172
|
-
end
|
|
7
|
+
# RSpec matcher for validating Servactory service internal definitions.
|
|
8
|
+
#
|
|
9
|
+
# ## Purpose
|
|
10
|
+
#
|
|
11
|
+
# Validates that a service class has the expected internal attribute with
|
|
12
|
+
# specified type, schema, and other options. Internal attributes are used
|
|
13
|
+
# for intermediate values during service execution.
|
|
14
|
+
#
|
|
15
|
+
# ## Usage
|
|
16
|
+
#
|
|
17
|
+
# ```ruby
|
|
18
|
+
# RSpec.describe MyService, type: :service do
|
|
19
|
+
# it { is_expected.to have_service_internal(:processed_data).type(Hash) }
|
|
20
|
+
# it { is_expected.to have_service_internal(:items).type(Array).consists_of(Item) }
|
|
21
|
+
# it { is_expected.to have_service_internal(:config).schema({ key: String }) }
|
|
22
|
+
# end
|
|
23
|
+
# ```
|
|
24
|
+
#
|
|
25
|
+
# ## Chain Methods
|
|
26
|
+
#
|
|
27
|
+
# - `.type(Class)` / `.types(Class, ...)` - expected type(s)
|
|
28
|
+
# - `.consists_of(Class)` - for Array/Hash element types
|
|
29
|
+
# - `.schema(Hash)` - expected schema definition
|
|
30
|
+
# - `.inclusion(Array)` - expected inclusion values
|
|
31
|
+
# - `.must(Array)` - custom validation rules
|
|
32
|
+
# - `.target(value, name:)` - target validation
|
|
33
|
+
# - `.message(String)` - expected error message (after other chain)
|
|
34
|
+
#
|
|
35
|
+
# ## Architecture
|
|
36
|
+
#
|
|
37
|
+
# Inherits from Base::AttributeMatcher with `for_attribute_type :internal`.
|
|
38
|
+
# Uses shared submatchers for common validations.
|
|
39
|
+
class HaveServiceInternalMatcher < Base::AttributeMatcher
|
|
40
|
+
for_attribute_type :internal
|
|
41
|
+
|
|
42
|
+
# Shared submatchers
|
|
43
|
+
register_submatcher :types,
|
|
44
|
+
class_name: "Shared::TypesSubmatcher",
|
|
45
|
+
chain_method: :type,
|
|
46
|
+
chain_aliases: [:types],
|
|
47
|
+
transform_args: ->(args, _kwargs = {}) { [Array(args).flatten] },
|
|
48
|
+
stores_option_types: true
|
|
49
|
+
|
|
50
|
+
register_submatcher :consists_of,
|
|
51
|
+
class_name: "Shared::ConsistsOfSubmatcher",
|
|
52
|
+
transform_args: ->(args, _kwargs = {}) { [Array(args).flatten] },
|
|
53
|
+
requires_option_types: true
|
|
54
|
+
|
|
55
|
+
register_submatcher :schema,
|
|
56
|
+
class_name: "Shared::SchemaSubmatcher",
|
|
57
|
+
transform_args: ->(args, _kwargs = {}) { [args.first || {}] },
|
|
58
|
+
requires_option_types: true
|
|
59
|
+
|
|
60
|
+
register_submatcher :inclusion,
|
|
61
|
+
class_name: "Shared::InclusionSubmatcher",
|
|
62
|
+
transform_args: (lambda do |args, _kwargs = {}|
|
|
63
|
+
[args.first.is_a?(Range) ? args.first : Array(args.first)]
|
|
64
|
+
end)
|
|
65
|
+
|
|
66
|
+
register_submatcher :target,
|
|
67
|
+
class_name: "Shared::TargetSubmatcher",
|
|
68
|
+
transform_args: (lambda do |args, kwargs = {}|
|
|
69
|
+
[kwargs.fetch(:name, :target), Array(args.first)]
|
|
70
|
+
end),
|
|
71
|
+
accepts_trailing_options: true
|
|
72
|
+
|
|
73
|
+
register_submatcher :must,
|
|
74
|
+
class_name: "Shared::MustSubmatcher",
|
|
75
|
+
transform_args: ->(args, _kwargs = {}) { [Array(args).flatten] }
|
|
76
|
+
|
|
77
|
+
register_submatcher :message,
|
|
78
|
+
class_name: "Shared::MessageSubmatcher",
|
|
79
|
+
transform_args: ->(args, _kwargs = {}) { [args.first] },
|
|
80
|
+
requires_last_submatcher: true
|
|
173
81
|
end
|
|
174
82
|
end
|
|
175
83
|
end
|