servactory 3.1.0.rc2 → 3.1.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.
- checksums.yaml +4 -4
- data/LICENSE +21 -0
- data/README.md +7 -2
- data/config/locales/ja.yml +134 -0
- data/lib/generators/README.md +2 -2
- data/lib/generators/servactory/extension/USAGE +1 -1
- data/lib/generators/servactory/install/USAGE +2 -2
- data/lib/generators/servactory/rspec/USAGE +4 -4
- data/lib/generators/servactory/rspec/rspec_generator.rb +2 -2
- data/lib/generators/servactory/service/USAGE +5 -5
- data/lib/servactory/actions/collection.rb +21 -5
- data/lib/servactory/actions/stages/collection.rb +19 -1
- data/lib/servactory/actions/tools/rules.rb +1 -1
- data/lib/servactory/actions/tools/runner.rb +1 -1
- data/lib/servactory/configuration/collection_mode/class_names_collection.rb +1 -1
- data/lib/servactory/configuration/config.rb +16 -0
- data/lib/servactory/configuration/configurable.rb +7 -4
- data/lib/servactory/configuration/factory.rb +30 -3
- data/lib/servactory/configuration/hash_mode/class_names_collection.rb +5 -1
- data/lib/servactory/configuration/option_helpers/option_helpers_collection.rb +156 -29
- data/lib/servactory/context/warehouse/inputs.rb +1 -1
- 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 +7 -7
- data/lib/servactory/info/builder.rb +4 -3
- data/lib/servactory/inputs/dsl.rb +5 -1
- data/lib/servactory/inputs/input.rb +1 -2
- data/lib/servactory/inputs/tools/validation.rb +10 -18
- data/lib/servactory/internals/dsl.rb +7 -1
- data/lib/servactory/maintenance/attributes/base.rb +4 -1
- data/lib/servactory/maintenance/attributes/collection.rb +61 -6
- data/lib/servactory/maintenance/options/collection.rb +71 -30
- data/lib/servactory/maintenance/options/option.rb +3 -12
- data/lib/servactory/maintenance/options/registrar.rb +3 -13
- data/lib/servactory/maintenance/validations/checkers/type.rb +23 -5
- data/lib/servactory/maintenance/validations/performer.rb +12 -23
- data/lib/servactory/outputs/dsl.rb +7 -1
- data/lib/servactory/result.rb +21 -8
- data/lib/servactory/test_kit/rspec/helpers/exception_validator.rb +112 -0
- data/lib/servactory/test_kit/rspec/helpers/input_validator.rb +3 -1
- data/lib/servactory/test_kit/rspec/helpers/mock_executor.rb +125 -125
- data/lib/servactory/test_kit/rspec/helpers/service_inputs_guard.rb +98 -0
- data/lib/servactory/test_kit/rspec/helpers/service_inputs_matcher.rb +75 -0
- data/lib/servactory/test_kit/rspec/helpers/service_mock_builder.rb +185 -50
- data/lib/servactory/test_kit/rspec/helpers/service_mock_config.rb +45 -10
- data/lib/servactory/test_kit/rspec/helpers/wrap_block_adapter.rb +74 -0
- data/lib/servactory/test_kit/rspec/helpers.rb +4 -0
- data/lib/servactory/test_kit/rspec/matchers/base/attribute_matcher.rb +72 -16
- data/lib/servactory/test_kit/rspec/matchers/base/message_expectation.rb +194 -0
- data/lib/servactory/test_kit/rspec/matchers/base/submatcher.rb +8 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/option_helper_rules.rb +148 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/proc_message.rb +117 -0
- data/lib/servactory/test_kit/rspec/matchers/concerns/required_message.rb +92 -0
- data/lib/servactory/test_kit/rspec/matchers/have_service_input_matcher.rb +27 -4
- data/lib/servactory/test_kit/rspec/matchers/have_service_internal_matcher.rb +10 -3
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/required_submatcher.rb +58 -23
- data/lib/servactory/test_kit/rspec/matchers/submatchers/input/valid_with_submatcher.rb +124 -59
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/message_submatcher.rb +133 -39
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/must_submatcher.rb +192 -17
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/target_submatcher.rb +4 -2
- data/lib/servactory/test_kit/rspec/matchers/submatchers/shared/types_submatcher.rb +6 -0
- data/lib/servactory/test_kit/rspec/matchers.rb +15 -0
- data/lib/servactory/tool_kit/dynamic_options/consists_of.rb +34 -11
- data/lib/servactory/tool_kit/dynamic_options/format.rb +3 -4
- data/lib/servactory/tool_kit/dynamic_options/inclusion.rb +3 -3
- data/lib/servactory/tool_kit/dynamic_options/max.rb +3 -3
- data/lib/servactory/tool_kit/dynamic_options/min.rb +3 -3
- data/lib/servactory/tool_kit/dynamic_options/multiple_of.rb +35 -7
- data/lib/servactory/tool_kit/dynamic_options/must.rb +2 -3
- data/lib/servactory/tool_kit/dynamic_options/schema.rb +40 -25
- data/lib/servactory/tool_kit/dynamic_options/target.rb +4 -4
- data/lib/servactory/utils.rb +13 -3
- data/lib/servactory/version.rb +1 -1
- data/lib/servactory.rb +3 -4
- metadata +20 -4
|
@@ -22,17 +22,38 @@ module Servactory
|
|
|
22
22
|
# end
|
|
23
23
|
# ```
|
|
24
24
|
#
|
|
25
|
+
# ## Messages
|
|
26
|
+
#
|
|
27
|
+
# `.message` checks the message of the option chained right before it,
|
|
28
|
+
# `.required(message)` checks the required message, and `.must` takes
|
|
29
|
+
# the expected message of each rule by its name:
|
|
30
|
+
#
|
|
31
|
+
# ```ruby
|
|
32
|
+
# it { is_expected.to have_service_input(:status).inclusion(%w[active inactive]).message("Unknown status") }
|
|
33
|
+
# it { is_expected.to have_service_input(:status).type(String).message(:default) }
|
|
34
|
+
# it { is_expected.to have_service_input(:email).required(/is required/) }
|
|
35
|
+
# it { is_expected.to have_service_input(:age).must(:be_adult, be_positive: "Age must be positive") }
|
|
36
|
+
# it { is_expected.to have_service_input(:age).must(:be_positive).message(be_a(Proc)) }
|
|
37
|
+
# ```
|
|
38
|
+
#
|
|
39
|
+
# A String must equal the message, a Regexp must match it, an RSpec
|
|
40
|
+
# matcher is applied to the message as defined, and `:default` checks
|
|
41
|
+
# that no custom message is defined. See MessageSubmatcher and
|
|
42
|
+
# MustSubmatcher for the details.
|
|
43
|
+
#
|
|
25
44
|
# ## Chain Methods
|
|
26
45
|
#
|
|
27
46
|
# - `.type(Class)` / `.types(Class, ...)` - expected type(s)
|
|
28
|
-
# - `.required` / `.optional` - required status
|
|
47
|
+
# - `.required` / `.optional` - required status, `.required(message)` also checks the message
|
|
29
48
|
# - `.default(value)` - expected default value
|
|
30
49
|
# - `.consists_of(Class)` - for Array/Hash element types
|
|
31
50
|
# - `.schema(Hash)` - expected schema definition
|
|
32
51
|
# - `.inclusion(Array)` - expected inclusion values
|
|
33
|
-
# - `.must(Array)` - custom validation rules
|
|
52
|
+
# - `.must(Array)` / `.must(*names, **messages)` - custom validation rules,
|
|
53
|
+
# optionally with the expected message of each rule
|
|
34
54
|
# - `.target(value, name:)` - target validation
|
|
35
|
-
# - `.message(String)` - expected error message (after other chain)
|
|
55
|
+
# - `.message(String | Regexp | matcher | :default)` - expected error message (after other chain);
|
|
56
|
+
# after `.must(:rule)` it is the expected message of that rule
|
|
36
57
|
class HaveServiceInputMatcher < Base::AttributeMatcher
|
|
37
58
|
for_attribute_type :input
|
|
38
59
|
|
|
@@ -69,7 +90,9 @@ module Servactory
|
|
|
69
90
|
|
|
70
91
|
register_submatcher :must,
|
|
71
92
|
class_name: "Shared::MustSubmatcher",
|
|
72
|
-
transform_args:
|
|
93
|
+
transform_args: (lambda do |args, _kwargs = {}|
|
|
94
|
+
Submatchers::Shared::MustSubmatcher.arguments_from(args)
|
|
95
|
+
end)
|
|
73
96
|
|
|
74
97
|
register_submatcher :message,
|
|
75
98
|
class_name: "Shared::MessageSubmatcher",
|
|
@@ -19,6 +19,9 @@ module Servactory
|
|
|
19
19
|
# it { is_expected.to have_service_internal(:processed_data).type(Hash) }
|
|
20
20
|
# it { is_expected.to have_service_internal(:items).type(Array).consists_of(Item) }
|
|
21
21
|
# it { is_expected.to have_service_internal(:config).schema({ key: String }) }
|
|
22
|
+
# it { is_expected.to have_service_internal(:items).type(Array).message(/must be an Array/) }
|
|
23
|
+
# it { is_expected.to have_service_internal(:total).must(be_positive: "Total must be positive") }
|
|
24
|
+
# it { is_expected.to have_service_internal(:total).must(:be_positive).message(:default) }
|
|
22
25
|
# end
|
|
23
26
|
# ```
|
|
24
27
|
#
|
|
@@ -28,9 +31,11 @@ module Servactory
|
|
|
28
31
|
# - `.consists_of(Class)` - for Array/Hash element types
|
|
29
32
|
# - `.schema(Hash)` - expected schema definition
|
|
30
33
|
# - `.inclusion(Array)` - expected inclusion values
|
|
31
|
-
# - `.must(Array)` - custom validation rules
|
|
34
|
+
# - `.must(Array)` / `.must(*names, **messages)` - custom validation rules,
|
|
35
|
+
# optionally with the expected message of each rule
|
|
32
36
|
# - `.target(value, name:)` - target validation
|
|
33
|
-
# - `.message(String)` - expected error message (after other chain)
|
|
37
|
+
# - `.message(String | Regexp | matcher | :default)` - expected error message (after other chain);
|
|
38
|
+
# after `.must(:rule)` it is the expected message of that rule
|
|
34
39
|
#
|
|
35
40
|
# ## Architecture
|
|
36
41
|
#
|
|
@@ -72,7 +77,9 @@ module Servactory
|
|
|
72
77
|
|
|
73
78
|
register_submatcher :must,
|
|
74
79
|
class_name: "Shared::MustSubmatcher",
|
|
75
|
-
transform_args:
|
|
80
|
+
transform_args: (lambda do |args, _kwargs = {}|
|
|
81
|
+
Submatchers::Shared::MustSubmatcher.arguments_from(args)
|
|
82
|
+
end)
|
|
76
83
|
|
|
77
84
|
register_submatcher :message,
|
|
78
85
|
class_name: "Shared::MessageSubmatcher",
|
|
@@ -18,21 +18,40 @@ module Servactory
|
|
|
18
18
|
# ```ruby
|
|
19
19
|
# it { is_expected.to have_service_input(:user_id).required }
|
|
20
20
|
# it { is_expected.to have_service_input(:name).required("Name is mandatory") }
|
|
21
|
+
# it { is_expected.to have_service_input(:name).required(/is mandatory/) }
|
|
22
|
+
# it { is_expected.to have_service_input(:name).required(:default) }
|
|
21
23
|
# ```
|
|
22
24
|
#
|
|
23
25
|
# ## Validation
|
|
24
26
|
#
|
|
25
27
|
# Checks the `:required` option in attribute data where `is: true`.
|
|
26
28
|
# If a custom message is provided, also validates the message matches.
|
|
29
|
+
#
|
|
30
|
+
# The expected message follows the rules of `.message`: a String is
|
|
31
|
+
# compared with the message exactly, a Regexp is matched against it,
|
|
32
|
+
# an RSpec matcher is applied to the message as defined, and
|
|
33
|
+
# `:default` checks that the input defines no custom message.
|
|
34
|
+
# The default required message does not depend on the given value,
|
|
35
|
+
# so a String or Regexp is compared with it when there is no
|
|
36
|
+
# custom message.
|
|
37
|
+
#
|
|
38
|
+
# A Proc message is called with the keyword arguments the library
|
|
39
|
+
# passes to it: `service:`, `input:` and `value:`, which is `nil`.
|
|
40
|
+
# A Proc message that raises or does not return a String does not
|
|
41
|
+
# match, and the failure message shows the error or the returned value.
|
|
27
42
|
class RequiredSubmatcher < Base::Submatcher
|
|
43
|
+
include Concerns::RequiredMessage
|
|
44
|
+
|
|
28
45
|
# Creates a new required submatcher.
|
|
29
46
|
#
|
|
30
47
|
# @param context [Base::SubmatcherContext] The submatcher context
|
|
31
|
-
# @param custom_message [String, nil] Optional expected error message
|
|
48
|
+
# @param custom_message [String, Regexp, Symbol, Object, nil] Optional expected error message,
|
|
49
|
+
# `:default` or an RSpec matcher
|
|
32
50
|
# @return [RequiredSubmatcher] New submatcher instance
|
|
51
|
+
# @raise [ArgumentError] If the expected message is of another kind
|
|
33
52
|
def initialize(context, custom_message = nil)
|
|
34
53
|
super(context)
|
|
35
|
-
@
|
|
54
|
+
@expectation = Base::MessageExpectation.new(custom_message) unless custom_message.nil?
|
|
36
55
|
end
|
|
37
56
|
|
|
38
57
|
# Returns description for RSpec output.
|
|
@@ -48,41 +67,57 @@ module Servactory
|
|
|
48
67
|
#
|
|
49
68
|
# @return [Boolean] True if input is required with matching message
|
|
50
69
|
def passes?
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
return false unless required?
|
|
71
|
+
return true if expectation.nil?
|
|
53
72
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
actual_message = required_data.fetch(:message) || default_required_message
|
|
58
|
-
actual_message.casecmp(custom_message).zero?
|
|
73
|
+
@message_mismatch = find_message_mismatch
|
|
74
|
+
@message_mismatch.nil?
|
|
59
75
|
end
|
|
60
76
|
|
|
61
77
|
# Builds the failure message for required validation.
|
|
62
78
|
#
|
|
63
79
|
# @return [String] Failure message with expected vs actual
|
|
64
80
|
def build_failure_message
|
|
65
|
-
|
|
66
|
-
should be required
|
|
81
|
+
return not_required_failure_message unless required?
|
|
67
82
|
|
|
68
|
-
|
|
69
|
-
got required: false
|
|
70
|
-
MESSAGE
|
|
83
|
+
"should be required with the expected message\n\n#{@message_mismatch.indent(2)}"
|
|
71
84
|
end
|
|
72
85
|
|
|
73
86
|
private
|
|
74
87
|
|
|
75
|
-
attr_reader :
|
|
88
|
+
attr_reader :expectation
|
|
89
|
+
|
|
90
|
+
# Checks if the input has `required: true`.
|
|
91
|
+
#
|
|
92
|
+
# @return [Boolean] True if input is required
|
|
93
|
+
def required?
|
|
94
|
+
attribute_data.fetch(:required).fetch(:is) == true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Compares the message of the required option with the expected message.
|
|
98
|
+
#
|
|
99
|
+
# Uses the custom message from `required: { message: }` if provided,
|
|
100
|
+
# otherwise the default message. A Proc message is called with the
|
|
101
|
+
# keyword arguments the library passes to it.
|
|
102
|
+
#
|
|
103
|
+
# @return [String, nil] Explanation of the mismatch, or nil if the messages match
|
|
104
|
+
def find_message_mismatch
|
|
105
|
+
expectation.mismatch_for(
|
|
106
|
+
required_custom_message,
|
|
107
|
+
default_message: default_required_message
|
|
108
|
+
) { |message| call_required_message(message) }
|
|
109
|
+
end
|
|
76
110
|
|
|
77
|
-
#
|
|
111
|
+
# Builds the failure message for an optional input.
|
|
78
112
|
#
|
|
79
|
-
# @return [String]
|
|
80
|
-
def
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
113
|
+
# @return [String] Failure message with expected vs actual required state
|
|
114
|
+
def not_required_failure_message
|
|
115
|
+
<<~MESSAGE
|
|
116
|
+
should be required
|
|
117
|
+
|
|
118
|
+
expected required: true
|
|
119
|
+
got required: false
|
|
120
|
+
MESSAGE
|
|
86
121
|
end
|
|
87
122
|
end
|
|
88
123
|
end
|
|
@@ -35,6 +35,10 @@ module Servactory
|
|
|
35
35
|
# 5. `failure_inclusion_passes?` - fails with value outside inclusion
|
|
36
36
|
# 6. `failure_target_passes?` - fails with value outside target
|
|
37
37
|
class ValidWithSubmatcher < Base::Submatcher # rubocop:disable Metrics/ClassLength
|
|
38
|
+
include Concerns::OptionHelperRules
|
|
39
|
+
include Concerns::ProcMessage
|
|
40
|
+
include Concerns::RequiredMessage
|
|
41
|
+
|
|
38
42
|
# Creates a new valid_with submatcher.
|
|
39
43
|
#
|
|
40
44
|
# @param context [Base::SubmatcherContext] The submatcher context
|
|
@@ -93,44 +97,59 @@ module Servactory
|
|
|
93
97
|
# Checks that service fails with wrong type.
|
|
94
98
|
#
|
|
95
99
|
# @return [Boolean] True if type validation fails as expected
|
|
96
|
-
def failure_type_passes?
|
|
97
|
-
|
|
100
|
+
def failure_type_passes?
|
|
101
|
+
wrong_value = Servactory::TestKit::FakeType.new
|
|
98
102
|
|
|
99
103
|
prepared_attributes = attributes.dup
|
|
100
|
-
prepared_attributes[attribute_name] =
|
|
104
|
+
prepared_attributes[attribute_name] = wrong_value
|
|
105
|
+
|
|
106
|
+
expect_failure_with!(prepared_attributes, type_error_message_for(wrong_value))
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Builds the message expected for a value of wrong type.
|
|
110
|
+
#
|
|
111
|
+
# Uses the custom message from `type: { is:, message: }` if provided,
|
|
112
|
+
# otherwise the default message.
|
|
113
|
+
#
|
|
114
|
+
# @param value [Object] The value of wrong type
|
|
115
|
+
# @return [String] Expected error message
|
|
116
|
+
def type_error_message_for(value)
|
|
117
|
+
message = attribute_data.fetch(:type).fetch(:message)
|
|
118
|
+
expected_type = attribute_data.fetch(:types).join(", ")
|
|
119
|
+
given_type = value.class.name
|
|
120
|
+
|
|
121
|
+
return default_type_error_message(expected_type:, given_type:) if message.blank?
|
|
122
|
+
return message unless message.is_a?(Proc)
|
|
123
|
+
|
|
124
|
+
call_proc_message(message, :type, value:, expected_type:, given_type:)
|
|
125
|
+
end
|
|
101
126
|
|
|
102
|
-
|
|
127
|
+
# Builds the default message for a value of wrong type.
|
|
128
|
+
#
|
|
129
|
+
# @param expected_type [String] Expected type names
|
|
130
|
+
# @param given_type [String] Name of the given value's class
|
|
131
|
+
# @return [String] Default error message
|
|
132
|
+
def default_type_error_message(expected_type:, given_type:)
|
|
133
|
+
I18n.t(
|
|
103
134
|
"#{i18n_root_key}.inputs.validations.type.default_error.default",
|
|
104
135
|
service_class_name: described_class.name,
|
|
105
136
|
input_name: attribute_name,
|
|
106
|
-
expected_type
|
|
107
|
-
given_type:
|
|
137
|
+
expected_type:,
|
|
138
|
+
given_type:
|
|
108
139
|
)
|
|
109
|
-
|
|
110
|
-
expect_failure_with!(prepared_attributes, input_required_message)
|
|
111
140
|
end
|
|
112
141
|
|
|
113
142
|
# Checks that required validation fails when input is nil.
|
|
114
143
|
#
|
|
115
144
|
# @return [Boolean] True if required validation fails as expected
|
|
116
|
-
def failure_required_passes?
|
|
145
|
+
def failure_required_passes?
|
|
117
146
|
input_required = attribute_data.fetch(:required).fetch(:is)
|
|
118
147
|
return true unless input_required
|
|
119
148
|
|
|
120
149
|
prepared_attributes = attributes.dup
|
|
121
150
|
prepared_attributes[attribute_name] = nil
|
|
122
151
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if input_required_message.nil?
|
|
126
|
-
input_required_message = I18n.t(
|
|
127
|
-
"#{i18n_root_key}.inputs.validations.required.default_error.default",
|
|
128
|
-
service_class_name: described_class.name,
|
|
129
|
-
input_name: attribute_name
|
|
130
|
-
)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
expect_failure_with!(prepared_attributes, input_required_message)
|
|
152
|
+
expect_failure_with!(prepared_attributes, required_message)
|
|
134
153
|
end
|
|
135
154
|
|
|
136
155
|
# Checks that optional input accepts nil without failure.
|
|
@@ -149,7 +168,7 @@ module Servactory
|
|
|
149
168
|
# Checks that inclusion validation fails with wrong value.
|
|
150
169
|
#
|
|
151
170
|
# @return [Boolean] True if inclusion validation fails as expected
|
|
152
|
-
def failure_inclusion_passes?
|
|
171
|
+
def failure_inclusion_passes?
|
|
153
172
|
input_inclusion_in = attribute_data.dig(:inclusion, :in)
|
|
154
173
|
return true if input_inclusion_in.blank?
|
|
155
174
|
|
|
@@ -158,31 +177,43 @@ module Servactory
|
|
|
158
177
|
prepared_attributes = attributes.dup
|
|
159
178
|
prepared_attributes[attribute_name] = wrong_value
|
|
160
179
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
# If message is a Proc, we can't easily evaluate it in the test context
|
|
164
|
-
# (it may require runtime args like input:, value:), so skip message comparison
|
|
165
|
-
if input_required_message.is_a?(Proc)
|
|
166
|
-
return expect_failure_with!(prepared_attributes, :skip_message_check)
|
|
167
|
-
end
|
|
180
|
+
expect_failure_with!(prepared_attributes, inclusion_error_message_for(wrong_value, input_inclusion_in))
|
|
181
|
+
end
|
|
168
182
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
183
|
+
# Builds the message expected for a value outside the inclusion set.
|
|
184
|
+
#
|
|
185
|
+
# Uses the custom message from `inclusion: { in:, message: }` if provided,
|
|
186
|
+
# otherwise the default message.
|
|
187
|
+
#
|
|
188
|
+
# @param value [Object] The value outside the inclusion set
|
|
189
|
+
# @param option_value [Range, Array, Object] The inclusion constraint
|
|
190
|
+
# @return [Object] Expected error message
|
|
191
|
+
def inclusion_error_message_for(value, option_value)
|
|
192
|
+
message = attribute_data.fetch(:inclusion).fetch(:message)
|
|
193
|
+
return default_inclusion_error_message(value:, option_value:) if message.blank?
|
|
194
|
+
|
|
195
|
+
dynamic_option_message(message, :inclusion, value:, option_value:)
|
|
196
|
+
end
|
|
178
197
|
|
|
179
|
-
|
|
198
|
+
# Builds the default message for a value outside the inclusion set.
|
|
199
|
+
#
|
|
200
|
+
# @param value [Object] The value outside the inclusion set
|
|
201
|
+
# @param option_value [Object] The inclusion constraint
|
|
202
|
+
# @return [String] Default error message
|
|
203
|
+
def default_inclusion_error_message(value:, option_value:)
|
|
204
|
+
I18n.t(
|
|
205
|
+
"#{i18n_root_key}.inputs.validations.must.dynamic_options.inclusion.default",
|
|
206
|
+
service_class_name: described_class.name,
|
|
207
|
+
input_name: attribute_name,
|
|
208
|
+
input_inclusion: option_value.inspect,
|
|
209
|
+
value: value.inspect
|
|
210
|
+
)
|
|
180
211
|
end
|
|
181
212
|
|
|
182
213
|
# Checks that target validation fails with wrong value.
|
|
183
214
|
#
|
|
184
215
|
# @return [Boolean] True if target validation fails as expected
|
|
185
|
-
def failure_target_passes?
|
|
216
|
+
def failure_target_passes?
|
|
186
217
|
input_target_in = attribute_data.dig(:target, :in)
|
|
187
218
|
return true if input_target_in.blank?
|
|
188
219
|
|
|
@@ -192,40 +223,74 @@ module Servactory
|
|
|
192
223
|
prepared_attributes = attributes.dup
|
|
193
224
|
prepared_attributes[attribute_name] = wrong_value
|
|
194
225
|
|
|
195
|
-
|
|
226
|
+
expect_failure_with!(prepared_attributes, target_error_message_for(wrong_value, input_target_in))
|
|
227
|
+
end
|
|
196
228
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
229
|
+
# Builds the message expected for a value outside the target set.
|
|
230
|
+
#
|
|
231
|
+
# Uses the custom message from `target: { in:, message: }` if provided,
|
|
232
|
+
# otherwise the default message.
|
|
233
|
+
#
|
|
234
|
+
# @param value [Object] The value outside the target set
|
|
235
|
+
# @param option_value [Array, Object] The target constraint
|
|
236
|
+
# @return [Object] Expected error message
|
|
237
|
+
def target_error_message_for(value, option_value)
|
|
238
|
+
message = attribute_data.fetch(:target).fetch(:message)
|
|
239
|
+
return default_target_error_message(value:, option_value:) if message.blank?
|
|
240
|
+
|
|
241
|
+
dynamic_option_message(message, :target, value:, option_value:)
|
|
242
|
+
end
|
|
202
243
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
244
|
+
# Builds the default message for a value outside the target set.
|
|
245
|
+
#
|
|
246
|
+
# @param value [Object] The value outside the target set
|
|
247
|
+
# @param option_value [Object] The target constraint
|
|
248
|
+
# @return [String] Default error message
|
|
249
|
+
def default_target_error_message(value:, option_value:)
|
|
250
|
+
I18n.t(
|
|
251
|
+
"#{i18n_root_key}.inputs.validations.must.dynamic_options.target.default",
|
|
252
|
+
service_class_name: described_class.name,
|
|
253
|
+
input_name: attribute_name,
|
|
254
|
+
expected_target: option_value.inspect,
|
|
255
|
+
value: value.inspect
|
|
256
|
+
)
|
|
257
|
+
end
|
|
212
258
|
|
|
213
|
-
|
|
259
|
+
# Returns the custom message of a dynamic option as the library builds it.
|
|
260
|
+
#
|
|
261
|
+
# A Proc message is called with the keywords the library passes to it
|
|
262
|
+
# when the value is outside the option, where the failure has no reason.
|
|
263
|
+
#
|
|
264
|
+
# @param message [Proc, Object] The custom message of the option
|
|
265
|
+
# @param option_name [Symbol] The option name, such as `inclusion`
|
|
266
|
+
# @param value [Object] The value outside the option
|
|
267
|
+
# @param option_value [Object] The value of the option
|
|
268
|
+
# @return [Object] Expected error message
|
|
269
|
+
def dynamic_option_message(message, option_name, value:, option_value:)
|
|
270
|
+
return message unless message.is_a?(Proc)
|
|
271
|
+
|
|
272
|
+
call_proc_message(
|
|
273
|
+
message,
|
|
274
|
+
:dynamic_option,
|
|
275
|
+
value:,
|
|
276
|
+
code: option_helper_rule_name(option_name),
|
|
277
|
+
reason: nil,
|
|
278
|
+
option_name:,
|
|
279
|
+
option_value:
|
|
280
|
+
)
|
|
214
281
|
end
|
|
215
282
|
|
|
216
283
|
# Calls service and verifies it fails with expected message.
|
|
217
284
|
#
|
|
218
285
|
# @param prepared_attributes [Hash] Attributes to pass to service
|
|
219
|
-
# @param expected_message [
|
|
286
|
+
# @param expected_message [Object, nil] Expected error message
|
|
220
287
|
# @return [Boolean] True if service fails with expected message
|
|
221
288
|
def expect_failure_with!(prepared_attributes, expected_message)
|
|
222
289
|
described_class.call!(prepared_attributes).success?
|
|
223
290
|
rescue Servactory::Exceptions::Input => e
|
|
224
|
-
return true if expected_message == :skip_message_check # Just verify error was raised
|
|
225
291
|
return false if expected_message.blank?
|
|
226
292
|
|
|
227
|
-
|
|
228
|
-
message_to_compare.to_s.casecmp(e.message.to_s).zero?
|
|
293
|
+
expected_message.to_s.casecmp(e.message.to_s).zero?
|
|
229
294
|
rescue Servactory::Exceptions::Internal, Servactory::Exceptions::Output
|
|
230
295
|
true
|
|
231
296
|
end
|