pact 1.66.2 → 1.67.1
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/CHANGELOG.md +25 -0
- data/lib/pact/v2/configuration.rb +23 -0
- data/lib/pact/v2/consumer/grpc_interaction_builder.rb +194 -0
- data/lib/pact/v2/consumer/http_interaction_builder.rb +162 -0
- data/lib/pact/v2/consumer/interaction_contents.rb +62 -0
- data/lib/pact/v2/consumer/message_interaction_builder.rb +287 -0
- data/lib/pact/v2/consumer/mock_server.rb +100 -0
- data/lib/pact/v2/consumer/pact_config/base.rb +24 -0
- data/lib/pact/v2/consumer/pact_config/grpc.rb +26 -0
- data/lib/pact/v2/consumer/pact_config/http.rb +55 -0
- data/lib/pact/v2/consumer/pact_config/message.rb +17 -0
- data/lib/pact/v2/consumer/pact_config/plugin_async_message.rb +26 -0
- data/lib/pact/v2/consumer/pact_config/plugin_http.rb +26 -0
- data/lib/pact/v2/consumer/pact_config/plugin_sync_message.rb +26 -0
- data/lib/pact/v2/consumer/pact_config.rb +30 -0
- data/lib/pact/v2/consumer/plugin_async_message_interaction_builder.rb +171 -0
- data/lib/pact/v2/consumer/plugin_http_interaction_builder.rb +201 -0
- data/lib/pact/v2/consumer/plugin_sync_message_interaction_builder.rb +180 -0
- data/lib/pact/v2/consumer.rb +8 -0
- data/lib/pact/v2/generators/base.rb +287 -0
- data/lib/pact/v2/generators.rb +49 -0
- data/lib/pact/v2/matchers/base.rb +74 -0
- data/lib/pact/v2/matchers/v1/equality.rb +19 -0
- data/lib/pact/v2/matchers/v2/regex.rb +19 -0
- data/lib/pact/v2/matchers/v2/type.rb +17 -0
- data/lib/pact/v2/matchers/v3/boolean.rb +17 -0
- data/lib/pact/v2/matchers/v3/content_type.rb +32 -0
- data/lib/pact/v2/matchers/v3/date.rb +18 -0
- data/lib/pact/v2/matchers/v3/date_time.rb +18 -0
- data/lib/pact/v2/matchers/v3/decimal.rb +17 -0
- data/lib/pact/v2/matchers/v3/each.rb +42 -0
- data/lib/pact/v2/matchers/v3/include.rb +17 -0
- data/lib/pact/v2/matchers/v3/integer.rb +17 -0
- data/lib/pact/v2/matchers/v3/null.rb +16 -0
- data/lib/pact/v2/matchers/v3/number.rb +17 -0
- data/lib/pact/v2/matchers/v3/semver.rb +23 -0
- data/lib/pact/v2/matchers/v3/time.rb +18 -0
- data/lib/pact/v2/matchers/v3/values.rb +16 -0
- data/lib/pact/v2/matchers/v4/each_key.rb +26 -0
- data/lib/pact/v2/matchers/v4/each_key_value.rb +32 -0
- data/lib/pact/v2/matchers/v4/each_value.rb +33 -0
- data/lib/pact/v2/matchers/v4/not_empty.rb +24 -0
- data/lib/pact/v2/matchers/v4/status_code.rb +17 -0
- data/lib/pact/v2/matchers.rb +110 -0
- data/lib/pact/v2/native/blocking_verifier.rb +17 -0
- data/lib/pact/v2/native/logger.rb +25 -0
- data/lib/pact/v2/provider/async_message_verifier.rb +28 -0
- data/lib/pact/v2/provider/base_verifier.rb +242 -0
- data/lib/pact/v2/provider/grpc_verifier.rb +38 -0
- data/lib/pact/v2/provider/gruf_server.rb +75 -0
- data/lib/pact/v2/provider/http_server.rb +79 -0
- data/lib/pact/v2/provider/http_verifier.rb +43 -0
- data/lib/pact/v2/provider/message_provider_servlet.rb +79 -0
- data/lib/pact/v2/provider/mixed_verifier.rb +22 -0
- data/lib/pact/v2/provider/pact_broker_proxy.rb +66 -0
- data/lib/pact/v2/provider/pact_broker_proxy_runner.rb +77 -0
- data/lib/pact/v2/provider/pact_config/async.rb +29 -0
- data/lib/pact/v2/provider/pact_config/base.rb +101 -0
- data/lib/pact/v2/provider/pact_config/grpc.rb +26 -0
- data/lib/pact/v2/provider/pact_config/http.rb +27 -0
- data/lib/pact/v2/provider/pact_config/mixed.rb +39 -0
- data/lib/pact/v2/provider/pact_config.rb +26 -0
- data/lib/pact/v2/provider/provider_server_runner.rb +89 -0
- data/lib/pact/v2/provider/provider_state_configuration.rb +32 -0
- data/lib/pact/v2/provider/provider_state_servlet.rb +86 -0
- data/lib/pact/v2/provider.rb +8 -0
- data/lib/pact/v2/railtie.rb +13 -0
- data/lib/pact/v2/rspec/support/pact_consumer_helpers.rb +93 -0
- data/lib/pact/v2/rspec/support/pact_message_helpers.rb +42 -0
- data/lib/pact/v2/rspec/support/pact_provider_helpers.rb +129 -0
- data/lib/pact/v2/rspec/support/waterdrop/pact_waterdrop_client.rb +27 -0
- data/lib/pact/v2/rspec/support/webmock/webmock_helpers.rb +30 -0
- data/lib/pact/v2/rspec.rb +17 -0
- data/lib/pact/v2/tasks/pact.rake +13 -0
- data/lib/pact/v2.rb +71 -0
- data/lib/pact/version.rb +1 -1
- data/lib/pact.rb +6 -0
- data/pact.gemspec +54 -7
- metadata +405 -65
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Generators
|
|
6
|
+
module Base
|
|
7
|
+
def as_basic
|
|
8
|
+
raise NotImplementedError, "Subclasses must implement the as_basic method"
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class RandomIntGenerator
|
|
13
|
+
include Base
|
|
14
|
+
|
|
15
|
+
def initialize(min:, max:)
|
|
16
|
+
@min = min
|
|
17
|
+
@max = max
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def as_basic
|
|
21
|
+
{
|
|
22
|
+
"pact:matcher:type" => "integer",
|
|
23
|
+
"pact:generator:type" => "RandomInt",
|
|
24
|
+
"min" => @min,
|
|
25
|
+
"max" => @max,
|
|
26
|
+
"value" => rand(@min..@max)
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class RandomDecimalGenerator
|
|
32
|
+
include Base
|
|
33
|
+
|
|
34
|
+
def initialize(digits:)
|
|
35
|
+
@digits = digits
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def as_basic
|
|
39
|
+
{
|
|
40
|
+
'pact:matcher:type' => 'decimal',
|
|
41
|
+
"pact:generator:type" => "RandomDecimal",
|
|
42
|
+
"digits" => @digits,
|
|
43
|
+
"value" => rand.round(@digits)
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class RandomHexadecimalGenerator
|
|
49
|
+
include Base
|
|
50
|
+
|
|
51
|
+
def initialize(digits:)
|
|
52
|
+
@digits = digits
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def as_basic
|
|
56
|
+
{
|
|
57
|
+
"pact:matcher:type" => "decimal",
|
|
58
|
+
"pact:generator:type" => "RandomHexadecimal",
|
|
59
|
+
"digits" => @digits,
|
|
60
|
+
"value" => SecureRandom.hex((@digits / 2.0).ceil)[0...@digits]
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class RandomStringGenerator
|
|
66
|
+
include Base
|
|
67
|
+
|
|
68
|
+
def initialize(size:, example: nil)
|
|
69
|
+
@size = size
|
|
70
|
+
@example = example
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def as_basic
|
|
74
|
+
{
|
|
75
|
+
"pact:matcher:type" => "type",
|
|
76
|
+
"pact:generator:type" => "RandomString",
|
|
77
|
+
"size" => @size,
|
|
78
|
+
"value" => @example || SecureRandom.alphanumeric(@size)
|
|
79
|
+
}
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class UuidGenerator
|
|
84
|
+
include Base
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def initialize(example: nil)
|
|
88
|
+
@example = example
|
|
89
|
+
@regexStr = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}';
|
|
90
|
+
if @example
|
|
91
|
+
regex = Regexp.new("^#{@regexStr}$")
|
|
92
|
+
unless @example.match?(regex)
|
|
93
|
+
raise ArgumentError, "regex: Example value '#{@example}' does not match the UUID regular expression '#{@regexStr}'"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def as_basic
|
|
99
|
+
{
|
|
100
|
+
"pact:matcher:type" => "regex",
|
|
101
|
+
"pact:generator:type" => "Uuid",
|
|
102
|
+
"regex" => @regexStr,
|
|
103
|
+
"value" => @example || SecureRandom.uuid
|
|
104
|
+
}
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
class DateGenerator
|
|
109
|
+
include Base
|
|
110
|
+
|
|
111
|
+
def initialize(format: nil, example: nil)
|
|
112
|
+
@format = format || default_format
|
|
113
|
+
@example = example || Time.now.strftime(convert_from_java_simple_date_format(@format))
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def as_basic
|
|
117
|
+
h = { "pact:generator:type" => type }
|
|
118
|
+
h["pact:matcher:type"] = matcher_type
|
|
119
|
+
h["format"] = @format if @format
|
|
120
|
+
h["value"] = @example
|
|
121
|
+
h
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def type
|
|
125
|
+
'Date'
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def matcher_type
|
|
129
|
+
'date'
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def default_format
|
|
133
|
+
'yyyy-MM-dd'
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Converts Java SimpleDateFormat to Ruby strftime format
|
|
137
|
+
def convert_from_java_simple_date_format(format)
|
|
138
|
+
f = format.dup
|
|
139
|
+
# Year
|
|
140
|
+
f.gsub!(/(?<!%)y{4,}/, '%Y')
|
|
141
|
+
f.gsub!(/(?<!%)y{1,3}/, '%y')
|
|
142
|
+
# Month
|
|
143
|
+
f.gsub!(/(?<!%)M{4,}/, '%B')
|
|
144
|
+
f.gsub!(/(?<!%)M{3}/, '%b')
|
|
145
|
+
f.gsub!(/(?<!%)M{1,2}/, '%m')
|
|
146
|
+
# Week
|
|
147
|
+
f.gsub!(/(?<!%)w{1,}/, '%W')
|
|
148
|
+
# Day
|
|
149
|
+
f.gsub!(/(?<!%)D{1,}/, '%j')
|
|
150
|
+
f.gsub!(/(?<!%)d{1,}/, '%d')
|
|
151
|
+
f.gsub!(/(?<!%)E{4,}/, '%A')
|
|
152
|
+
f.gsub!(/(?<!%)E{1,3}/, '%a')
|
|
153
|
+
f.gsub!(/(?<!%)u{1,}/, '%u')
|
|
154
|
+
# Time
|
|
155
|
+
f.gsub!(/(?<!%)a{1,}/, '%p')
|
|
156
|
+
f.gsub!(/(?<!%)k{1,}/, '%H')
|
|
157
|
+
f.gsub!(/(?<!%)n{1,}/, '%M')
|
|
158
|
+
f.gsub!(/(?<!%)s{1,}/, '%S')
|
|
159
|
+
f.gsub!(/(?<!%)S{1,}/, '%L')
|
|
160
|
+
# Timezone
|
|
161
|
+
f.gsub!(/(?<!%)z{1,}/, '%z')
|
|
162
|
+
f.gsub!(/(?<!%)Z{1,}/, '%z')
|
|
163
|
+
f.gsub!(/(?<!%)X{1,}/, '%Z')
|
|
164
|
+
# Java 'H' (hour in day, 0-23) to Ruby '%H'
|
|
165
|
+
f.gsub!(/(?<!%)H{2}/, '%H')
|
|
166
|
+
f.gsub!(/(?<!%)H{1}/, '%k')
|
|
167
|
+
# Java 'm' (minute in hour) to Ruby '%M'
|
|
168
|
+
f.gsub!(/(?<!%)m{2}/, '%M')
|
|
169
|
+
f.gsub!(/(?<!%)m{1}/, '%-M')
|
|
170
|
+
# Java 'h' (hour in am/pm, 1-12) to Ruby '%I'
|
|
171
|
+
f.gsub!(/(?<!%)h{2}/, '%I')
|
|
172
|
+
f.gsub!(/(?<!%)h{1}/, '%-I')
|
|
173
|
+
# Java 's' (second in minute) to Ruby '%S'
|
|
174
|
+
f.gsub!(/(?<!%)s{1,}/, '%S')
|
|
175
|
+
# Java 'a' (am/pm marker) to Ruby '%p'
|
|
176
|
+
f.gsub!(/(?<!%)a/, '%p')
|
|
177
|
+
# Java 'K' (hour in am/pm, 0-11) to Ruby '%l'
|
|
178
|
+
f.gsub!(/(?<!%)K{2}/, '%l')
|
|
179
|
+
f.gsub!(/(?<!%)K{1}/, '%-l')
|
|
180
|
+
# Java 'S' (fractional seconds, milliseconds) to Ruby '%L'
|
|
181
|
+
f.gsub!(/(?<!%)S{1,}/, '%L')
|
|
182
|
+
# Java 'z' (general time zone) to Ruby '%Z'
|
|
183
|
+
f.gsub!(/(?<!%)z{1,}/, '%Z')
|
|
184
|
+
# Java 'Z' (RFC 822 time zone) to Ruby '%z'
|
|
185
|
+
f.gsub!(/(?<!%)Z{1,}/, '%z')
|
|
186
|
+
# Java 'X' (ISO 8601 time zone) to Ruby '%z'
|
|
187
|
+
f.gsub!(/(?<!%)X{1,}/, '%z')
|
|
188
|
+
# Java 'G' (era designator) - no direct Ruby equivalent, remove or leave as is
|
|
189
|
+
f.gsub!(/(?<!%)G+/, '%G')
|
|
190
|
+
# Java 'Q' (quarter) - no direct Ruby equivalent, remove or leave as is
|
|
191
|
+
f.gsub!(/(?<!%)Q+/, '')
|
|
192
|
+
# Java 'F' (day of week in month) - no direct Ruby equivalent, remove or leave as is
|
|
193
|
+
f.gsub!(/(?<!%)F+/, '')
|
|
194
|
+
# Java 'c' (stand-alone day of week) - no direct Ruby equivalent, remove or leave as is
|
|
195
|
+
f.gsub!(/(?<!%)c+/, '')
|
|
196
|
+
# Java 'L' (stand-alone month) - treat as month
|
|
197
|
+
f.gsub!(/(?<!%)L{4,}/, '%B')
|
|
198
|
+
f.gsub!(/(?<!%)L{3}/, '%b')
|
|
199
|
+
f.gsub!(/(?<!%)L{1,2}/, '%m')
|
|
200
|
+
f
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Time provides the time generator which will give the current time in the defined format
|
|
205
|
+
class TimeGenerator < DateGenerator
|
|
206
|
+
def type
|
|
207
|
+
'Time'
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def matcher_type
|
|
211
|
+
'time'
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def default_format
|
|
215
|
+
'HH:mm'
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
class DateTimeGenerator < DateGenerator
|
|
220
|
+
def type
|
|
221
|
+
'DateTime'
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def matcher_type
|
|
225
|
+
'datetime'
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def default_format
|
|
229
|
+
'yyyy-MM-dd HH:mm'
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
class RandomBooleanGenerator
|
|
234
|
+
include Base
|
|
235
|
+
|
|
236
|
+
def initialize(example: nil)
|
|
237
|
+
@example = example
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def as_basic
|
|
241
|
+
{
|
|
242
|
+
"pact:matcher:type" => "boolean",
|
|
243
|
+
"pact:generator:type" => "RandomBoolean",
|
|
244
|
+
"value" => @example.nil? ? [true, false].sample : @example
|
|
245
|
+
}
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
class ProviderStateGenerator
|
|
250
|
+
include Base
|
|
251
|
+
|
|
252
|
+
def initialize(expression:, example:)
|
|
253
|
+
@expression = expression
|
|
254
|
+
@value = example
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def as_basic
|
|
258
|
+
{
|
|
259
|
+
'pact:matcher:type': 'type',
|
|
260
|
+
"pact:generator:type" => "ProviderState",
|
|
261
|
+
"expression" => @expression,
|
|
262
|
+
"value" => @value
|
|
263
|
+
}
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
class MockServerURLGenerator
|
|
268
|
+
include Base
|
|
269
|
+
|
|
270
|
+
def initialize(regex:, example:)
|
|
271
|
+
@regex = regex
|
|
272
|
+
@example = example
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def as_basic
|
|
276
|
+
{
|
|
277
|
+
"pact:generator:type" => "MockServerURL",
|
|
278
|
+
"pact:matcher:type" => "regex",
|
|
279
|
+
"regex" => @regex,
|
|
280
|
+
"example" => @example,
|
|
281
|
+
"value" => @example
|
|
282
|
+
}
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Generators
|
|
6
|
+
|
|
7
|
+
def generate_random_int(min:, max:)
|
|
8
|
+
Pact::V2::Generators::RandomIntGenerator.new(min: min, max: max)
|
|
9
|
+
end
|
|
10
|
+
def generate_random_decimal(digits:)
|
|
11
|
+
Pact::V2::Generators::RandomDecimalGenerator.new(digits: digits)
|
|
12
|
+
end
|
|
13
|
+
def generate_random_hexadecimal(digits:)
|
|
14
|
+
Pact::V2::Generators::RandomHexadecimalGenerator.new(digits: digits)
|
|
15
|
+
end
|
|
16
|
+
def generate_random_string(size:)
|
|
17
|
+
Pact::V2::Generators::RandomStringGenerator.new(size: size)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def generate_uuid(example: nil)
|
|
21
|
+
Pact::V2::Generators::UuidGenerator.new(example: example)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def generate_date(format: nil, example: nil)
|
|
25
|
+
Pact::V2::Generators::DateGenerator.new(format: format, example: example)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def generate_time(format: nil)
|
|
29
|
+
Pact::V2::Generators::TimeGenerator.new(format: format)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def generate_datetime(format: nil)
|
|
33
|
+
Pact::V2::Generators::DateTimeGenerator.new(format: format)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def generate_random_boolean
|
|
37
|
+
Pact::V2::Generators::RandomBooleanGenerator.new
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def generate_from_provider_state(expression:, example:)
|
|
41
|
+
Pact::V2::Generators::ProviderStateGenerator.new(expression: expression, example: example).as_basic
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def generate_mock_server_url(regex: nil, example: nil)
|
|
45
|
+
Pact::V2::Generators::MockServerURLGenerator.new(regex: regex, example: example)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
# see https://github.com/pact-foundation/pact-reference/blob/master/rust/pact_ffi/IntegrationJson.md
|
|
7
|
+
class Base
|
|
8
|
+
attr_reader :spec_version, :kind, :template, :opts
|
|
9
|
+
|
|
10
|
+
class MatcherInitializationError < Pact::V2::Error; end
|
|
11
|
+
|
|
12
|
+
def initialize(spec_version:, kind:, template: nil, opts: {})
|
|
13
|
+
@spec_version = spec_version
|
|
14
|
+
@kind = kind
|
|
15
|
+
@template = template
|
|
16
|
+
@opts = opts
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def as_basic
|
|
20
|
+
result = {
|
|
21
|
+
"pact:matcher:type" => serialize!(@kind.deep_dup, :basic)
|
|
22
|
+
}
|
|
23
|
+
result["status"] = serialize!(@opts[:status].deep_dup, :basic) if @opts[:status]
|
|
24
|
+
result["value"] = serialize!(@template.deep_dup, :basic) unless @template.nil?
|
|
25
|
+
result.merge!(serialize!(@opts.deep_dup, :basic))
|
|
26
|
+
result
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def as_plugin
|
|
30
|
+
params = @opts.values.map { |v| format_primitive(v) }.join(",")
|
|
31
|
+
value = format_primitive(@template) unless @template.nil?
|
|
32
|
+
|
|
33
|
+
if @template.nil?
|
|
34
|
+
return "matching(#{@kind}#{params.present? ? ", #{params}" : ""})"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
return "matching(#{@kind}, #{params}, #{value})" if params.present?
|
|
38
|
+
|
|
39
|
+
"matching(#{@kind}, #{value})"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def serialize!(data, format)
|
|
45
|
+
# serialize complex types recursively
|
|
46
|
+
case data
|
|
47
|
+
when TrueClass, FalseClass, Numeric, String
|
|
48
|
+
data
|
|
49
|
+
when Array
|
|
50
|
+
data.map { |v| serialize!(v, format) }
|
|
51
|
+
when Hash
|
|
52
|
+
data.transform_values { |v| serialize!(v, format) }
|
|
53
|
+
when Pact::V2::Matchers::Base
|
|
54
|
+
return data.as_basic if format == :basic
|
|
55
|
+
data.as_plugin if format == :plugin
|
|
56
|
+
else
|
|
57
|
+
data
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def format_primitive(arg)
|
|
62
|
+
case arg
|
|
63
|
+
when TrueClass, FalseClass, Numeric
|
|
64
|
+
arg.to_s
|
|
65
|
+
when String
|
|
66
|
+
"'#{arg}'"
|
|
67
|
+
else
|
|
68
|
+
raise "#{arg.class} is not a primitive"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V1
|
|
7
|
+
class Equality < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(template)
|
|
9
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V1, kind: "equality", template: template)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def as_plugin
|
|
13
|
+
"matching(equalTo, #{format_primitive(@template)})"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V2
|
|
7
|
+
class Regex < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(regex, template)
|
|
9
|
+
raise MatcherInitializationError, "#{self.class}: #{regex} should be an instance of Regexp" unless regex.is_a?(Regexp)
|
|
10
|
+
raise MatcherInitializationError, "#{self.class}: #{template} should be an instance of String or Array" unless template.is_a?(String) || template.is_a?(Array)
|
|
11
|
+
raise MatcherInitializationError, "#{self.class}: #{template} array values should be strings" if template.is_a?(Array) && !template.all?(String)
|
|
12
|
+
|
|
13
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V2, kind: "regex", template: template, opts: {regex: regex.to_s})
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V2
|
|
7
|
+
class Type < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(template)
|
|
9
|
+
raise MatcherInitializationError, "#{self.class}: template is not a primitive" unless template.is_a?(TrueClass) || template.is_a?(FalseClass) || template.is_a?(Numeric) || template.is_a?(String) || template.is_a?(Array) || template.is_a?(Hash)
|
|
10
|
+
|
|
11
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V2, kind: "type", template: template)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V3
|
|
7
|
+
class Boolean < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(template)
|
|
9
|
+
raise MatcherInitializationError, "#{self.class}: #{template} should be an instance of Boolean" unless template.is_a?(TrueClass) || template.is_a?(FalseClass)
|
|
10
|
+
|
|
11
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V3, kind: "boolean", template: template)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V3
|
|
7
|
+
class ContentType < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(content_type, template: nil)
|
|
9
|
+
@content_type = content_type
|
|
10
|
+
@template = template
|
|
11
|
+
@opts = {}
|
|
12
|
+
@opts[:plugin_template] = template unless template.nil?
|
|
13
|
+
unless content_type.is_a?(String) && !content_type.empty?
|
|
14
|
+
raise MatcherInitializationError, "#{self.class}: content_type must be a non-empty String"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
super(
|
|
18
|
+
spec_version: Pact::V2::Matchers::PACT_SPEC_V3,
|
|
19
|
+
kind: "contentType",
|
|
20
|
+
template: content_type,
|
|
21
|
+
opts: @opts
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def as_plugin
|
|
26
|
+
"matching(contentType, '#{@content_type}', '#{@opts[:plugin_template]}')"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V3
|
|
7
|
+
class Date < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(format, template)
|
|
9
|
+
raise MatcherInitializationError, "#{self.class}: #{format} should be an instance of String" unless template.is_a?(String)
|
|
10
|
+
raise MatcherInitializationError, "#{self.class}: #{template} should be an instance of String" unless template.is_a?(String)
|
|
11
|
+
|
|
12
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V3, kind: "date", template: template, opts: {format: format})
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V3
|
|
7
|
+
class DateTime < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(format, template)
|
|
9
|
+
raise MatcherInitializationError, "#{self.class}: #{format} should be an instance of String" unless template.is_a?(String)
|
|
10
|
+
raise MatcherInitializationError, "#{self.class}: #{template} should be an instance of String" unless template.is_a?(String)
|
|
11
|
+
|
|
12
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V3, kind: "datetime", template: template, opts: {format: format})
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V3
|
|
7
|
+
class Decimal < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(template)
|
|
9
|
+
raise MatcherInitializationError, "#{self.class}: #{template} should be an instance of Float" unless template.is_a?(Float)
|
|
10
|
+
|
|
11
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V3, kind: "decimal", template: template)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V3
|
|
7
|
+
class Each < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(template, min)
|
|
9
|
+
raise MatcherInitializationError, "#{self.class}: #{min} should be greater than 0" if min.present? && min < 1
|
|
10
|
+
|
|
11
|
+
min_array_size = min.presence || 1
|
|
12
|
+
val = template.is_a?(Array) ? template : [template] * min_array_size
|
|
13
|
+
|
|
14
|
+
raise MatcherInitializationError, "#{self.class}: #{min} is invalid: template size is #{val.size}" if min_array_size != val.size
|
|
15
|
+
|
|
16
|
+
super(
|
|
17
|
+
spec_version: Pact::V2::Matchers::PACT_SPEC_V3,
|
|
18
|
+
kind: "type",
|
|
19
|
+
template: val,
|
|
20
|
+
opts: {min: min_array_size})
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def as_plugin
|
|
24
|
+
if @template.first.is_a?(Hash)
|
|
25
|
+
return {
|
|
26
|
+
"pact:match" => "eachValue(matching($'SAMPLE'))",
|
|
27
|
+
"SAMPLE" => serialize!(@template.first.deep_dup, :plugin)
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
params = @opts.except(:min).values.map { |v| format_primitive(v) }.join(",")
|
|
32
|
+
value = format_primitive(@template.first)
|
|
33
|
+
|
|
34
|
+
return "eachValue(matching(#{@kind}, #{params}, #{value}))" if params.present?
|
|
35
|
+
|
|
36
|
+
"eachValue(matching(#{@kind}, #{value}))"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V3
|
|
7
|
+
class Include < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(template)
|
|
9
|
+
raise MatcherInitializationError, "#{self.class}: #{template} should be an instance of String" unless template.is_a?(String)
|
|
10
|
+
|
|
11
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V3, kind: "include", template: template)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V3
|
|
7
|
+
class Integer < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(template)
|
|
9
|
+
raise MatcherInitializationError, "#{self.class}: #{template} should be an instance of Integer" unless template.is_a?(::Integer)
|
|
10
|
+
|
|
11
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V3, kind: "integer", template: template)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V3
|
|
7
|
+
class Null < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize
|
|
9
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V3, kind: "null", template: nil)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module V2
|
|
5
|
+
module Matchers
|
|
6
|
+
module V3
|
|
7
|
+
class Number < Pact::V2::Matchers::Base
|
|
8
|
+
def initialize(template)
|
|
9
|
+
raise MatcherInitializationError, "#{self.class}: #{template} should be an instance of Numeric" unless template.is_a?(Numeric)
|
|
10
|
+
|
|
11
|
+
super(spec_version: Pact::V2::Matchers::PACT_SPEC_V3, kind: "number", template: template)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|