pact-v2 2.0.0.pre.preview2 → 2.0.0.pre.preview3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/lib/pact/provider/request.rb +14 -1
  4. data/lib/pact/v2/consumer/grpc_interaction_builder.rb +10 -3
  5. data/lib/pact/v2/consumer/http_interaction_builder.rb +1 -2
  6. data/lib/pact/v2/consumer/interaction_contents.rb +8 -0
  7. data/lib/pact/v2/consumer/message_interaction_builder.rb +9 -2
  8. data/lib/pact/v2/consumer/mock_server.rb +4 -3
  9. data/lib/pact/v2/consumer/pact_config/plugin_async_message.rb +26 -0
  10. data/lib/pact/v2/consumer/pact_config/plugin_http.rb +26 -0
  11. data/lib/pact/v2/consumer/pact_config/plugin_sync_message.rb +26 -0
  12. data/lib/pact/v2/consumer/pact_config.rb +6 -0
  13. data/lib/pact/v2/consumer/plugin_async_message_interaction_builder.rb +171 -0
  14. data/lib/pact/v2/consumer/plugin_http_interaction_builder.rb +201 -0
  15. data/lib/pact/v2/consumer/plugin_sync_message_interaction_builder.rb +180 -0
  16. data/lib/pact/v2/generators/base.rb +287 -0
  17. data/lib/pact/v2/generators.rb +49 -0
  18. data/lib/pact/v2/matchers/base.rb +13 -6
  19. data/lib/pact/v2/matchers/v3/content_type.rb +32 -0
  20. data/lib/pact/v2/matchers/v3/null.rb +16 -0
  21. data/lib/pact/v2/matchers/v3/semver.rb +23 -0
  22. data/lib/pact/v2/matchers/v3/values.rb +16 -0
  23. data/lib/pact/v2/matchers/v4/not_empty.rb +10 -3
  24. data/lib/pact/v2/matchers/v4/status_code.rb +17 -0
  25. data/lib/pact/v2/matchers.rb +16 -0
  26. data/lib/pact/v2/provider/pact_broker_proxy.rb +0 -5
  27. data/lib/pact/v2/rspec/support/pact_consumer_helpers.rb +14 -1
  28. data/lib/pact/v2/version.rb +2 -2
  29. data/lib/pact/version.rb +1 -1
  30. data/lib/pact.rb +7 -13
  31. data/pact.gemspec +57 -67
  32. metadata +203 -204
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pact/ffi/sync_message_consumer"
4
+ require "pact/ffi/plugin_consumer"
5
+ require "pact/ffi/logger"
6
+
7
+ module Pact
8
+ module V2
9
+ module Consumer
10
+ class PluginSyncMessageInteractionBuilder
11
+
12
+ class PluginInitError < Pact::V2::FfiError; end
13
+
14
+ # https://docs.rs/pact_ffi/0.4.17/pact_ffi/plugins/fn.pactffi_using_plugin.html
15
+ INIT_PLUGIN_ERRORS = {
16
+ 1 => {reason: :internal_error, status: 1, description: "A general panic was caught"},
17
+ 2 => {reason: :plugin_load_failed, status: 2, description: "Failed to load the plugin"},
18
+ 3 => {reason: :invalid_handle, status: 3, description: "Pact Handle is not valid"}
19
+ }.freeze
20
+
21
+ # https://docs.rs/pact_ffi/0.4.17/pact_ffi/plugins/fn.pactffi_interaction_contents.html
22
+ CREATE_INTERACTION_ERRORS = {
23
+ 1 => {reason: :internal_error, status: 1, description: "A general panic was caught"},
24
+ 2 => {reason: :mock_server_already_running, status: 2, description: "The mock server has already been started"},
25
+ 3 => {reason: :invalid_handle, status: 3, description: "The interaction handle is invalid"},
26
+ 4 => {reason: :invalid_content_type, status: 4, description: "The content type is not valid"},
27
+ 5 => {reason: :invalid_contents, status: 5, description: "The contents JSON is not valid JSON"},
28
+ 6 => {reason: :plugin_error, status: 6, description: "The plugin returned an error"}
29
+ }.freeze
30
+
31
+ class CreateInteractionError < Pact::V2::FfiError; end
32
+
33
+ class InteractionMismatchesError < Pact::V2::Error; end
34
+
35
+ class InteractionBuilderError < Pact::V2::Error; end
36
+
37
+ def initialize(pact_config, description: nil)
38
+ @pact_config = pact_config
39
+ @description = description || ""
40
+ @request = nil
41
+ @response = nil
42
+ @response_meta = nil
43
+ @provider_state_meta = nil
44
+ end
45
+
46
+ def with_plugin(plugin_name, plugin_version)
47
+ raise InteractionBuilderError.new("plugin_name is required") if plugin_name.blank?
48
+ raise InteractionBuilderError.new("plugin_version is required") if plugin_version.blank?
49
+
50
+ @plugin_name = plugin_name
51
+ @plugin_version = plugin_version
52
+ self
53
+ end
54
+
55
+ def given(provider_state, metadata = {})
56
+ @provider_state_meta = {provider_state => metadata}
57
+ self
58
+ end
59
+
60
+ def upon_receiving(description)
61
+ @description = description
62
+ self
63
+ end
64
+
65
+ def with_request(req_hash)
66
+ @request = InteractionContents.plugin(req_hash)
67
+ self
68
+ end
69
+
70
+ def with_content_type(content_type)
71
+ @content_type = content_type
72
+ self
73
+ end
74
+
75
+ def will_respond_with(resp_hash)
76
+ @response = InteractionContents.plugin(resp_hash)
77
+ self
78
+ end
79
+
80
+ def will_respond_with_meta(meta_hash)
81
+ @response_meta = InteractionContents.plugin(meta_hash)
82
+ self
83
+ end
84
+
85
+ def with_plugin_metadata(meta_hash)
86
+ @plugin_metadata = meta_hash
87
+ self
88
+ end
89
+
90
+ def with_transport(transport)
91
+ @transport = transport
92
+ self
93
+ end
94
+
95
+ def interaction_json
96
+ result = {
97
+ request: @request
98
+ }
99
+ result.merge!(@plugin_metadata) if @plugin_metadata.is_a?(Hash)
100
+
101
+ result[:response] = @response if @response.is_a?(Hash)
102
+ result[:responseMetadata] = @response_meta if @response_meta.is_a?(Hash)
103
+
104
+ JSON.dump(result)
105
+ end
106
+
107
+ def validate!
108
+ raise InteractionBuilderError.new("invalid request format, should be a hash") unless @request.is_a?(Hash)
109
+ raise InteractionBuilderError.new("invalid response format, should be a hash") unless @response.is_a?(Hash) || @response_meta.is_a?(Hash)
110
+ end
111
+
112
+ def execute(&block)
113
+ raise InteractionBuilderError.new("interaction is designed to be used one-time only") if defined?(@used)
114
+
115
+ validate!
116
+
117
+ pact_handle = init_pact
118
+ init_plugin!(pact_handle)
119
+
120
+ message_pact = PactFfi::SyncMessageConsumer.new_interaction(pact_handle, @description)
121
+ @provider_state_meta&.each_pair do |provider_state, meta|
122
+ if meta.present?
123
+ meta.each_pair { |k, v| PactFfi.given_with_param(message_pact, provider_state, k.to_s, v.to_s) }
124
+ else
125
+ PactFfi.given(message_pact, provider_state)
126
+ end
127
+ end
128
+ result = PactFfi::PluginConsumer.interaction_contents(message_pact, 0, @content_type, interaction_json)
129
+ if CREATE_INTERACTION_ERRORS[result].present?
130
+ error = CREATE_INTERACTION_ERRORS[result]
131
+ raise CreateInteractionError.new("There was an error while trying to add interaction \"#{@description}\"", error[:reason], error[:status])
132
+ end
133
+
134
+ mock_server = MockServer.create_for_transport!(pact: pact_handle, transport: @transport, host: @pact_config.mock_host, port: @pact_config.mock_port)
135
+
136
+ yield(message_pact, mock_server)
137
+
138
+ ensure
139
+ if mock_server.matched?
140
+ mock_server.write_pacts!(@pact_config.pact_dir)
141
+ else
142
+ msg = mismatches_error_msg(mock_server)
143
+ raise InteractionMismatchesError.new(msg)
144
+ end
145
+ @used = true
146
+ mock_server&.cleanup
147
+ PactFfi::PluginConsumer.cleanup_plugins(pact_handle)
148
+ PactFfi.free_pact_handle(pact_handle)
149
+ end
150
+
151
+ private
152
+
153
+ def mismatches_error_msg(mock_server)
154
+ rspec_example_desc = RSpec.current_example&.description
155
+ return "interaction for has mismatches: #{mock_server.mismatches}" if rspec_example_desc.blank?
156
+
157
+ "#{rspec_example_desc} has mismatches: #{mock_server.mismatches}"
158
+ end
159
+
160
+ def init_pact
161
+ handle = PactFfi.new_pact(@pact_config.consumer_name, @pact_config.provider_name)
162
+ PactFfi.with_specification(handle, PactFfi::FfiSpecificationVersion["SPECIFICATION_VERSION_V4"])
163
+ PactFfi.with_pact_metadata(handle, "pact-ruby-v2", "pact-ffi", PactFfi.version)
164
+
165
+ Pact::V2::Native::Logger.log_to_stdout(@pact_config.log_level)
166
+
167
+ handle
168
+ end
169
+
170
+ def init_plugin!(pact_handle)
171
+ result = PactFfi::PluginConsumer.using_plugin(pact_handle, @plugin_name, @plugin_version)
172
+ return result if INIT_PLUGIN_ERRORS[result].blank?
173
+
174
+ error = INIT_PLUGIN_ERRORS[result]
175
+ raise PluginInitError.new("There was an error while trying to initialize plugin #{@plugin_name}/#{@plugin_version}", error[:reason], error[:status])
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -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
@@ -9,7 +9,7 @@ module Pact
9
9
 
10
10
  class MatcherInitializationError < Pact::V2::Error; end
11
11
 
12
- def initialize(spec_version:, kind:, template:, opts: {})
12
+ def initialize(spec_version:, kind:, template: nil, opts: {})
13
13
  @spec_version = spec_version
14
14
  @kind = kind
15
15
  @template = template
@@ -17,15 +17,22 @@ module Pact
17
17
  end
18
18
 
19
19
  def as_basic
20
- {
21
- "pact:matcher:type" => serialize!(@kind.deep_dup, :basic),
22
- "value" => serialize!(@template.deep_dup, :basic)
23
- }.merge(serialize!(@opts.deep_dup, :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
24
27
  end
25
28
 
26
29
  def as_plugin
27
30
  params = @opts.values.map { |v| format_primitive(v) }.join(",")
28
- value = format_primitive(@template)
31
+ value = format_primitive(@template) unless @template.nil?
32
+
33
+ if @template.nil?
34
+ return "matching(#{@kind}#{params.present? ? ", #{params}" : ""})"
35
+ end
29
36
 
30
37
  return "matching(#{@kind}, #{params}, #{value})" if params.present?
31
38
 
@@ -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,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,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pact
4
+ module V2
5
+ module Matchers
6
+ module V3
7
+ class Semver < Pact::V2::Matchers::Base
8
+ def initialize(template = nil)
9
+ @template = template
10
+ super(spec_version: Pact::V2::Matchers::PACT_SPEC_V3, kind: "semver", template: template)
11
+ end
12
+
13
+ def as_plugin
14
+ if @template.nil? || @template.blank?
15
+ raise MatcherInitializationError, "#{self.class}: template must be provided when calling as_plugin"
16
+ end
17
+ "matching(semver, '#{@template}')"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ 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 Values < Pact::V2::Matchers::Base
8
+ def initialize
9
+ super(spec_version: Pact::V2::Matchers::PACT_SPEC_V3, kind: "values", template: nil)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -5,10 +5,17 @@ module Pact
5
5
  module Matchers
6
6
  module V4
7
7
  class NotEmpty < Pact::V2::Matchers::Base
8
- def initialize(template)
9
- raise MatcherInitializationError, "#{self.class}: #{template} should not be empty" if template.blank?
8
+ def initialize(template = nil)
9
+ @template = template
10
+ super(spec_version: Pact::V2::Matchers::PACT_SPEC_V4, kind: 'notEmpty', template: @template)
11
+ end
12
+
13
+ def as_plugin
14
+ if @template.nil? || @template.blank?
15
+ raise MatcherInitializationError, "#{self.class}: template must be provided when calling as_plugin"
16
+ end
10
17
 
11
- super(spec_version: Pact::V2::Matchers::PACT_SPEC_V4, kind: "time", template: template)
18
+ "notEmpty('#{@template}')"
12
19
  end
13
20
  end
14
21
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pact
4
+ module V2
5
+ module Matchers
6
+ module V4
7
+ class StatusCode < Pact::V2::Matchers::Base
8
+ def initialize(template = nil)
9
+ super(spec_version: Pact::V2::Matchers::PACT_SPEC_V4, kind: 'statusCode', opts: {
10
+ 'status' => template
11
+ })
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end