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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- 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 +1 -1
- 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/tool_kit/dynamic_options/format.rb +16 -12
- data/lib/servactory/version.rb +1 -1
- data/lib/servactory.rb +1 -0
- metadata +26 -7
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module TestKit
|
5
|
+
module Rspec
|
6
|
+
module Matchers
|
7
|
+
module HaveServiceAttributeMatchers
|
8
|
+
class MustMatcher
|
9
|
+
attr_reader :missing_option
|
10
|
+
|
11
|
+
def initialize(described_class, attribute_type, attribute_name, must_names)
|
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
|
+
@must_names = must_names
|
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
|
+
"must: #{must_names.join(', ')}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def matches?(subject)
|
28
|
+
if 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
|
+
:must_names,
|
44
|
+
:attribute_data
|
45
|
+
|
46
|
+
def submatcher_passes?(_subject)
|
47
|
+
attribute_must = attribute_data.fetch(:must)
|
48
|
+
|
49
|
+
attribute_must.keys.difference(must_names).empty? &&
|
50
|
+
must_names.difference(attribute_must.keys).empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_missing_option
|
54
|
+
"should #{must_names.join(', ')}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module TestKit
|
5
|
+
module Rspec
|
6
|
+
module Matchers
|
7
|
+
module HaveServiceAttributeMatchers
|
8
|
+
class TypesMatcher
|
9
|
+
attr_reader :missing_option
|
10
|
+
|
11
|
+
def initialize(described_class, attribute_type, attribute_name, types)
|
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
|
+
@types = types
|
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
|
+
result = "type"
|
25
|
+
result += types.size > 1 ? "s: " : ": "
|
26
|
+
result + types.join(", ")
|
27
|
+
end
|
28
|
+
|
29
|
+
def matches?(subject)
|
30
|
+
if submatcher_passes?(subject)
|
31
|
+
true
|
32
|
+
else
|
33
|
+
@missing_option = build_missing_option
|
34
|
+
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :described_class,
|
42
|
+
:attribute_type,
|
43
|
+
:attribute_type_plural,
|
44
|
+
:attribute_name,
|
45
|
+
:types,
|
46
|
+
:attribute_data
|
47
|
+
|
48
|
+
def submatcher_passes?(_subject)
|
49
|
+
option_types = attribute_data.fetch(:types)
|
50
|
+
|
51
|
+
option_types.difference(types).empty? &&
|
52
|
+
types.difference(option_types).empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_missing_option
|
56
|
+
option_types = attribute_data.fetch(:types)
|
57
|
+
|
58
|
+
text_about_types = types.size > 1 ? "with one of the following types" : "type"
|
59
|
+
|
60
|
+
<<~MESSAGE
|
61
|
+
should be a value with #{text_about_types}
|
62
|
+
|
63
|
+
expected #{types.inspect}
|
64
|
+
got #{option_types.inspect}
|
65
|
+
MESSAGE
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module TestKit
|
5
|
+
module Rspec
|
6
|
+
module Matchers
|
7
|
+
class HaveServiceInputMatcher # rubocop:disable Metrics/ClassLength
|
8
|
+
attr_reader :described_class, :input_name, :options
|
9
|
+
|
10
|
+
def initialize(described_class, input_name)
|
11
|
+
@described_class = described_class
|
12
|
+
@input_name = input_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
|
+
:input,
|
30
|
+
input_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
|
+
:input,
|
42
|
+
input_name,
|
43
|
+
@option_types
|
44
|
+
)
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def required(custom_message = nil)
|
49
|
+
remove_submatcher(HaveServiceInputMatchers::OptionalMatcher)
|
50
|
+
add_submatcher(
|
51
|
+
HaveServiceInputMatchers::RequiredMatcher,
|
52
|
+
described_class,
|
53
|
+
:input,
|
54
|
+
input_name,
|
55
|
+
custom_message
|
56
|
+
)
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def optional
|
61
|
+
remove_submatcher(HaveServiceInputMatchers::RequiredMatcher)
|
62
|
+
add_submatcher(
|
63
|
+
HaveServiceInputMatchers::OptionalMatcher,
|
64
|
+
described_class,
|
65
|
+
:input,
|
66
|
+
input_name
|
67
|
+
)
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def default(value)
|
72
|
+
add_submatcher(
|
73
|
+
HaveServiceInputMatchers::DefaultMatcher,
|
74
|
+
described_class,
|
75
|
+
:input,
|
76
|
+
input_name,
|
77
|
+
value
|
78
|
+
)
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
def consists_of(*types) # rubocop:disable Metrics/MethodLength
|
83
|
+
message = block_given? ? yield : nil
|
84
|
+
|
85
|
+
add_submatcher(
|
86
|
+
HaveServiceAttributeMatchers::ConsistsOfMatcher,
|
87
|
+
described_class,
|
88
|
+
:input,
|
89
|
+
input_name,
|
90
|
+
@option_types,
|
91
|
+
Array(types),
|
92
|
+
message
|
93
|
+
)
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def inclusion(values)
|
98
|
+
add_submatcher(
|
99
|
+
HaveServiceAttributeMatchers::InclusionMatcher,
|
100
|
+
described_class,
|
101
|
+
:input,
|
102
|
+
input_name,
|
103
|
+
Array(values)
|
104
|
+
)
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
def must(*must_names)
|
109
|
+
add_submatcher(
|
110
|
+
HaveServiceAttributeMatchers::MustMatcher,
|
111
|
+
described_class,
|
112
|
+
:input,
|
113
|
+
input_name,
|
114
|
+
Array(must_names)
|
115
|
+
)
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
def valid_with(attributes)
|
120
|
+
add_submatcher(
|
121
|
+
HaveServiceInputMatchers::ValidWithMatcher,
|
122
|
+
described_class,
|
123
|
+
:input,
|
124
|
+
input_name,
|
125
|
+
attributes
|
126
|
+
)
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
130
|
+
# NOTE: Used for delayed chain implementation
|
131
|
+
# def not_implemented_chain(*description)
|
132
|
+
# Kernel.warn <<-MESSAGE.squish
|
133
|
+
# This chain has not yet been implemented.
|
134
|
+
# This message is for informational purposes only.
|
135
|
+
# Description: #{description}
|
136
|
+
# MESSAGE
|
137
|
+
# self
|
138
|
+
# end
|
139
|
+
|
140
|
+
def description
|
141
|
+
"#{input_name} with #{submatchers.map(&:description).join(', ')}"
|
142
|
+
end
|
143
|
+
|
144
|
+
def failure_message
|
145
|
+
"Expected #{expectation}, which #{missing_options}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def failure_message_when_negated
|
149
|
+
"Did not expect #{expectation} with specified options"
|
150
|
+
end
|
151
|
+
|
152
|
+
def matches?(subject)
|
153
|
+
@subject = subject
|
154
|
+
|
155
|
+
submatchers_match?
|
156
|
+
end
|
157
|
+
|
158
|
+
protected
|
159
|
+
|
160
|
+
attr_reader :submatchers, :missing, :subject
|
161
|
+
|
162
|
+
def add_submatcher(matcher_class, *args)
|
163
|
+
remove_submatcher(matcher_class)
|
164
|
+
submatchers << matcher_class.new(*args)
|
165
|
+
end
|
166
|
+
|
167
|
+
def remove_submatcher(matcher_class)
|
168
|
+
submatchers.delete_if do |submatcher|
|
169
|
+
submatcher.is_a?(matcher_class)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def expectation
|
174
|
+
"#{described_class.name} to have a service input attribute named #{input_name}"
|
175
|
+
end
|
176
|
+
|
177
|
+
def missing_options
|
178
|
+
missing_options = [missing, missing_options_for_failing_submatchers]
|
179
|
+
missing_options.flatten.select(&:present?).join(", ")
|
180
|
+
end
|
181
|
+
|
182
|
+
def failing_submatchers
|
183
|
+
@failing_submatchers ||= submatchers.reject do |matcher|
|
184
|
+
matcher.matches?(subject)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def missing_options_for_failing_submatchers
|
189
|
+
if defined?(failing_submatchers)
|
190
|
+
failing_submatchers.map(&:missing_option)
|
191
|
+
else
|
192
|
+
[]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def submatchers_match?
|
197
|
+
failing_submatchers.empty?
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module TestKit
|
5
|
+
module Rspec
|
6
|
+
module Matchers
|
7
|
+
module HaveServiceInputMatchers
|
8
|
+
class DefaultMatcher
|
9
|
+
attr_reader :missing_option
|
10
|
+
|
11
|
+
def initialize(described_class, attribute_type, attribute_name, default_value)
|
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
|
+
@default_value = default_value
|
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
|
+
"default: #{default_value.inspect}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def matches?(subject)
|
28
|
+
if 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
|
+
:default_value,
|
44
|
+
:attribute_data
|
45
|
+
|
46
|
+
def submatcher_passes?(_subject)
|
47
|
+
attribute_default_value = attribute_data.fetch(:default)
|
48
|
+
|
49
|
+
attribute_default_value.casecmp(default_value).zero?
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_missing_option
|
53
|
+
attribute_default_value = attribute_data.fetch(:default)
|
54
|
+
|
55
|
+
<<~MESSAGE
|
56
|
+
should have a default value
|
57
|
+
|
58
|
+
expected #{default_value.inspect}
|
59
|
+
got #{attribute_default_value.inspect}
|
60
|
+
MESSAGE
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module TestKit
|
5
|
+
module Rspec
|
6
|
+
module Matchers
|
7
|
+
module HaveServiceInputMatchers
|
8
|
+
class OptionalMatcher
|
9
|
+
attr_reader :missing_option
|
10
|
+
|
11
|
+
def initialize(described_class, attribute_type, attribute_name)
|
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
|
+
|
17
|
+
@attribute_data = described_class.info.public_send(attribute_type_plural).fetch(attribute_name)
|
18
|
+
|
19
|
+
@missing_option = ""
|
20
|
+
end
|
21
|
+
|
22
|
+
def description
|
23
|
+
"required: false"
|
24
|
+
end
|
25
|
+
|
26
|
+
def matches?(subject)
|
27
|
+
if submatcher_passes?(subject)
|
28
|
+
true
|
29
|
+
else
|
30
|
+
@missing_option = build_missing_option
|
31
|
+
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :described_class,
|
39
|
+
:attribute_type,
|
40
|
+
:attribute_type_plural,
|
41
|
+
:attribute_name,
|
42
|
+
:attribute_data
|
43
|
+
|
44
|
+
def submatcher_passes?(_subject)
|
45
|
+
input_required = attribute_data.fetch(:required).fetch(:is)
|
46
|
+
|
47
|
+
input_required == false
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_missing_option
|
51
|
+
<<~MESSAGE
|
52
|
+
should be optional
|
53
|
+
|
54
|
+
expected required: false
|
55
|
+
got required: true
|
56
|
+
MESSAGE
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Servactory
|
4
|
+
module TestKit
|
5
|
+
module Rspec
|
6
|
+
module Matchers
|
7
|
+
module HaveServiceInputMatchers
|
8
|
+
class RequiredMatcher
|
9
|
+
attr_reader :missing_option
|
10
|
+
|
11
|
+
def initialize(described_class, attribute_type, attribute_name, custom_message)
|
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
|
+
@custom_message = custom_message
|
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
|
+
"required: true"
|
25
|
+
end
|
26
|
+
|
27
|
+
def matches?(subject)
|
28
|
+
if 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
|
+
:custom_message,
|
44
|
+
:attribute_data
|
45
|
+
|
46
|
+
def submatcher_passes?(_subject) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
47
|
+
attribute_required = attribute_data.fetch(:required).fetch(:is)
|
48
|
+
attribute_required_message = attribute_data.fetch(:required).fetch(:message)
|
49
|
+
|
50
|
+
matched = attribute_required == true
|
51
|
+
|
52
|
+
if attribute_required_message.nil?
|
53
|
+
attribute_required_message = I18n.t(
|
54
|
+
"servactory.#{attribute_type_plural}.validations.required.default_error.default",
|
55
|
+
service_class_name: described_class.name,
|
56
|
+
"#{attribute_type}_name": attribute_name
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
matched &&= attribute_required_message.casecmp(custom_message).zero? if custom_message.present?
|
61
|
+
|
62
|
+
matched
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_missing_option
|
66
|
+
<<~MESSAGE
|
67
|
+
should be required
|
68
|
+
|
69
|
+
expected required: true
|
70
|
+
got required: false
|
71
|
+
MESSAGE
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|