pact-mock_service 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +63 -17
  4. data/lib/pact/consumer/mock_service/error_handler.rb +3 -0
  5. data/lib/pact/consumer_contract/consumer_contract_writer.rb +28 -15
  6. data/lib/pact/mock_service/app.rb +5 -1
  7. data/lib/pact/mock_service/cli.rb +35 -32
  8. data/lib/pact/mock_service/errors.rb +9 -0
  9. data/lib/pact/mock_service/interactions/interaction_diff_message.rb +1 -1
  10. data/lib/pact/mock_service/interactions/interactions_filter.rb +54 -22
  11. data/lib/pact/mock_service/request_handlers/base_request_handler.rb +8 -0
  12. data/lib/pact/mock_service/request_handlers/index_get.rb +1 -1
  13. data/lib/pact/mock_service/request_handlers/interaction_delete.rb +4 -2
  14. data/lib/pact/mock_service/request_handlers/interaction_post.rb +3 -3
  15. data/lib/pact/mock_service/request_handlers/interaction_replay.rb +2 -2
  16. data/lib/pact/mock_service/request_handlers/interactions_put.rb +3 -3
  17. data/lib/pact/mock_service/request_handlers/log_get.rb +1 -1
  18. data/lib/pact/mock_service/request_handlers/missing_interactions_get.rb +1 -1
  19. data/lib/pact/mock_service/request_handlers/options.rb +0 -2
  20. data/lib/pact/mock_service/request_handlers/pact_post.rb +6 -6
  21. data/lib/pact/mock_service/request_handlers/session_delete.rb +1 -2
  22. data/lib/pact/mock_service/request_handlers/verification_get.rb +7 -5
  23. data/lib/pact/mock_service/run.rb +2 -1
  24. data/lib/pact/mock_service/session.rb +14 -6
  25. data/lib/pact/mock_service/spawn.rb +2 -1
  26. data/lib/pact/mock_service/version.rb +1 -1
  27. metadata +25 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0cf900a12d4f91a4ff30a3e9f524f989ffedca18
4
- data.tar.gz: ac91a2457b5d83c349c70663682dfae00e22af08
3
+ metadata.gz: e7ba885324798c8512e3957562c9c07f903ef605
4
+ data.tar.gz: 7c8718131be50169a6a1d8341905b20e25b4e73d
5
5
  SHA512:
6
- metadata.gz: 296f813db0e501057558d0cdb241b56fba89144be5822f2aac010b9cfbdabf960ae644a4a508eb6ae37192b042901f3180a093a210e7c3372ee20ba3db487598
7
- data.tar.gz: fbc2b07d41210eaf4fafe758e6135c9ca41bfe9038a5d8f327eb5dd86ef6fb9a89d81d82f0a4caaa8a0fbf6a1c46f2b1c68ce2efc7e0444f3792631a3cf57c0a
6
+ metadata.gz: abbd1c16191e86672a8aac3217836bf98190ff218c84362f99faaa4618e6802327e697b1d9dec1433f05a263f5994ec4c68c4b20ea983e1e4066de8f37b4ff8a
7
+ data.tar.gz: d811402182f55a9b91ef04f122f02da0ba5ea7ff0168c57332e2ceac000976b861206812c67fd647ff87f2e20dac5f2f9aade0166322ac0fa4a9e6161cb5b858
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@ Do this to generate your change history
2
2
 
3
3
  git log --pretty=format:' * %h - %s (%an, %ad)' vX.Y.Z..HEAD
4
4
 
5
+ ### 2.3.0 (2017-20-04)
6
+ * 79cbdc9 - feat: add example script showing usage (Beth Skurrie, Wed Oct 4 13:42:06 2017 +1100)
7
+ * 873d9ee - feat: only write pact on shutdown of mock service if pact has not already been written (Beth Skurrie, Wed Oct 4 13:41:01 2017 +1100)
8
+ * d3c6067 - feat(cli): add --pact-file-write-mode to cli and remove --unique-pact-file-names (Beth Skurrie, Wed Oct 4 08:01:25 2017 +1100)
9
+ * 476ae5c - feat: only include backtrace in error response for standard errors, not for pact::error (Beth Skurrie, Tue Oct 3 16:40:06 2017 +1100)
10
+ * a76dc7e - feat: add 'merge' pactfile_write_mode (Beth Skurrie, Tue Oct 3 10:13:36 2017 +1100)
11
+ * d0d82f2 - feat: use file locking to ensure pact file isn't corrupted when running multiple mock services in parallel (Beth Skurrie, Mon Oct 2 08:32:34 2017 +1100)
12
+
5
13
  ### 2.2.0 (2017-09-30)
6
14
  * 3949e3d - feat(cli): add --unique-pact-file-names option (Beth Skurrie, Thu Sep 28 11:16:22 2017 +1000)
7
15
 
data/README.md CHANGED
@@ -1,42 +1,86 @@
1
+
1
2
  # Pact Mock Service
2
3
 
3
4
  [![Build Status](https://travis-ci.org/pact-foundation/pact-mock_service.svg?branch=master)](https://travis-ci.org/pact-foundation/pact-mock_service)
4
5
 
5
- This codebase provides the mock service used by implementations of [Pact][pact]. It is packaged as a gem, and as a standalone executable for Mac OSX and Linux and Windows.
6
+ This codebase provides the HTTP mock service used by implementations of [Pact][pact]. It is packaged as a gem, and as a standalone executable for Mac OSX and Linux and Windows.
6
7
 
7
8
  The mock service provides the following endpoints:
8
9
 
9
10
  * DELETE /interactions - clear previously mocked interactions
10
11
  * POST /interactions - set up an expected interaction
12
+ * PUT /interactions - clear and set up multiple expected interactions in one call
11
13
  * GET /interactions/verification - determine whether the expected interactions have taken place
12
14
  * POST /pact - write the pact file
15
+ * GET / - the healthcheck endpoint
13
16
 
14
- As the Pact mock service can be used as a standalone executable and administered via HTTP, it can be used for testing with any language. All that is required is a library in the native language to create the HTTP calls listed above. Check out [docs.pact.io](https://docs.pact.io) for a list of implemented languages. If you are interested in creating bindings in a new langauge, and have a chat to one of us on the [pact-dev Google group][pact-dev].
17
+ All requests to the "administration" endpoints listed above must contain the header `X-Pact-Mock-Service: true` to allow the mock service to know whether the request is an administration request or a request from the actual consumer code.
15
18
 
16
- ## Documentation
19
+ As the Pact mock service can be used as a standalone executable and administered via HTTP, it can be used for testing with any language. All that is required is a library in the native language to create the HTTP calls listed above. Check out [docs.pact.io](https://docs.pact.io) for a list of implemented languages. If you are interested in creating bindings in a new langauge, and have a chat to one of us on the [pact-dev Google group][pact-dev].
17
20
 
18
- You can find documentation for the mock service in the repository [wiki][wiki].
21
+ ## Installation
19
22
 
20
- ## Usage
23
+ ### Without Ruby
21
24
 
22
- For some examples of the HTTP requests that occur under the hood when using either the Ruby client or the Javascript client, see this [gist](https://gist.github.com/bethesque/9d81f21d6f77650811f4).
25
+ Use the Pact standalone [executables][executables].
23
26
 
24
- ### Mac OSX, Linux and Windows, without Ruby
27
+ ### With Ruby
25
28
 
26
- See the [releases][releases] page for the latest standalone executables.
29
+ Use the [pact][pact] gem if you would like the full Pact DSL, mock service and verification functionality in your ruby project.
27
30
 
28
- ### With Ruby on Mac OSX and Linux
31
+ Otherwise:
29
32
 
30
33
  $ gem install pact-mock_service
31
- $ pact-mock-service --port 1234
34
+ $ pact-mock-service --consumer Foo --provider Bar --port 1234
35
+
36
+ Or add `gem "pact-mock_service"` to your Gemfile then run:
37
+
38
+ $ bundle install
39
+ $ bundle exec pact-mock-service --consumer Foo --provider Bar --port 1234
32
40
 
33
41
  Run `pact-mock-service help` for command line options.
34
42
 
35
- ### With Ruby on Windows
43
+ ## Usage
44
+
45
+ The lifecycle of the a mock service instance during at test suite is as follows:
36
46
 
37
- Check out the wiki page [here][install-windows].
47
+ _Before suite:_ start mock service
48
+ _Before each test:_ clear interactions from previous test
49
+ _During test:_ set up interactions, execute interactions
50
+ _After each test:_ verify interactions
51
+ _After suite:_ write pact file, stop mock service
38
52
 
39
- #### With SSL
53
+ Each mock service instance can only handle one test process/thread at a time. If you wish to run multiple test threads in parallel, you will need to start each mock service instance on a different port, and set the `--pact-file-write-mode` to `merge` (see usage notes below).
54
+
55
+ ```
56
+ Usage:
57
+ pact-mock-service service
58
+
59
+ Options:
60
+ [--consumer=CONSUMER] # Consumer name
61
+ [--provider=PROVIDER] # Provider name
62
+ -p, [--port=PORT] # Port on which to run the service
63
+ -h, [--host=HOST] # Host on which to bind the service
64
+ # Default: localhost
65
+ -d, [--pact-dir=PACT_DIR] # Directory to which the pacts will be written
66
+ -m, [--pact-file-write-mode=PACT_FILE_WRITE_MODE] # `overwrite` or `merge`. Use `merge` when running multiple mock service instances in parallel for the same consumer/provider pair. Ensure the pact file is deleted before running tests when using this option so that interactions deleted from the code are not maintained in the file.
67
+ # Default: overwrite
68
+ -i, [--pact-specification-version=PACT_SPECIFICATION_VERSION] # The pact specification version to use when writing the pact
69
+ # Default: 1
70
+ -l, [--log=LOG] # File to which to log output
71
+ -o, [--cors=CORS] # Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses
72
+ [--ssl], [--no-ssl] # Use a self-signed SSL cert to run the service over HTTPS
73
+ [--sslcert=SSLCERT] # Specify the path to the SSL cert to use when running the service over HTTPS
74
+ [--sslkey=SSLKEY] # Specify the path to the SSL key to use when running the service over HTTPS
75
+
76
+ Start a mock service. If the consumer, provider and pact-dir options are provided, the pact will be written automatically on shutdown.
77
+ ```
78
+
79
+ See [script/example.sh](script/example.sh) for an executable example.
80
+
81
+ You can find more documentation for the mock service in the repository [wiki][wiki].
82
+
83
+ ### With SSL
40
84
 
41
85
  If you need to use the mock service with HTTPS, you can use the built-in SSL mode which relies on and generates a self-signed certificate.
42
86
 
@@ -46,14 +90,16 @@ If you need to provide your own certificate and key, use the following syntax.
46
90
 
47
91
  $ pact-mock-service --port 1234 --ssl --sslcert PATH_TO_CERT --sslkey PATH_TO_KEY
48
92
 
93
+ ### With CORS
94
+
95
+ Read the wiki page on [CORS][cors].
96
+
49
97
  ## Contributing
50
98
 
51
99
  See [CONTRIBUTING.md](/CONTRIBUTING.md)
52
100
 
53
101
  [pact]: https://github.com/realestate-com-au/pact
54
- [releases]: https://github.com/pact-foundation/pact-mock_service/releases
55
- [javascript]: https://github.com/DiUS/pact-consumer-js-dsl
102
+ [executables]: https://github.com/pact-foundation/pact-ruby-standalone/releases
56
103
  [pact-dev]: https://groups.google.com/forum/#!forum/pact-dev
57
- [install-windows]: https://github.com/bethesque/pact-mock_service/wiki/Installing-the-pact-mock_service-gem-on-Windows
58
- [why-generated]: https://github.com/realestate-com-au/pact/wiki/FAQ#why-are-the-pacts-generated-and-not-static
59
104
  [wiki]: https://github.com/pact-foundation/pact-mock_service/wiki
105
+ [cors]: https://github.com/pact-foundation/pact-mock_service/wiki/Using-the-mock-service-with-CORS
@@ -11,6 +11,9 @@ module Pact
11
11
  def call env
12
12
  begin
13
13
  @app.call(env)
14
+ rescue Pact::Error => e
15
+ @logger.error e.message
16
+ [500, {'Content-Type' => 'application/json'}, [{message: e.message}.to_json]]
14
17
  rescue StandardError => e
15
18
  message = "Error ocurred in mock service: #{e.class} - #{e.message}"
16
19
  @logger.error message
@@ -5,6 +5,7 @@ require 'pact/consumer_contract/pact_file'
5
5
  require 'pact/consumer_contract/consumer_contract_decorator'
6
6
  require 'pact/shared/active_support_support'
7
7
  require 'fileutils'
8
+ require 'filelock'
8
9
 
9
10
  module Pact
10
11
 
@@ -21,7 +22,7 @@ module Pact
21
22
  def initialize consumer_contract_details, logger
22
23
  @logger = logger
23
24
  @consumer_contract_details = consumer_contract_details
24
- @pactfile_write_mode = consumer_contract_details.fetch(:pactfile_write_mode, :overwrite).to_sym
25
+ @pactfile_write_mode = (consumer_contract_details[:pactfile_write_mode] || :overwrite).to_sym
25
26
  @interactions = consumer_contract_details.fetch(:interactions)
26
27
  @pact_specification_version = (consumer_contract_details[:pact_specification_version] || DEFAULT_PACT_SPECIFICATION_VERSION).to_s
27
28
  end
@@ -54,11 +55,9 @@ module Pact
54
55
 
55
56
  def update_pactfile
56
57
  logger.info log_message
57
-
58
58
  FileUtils.mkdir_p File.dirname(pactfile_path)
59
- new_pact_json = pact_json
60
- File.open(pactfile_path, 'w') do |f|
61
- f.write new_pact_json
59
+ Filelock pactfile_path do | file |
60
+ file.write pact_json
62
61
  end
63
62
  end
64
63
 
@@ -71,9 +70,9 @@ module Pact
71
70
  end
72
71
 
73
72
  def interactions_for_new_consumer_contract
74
- if updating?
73
+ if pactfile_exists? && (updating? || merging?)
75
74
  merged_interactions = existing_interactions.dup
76
- filter = Pact::MockService::Interactions::UpdatableInteractionsFilter.new(merged_interactions)
75
+ filter = Pact::MockService::Interactions.filter(merged_interactions, pactfile_write_mode)
77
76
  interactions.each {|i| filter << i }
78
77
  merged_interactions
79
78
  else
@@ -83,19 +82,14 @@ module Pact
83
82
 
84
83
  def existing_interactions
85
84
  interactions = []
86
- if pactfile_exists?
85
+ if pactfile_exists? && pactfile_has_contents?
87
86
  begin
88
87
  interactions = existing_consumer_contract.interactions
89
- info_and_puts "*****************************************************************************"
90
- info_and_puts "Updating existing file .#{pactfile_path.gsub(Dir.pwd, '')} as config.pactfile_write_mode is :update"
91
- info_and_puts "Only interactions defined in this test run will be updated."
92
- info_and_puts "As interactions are identified by description and provider state, pleased note that if either of these have changed, the old interactions won't be removed from the pact file until the specs are next run with :pactfile_write_mode => :overwrite."
93
- info_and_puts "*****************************************************************************"
88
+ print_updating_warning if updating?
94
89
  rescue StandardError => e
95
- warn_and_stderr "Could not load existing consumer contract from #{pactfile_path} due to #{e}"
90
+ warn_and_stderr "Could not load existing consumer contract from #{pactfile_path} due to #{e}. Creating a new file."
96
91
  logger.error e
97
92
  logger.error e.backtrace
98
- warn_and_stderr "Creating a new file."
99
93
  end
100
94
  end
101
95
  interactions
@@ -105,7 +99,12 @@ module Pact
105
99
  File.exist?(pactfile_path)
106
100
  end
107
101
 
102
+ def pactfile_has_contents?
103
+ File.size(pactfile_path) != 0
104
+ end
105
+
108
106
  def existing_consumer_contract
107
+ # This must only be read after the file has been locked
109
108
  Pact::ConsumerContract.from_uri(pactfile_path)
110
109
  end
111
110
 
@@ -143,12 +142,26 @@ module Pact
143
142
  pactfile_write_mode == :update
144
143
  end
145
144
 
145
+ def merging?
146
+ pactfile_write_mode == :merge
147
+ end
148
+
146
149
  def log_message
147
150
  if updating?
148
151
  "Updating pact for #{provider_name} at #{pactfile_path}"
152
+ elsif merging?
153
+ "Merging interactions into pact for #{provider_name} at #{pactfile_path}"
149
154
  else
150
155
  "Writing pact for #{provider_name} to #{pactfile_path}"
151
156
  end
152
157
  end
158
+
159
+ def print_updating_warning
160
+ info_and_puts "*****************************************************************************"
161
+ info_and_puts "Updating existing file .#{pactfile_path.gsub(Dir.pwd, '')} as pactfile_write_mode is :update"
162
+ info_and_puts "Only interactions defined in this test run will be updated."
163
+ info_and_puts "As interactions are identified by description and provider state, pleased note that if either of these have changed, the old interactions won't be removed from the pact file until the specs are next run with :pactfile_write_mode => :overwrite."
164
+ info_and_puts "*****************************************************************************"
165
+ end
153
166
  end
154
167
  end
@@ -38,7 +38,10 @@ module Pact
38
38
 
39
39
  def write_pact_if_configured
40
40
  consumer_contract_writer = ConsumerContractWriter.new(@session.consumer_contract_details, StdoutLogger.new)
41
- consumer_contract_writer.write if consumer_contract_writer.can_write?
41
+ if consumer_contract_writer.can_write? && !@session.pact_written?
42
+ $stdout.puts "Writing pact before shutting down"
43
+ consumer_contract_writer.write
44
+ end
42
45
  end
43
46
 
44
47
  def to_s
@@ -47,6 +50,7 @@ module Pact
47
50
  end
48
51
 
49
52
  # Can't write to a file in a TRAP, might deadlock
53
+ # Not sure why we can still write to the pact file though
50
54
  class StdoutLogger
51
55
  def info message
52
56
  $stdout.puts "\n#{message}"
@@ -10,19 +10,22 @@ module Pact
10
10
  module MockService
11
11
  class CLI < Thor
12
12
 
13
+ PACT_FILE_WRITE_MODE_DESC = "`overwrite` or `merge`. Use `merge` when running multiple mock service instances in parallel for the same consumer/provider pair." +
14
+ " Ensure the pact file is deleted before running tests when using this option so that interactions deleted from the code are not maintained in the file."
15
+
13
16
  desc 'service', "Start a mock service. If the consumer, provider and pact-dir options are provided, the pact will be written automatically on shutdown."
17
+ method_option :consumer, desc: "Consumer name"
18
+ method_option :provider, desc: "Provider name"
14
19
  method_option :port, aliases: "-p", desc: "Port on which to run the service"
15
20
  method_option :host, aliases: "-h", desc: "Host on which to bind the service", default: 'localhost'
21
+ method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
22
+ method_option :pact_file_write_mode, aliases: "-m", desc: PACT_FILE_WRITE_MODE_DESC, type: :string, default: 'overwrite'
23
+ method_option :pact_specification_version, aliases: "-i", desc: "The pact specification version to use when writing the pact", default: '1'
16
24
  method_option :log, aliases: "-l", desc: "File to which to log output"
25
+ method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
17
26
  method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS", type: :boolean, default: false
18
27
  method_option :sslcert, desc: "Specify the path to the SSL cert to use when running the service over HTTPS"
19
28
  method_option :sslkey, desc: "Specify the path to the SSL key to use when running the service over HTTPS"
20
- method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
21
- method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
22
- method_option :unique_pact_file_names, aliases: "-u", desc: "Set to true when running multiple mock service instances in parallel for the same consumer/provider pair", type: :boolean, default: false
23
- method_option :pact_specification_version, aliases: "-i", desc: "The pact specification version to use when writing the pact", default: '1'
24
- method_option :consumer, desc: "Consumer name"
25
- method_option :provider, desc: "Provider name"
26
29
 
27
30
  def service
28
31
  require 'pact/mock_service/run'
@@ -32,14 +35,14 @@ module Pact
32
35
  desc 'control', "Run a Pact mock service control server."
33
36
  method_option :port, aliases: "-p", desc: "Port on which to run the service"
34
37
  method_option :host, aliases: "-h", desc: "Host on which to bind the service", default: 'localhost'
35
- method_option :log_dir, aliases: "-l", desc: "File to which to log output"
36
38
  method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
37
- method_option :unique_pact_file_names, aliases: "-u", desc: "Set to true when running multiple mock service instances in parallel for the same consumer/provider pair", type: :boolean, default: false
39
+ method_option :log_dir, aliases: "-l", desc: "File to which to log output"
40
+ method_option :pact_file_write_mode, aliases: "-m", desc: PACT_FILE_WRITE_MODE_DESC, type: :string, default: 'overwrite'
38
41
  method_option :pact_specification_version, aliases: "-i", desc: "The pact specification version to use when writing the pact", default: '1'
42
+ method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
39
43
  method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS", type: :boolean, default: false
40
44
  method_option :sslcert, desc: "Specify the path to the SSL cert to use when running the service over HTTPS"
41
45
  method_option :sslkey, desc: "Specify the path to the SSL key to use when running the service over HTTPS"
42
- method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
43
46
 
44
47
  def control
45
48
  require 'pact/mock_service/control_server/run'
@@ -47,19 +50,19 @@ module Pact
47
50
  end
48
51
 
49
52
  desc 'start', "Start a mock service. If the consumer, provider and pact-dir options are provided, the pact will be written automatically on shutdown."
53
+ method_option :consumer, desc: "Consumer name"
54
+ method_option :provider, desc: "Provider name"
50
55
  method_option :port, aliases: "-p", default: '1234', desc: "Port on which to run the service"
51
56
  method_option :host, aliases: "-h", desc: "Host on which to bind the service", default: 'localhost'
57
+ method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
58
+ method_option :pact_file_write_mode, aliases: "-m", desc: PACT_FILE_WRITE_MODE_DESC, type: :string, default: 'overwrite'
59
+ method_option :pid_dir, desc: "PID dir", default: 'tmp/pids'
52
60
  method_option :log, aliases: "-l", desc: "File to which to log output"
61
+ method_option :pact_specification_version, aliases: "-i", desc: "The pact specification version to use when writing the pact", default: '1'
62
+ method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
53
63
  method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS", type: :boolean, default: false
54
64
  method_option :sslcert, desc: "Specify the path to the SSL cert to use when running the service over HTTPS"
55
65
  method_option :sslkey, desc: "Specify the path to the SSL key to use when running the service over HTTPS"
56
- method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
57
- method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
58
- method_option :unique_pact_file_names, aliases: "-u", desc: "Set to true when running multiple mock service instances in parallel for the same consumer/provider pair", type: :boolean, default: false
59
- method_option :pact_specification_version, aliases: "-i", desc: "The pact specification version to use when writing the pact", default: '1'
60
- method_option :consumer, desc: "Consumer name"
61
- method_option :provider, desc: "Provider name"
62
- method_option :pid_dir, desc: "PID dir", default: 'tmp/pids'
63
66
 
64
67
  def start
65
68
  start_server(mock_service_pidfile) do
@@ -76,19 +79,19 @@ module Pact
76
79
  end
77
80
 
78
81
  desc 'restart', "Start or restart a mock service. If the consumer, provider and pact-dir options are provided, the pact will be written automatically on shutdown."
82
+ method_option :consumer, desc: "Consumer name"
83
+ method_option :provider, desc: "Provider name"
79
84
  method_option :port, aliases: "-p", default: '1234', desc: "Port on which to run the service"
80
85
  method_option :host, aliases: "-h", desc: "Host on which to bind the service", default: 'localhost'
86
+ method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
87
+ method_option :pact_file_write_mode, aliases: "-m", desc: PACT_FILE_WRITE_MODE_DESC, type: :string, default: 'overwrite'
88
+ method_option :pid_dir, desc: "PID dir", default: 'tmp/pids'
81
89
  method_option :log, aliases: "-l", desc: "File to which to log output"
90
+ method_option :pact_specification_version, aliases: "-i", desc: "The pact specification version to use when writing the pact", default: '1'
91
+ method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
82
92
  method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS", type: :boolean, default: false
83
93
  method_option :sslcert, desc: "Specify the path to the SSL cert to use when running the service over HTTPS"
84
94
  method_option :sslkey, desc: "Specify the path to the SSL key to use when running the service over HTTPS"
85
- method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
86
- method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written"
87
- method_option :unique_pact_file_names, aliases: "-u", desc: "Set to true when running multiple mock service instances in parallel for the same consumer/provider pair", type: :boolean, default: false
88
- method_option :pact_specification_version, aliases: "-i", desc: "The pact specification version to use when writing the pact", default: '1'
89
- method_option :consumer, desc: "Consumer name"
90
- method_option :provider, desc: "Provider name"
91
- method_option :pid_dir, desc: "PID dir", default: 'tmp/pids'
92
95
 
93
96
  def restart
94
97
  restart_server(mock_service_pidfile) do
@@ -99,14 +102,14 @@ module Pact
99
102
  desc 'control-start', "Start a Pact mock service control server."
100
103
  method_option :port, aliases: "-p", desc: "Port on which to run the service", default: '1234'
101
104
  method_option :log_dir, aliases: "-l", desc: "File to which to log output", default: "log"
105
+ method_option :pact_file_write_mode, aliases: "-m", desc: PACT_FILE_WRITE_MODE_DESC, type: :string, default: 'overwrite'
106
+ method_option :pact_specification_version, aliases: "-i", desc: "The pact specification version to use when writing the pact", default: '1'
107
+ method_option :pid_dir, desc: "PID dir", default: "tmp/pids"
108
+ method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
102
109
  method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS", type: :boolean, default: false
103
110
  method_option :sslcert, desc: "Specify the path to the SSL cert to use when running the service over HTTPS"
104
111
  method_option :sslkey, desc: "Specify the path to the SSL key to use when running the service over HTTPS"
105
- method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
106
112
  method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written", default: "."
107
- method_option :unique_pact_file_names, aliases: "-u", desc: "Set to true when running multiple mock service instances in parallel for the same consumer/provider pair", type: :boolean, default: false
108
- method_option :pact_specification_version, aliases: "-i", desc: "The pact specification version to use when writing the pact", default: '1'
109
- method_option :pid_dir, desc: "PID dir", default: "tmp/pids"
110
113
 
111
114
  def control_start
112
115
  start_server(control_server_pidfile) do
@@ -125,14 +128,14 @@ module Pact
125
128
  desc 'control-restart', "Start a Pact mock service control server."
126
129
  method_option :port, aliases: "-p", desc: "Port on which to run the service", default: '1234'
127
130
  method_option :log_dir, aliases: "-l", desc: "File to which to log output", default: "log"
128
- method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS", type: :boolean, default: false
129
- method_option :sslcert, desc: "Specify the path to the SSL cert to use when running the service over HTTPS"
130
- method_option :sslkey, desc: "Specify the path to the SSL key to use when running the service over HTTPS"
131
- method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
132
131
  method_option :pact_dir, aliases: "-d", desc: "Directory to which the pacts will be written", default: "."
133
- method_option :unique_pact_file_names, aliases: "-u", desc: "Set to true when running multiple mock service instances in parallel for the same consumer/provider pair", type: :boolean, default: false
132
+ method_option :pact_file_write_mode, aliases: "-m", desc: PACT_FILE_WRITE_MODE_DESC, type: :string, default: 'overwrite'
134
133
  method_option :pact_specification_version, aliases: "-i", desc: "The pact specification version to use when writing the pact", default: '1'
135
134
  method_option :pid_dir, desc: "PID dir", default: "tmp/pids"
135
+ method_option :cors, aliases: "-o", desc: "Support browser security in tests by responding to OPTIONS requests and adding CORS headers to mocked responses"
136
+ method_option :ssl, desc: "Use a self-signed SSL cert to run the service over HTTPS", type: :boolean, default: false
137
+ method_option :sslcert, desc: "Specify the path to the SSL cert to use when running the service over HTTPS"
138
+ method_option :sslkey, desc: "Specify the path to the SSL key to use when running the service over HTTPS"
136
139
 
137
140
  def control_restart
138
141
  restart_server(control_server_pidfile) do
@@ -0,0 +1,9 @@
1
+ require 'pact/errors'
2
+
3
+ module Pact
4
+ module MockService
5
+ class AlmostDuplicateInteractionError < Pact::Error; end
6
+
7
+ class SameSameButDifferentError < ::Pact::Error; end
8
+ end
9
+ end
@@ -12,7 +12,7 @@ module Pact
12
12
  end
13
13
 
14
14
  def to_s
15
- "An interaction with same description (#{new_interaction.description.inspect}) and provider state (#{new_interaction.provider_state.inspect}) but a different #{differences} has already been used. Please use a different description or provider state, or hard-code any random data."
15
+ "An interaction with same description (#{new_interaction.description.inspect}) and provider state (#{new_interaction.provider_state.inspect}) but a different #{differences} has already been used. Please use a different description or provider state, or remove any random data in the interaction."
16
16
  end
17
17
 
18
18
  private
@@ -1,34 +1,66 @@
1
+ require 'pact/mock_service/errors'
2
+ require 'pact/mock_service/interactions/interaction_diff_message'
3
+
1
4
  #
2
5
  # When running in pactfile_write_mode :overwrite, all interactions are cleared from the
3
6
  # pact file, and all new interactions should be distinct (unique description and provider state).
4
7
  # When running in pactfile_write_mode :update, an interaction with the same description
5
8
  # and provider state as an existing one will just overwrite that one interaction.
6
- #
9
+ # When running in pactfile_write_mode :merge, an interaction with the same description and provider
10
+ # state must be identical to the existing one, otherwise an exception will be raised.
7
11
 
8
12
  module Pact
9
- module MockService
10
- module Interactions
13
+ module MockService
14
+ module Interactions
11
15
 
12
- #TODO: think of a better word than filter
13
- class InteractionsFilter
14
- def initialize interactions = []
15
- @interactions = interactions
16
- end
16
+ def self.filter existing_interactions, pactfile_write_mode
17
+ if pactfile_write_mode == :update
18
+ UpdatableInteractionsFilter.new(existing_interactions)
19
+ elsif pactfile_write_mode == :merge
20
+ MergingInteractionsFilter.new(existing_interactions)
21
+ else
22
+ existing_interactions
23
+ end
24
+ end
17
25
 
18
- def index_of interaction
19
- @interactions.find_index{ |i| i.matches_criteria?(description: interaction.description, provider_state: interaction.provider_state)}
20
- end
21
- end
22
-
23
- class UpdatableInteractionsFilter < InteractionsFilter
24
- def << interaction
25
- if (ndx = index_of(interaction))
26
- @interactions[ndx] = interaction
27
- else
28
- @interactions << interaction
29
- end
26
+ #TODO: think of a better word than filter
27
+ class InteractionsFilter
28
+ def initialize interactions = []
29
+ @interactions = interactions
30
+ end
31
+
32
+ def index_of interaction
33
+ @interactions.find_index{ |i| i.matches_criteria?(description: interaction.description, provider_state: interaction.provider_state)}
34
+ end
35
+ end
36
+
37
+ class UpdatableInteractionsFilter < InteractionsFilter
38
+ def << interaction
39
+ if (ndx = index_of(interaction))
40
+ @interactions[ndx] = interaction
41
+ else
42
+ @interactions << interaction
43
+ end
44
+ end
45
+ end
46
+
47
+ class MergingInteractionsFilter < InteractionsFilter
48
+ def << interaction
49
+ if (ndx = index_of(interaction))
50
+ if same_same_but_different?(@interactions[ndx], interaction)
51
+ message = Interactions::InteractionDiffMessage.new(@interactions[ndx], interaction).to_s
52
+ raise SameSameButDifferentError, message
30
53
  end
31
- end
54
+ @interactions[ndx] = interaction
55
+ else
56
+ @interactions << interaction
57
+ end
58
+ end
59
+
60
+ def same_same_but_different?(existing_interaction, new_interaction)
61
+ existing_interaction != new_interaction
62
+ end
32
63
  end
33
- end
64
+ end
65
+ end
34
66
  end
@@ -16,6 +16,14 @@ module Pact
16
16
  def call env
17
17
  match?(env) ? respond(env) : NOT_FOUND_RESPONSE
18
18
  end
19
+
20
+ def json_response json = nil, status = 200
21
+ [status, {'Content-Type' => 'application/json'}, json ? [json + "\n"]: []]
22
+ end
23
+
24
+ def text_response text = nil, status = 200
25
+ [status, {'Content-Type' => 'text/plain'}, text ? [text + "\n"]: []]
26
+ end
19
27
  end
20
28
  end
21
29
  end
@@ -15,7 +15,7 @@ module Pact
15
15
  end
16
16
 
17
17
  def respond env
18
- [200, {'Content-Type' => 'text/plain'}, ['Mock service running']]
18
+ text_response('Mock service running')
19
19
  end
20
20
  end
21
21
  end
@@ -23,8 +23,10 @@ module Pact
23
23
 
24
24
  def respond env
25
25
  session.clear_expected_and_actual_interactions
26
- logger.info "Cleared interactions before example \"#{example_description(env)}\""
27
- [200, {}, ['Deleted interactions']]
26
+ example_desc = example_description(env)
27
+ example_desc = example_desc ? " for example #{example_desc.inspect}" : ''
28
+ logger.info "Cleared interactions#{example_desc}"
29
+ text_response('Cleared interactions')
28
30
  end
29
31
 
30
32
  def example_description env
@@ -25,9 +25,9 @@ module Pact
25
25
 
26
26
  begin
27
27
  session.add_expected_interaction interaction
28
- [200, {}, ['Set interactions']]
29
- rescue AlmostDuplicateInteractionError => e
30
- [500, {}, [e.message]]
28
+ text_response 'Registered interactions'
29
+ rescue ::Pact::Error => e
30
+ text_response e.message, 500
31
31
  end
32
32
 
33
33
  end
@@ -92,7 +92,7 @@ module Pact
92
92
  message: "Multiple interaction found for #{actual_request.method_and_path}",
93
93
  matching_interactions: matching_interactions.collect{ | interaction | request_summary_for(interaction) }
94
94
  }
95
- [500, {'Content-Type' => 'application/json'}, [response.to_json]]
95
+ [500, {'Content-Type' => 'application/json'}, [response.to_json + "\n"]]
96
96
  end
97
97
 
98
98
  def self.request_summary_for interaction
@@ -121,7 +121,7 @@ module Pact
121
121
  message: "No interaction found for #{interaction_mismatch.actual_request.method_and_path}",
122
122
  interaction_diffs: interaction_mismatch.to_hash
123
123
  }
124
- [500, {'Content-Type' => 'application/json'}, [response.to_json]]
124
+ [500, {'Content-Type' => 'application/json'}, [response.to_json + "\n"]]
125
125
  end
126
126
 
127
127
  def self.interaction_mismatch actual_request, candidate_interactions
@@ -26,9 +26,9 @@ module Pact
26
26
  interactions = request_body['interactions'].collect { | hash | Interaction.from_hash(hash) }
27
27
  begin
28
28
  session.set_expected_interactions interactions
29
- [200, {}, ['Set interactions']]
30
- rescue AlmostDuplicateInteractionError => e
31
- [500, {}, [e.message]]
29
+ text_response('Registered interactions')
30
+ rescue Pact::Error => e
31
+ text_response(e.message, 500)
32
32
  end
33
33
  end
34
34
 
@@ -15,7 +15,7 @@ module Pact
15
15
 
16
16
  def respond env
17
17
  logger.info "Debug message from client - #{message(env)}"
18
- [200, {}, []]
18
+ text_response
19
19
  end
20
20
 
21
21
  def message env
@@ -25,7 +25,7 @@ module Pact
25
25
  verification = Pact::MockService::Interactions::Verification.new(@expected_interactions, @actual_interactions)
26
26
  number_of_missing_interactions = verification.missing_interactions.size
27
27
  logger.info "Number of missing interactions for mock \"#{name}\" = #{number_of_missing_interactions}"
28
- [200, {}, [{size: number_of_missing_interactions}.to_json]]
28
+ json_response({size: number_of_missing_interactions}.to_json)
29
29
  end
30
30
  end
31
31
  end
@@ -3,7 +3,6 @@ require 'pact/mock_service/request_handlers/base_request_handler'
3
3
  module Pact
4
4
  module MockService
5
5
  module RequestHandlers
6
-
7
6
  class Options < BaseRequestHandler
8
7
 
9
8
  attr_reader :name, :logger, :cors_enabled
@@ -35,7 +34,6 @@ module Pact
35
34
  def is_administration_request? env
36
35
  env["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"].match(/x-pact-mock-service/i)
37
36
  end
38
-
39
37
  end
40
38
  end
41
39
  end
@@ -6,13 +6,14 @@ module Pact
6
6
  module RequestHandlers
7
7
  class PactPost < BaseAdministrationRequestHandler
8
8
 
9
- attr_accessor :consumer_contract, :verified_interactions, :default_options
9
+ attr_accessor :consumer_contract, :verified_interactions, :default_options, :session
10
10
 
11
11
  def initialize name, logger, session
12
12
  super name, logger
13
13
  @verified_interactions = session.verified_interactions
14
14
  @default_options = {}
15
15
  @default_options.merge!(session.consumer_contract_details)
16
+ @session = session
16
17
  end
17
18
 
18
19
  def request_path
@@ -24,13 +25,12 @@ module Pact
24
25
  end
25
26
 
26
27
  def respond env
27
- consumer_contract_details = JSON.parse(env['rack.input'].string, symbolize_names: true)
28
- logger.info "Writing pact with details #{consumer_contract_details}"
28
+ body = env['rack.input'].string
29
+ consumer_contract_details = body.size > 0 ? JSON.parse(body, symbolize_names: true) : {}
29
30
  consumer_contract_params = default_options.merge(consumer_contract_details.merge(interactions: verified_interactions))
30
31
  consumer_contract_writer = ConsumerContractWriter.new(consumer_contract_params, logger)
31
- json = consumer_contract_writer.write
32
-
33
- [200, {'Content-Type' =>'application/json'}, [json]]
32
+ session.record_pact_written
33
+ json_response(consumer_contract_writer.write)
34
34
  end
35
35
  end
36
36
  end
@@ -24,9 +24,8 @@ module Pact
24
24
  def respond env
25
25
  session.clear_all
26
26
  logger.info "Cleared session"
27
- [200, {}, ['Cleared session']]
27
+ text_response 'Cleared session'
28
28
  end
29
-
30
29
  end
31
30
  end
32
31
  end
@@ -21,14 +21,17 @@ module Pact
21
21
 
22
22
  def respond env
23
23
  verification = Pact::MockService::Interactions::Verification.new(expected_interactions, actual_interactions)
24
+ example_desc = example_description(env)
25
+ example_desc = example_desc ? " for example #{example_desc.inspect}" : ''
24
26
  if verification.all_matched?
25
- logger.info "Verifying - interactions matched for example \"#{example_description(env)}\""
26
- [200, {'Content-Type' => 'text/plain'}, ['Interactions matched']]
27
+ logger.info "Verifying - interactions matched#{example_desc}"
28
+ text_response('Interactions matched')
27
29
  else
28
30
  error_message = FailureMessage.new(verification).to_s
29
- logger.warn "Verifying - actual interactions do not match expected interactions for example \"#{example_description(env)}\". \n#{error_message}"
31
+ logger.warn "Verifying - actual interactions do not match expected interactions#{example_desc}. \n#{error_message}"
30
32
  logger.warn error_message
31
- [500, {'Content-Type' => 'text/plain'}, ["Actual interactions do not match expected interactions for mock #{name}.\n\n#{error_message}See #{logger.description} for details."]]
33
+ response_message = "Actual interactions do not match expected interactions for mock #{name}.\n\n#{error_message}See #{logger.description} for details."
34
+ text_response(response_message, 500)
32
35
  end
33
36
  end
34
37
 
@@ -64,7 +67,6 @@ module Pact
64
67
  "Unexpected requests" => verification.unexpected_requests_summaries,
65
68
  }
66
69
  end
67
-
68
70
  end
69
71
  end
70
72
  end
@@ -52,7 +52,8 @@ module Pact
52
52
  consumer: options[:consumer],
53
53
  provider: options[:provider],
54
54
  cors_enabled: options[:cors],
55
- pact_specification_version: options[:pact_specification_version]
55
+ pact_specification_version: options[:pact_specification_version],
56
+ pactfile_write_mode: options[:pact_file_write_mode]
56
57
  }
57
58
  service_options[:log_file] = open_log_file if options[:log]
58
59
  service_options
@@ -3,31 +3,38 @@ require 'pact/mock_service/interactions/actual_interactions'
3
3
  require 'pact/mock_service/interactions/verified_interactions'
4
4
  require 'pact/mock_service/interaction_decorator'
5
5
  require 'pact/mock_service/interactions/interaction_diff_message'
6
+ require 'pact/mock_service/errors'
6
7
 
7
8
  module Pact
8
9
  module MockService
9
-
10
- class AlmostDuplicateInteractionError < StandardError; end
11
-
12
10
  class Session
13
11
 
14
12
  attr_reader :expected_interactions, :actual_interactions, :verified_interactions, :consumer_contract_details, :logger
15
13
 
16
14
  def initialize options
15
+ @pact_written = false
17
16
  @logger = options[:logger]
18
17
  @expected_interactions = Interactions::ExpectedInteractions.new
19
18
  @actual_interactions = Interactions::ActualInteractions.new
20
19
  @verified_interactions = Interactions::VerifiedInteractions.new
21
20
  @consumer_contract_details = {
22
21
  pact_dir: options[:pact_dir],
23
- unique_pact_file_names: options[:unique_pact_file_names],
24
22
  consumer: {name: options[:consumer]},
25
23
  provider: {name: options[:provider]},
26
24
  interactions: verified_interactions,
27
- pact_specification_version: options[:pact_specification_version]
25
+ pact_specification_version: options[:pact_specification_version],
26
+ pactfile_write_mode: options[:pactfile_write_mode]
28
27
  }
29
28
  end
30
29
 
30
+ def pact_written?
31
+ @pact_written
32
+ end
33
+
34
+ def record_pact_written
35
+ @pact_written = true
36
+ end
37
+
31
38
  def set_expected_interactions interactions
32
39
  clear_expected_and_actual_interactions
33
40
  interactions.each do | interaction |
@@ -44,6 +51,7 @@ module Pact
44
51
  expected_interactions.clear
45
52
  actual_interactions.clear
46
53
  verified_interactions.clear
54
+ @pact_written = false
47
55
  end
48
56
 
49
57
  def add_expected_interaction interaction
@@ -65,7 +73,7 @@ module Pact
65
73
  def handle_almost_duplicate_interaction previous_interaction, interaction
66
74
  message = Interactions::InteractionDiffMessage.new(previous_interaction, interaction).to_s
67
75
  logger.error message
68
- raise AlmostDuplicateInteractionError, message
76
+ raise SameSameButDifferentError, message
69
77
  end
70
78
 
71
79
  def interaction_already_verified_with_same_description_and_provider_state_but_not_equal interaction
@@ -42,7 +42,8 @@ module Pact
42
42
  provider: provider,
43
43
  pact_dir: options[:pact_dir] || ".",
44
44
  cors_enabled: options[:cors_enabled],
45
- pact_specification_version: options[:pact_specification_version]
45
+ pact_specification_version: options[:pact_specification_version],
46
+ pactfile_write_mode: options[:pact_file_write_mode]
46
47
  )
47
48
  end
48
49
 
@@ -1,5 +1,5 @@
1
1
  module Pact
2
2
  module MockService
3
- VERSION = "2.2.0"
3
+ VERSION = "2.3.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pact-mock_service
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Fraser
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2017-09-30 00:00:00.000000000 Z
15
+ date: 2017-10-04 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: rack
@@ -133,6 +133,9 @@ dependencies:
133
133
  - - "~>"
134
134
  - !ruby/object:Gem::Version
135
135
  version: '1.2'
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 1.2.1
136
139
  type: :runtime
137
140
  prerelease: false
138
141
  version_requirements: !ruby/object:Gem::Requirement
@@ -140,6 +143,23 @@ dependencies:
140
143
  - - "~>"
141
144
  - !ruby/object:Gem::Version
142
145
  version: '1.2'
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: 1.2.1
149
+ - !ruby/object:Gem::Dependency
150
+ name: filelock
151
+ requirement: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - "~>"
154
+ - !ruby/object:Gem::Version
155
+ version: '1.1'
156
+ type: :runtime
157
+ prerelease: false
158
+ version_requirements: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - "~>"
161
+ - !ruby/object:Gem::Version
162
+ version: '1.1'
143
163
  - !ruby/object:Gem::Dependency
144
164
  name: rake
145
165
  requirement: !ruby/object:Gem::Requirement
@@ -258,7 +278,7 @@ email:
258
278
  - sergei.matheson@gmail.com
259
279
  - brent@fuglylogic.com
260
280
  - uglyog@gmail.com
261
- - bskurrie@dius.com.au
281
+ - beth@bethesque.com
262
282
  executables:
263
283
  - pact-mock-service
264
284
  extensions: []
@@ -292,6 +312,7 @@ files:
292
312
  - lib/pact/mock_service/control_server/mock_services.rb
293
313
  - lib/pact/mock_service/control_server/require_pacticipant_headers.rb
294
314
  - lib/pact/mock_service/control_server/run.rb
315
+ - lib/pact/mock_service/errors.rb
295
316
  - lib/pact/mock_service/interaction_decorator.rb
296
317
  - lib/pact/mock_service/interactions/actual_interactions.rb
297
318
  - lib/pact/mock_service/interactions/candidate_interactions.rb
@@ -345,7 +366,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
345
366
  version: '0'
346
367
  requirements: []
347
368
  rubyforge_project:
348
- rubygems_version: 2.6.12
369
+ rubygems_version: 2.6.13
349
370
  signing_key:
350
371
  specification_version: 4
351
372
  summary: Provides a mock service for use with Pact