diff --git a/.github/workflows/release_gem.yml b/.github/workflows/release_gem.yml
new file mode 100644
index 00000000..fac97a38
--- /dev/null
+++ b/.github/workflows/release_gem.yml
@@ -0,0 +1,59 @@
+name: Release gem
+
+on:
+ repository_dispatch:
+ types:
+ - release-triggered
+ workflow_dispatch:
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-ruby@v1
+ with:
+ ruby-version: '2.6'
+ - run: |
+ gem install bundler -v 2.1
+ bundle install
+ - name: Test
+ run: bundle exec rake
+
+ release:
+ needs: test
+ runs-on: ubuntu-latest
+ outputs:
+ gem_name: ${{ steps.release-gem.outputs.gem_name }}
+ version: ${{ steps.release-gem.outputs.version }}
+ increment: ${{ steps.release-gem.outputs.increment }}
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+ - id: release-gem
+ uses: pact-foundation/release-gem@v0.0.11
+ env:
+ GEM_HOST_API_KEY: '${{ secrets.RUBYGEMS_API_KEY }}'
+ GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
+ INCREMENT: '${{ github.event.client_payload.increment }}'
+
+ notify-gem-released:
+ needs: release
+ strategy:
+ matrix:
+ repository: [pact-foundation/pact-ruby-cli, pact-foundation/pact-ruby-standalone, pact-foundation/pact_broker-client]
+ runs-on: ubuntu-latest
+ steps:
+ - name: Notify ${{ matrix.repository }} of gem release
+ uses: peter-evans/repository-dispatch@v1
+ with:
+ token: ${{ secrets.GHTOKENFORPACTCLIRELEASE }}
+ repository: ${{ matrix.repository }}
+ event-type: gem-released
+ client-payload: |
+ {
+ "name": "${{ needs.release.outputs.gem_name }}",
+ "version": "${{ needs.release.outputs.version }}",
+ "increment": "${{ needs.release.outputs.increment }}"
+ }
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..0cb529a4
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,20 @@
+name: Test
+
+on: push
+
+jobs:
+ test:
+ runs-on: "ubuntu-latest"
+ continue-on-error: ${{ matrix.experimental }}
+ strategy:
+ fail-fast: false
+ matrix:
+ ruby_version: ["2.2", "2.7", "3.0"]
+ experimental: [false]
+ steps:
+ - uses: actions/checkout@v2
+ - uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby_version }}
+ - run: "bundle install"
+ - run: "bundle exec rake"
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index edc2f435..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-language: ruby
-rvm:
-- 2.2.4
-- 2.3.1
-- 2.4
-- jruby-9.0.5.0
-- jruby-9.1.13.0
-gemfile:
-- gemfiles/default.gemfile
-- gemfiles/rspec_2.gemfile
-- gemfiles/rspec_3.0.gemfile
-deploy:
- provider: rubygems
- api_key:
- secure: AzTHDbKRr1ZO4E2mRyvU054Tx8c2cZbKkoDBZjSAQ2CY3E7oH137NTAIGd4BthH/E9mbEXtGpZIDfWPbaOcUJQ5Bz24CWTKmGyic6FrPhJnOW5CKVSLGCDPzpmqHULv/GTN16YN0Dh1HLeGYZzlHlxT0+4AVvbvBAleHrAFeJs8=
- gem: pact
- on:
- tags: true
- repo: pact-foundation/pact-ruby
diff --git a/Appraisals b/Appraisals
deleted file mode 100644
index e9289b87..00000000
--- a/Appraisals
+++ /dev/null
@@ -1,10 +0,0 @@
-appraise "default" do
-end
-
-appraise "rspec-2" do
- gem "rspec", "2.14.1"
-end
-
-appraise "rspec-3.0" do
- gem "rspec", "3.0.0"
-end
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6d075072..133c14e9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,401 @@
+
+### v1.61.0 (2021-12-16)
+
+#### Features
+
+* support description of matching_branch and matching_tag consumer version selectors ([8e8bb22](/../../commit/8e8bb22))
+
+#### Bug Fixes
+
+* pass through includePendingStatus to the 'pacts for verification' API when it is false ([f0e37a4](/../../commit/f0e37a4))
+
+
+### v1.60.0 (2021-10-01)
+
+#### Features
+
+* allow SSL verification to be disabled in the HAL client by setting the environment variable PACT_DISABLE_SSL_VERIFICATION=true ([ce07d32](/../../commit/ce07d32))
+
+
+### v1.59.0 (2021-09-07)
+
+#### Features
+
+* update descriptions for new consumer version selectors ([0471397](/../../commit/0471397))
+
+
+### v1.58.0 (2021-09-01)
+
+#### Features
+
+* support publishing verification results with a version branch ([da2facf](/../../commit/da2facf))
+
+#### Bug Fixes
+
+* gracefully handle display of username that causes InvalidComponentError to be raised when composing a URI ([cecb98f](/../../commit/cecb98f))
+
+
+### v1.57.0 (2021-01-27)
+
+#### Features
+
+* allow verbose flag to be set when publishing verifications ([9238e4c](/../../commit/9238e4c))
+
+
+### v1.56.0 (2021-01-22)
+
+#### Features
+
+* catch and log error during help text generation ([182a7cd](/../../commit/182a7cd))
+
+
+### v1.55.7 (2020-11-25)
+
+#### Bug Fixes
+
+* add consumer name to the selection description (#229) ([5127036](/../../commit/5127036))
+
+
+### v1.55.6 (2020-11-06)
+
+#### Bug Fixes
+
+* require rspec now that pact-support does not depend on it ([5b5c27c](/../../commit/5b5c27c))
+
+
+### v1.55.5 (2020-10-12)
+
+#### Bug Fixes
+
+* **security**
+ * hide personal access token given in uri (#225) ([f6db12d](/../../commit/f6db12d))
+
+
+### v1.55.4 (2020-10-09)
+
+#### Bug Fixes
+
+* add back missing output describing the interactions filter ([1a2d7c1](/../../commit/1a2d7c1))
+
+
+### v1.55.3 (2020-09-28)
+
+#### Bug Fixes
+
+* correct logic for determining if all interactions for a pact have been verified ([c4f968e](/../../commit/c4f968e))
+* de-duplicate re-run commands ([0813498](/../../commit/0813498))
+
+
+### v1.55.2 (2020-09-26)
+
+#### Bug Fixes
+
+* correctly calculate exit code when a mix of pending and non pending pacts are verified ([533faa1](/../../commit/533faa1))
+
+
+### v1.55.1 (2020-09-26)
+
+#### Bug Fixes
+
+* remove accidentally committed debug logging ([081423e](/../../commit/081423e))
+
+
+### v1.55.0 (2020-09-26)
+
+#### Features
+
+* add consumer_version_selectors to pact verification DSL, and convert consumer_version_tags to selectors ([39e6c4a](/../../commit/39e6c4a))
+* allow verification task to set just a pact_helper without a URI ([303077d](/../../commit/303077d))
+* split pending and failed rerun commands into separate sections ([f839391](/../../commit/f839391))
+* update output during verification so the pact info shows before the describe blocks of the pact that is being verified ([15ec231](/../../commit/15ec231))
+
+
+### v1.54.0 (2020-09-12)
+
+#### Features
+
+* use pb relation in preference to beta relation when fetching pacts for verification ([7563fcf](/../../commit/7563fcf))
+* allow include_wip_pacts_since to use a Date, DateTime or Time ([dd35366](/../../commit/dd35366))
+* add support for include_wip_pacts_since ([f2247b8](/../../commit/f2247b8))
+
+
+### v1.53.0 (2020-09-11)
+
+#### Features
+
+* add support for the enable_pending flag ([16866f4](/../../commit/16866f4))
+
+
+### v1.52.0 (2020-09-10)
+
+#### Features
+
+* support webdav http methods ([fa1d712](/../../commit/fa1d712))
+
+
+### v1.51.1 (2020-08-12)
+
+#### Bug Fixes
+
+* update thor dependency (#218) ([bf3ce69](/../../commit/bf3ce69))
+* bump rake dependency per CVE-2020-8130 (#219) ([09feaa6](/../../commit/09feaa6))
+
+
+### v1.51.0 (2020-06-24)
+
+
+#### Features
+
+* allow individual interactions to be re-run by setting PACT_BROKER_INTERACTION_ID ([a586d80](/../../commit/a586d80))
+
+
+
+### v1.50.1 (2020-06-15)
+
+
+#### Bug Fixes
+
+* fix integration with pact-message-ruby (#216) ([d2da13e](/../../commit/d2da13e))
+
+
+
+### v1.50.0 (2020-04-25)
+
+
+#### Features
+
+* Set expected interactions on mock service but without writing them to pact file (#210) ([14f5327](/../../commit/14f5327))
+
+
+
+### v1.49.3 (2020-04-22)
+
+
+#### Bug Fixes
+
+* pact selection verification options logging ([9ff59f4](/../../commit/9ff59f4))
+
+
+
+### v1.49.2 (2020-04-08)
+
+
+#### Bug Fixes
+
+* json parser error for top level JSON values ([dafbc35](/../../commit/dafbc35))
+
+
+
+### v1.49.1 (2020-03-21)
+
+
+#### Bug Fixes
+
+* ensure diff is included in the json output ([0bd9753](/../../commit/0bd9753))
+* ensure the presence of basic auth credentials does not cause an error when displaying the path of a pact on the local filesystem ([f6a0b4d](/../../commit/f6a0b4d))
+
+
+
+### v1.49.0 (2020-02-18)
+
+
+#### Features
+
+* use environment variables PACT_BROKER_USERNAME and PACT_BROKER_PASSWORD when verifying a pact by URL, if the environment variables are present ([308f25d](/../../commit/308f25d))
+
+
+
+### v1.48.0 (2020-02-13)
+
+
+#### Features
+
+* use certificates from SSL_CERT_FILE and SSL_CERT_DIR environment variables in HTTP connections ([164912b](/../../commit/164912b))
+
+
+
+### v1.47.0 (2020-02-08)
+
+
+#### Features
+
+* update json formatter output ([376e47a](/../../commit/376e47a))
+* add pact metadata to json formatter ([6c6ddb8](/../../commit/6c6ddb8))
+
+
+
+### v1.46.1 (2020-01-22)
+
+
+#### Bug Fixes
+
+* send output messages to the correct stream when using the XML formatter ([e768a33](/../../commit/e768a33))
+
+
+
+### v1.46.0 (2020-01-22)
+
+
+#### Features
+
+* expose full notice object in JSON output ([bdc2711](/../../commit/bdc2711))
+
+
+#### Bug Fixes
+
+* remove accidentally committed verbose: true ([498518c](/../../commit/498518c))
+
+
+
+### v1.45.0 (2020-01-21)
+
+
+#### Features
+
+* use custom json formatter when --format json is specified and send it straight to stdout or the configured file ([6c703a1](/../../commit/6c703a1))
+* support pending pacts in json formatter ([2c0d20d](/../../commit/2c0d20d))
+
+
+#### Bug Fixes
+
+* show pending test output in yellow instead of red ([e8d4a55](/../../commit/e8d4a55))
+
+
+
+### v1.44.1 (2020-01-20)
+
+
+#### Bug Fixes
+
+* print notices from 'pacts for verification' response to indicate why pacts are included an/or pending ([b107348](/../../commit/b107348))
+
+
+
+### v1.44.0 (2020-01-16)
+
+
+#### Features
+
+* **message pact**
+ * add DSL for configuring Message Pact verifications ([a5181b6](/../../commit/a5181b6))
+
+
+
+### v1.43.1 (2020-01-11)
+
+
+#### Bug Fixes
+
+* use configured credentials when fetching the diff with previous version ([b9deb09](/../../commit/b9deb09))
+* use URI.open instead of Kernel.open ([7b3ea81](/../../commit/7b3ea81))
+
+
+
+### v1.43.0 (2020-01-11)
+
+
+#### Features
+
+* **verify**
+ * allow includePendingStatus to be specified when fetching pacts ([1f5fc9c](/../../commit/1f5fc9c))
+
+
+
+### v1.42.3 (2019-11-15)
+
+
+#### Bug Fixes
+
+* **verify**
+ * exit with status 0 if all pacts are in pending state ([2f7110b](/../../commit/2f7110b))
+
+
+
+### v1.42.2 (2019-11-09)
+
+
+#### Bug Fixes
+
+* remove missed &. ([be700d8](/../../commit/be700d8))
+
+
+
+### v1.42.1 (2019-11-09)
+
+
+#### Bug Fixes
+
+* can't use safe navigation operator because of Ruby 2.2 in Travelling Ruby for the pact-ruby-standalone ([3068ceb](/../../commit/3068ceb))
+
+
+
+### v1.42.0 (2019-09-26)
+
+
+#### Features
+
+* use new 'pacts for verification' endpoint to retrieve pacts (#199) ([55bb935](/../../commit/55bb935))
+
+
+
+### v1.41.2 (2019-09-10)
+
+
+#### Bug Fixes
+
+* **pact_helper_locator**
+ * add 'test' dir to file patterns (#196) ([746883d](/../../commit/746883d))
+
+* file upload spec ([0fe072c](/../../commit/0fe072c))
+
+
+
+### v1.41.1 (2019-09-04)
+
+
+#### Bug Fixes
+
+* use to_json instead of JSON.dump because it generates different JSON when used in conjuction with other libraries (eg. Oj) ([14566fb](/../../commit/14566fb))
+
+
+
+### v1.41.0 (2019-05-22)
+
+
+#### Features
+
+* redact Authorization header from HTTP client debug output ([c48c991](/../../commit/c48c991))
+
+
+
+### v1.40.0 (2019-02-22)
+
+
+#### Features
+
+* remove ruby 2.2 tests ([4a30791](/../../commit/4a30791))
+* add support for bearer token ([297268d](/../../commit/297268d))
+
+
+
+### v1.39.0 (2019-02-21)
+
+
+#### Features
+
+* allow host of mock service to be specified ([de267bd](/../../commit/de267bd))
+
+
+
+### v1.38.0 (2019-02-11)
+
+
+#### Features
+
+* unlock rack-test dependency to allow version 1.1.0 ([b0c40f6](/../../commit/b0c40f6))
+* update http client code ([bba3a08](/../../commit/bba3a08))
+
+
### v1.37.0 (2018-11-15)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d4d7e71e..7e805dcd 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,7 +11,7 @@ Please provide the following information with your issue to enable us to respond
1. Fork it
2. Create your feature branch (`git checkout -b feat/my-new-feature`)
-3. Commit your changes. **Please use the contentional changelog format for [sematic commit messages](http://karma-runner.github.io/1.0/dev/git-commit-msg.html)** (`git commit -am 'feat(some new feat): add a thing'`)
+3. Commit your changes. **Please use the conventional changelog format for [semantic commit messages](http://karma-runner.github.io/1.0/dev/git-commit-msg.html)** (`git commit -am 'feat(some new feat): add a thing'`)
4. Push to the branch (`git push origin feat/my-new-feature`)
5. Create new Pull Request
diff --git a/Gemfile b/Gemfile
index b324a79a..f2371c89 100644
--- a/Gemfile
+++ b/Gemfile
@@ -7,4 +7,8 @@ if ENV['X_PACT_DEVELOPMENT']
gem "pact-support", path: '../pact-support'
gem "pact-mock_service", path: '../pact-mock_service'
gem "pry-byebug"
-end
\ No newline at end of file
+end
+
+group :local_development do
+ gem "pry-byebug"
+end
diff --git a/README.md b/README.md
index 81169a55..1ec32987 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
# Pact
-[](https://travis-ci.org/pact-foundation/pact-ruby)
+
[](#backers)
[](#sponsors)
@@ -85,7 +85,7 @@ We're going to write an integration, with Pact tests, between a consumer, the Zo
#### 1. Start with your model
-Imagine a model class that looks something like this. The attributes for a Alligator live on a remote server, and will need to be retrieved by an HTTP call to the Animal Service.
+Imagine a model class that looks something like this. The attributes for an Alligator live on a remote server, and will need to be retrieved by an HTTP call to the Animal Service.
```ruby
class Alligator
@@ -131,6 +131,7 @@ Pact.service_consumer "Zoo App" do
has_pact_with "Animal Service" do
mock_service :animal_service do
port 1234
+ host "..." # optional, defaults to "localhost"
end
end
end
@@ -165,7 +166,7 @@ describe AnimalServiceClient, :pact => true do
body: {name: 'Betty'} )
end
- it "returns a alligator" do
+ it "returns an alligator" do
expect(subject.get_alligator).to eq(Alligator.new('Betty'))
end
@@ -305,7 +306,7 @@ Currently, Ruby Pact supports writing Pacts in v2, and verifying Pacts in v3 for
## Links
-[Simplifying microservices testing with pacts](http://dius.com.au/2014/05/20/simplifying-micro-service-testing-with-pacts/) - Ron Holshausen (one of the original pact authors)
+[Simplifying microservices testing with pacts](http://dius.com.au/2014/05/19/simplifying-micro-service-testing-with-pacts/) - Ron Holshausen (one of the original pact authors)
[Pact specification](https://github.com/pact-foundation/pact-specification)
diff --git a/ROADMAP.md b/ROADMAP.md
index 501154b7..65943c69 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -6,4 +6,4 @@
## Long term
* Add XML support
-* Create a test matrix to ensure compatiblity with implementations in other languages
+* Create a test matrix to ensure compatibility with implementations in other languages
diff --git a/Rakefile b/Rakefile
index 78539201..b6d716fd 100644
--- a/Rakefile
+++ b/Rakefile
@@ -6,5 +6,5 @@ require 'rspec/core/rake_task'
Dir.glob('./lib/tasks/**/*.rake').each { |task| load task }
Dir.glob('./tasks/**/*.rake').each { |task| load task }
-task :default => [:spec, 'spec:provider', 'pact:tests:all']
+task :default => [:spec, :spec_with_active_support, 'spec:provider', 'pact:tests:all']
diff --git a/example/animal-service/Gemfile b/example/animal-service/Gemfile
index fd0d85cf..00445e10 100644
--- a/example/animal-service/Gemfile
+++ b/example/animal-service/Gemfile
@@ -7,8 +7,7 @@ group :development, :test do
end
gem 'rake'
-gem 'rack', '~> 2.0'
-gem 'json', '~>1.8'
+gem 'rack', '~> 2.1'
gem 'sqlite3'
gem 'sequel'
gem 'sinatra'
diff --git a/example/animal-service/Gemfile.lock b/example/animal-service/Gemfile.lock
index e0ffc240..37164e54 100644
--- a/example/animal-service/Gemfile.lock
+++ b/example/animal-service/Gemfile.lock
@@ -1,91 +1,89 @@
PATH
remote: ../..
specs:
- pact (1.22.2)
- json (> 1.8.5)
- pact-mock_service (~> 2.0)
- pact-support (~> 1.4)
- rack-test (~> 0.6, >= 0.6.3)
- randexp (~> 0.1.7)
- rspec (>= 2.14)
+ pact (1.53.0)
+ pact-mock_service (~> 3.0, >= 3.3.1)
+ pact-support (~> 1.9)
+ rack-test (>= 0.6.3, < 2.0.0)
+ rspec (~> 3.0)
term-ansicolor (~> 1.0)
- thor
- webrick
+ thor (>= 0.20, < 2.0)
+ webrick (~> 1.3)
GEM
remote: https://rubygems.org/
specs:
awesome_print (1.8.0)
- coderay (1.1.2)
- diff-lcs (1.3)
+ coderay (1.1.3)
+ diff-lcs (1.4.4)
filelock (1.1.1)
find_a_port (1.0.1)
- json (1.8.6)
- method_source (0.9.0)
- mustermann (1.0.2)
- pact-mock_service (2.6.4)
+ json (2.3.1)
+ method_source (1.0.0)
+ mustermann (1.1.1)
+ ruby2_keywords (~> 0.0.1)
+ pact-mock_service (3.6.2)
filelock (~> 1.1)
find_a_port (~> 1.0.1)
json
- pact-support (~> 1.2, >= 1.2.1)
+ pact-support (~> 1.12, >= 1.12.0)
rack (~> 2.0)
rspec (>= 2.14)
term-ansicolor (~> 1.0)
- thor (~> 0.19)
+ thor (>= 0.19, < 2.0)
webrick (~> 1.3)
- pact-support (1.6.0)
+ pact-support (1.15.1)
awesome_print (~> 1.1)
- find_a_port (~> 1.0.1)
- json
randexp (~> 0.1.7)
rspec (>= 2.14)
term-ansicolor (~> 1.0)
- thor
- pry (0.11.3)
- coderay (~> 1.1.0)
- method_source (~> 0.9.0)
- rack (2.0.4)
- rack-protection (2.0.1)
+ pry (0.13.1)
+ coderay (~> 1.1)
+ method_source (~> 1.0)
+ rack (2.2.3)
+ rack-protection (2.1.0)
rack
- rack-test (0.8.3)
+ rack-test (1.1.0)
rack (>= 1.0, < 3)
- rake (12.3.1)
+ rake (13.0.1)
randexp (0.1.7)
- rspec (3.7.0)
- rspec-core (~> 3.7.0)
- rspec-expectations (~> 3.7.0)
- rspec-mocks (~> 3.7.0)
- rspec-core (3.7.1)
- rspec-support (~> 3.7.0)
- rspec-expectations (3.7.0)
+ rspec (3.9.0)
+ rspec-core (~> 3.9.0)
+ rspec-expectations (~> 3.9.0)
+ rspec-mocks (~> 3.9.0)
+ rspec-core (3.9.2)
+ rspec-support (~> 3.9.3)
+ rspec-expectations (3.9.2)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.7.0)
- rspec-mocks (3.7.0)
+ rspec-support (~> 3.9.0)
+ rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.7.0)
- rspec-support (3.7.1)
- sequel (5.7.1)
- sinatra (2.0.1)
+ rspec-support (~> 3.9.0)
+ rspec-support (3.9.3)
+ ruby2_keywords (0.0.2)
+ sequel (5.36.0)
+ sinatra (2.1.0)
mustermann (~> 1.0)
- rack (~> 2.0)
- rack-protection (= 2.0.1)
+ rack (~> 2.2)
+ rack-protection (= 2.1.0)
tilt (~> 2.0)
- sqlite3 (1.3.13)
- term-ansicolor (1.6.0)
+ sqlite3 (1.4.2)
+ sync (0.5.0)
+ term-ansicolor (1.7.1)
tins (~> 1.0)
- thor (0.20.0)
- tilt (2.0.8)
- tins (1.16.3)
- webrick (1.4.2)
+ thor (1.0.1)
+ tilt (2.0.10)
+ tins (1.25.0)
+ sync
+ webrick (1.6.0)
PLATFORMS
ruby
DEPENDENCIES
- json (~> 1.8)
pact!
pry
- rack (~> 2.0)
+ rack (~> 2.1)
rake
rspec
sequel
@@ -93,4 +91,4 @@ DEPENDENCIES
sqlite3
BUNDLED WITH
- 1.15.4
+ 2.0.2
diff --git a/example/zoo-app/Gemfile b/example/zoo-app/Gemfile
index dfa52213..d35c1500 100644
--- a/example/zoo-app/Gemfile
+++ b/example/zoo-app/Gemfile
@@ -8,5 +8,5 @@ group :development, :test do
end
gem 'rake'
-gem 'rack', '~>2.0'
+gem 'rack', '~>2.1'
gem 'httparty'
diff --git a/example/zoo-app/Gemfile.lock b/example/zoo-app/Gemfile.lock
index 9298ac9d..37143d82 100644
--- a/example/zoo-app/Gemfile.lock
+++ b/example/zoo-app/Gemfile.lock
@@ -1,82 +1,82 @@
PATH
remote: ../..
specs:
- pact (1.36.2)
- json (> 1.8.5)
- pact-mock_service (~> 2.10)
- pact-support (~> 1.8)
- rack-test (~> 0.6, >= 0.6.3)
- randexp (~> 0.1.7)
- rspec (>= 2.14)
+ pact (1.53.0)
+ pact-mock_service (~> 3.0, >= 3.3.1)
+ pact-support (~> 1.9)
+ rack-test (>= 0.6.3, < 2.0.0)
+ rspec (~> 3.0)
term-ansicolor (~> 1.0)
- thor
- webrick
+ thor (>= 0.20, < 2.0)
+ webrick (~> 1.3)
GEM
remote: https://rubygems.org/
specs:
awesome_print (1.8.0)
- coderay (1.1.2)
- diff-lcs (1.3)
+ coderay (1.1.3)
+ diff-lcs (1.4.4)
filelock (1.1.1)
find_a_port (1.0.1)
- httparty (0.16.2)
+ httparty (0.18.1)
+ mime-types (~> 3.0)
multi_xml (>= 0.5.2)
- json (2.1.0)
- method_source (0.9.0)
+ json (2.3.1)
+ method_source (1.0.0)
+ mime-types (3.3.1)
+ mime-types-data (~> 3.2015)
+ mime-types-data (3.2020.0512)
multi_xml (0.6.0)
- pact-mock_service (2.12.0)
+ pact-mock_service (3.6.2)
filelock (~> 1.1)
find_a_port (~> 1.0.1)
json
- pact-support (~> 1.2, >= 1.2.1)
+ pact-support (~> 1.12, >= 1.12.0)
rack (~> 2.0)
rspec (>= 2.14)
term-ansicolor (~> 1.0)
- thor (~> 0.19)
+ thor (>= 0.19, < 2.0)
webrick (~> 1.3)
- pact-support (1.8.0)
+ pact-support (1.15.1)
awesome_print (~> 1.1)
- find_a_port (~> 1.0.1)
- json
randexp (~> 0.1.7)
rspec (>= 2.14)
term-ansicolor (~> 1.0)
- thor
- pact_broker-client (1.16.2)
- httparty
- json
- rake
+ pact_broker-client (1.29.1)
+ httparty (~> 0.18)
+ rake (~> 13.0)
table_print (~> 1.5)
- term-ansicolor
+ term-ansicolor (~> 1.7)
thor (~> 0.20)
- pry (0.11.3)
- coderay (~> 1.1.0)
- method_source (~> 0.9.0)
- rack (2.0.5)
- rack-test (0.8.3)
+ pry (0.13.1)
+ coderay (~> 1.1)
+ method_source (~> 1.0)
+ rack (2.2.3)
+ rack-test (1.1.0)
rack (>= 1.0, < 3)
- rake (12.3.1)
+ rake (13.0.1)
randexp (0.1.7)
- rspec (3.8.0)
- rspec-core (~> 3.8.0)
- rspec-expectations (~> 3.8.0)
- rspec-mocks (~> 3.8.0)
- rspec-core (3.8.0)
- rspec-support (~> 3.8.0)
- rspec-expectations (3.8.2)
+ rspec (3.9.0)
+ rspec-core (~> 3.9.0)
+ rspec-expectations (~> 3.9.0)
+ rspec-mocks (~> 3.9.0)
+ rspec-core (3.9.2)
+ rspec-support (~> 3.9.3)
+ rspec-expectations (3.9.2)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.8.0)
- rspec-mocks (3.8.0)
+ rspec-support (~> 3.9.0)
+ rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.8.0)
- rspec-support (3.8.0)
- table_print (1.5.6)
- term-ansicolor (1.6.0)
+ rspec-support (~> 3.9.0)
+ rspec-support (3.9.3)
+ sync (0.5.0)
+ table_print (1.5.7)
+ term-ansicolor (1.7.1)
tins (~> 1.0)
- thor (0.20.0)
- tins (1.17.0)
- webrick (1.4.2)
+ thor (0.20.3)
+ tins (1.25.0)
+ sync
+ webrick (1.6.0)
PLATFORMS
ruby
@@ -86,9 +86,9 @@ DEPENDENCIES
pact!
pact_broker-client
pry
- rack (~> 2.0)
+ rack (~> 2.1)
rake
rspec
BUNDLED WITH
- 1.16.2
+ 2.0.2
diff --git a/gemfiles/default.gemfile b/gemfiles/default.gemfile
deleted file mode 100644
index 095e6608..00000000
--- a/gemfiles/default.gemfile
+++ /dev/null
@@ -1,5 +0,0 @@
-# This file was generated by Appraisal
-
-source "https://rubygems.org"
-
-gemspec path: "../"
diff --git a/gemfiles/rspec_2.gemfile b/gemfiles/rspec_2.gemfile
deleted file mode 100644
index 9b17f72a..00000000
--- a/gemfiles/rspec_2.gemfile
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file was generated by Appraisal
-
-source "https://rubygems.org"
-
-gem "rspec", "2.14.1"
-
-gemspec path: "../"
diff --git a/gemfiles/rspec_3.0.gemfile b/gemfiles/rspec_3.0.gemfile
deleted file mode 100644
index deb36d2f..00000000
--- a/gemfiles/rspec_3.0.gemfile
+++ /dev/null
@@ -1,7 +0,0 @@
-# This file was generated by Appraisal
-
-source "https://rubygems.org"
-
-gem "rspec", "3.0.0"
-
-gemspec path: "../"
diff --git a/gemfiles/ruby_under_22.gemfile b/gemfiles/ruby_under_22.gemfile
deleted file mode 100644
index a22132eb..00000000
--- a/gemfiles/ruby_under_22.gemfile
+++ /dev/null
@@ -1,9 +0,0 @@
-# This file was generated by Appraisal
-
-source "https://rubygems.org"
-
-gem "rack", "< 2.0"
-gem "rack-test", "0.6.3"
-gem "activesupport", "< 5.0.0"
-
-gemspec path: "../"
diff --git a/gemfiles/ruby_under_22_with_rspec_2.gemfile b/gemfiles/ruby_under_22_with_rspec_2.gemfile
deleted file mode 100644
index a21df676..00000000
--- a/gemfiles/ruby_under_22_with_rspec_2.gemfile
+++ /dev/null
@@ -1,10 +0,0 @@
-# This file was generated by Appraisal
-
-source "https://rubygems.org"
-
-gem "rspec", "2.14.1"
-gem "rack", "< 2.0"
-gem "rack-test", "0.6.3"
-gem "activesupport", "< 5.0.0"
-
-gemspec path: "../"
diff --git a/lib/pact/cli.rb b/lib/pact/cli.rb
index ba2ee8c5..5541139e 100755
--- a/lib/pact/cli.rb
+++ b/lib/pact/cli.rb
@@ -4,6 +4,9 @@
module Pact
class CLI < Thor
+ def self.exit_on_failure? # Thor 1.0 deprecation guard
+ false
+ end
desc 'verify', "Verify a pact"
method_option :pact_helper, aliases: "-h", desc: "Pact helper file", :required => true
@@ -11,12 +14,16 @@ class CLI < Thor
method_option :ignore_failures, type: :boolean, default: false, desc: "Process will always exit with exit code 0", hide: true
method_option :pact_broker_username, aliases: "-u", desc: "Pact broker user name"
method_option :pact_broker_password, aliases: "-w", desc: "Pact broker password"
+ method_option :pact_broker_token, aliases: "-k", desc: "Pact broker token"
method_option :backtrace, aliases: "-b", desc: "Show full backtrace", :default => false, :type => :boolean
+ method_option :verbose, aliases: "-v", desc: "Show verbose HTTP logging", :default => false, :type => :boolean
method_option :interactions_replay_order, aliases: "-o",
desc: "Interactions replay order: randomised or recorded (default)",
default: Pact.configuration.interactions_replay_order
method_option :description, aliases: "-d", desc: "Interaction description filter"
method_option :provider_state, aliases: "-s", desc: "Provider state filter"
+ method_option :interaction_index, type: :numeric, desc: "Index filter"
+ method_option :pact_broker_interaction_id, desc: "Pact Broker interaction ID filter"
method_option :format, aliases: "-f", banner: "FORMATTER", desc: "RSpec formatter. Defaults to custom Pact formatter. [j]son may also be used."
method_option :out, aliases: "-o", banner: "FILE", desc: "Write output to a file instead of $stdout."
diff --git a/lib/pact/cli/run_pact_verification.rb b/lib/pact/cli/run_pact_verification.rb
index 203f11ef..333120b5 100644
--- a/lib/pact/cli/run_pact_verification.rb
+++ b/lib/pact/cli/run_pact_verification.rb
@@ -3,7 +3,6 @@
module Pact
module Cli
class RunPactVerification
-
attr_reader :options
def initialize options
@@ -15,6 +14,7 @@ def self.call options
end
def call
+ configure_output
initialize_rspec
setup_load_path
load_pact_helper
@@ -27,6 +27,7 @@ def initialize_rspec
# With RSpec3, if the pact_helper loads a library that adds its own formatter before we set one,
# we will get a ProgressFormatter too, and get little dots sprinkled throughout our output.
# Load a NilFormatter here to prevent that.
+ require 'rspec'
require 'pact/rspec'
::RSpec.configuration.add_formatter Pact::RSpec.formatter_class.const_get('NilFormatter')
end
@@ -44,23 +45,32 @@ def load_pact_helper
end
def run_specs
- exit_code = if options[:pact_uri]
- run_with_pact_uri
+ exit_code = if options[:pact_uri].is_a?(String)
+ run_with_pact_url_string
+ elsif options[:pact_uri]
+ run_with_pact_uri_object # from pact-provider-verifier
else
- run_with_configured_pacts
+ run_with_configured_pacts_from_pact_helper
end
exit exit_code
end
- def run_with_pact_uri
+ def run_with_pact_url_string
pact_repository_uri_options = {}
+ pact_repository_uri_options[:username] = ENV['PACT_BROKER_USERNAME'] if ENV['PACT_BROKER_USERNAME']
+ pact_repository_uri_options[:password] = ENV['PACT_BROKER_PASSWORD'] if ENV['PACT_BROKER_PASSWORD']
+ pact_repository_uri_options[:token] = ENV['PACT_BROKER_TOKEN']
pact_repository_uri_options[:username] = options[:pact_broker_username] if options[:pact_broker_username]
pact_repository_uri_options[:password] = options[:pact_broker_password] if options[:pact_broker_password]
pact_uri = ::Pact::Provider::PactURI.new(options[:pact_uri], pact_repository_uri_options)
Pact::Provider::PactSpecRunner.new([pact_uri], pact_spec_options).run
end
- def run_with_configured_pacts
+ def run_with_pact_uri_object
+ Pact::Provider::PactSpecRunner.new([options[:pact_uri]], pact_spec_options).run
+ end
+
+ def run_with_configured_pacts_from_pact_helper
pact_urls = Pact.provider_world.pact_urls
raise "Please configure a pact to verify" if pact_urls.empty?
Pact::Provider::PactSpecRunner.new(pact_urls, pact_spec_options).run
@@ -69,6 +79,7 @@ def run_with_configured_pacts
def pact_spec_options
{
full_backtrace: options[:backtrace],
+ verbose: options[:verbose] || ENV['VERBOSE'] == 'true',
criteria: SpecCriteria.call(options),
format: options[:format],
out: options[:out],
@@ -76,6 +87,14 @@ def pact_spec_options
request_customizer: options[:request_customizer]
}
end
+
+ def configure_output
+ if options[:format] == 'json' && !options[:out]
+ # Don't want to mess up the JSON parsing with messages to stdout, so send it to stderr
+ require 'pact/configuration'
+ Pact.configuration.output_stream = Pact.configuration.error_stream
+ end
+ end
end
end
end
diff --git a/lib/pact/cli/spec_criteria.rb b/lib/pact/cli/spec_criteria.rb
index f9b84a2e..b2212af6 100644
--- a/lib/pact/cli/spec_criteria.rb
+++ b/lib/pact/cli/spec_criteria.rb
@@ -6,8 +6,11 @@ def self.call options
criteria = {}
criteria[:description] = Regexp.new(options[:description]) if options[:description]
+ criteria[:_id] = options[:pact_broker_interaction_id] if options[:pact_broker_interaction_id]
+ criteria[:index] = options[:interaction_index] if options[:interaction_index]
provider_state = options[:provider_state]
+
if provider_state
if provider_state.length == 0
criteria[:provider_state] = nil #Allow PACT_PROVIDER_STATE="" to mean no provider state
diff --git a/lib/pact/consumer/configuration/dsl.rb b/lib/pact/consumer/configuration/dsl.rb
index 6a212da0..3017c9f3 100644
--- a/lib/pact/consumer/configuration/dsl.rb
+++ b/lib/pact/consumer/configuration/dsl.rb
@@ -1,7 +1,6 @@
require 'pact/consumer/configuration/service_consumer'
module Pact
-
module Consumer
module DSL
def service_consumer name, &block
diff --git a/lib/pact/consumer/configuration/mock_service.rb b/lib/pact/consumer/configuration/mock_service.rb
index b4b4fd7f..f64d74d7 100644
--- a/lib/pact/consumer/configuration/mock_service.rb
+++ b/lib/pact/consumer/configuration/mock_service.rb
@@ -11,13 +11,14 @@ class MockService
extend Pact::DSL
- attr_accessor :port, :standalone, :verify, :provider_name, :consumer_name, :pact_specification_version
+ attr_accessor :port, :host, :standalone, :verify, :provider_name, :consumer_name, :pact_specification_version
def initialize name, consumer_name, provider_name
@name = name
@consumer_name = consumer_name
@provider_name = provider_name
@port = nil
+ @host = "localhost"
@standalone = false
@verify = true
@pact_specification_version = '2'
@@ -29,6 +30,10 @@ def port port
self.port = port
end
+ def host host
+ self.host = host
+ end
+
def standalone standalone
self.standalone = standalone
end
@@ -52,7 +57,7 @@ def finalize
private
def register_mock_service
- url = "http://localhost#{port.nil? ? '' : ":#{port}"}"
+ url = "http://#{host}#{port.nil? ? '' : ":#{port}"}"
ret = Pact::MockService::AppManager.instance.register_mock_service_for(provider_name, url, mock_service_options)
raise "pact-mock_service(v#{Pact::MockService::VERSION}) does not support 'find available port' feature" unless ret
@port = ret
@@ -71,6 +76,7 @@ def create_consumer_contract_builder
:provider_name => provider_name,
:pactfile_write_mode => Pact.configuration.pactfile_write_mode,
:port => port,
+ :host => host,
:pact_dir => Pact.configuration.pact_dir
}
Pact::Consumer::ConsumerContractBuilder.new consumer_contract_builder_fields
diff --git a/lib/pact/consumer/consumer_contract_builder.rb b/lib/pact/consumer/consumer_contract_builder.rb
index c186b7ce..be556d6b 100644
--- a/lib/pact/consumer/consumer_contract_builder.rb
+++ b/lib/pact/consumer/consumer_contract_builder.rb
@@ -21,8 +21,12 @@ def initialize(attributes)
pactfile_write_mode: attributes[:pactfile_write_mode].to_s,
pact_dir: attributes.fetch(:pact_dir)
}
- @mock_service_client = Pact::MockService::Client.new(attributes[:port])
- @mock_service_base_url = "http://localhost:#{attributes[:port]}"
+ @mock_service_client = Pact::MockService::Client.new(attributes[:port], attributes[:host])
+ @mock_service_base_url = "http://#{attributes[:host]}:#{attributes[:port]}"
+ end
+
+ def without_writing_to_pact
+ interaction_builder.without_writing_to_pact
end
def given(provider_state)
diff --git a/lib/pact/consumer/interaction_builder.rb b/lib/pact/consumer/interaction_builder.rb
index 7916b712..78398ea7 100644
--- a/lib/pact/consumer/interaction_builder.rb
+++ b/lib/pact/consumer/interaction_builder.rb
@@ -13,6 +13,12 @@ def initialize &block
@callback = block
end
+ def without_writing_to_pact
+ interaction.metadata ||= {}
+ interaction.metadata[:write_to_pact] = false
+ self
+ end
+
def upon_receiving description
@interaction.description = description
self
diff --git a/lib/pact/consumer/spec_hooks.rb b/lib/pact/consumer/spec_hooks.rb
index cf7c637b..8fa915a5 100644
--- a/lib/pact/consumer/spec_hooks.rb
+++ b/lib/pact/consumer/spec_hooks.rb
@@ -15,8 +15,8 @@ def before_all
def before_each example_description
Pact.consumer_world.register_pact_example_ran
Pact.configuration.logger.info "Clearing all expectations"
- Pact::MockService::AppManager.instance.ports_of_mock_services.each do | port |
- Pact::MockService::Client.clear_interactions port, example_description
+ Pact::MockService::AppManager.instance.urls_of_mock_services.each do | url |
+ Pact::MockService::Client.clear_interactions url, example_description
end
end
@@ -29,7 +29,7 @@ def after_each example_description
def after_suite
if Pact.consumer_world.any_pact_examples_ran?
- Pact.consumer_world.consumer_contract_builders.each { | c | c.write_pact }
+ Pact.consumer_world.consumer_contract_builders.each(&:write_pact)
Pact::Doc::Generate.call
Pact::MockService::AppManager.instance.kill_all
Pact::MockService::AppManager.instance.clear_all
@@ -37,4 +37,4 @@ def after_suite
end
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/pact/doc/sort_interactions.rb b/lib/pact/doc/sort_interactions.rb
index 089f033c..398c11f2 100644
--- a/lib/pact/doc/sort_interactions.rb
+++ b/lib/pact/doc/sort_interactions.rb
@@ -3,7 +3,7 @@ module Doc
class SortInteractions
def self.call interactions
- interactions.sort{|a, b| sortable_id(a) <=> sortable_id(b)}
+ interactions.sort_by { |interaction| sortable_id(interaction) }
end
private
@@ -11,7 +11,6 @@ def self.call interactions
def self.sortable_id interaction
"#{interaction.description.downcase} #{interaction.response.status} #{(interaction.provider_state || '').downcase}"
end
-
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/pact/hal/authorization_header_redactor.rb b/lib/pact/hal/authorization_header_redactor.rb
new file mode 100644
index 00000000..a0c37537
--- /dev/null
+++ b/lib/pact/hal/authorization_header_redactor.rb
@@ -0,0 +1,32 @@
+require 'delegate'
+
+module Pact
+ module Hal
+ class AuthorizationHeaderRedactor < SimpleDelegator
+ def puts(*args)
+ __getobj__().puts(*redact_args(args))
+ end
+
+ def print(*args)
+ __getobj__().puts(*redact_args(args))
+ end
+
+ def <<(*args)
+ __getobj__().send(:<<, *redact_args(args))
+ end
+
+ private
+
+ attr_reader :redactions
+
+ def redact_args(args)
+ args.collect{ | s| redact(s) }
+ end
+
+ def redact(string)
+ return string unless string.is_a?(String)
+ string.gsub(/Authorization: .*\\r\\n/, "Authorization: [redacted]\\r\\n")
+ end
+ end
+ end
+end
diff --git a/lib/pact/hal/http_client.rb b/lib/pact/hal/http_client.rb
index 21428439..1e78b22d 100644
--- a/lib/pact/hal/http_client.rb
+++ b/lib/pact/hal/http_client.rb
@@ -1,21 +1,27 @@
require 'pact/retry'
+require 'pact/hal/authorization_header_redactor'
require 'net/http'
+require 'rack'
+require 'openssl'
module Pact
module Hal
class HttpClient
- attr_accessor :username, :password, :verbose
+ attr_accessor :username, :password, :verbose, :token
def initialize options
@username = options[:username]
@password = options[:password]
@verbose = options[:verbose]
+ @token = options[:token]
end
def get href, params = {}, headers = {}
- query = params.collect{ |(key, value)| "#{CGI::escape(key)}=#{CGI::escape(value)}" }.join("&")
uri = URI(href)
- uri.query = query
+ if params && params.any?
+ existing_params = Rack::Utils.parse_nested_query(uri.query)
+ uri.query = Rack::Utils.build_nested_query(existing_params.merge(params))
+ end
perform_request(create_request(uri, 'Get', nil, headers), uri)
end
@@ -31,22 +37,28 @@ def post href, body = nil, headers = {}
def create_request uri, http_method, body = nil, headers = {}
request = Net::HTTP.const_get(http_method).new(uri.request_uri)
- request['Content-Type'] = "application/json" if ['Post', 'Put', 'Patch'].include?(http_method)
- request['Accept'] = "application/hal+json"
headers.each do | key, value |
request[key] = value
end
-
request.body = body if body
request.basic_auth username, password if username
+ request['Authorization'] = "Bearer #{token}" if token
request
end
def perform_request request, uri
response = Retry.until_true do
http = Net::HTTP.new(uri.host, uri.port, :ENV)
- http.set_debug_output(Pact.configuration.output_stream) if verbose
+ http.set_debug_output(output_stream) if verbose?
http.use_ssl = (uri.scheme == 'https')
+ http.ca_file = ENV['SSL_CERT_FILE'] if ENV['SSL_CERT_FILE'] && ENV['SSL_CERT_FILE'] != ''
+ http.ca_path = ENV['SSL_CERT_DIR'] if ENV['SSL_CERT_DIR'] && ENV['SSL_CERT_DIR'] != ''
+ if disable_ssl_verification?
+ if verbose?
+ Pact.configuration.output_stream.puts("SSL verification is disabled")
+ end
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
http.start do |http|
http.request request
end
@@ -54,6 +66,18 @@ def perform_request request, uri
Response.new(response)
end
+ def output_stream
+ AuthorizationHeaderRedactor.new(Pact.configuration.output_stream)
+ end
+
+ def verbose?
+ verbose || ENV['VERBOSE'] == 'true'
+ end
+
+ def disable_ssl_verification?
+ ENV['PACT_DISABLE_SSL_VERIFICATION'] == 'true' || ENV['PACT_BROKER_DISABLE_SSL_VERIFICATION'] == 'true'
+ end
+
class Response < SimpleDelegator
def body
bod = raw_body
@@ -68,9 +92,17 @@ def raw_body
__getobj__().body
end
+ def status
+ code.to_i
+ end
+
def success?
__getobj__().code.start_with?("2")
end
+
+ def json?
+ self['content-type'] && self['content-type'] =~ /json/
+ end
end
end
end
diff --git a/lib/pact/hal/link.rb b/lib/pact/hal/link.rb
index 24e8cafb..d248b432 100644
--- a/lib/pact/hal/link.rb
+++ b/lib/pact/hal/link.rb
@@ -6,6 +6,15 @@ module Hal
class Link
attr_reader :request_method, :href
+ DEFAULT_GET_HEADERS = {
+ "Accept" => "application/hal+json"
+ }.freeze
+
+ DEFAULT_POST_HEADERS = {
+ "Accept" => "application/hal+json",
+ "Content-Type" => "application/json"
+ }.freeze
+
def initialize(attrs, http_client)
@attrs = attrs
@request_method = attrs.fetch(:method, :get).to_sym
@@ -14,40 +23,80 @@ def initialize(attrs, http_client)
end
def run(payload = nil)
- response = case request_method
- when :get
- get(payload)
- when :put
- put(payload)
- when :post
- post(payload)
- end
+ case request_method
+ when :get
+ get(payload)
+ when :put
+ put(payload)
+ when :post
+ post(payload)
+ end
+ end
+
+ def title_or_name
+ title || name
+ end
+
+ def title
+ @attrs['title']
+ end
+
+ def name
+ @attrs['name']
end
def get(payload = {}, headers = {})
- wrap_response(href, @http_client.get(href, payload, headers))
+ wrap_response(href, @http_client.get(href, payload, DEFAULT_GET_HEADERS.merge(headers)))
+ end
+
+ def get!(*args)
+ get(*args).assert_success!
end
def put(payload = nil, headers = {})
- wrap_response(href, @http_client.put(href, payload ? JSON.dump(payload) : nil, headers))
+ wrap_response(href, @http_client.put(href, payload ? payload.to_json : nil, DEFAULT_POST_HEADERS.merge(headers)))
end
def post(payload = nil, headers = {})
- wrap_response(href, @http_client.post(href, payload ? JSON.dump(payload) : nil, headers))
+ wrap_response(href, @http_client.post(href, payload ? payload.to_json : nil, DEFAULT_POST_HEADERS.merge(headers)))
+ end
+
+ def post!(payload = nil, headers = {})
+ post(payload, headers).assert_success!
end
def expand(params)
expanded_url = expand_url(params, href)
new_attrs = @attrs.merge('href' => expanded_url)
- Link.new(new_attrs, @http_client)
+ Link.new(new_attrs, http_client)
+ end
+
+ def with_query(query)
+ if query && query.any?
+ uri = URI(href)
+ existing_query_params = Rack::Utils.parse_nested_query(uri.query)
+ uri.query = Rack::Utils.build_nested_query(existing_query_params.merge(query))
+ new_attrs = attrs.merge('href' => uri.to_s)
+ Link.new(new_attrs, http_client)
+ else
+ self
+ end
end
private
+ attr_reader :attrs, :http_client
+
def wrap_response(href, http_response)
require 'pact/hal/entity' # avoid circular reference
+ require 'pact/hal/non_json_entity'
+
if http_response.success?
- Entity.new(href, http_response.body, @http_client, http_response)
+ if http_response.json?
+ Entity.new(href, http_response.body, @http_client, http_response)
+ else
+ NonJsonEntity.new(href, http_response.raw_body, @http_client, http_response)
+ end
else
ErrorEntity.new(href, http_response.raw_body, @http_client, http_response)
end
diff --git a/lib/pact/hal/non_json_entity.rb b/lib/pact/hal/non_json_entity.rb
new file mode 100644
index 00000000..83c96da7
--- /dev/null
+++ b/lib/pact/hal/non_json_entity.rb
@@ -0,0 +1,28 @@
+module Pact
+ module Hal
+ class NonJsonEntity
+ def initialize(href, body, http_client, response = nil)
+ @href = href
+ @body = body
+ @client = http_client
+ @response = response
+ end
+
+ def success?
+ true
+ end
+
+ def response
+ @response
+ end
+
+ def body
+ @body
+ end
+
+ def assert_success!
+ self
+ end
+ end
+ end
+end
diff --git a/lib/pact/hash_refinements.rb b/lib/pact/hash_refinements.rb
new file mode 100644
index 00000000..3e738521
--- /dev/null
+++ b/lib/pact/hash_refinements.rb
@@ -0,0 +1,17 @@
+module Pact
+ module HashRefinements
+ refine Hash do
+ def compact
+ h = {}
+ each do |key, value|
+ h[key] = value unless value == nil
+ end
+ h
+ end unless Hash.method_defined? :compact
+
+ def compact!
+ reject! {|_key, value| value == nil}
+ end unless Hash.method_defined? :compact!
+ end
+ end
+end
diff --git a/lib/pact/pact_broker.rb b/lib/pact/pact_broker.rb
index 45293c07..c91f7ed2 100644
--- a/lib/pact/pact_broker.rb
+++ b/lib/pact/pact_broker.rb
@@ -1,19 +1,25 @@
require 'pact/pact_broker/fetch_pacts'
-require 'pact/pact_broker/fetch_pending_pacts'
+require 'pact/pact_broker/fetch_pact_uris_for_verification'
+require 'pact/provider/pact_uri'
#
-# @public Use by Pact Provider Verifier
+# @public Used by Pact Provider Verifier
#
module Pact
module PactBroker
extend self
+ # Keep for backwards compatibility with pact-provider-verifier < 1.23.1
def fetch_pact_uris *args
Pact::PactBroker::FetchPacts.call(*args).collect(&:uri)
end
- def fetch_pending_pact_uris *args
- Pact::PactBroker::FetchPendingPacts.call(*args).collect(&:uri)
+ def fetch_pact_uris_for_verification *args
+ Pact::PactBroker::FetchPactURIsForVerification.call(*args)
+ end
+
+ def build_pact_uri(*args)
+ Pact::Provider::PactURI.new(*args)
end
end
end
diff --git a/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb b/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb
new file mode 100644
index 00000000..631562d3
--- /dev/null
+++ b/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb
@@ -0,0 +1,101 @@
+require 'pact/hal/entity'
+require 'pact/hal/http_client'
+require 'pact/provider/pact_uri'
+require 'pact/errors'
+require 'pact/pact_broker/fetch_pacts'
+require 'pact/pact_broker/notices'
+require 'pact/pact_broker/pact_selection_description'
+require "pact/hash_refinements"
+
+module Pact
+ module PactBroker
+ class FetchPactURIsForVerification
+ using Pact::HashRefinements
+
+ include PactSelectionDescription
+ attr_reader :provider, :consumer_version_selectors, :provider_version_branch, :provider_version_tags, :broker_base_url, :http_client_options, :http_client, :options
+
+ PACTS_FOR_VERIFICATION_RELATION = 'pb:provider-pacts-for-verification'.freeze
+ PACTS_FOR_VERIFICATION_RELATION_BETA = 'beta:provider-pacts-for-verification'.freeze
+ PACTS = 'pacts'.freeze
+ HREF = 'href'.freeze
+ LINKS = '_links'.freeze
+ SELF = 'self'.freeze
+ EMBEDDED = '_embedded'.freeze
+
+ def initialize(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options, options = {})
+ @provider = provider
+ @consumer_version_selectors = consumer_version_selectors || []
+ @provider_version_branch = provider_version_branch
+ @provider_version_tags = [*provider_version_tags]
+ @http_client_options = http_client_options
+ @broker_base_url = broker_base_url
+ @http_client = Pact::Hal::HttpClient.new(http_client_options)
+ @options = options
+ end
+
+ def self.call(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options, options = {})
+ new(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options, options).call
+ end
+
+ def call
+ if index.can?(PACTS_FOR_VERIFICATION_RELATION) || index.can?(PACTS_FOR_VERIFICATION_RELATION_BETA)
+ log_message
+ pacts_for_verification
+ else
+ old_selectors = consumer_version_selectors.collect do | selector |
+ { name: selector[:tag], all: !selector[:latest], fallback: selector[:fallbackTag]}
+ end
+ # Fall back to old method of fetching pacts
+ FetchPacts.call(provider, old_selectors, broker_base_url, http_client_options)
+ end
+ end
+
+ private
+
+ def index
+ @index_entity ||= Pact::Hal::Link.new({ "href" => broker_base_url }, http_client).get.assert_success!
+ end
+
+ def pacts_for_verification
+ pacts_for_verification_entity.response.body[EMBEDDED][PACTS].collect do | pact |
+ metadata = {
+ pending: pact["verificationProperties"]["pending"],
+ notices: extract_notices(pact),
+ short_description: pact["shortDescription"]
+ }
+ Pact::Provider::PactURI.new(pact[LINKS][SELF][HREF], http_client_options, metadata)
+ end
+ end
+
+ def pacts_for_verification_entity
+ index
+ ._link(PACTS_FOR_VERIFICATION_RELATION, PACTS_FOR_VERIFICATION_RELATION_BETA)
+ .expand(provider: provider)
+ .post!(query)
+ end
+
+ def query
+ q = {}
+ q["includePendingStatus"] = options[:include_pending_status]
+ q["consumerVersionSelectors"] = consumer_version_selectors if consumer_version_selectors.any?
+ q["providerVersionTags"] = provider_version_tags if provider_version_tags.any?
+ q["providerVersionBranch"] = provider_version_branch
+ q["includeWipPactsSince"] = options[:include_wip_pacts_since]
+ q.compact
+ end
+
+ def extract_notices(pact)
+ Notices.new((pact["verificationProperties"]["notices"] || []).collect{ |notice| symbolize_keys(notice) })
+ end
+
+ def symbolize_keys(hash)
+ hash.each_with_object({}){ |(k,v), h| h[k.to_sym] = v }
+ end
+
+ def log_message
+ Pact.configuration.output_stream.puts "INFO: #{pact_selection_description(provider, consumer_version_selectors, options, broker_base_url)}"
+ end
+ end
+ end
+end
diff --git a/lib/pact/pact_broker/fetch_pending_pacts.rb b/lib/pact/pact_broker/fetch_pending_pacts.rb
deleted file mode 100644
index 10faee5d..00000000
--- a/lib/pact/pact_broker/fetch_pending_pacts.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require 'pact/hal/entity'
-require 'pact/hal/http_client'
-require 'pact/provider/pact_uri'
-require 'pact/errors'
-
-module Pact
- module PactBroker
- class FetchPendingPacts
- attr_reader :provider, :tags, :broker_base_url, :http_client_options, :http_client, :index_entity
-
- PENDING_PROVIDER_RELATION = 'beta:pending-provider-pacts'.freeze
- WIP_PROVIDER_RELATION = 'beta:wip-provider-pacts'.freeze # deprecated
- PACTS = 'pacts'.freeze
- PB_PACTS = 'pb:pacts'.freeze
- HREF = 'href'.freeze
-
- def initialize(provider, broker_base_url, http_client_options)
- @provider = provider
- @http_client_options = http_client_options
- @broker_base_url = broker_base_url
- @http_client = Pact::Hal::HttpClient.new(http_client_options)
- end
-
- def self.call(provider, broker_base_url, http_client_options)
- new(provider, broker_base_url, http_client_options).call
- end
-
- def call
- if index.success?
- pending_pacts_for_provider
- else
- raise Pact::Error.new("Error retrieving #{broker_base_url} status=#{index_entity.response.code} #{index_entity.response.raw_body}")
- end
- end
-
- private
-
- def index
- @index_entity ||= Pact::Hal::Link.new({ "href" => broker_base_url }, http_client).get.assert_success!
- end
-
- def pending_pacts_for_provider
- link = index_entity._link(WIP_PROVIDER_RELATION, PENDING_PROVIDER_RELATION)
- if link
- get_pact_urls(link.expand(provider: provider).get)
- else
- []
- end
- end
-
- def get_pact_urls(link_by_provider)
- link_by_provider.fetch(PB_PACTS, PACTS).collect do |pact|
- Pact::Provider::PactURI.new(pact[HREF], http_client_options)
- end
- end
- end
- end
-end
diff --git a/lib/pact/pact_broker/notices.rb b/lib/pact/pact_broker/notices.rb
new file mode 100644
index 00000000..4d854bcf
--- /dev/null
+++ b/lib/pact/pact_broker/notices.rb
@@ -0,0 +1,34 @@
+module Pact
+ module PactBroker
+ class Notices < Array
+ def before_verification_notices
+ select { | notice | notice[:when].nil? || notice[:when].start_with?('before_verification') }
+ end
+
+ def before_verification_notices_text
+ before_verification_notices.collect{ | notice | notice[:text] }
+ end
+
+ def after_verification_notices(success, published)
+ select { | notice | notice[:when] == "after_verification:success_#{success}_published_#{published}" || notice[:when] == "after_verification" }
+ .collect do | notice |
+ notice.merge(:when => simplify_notice_when(notice[:when]))
+ end
+ end
+
+ def after_verification_notices_text(success, published)
+ after_verification_notices(success, published).collect{ | notice | notice[:text] }
+ end
+
+ def all_notices(success, published)
+ before_verification_notices + after_verification_notices(success, published)
+ end
+
+ private
+
+ def simplify_notice_when(when_key)
+ when_key.split(":").first
+ end
+ end
+ end
+end
diff --git a/lib/pact/pact_broker/pact_selection_description.rb b/lib/pact/pact_broker/pact_selection_description.rb
new file mode 100644
index 00000000..6b6a8110
--- /dev/null
+++ b/lib/pact/pact_broker/pact_selection_description.rb
@@ -0,0 +1,66 @@
+module Pact
+ module PactBroker
+ module PactSelectionDescription
+ def pact_selection_description(provider, consumer_version_selectors, options, broker_base_url)
+ message = "Fetching pacts for #{provider} from #{broker_base_url} with the selection criteria: "
+ if consumer_version_selectors.any?
+ desc = consumer_version_selectors.collect do |selector|
+ desc = nil
+ if selector[:tag]
+ desc = !selector[:latest] ? "all for tag #{selector[:tag]}" : "latest for tag #{selector[:tag]}"
+ desc = "#{desc} of #{selector[:consumer]}" if selector[:consumer]
+ elsif selector[:branch]
+ desc = "latest from branch #{selector[:branch]}"
+ desc = "#{desc} of #{selector[:consumer]}" if selector[:consumer]
+ elsif selector[:mainBranch]
+ desc = "latest from main branch"
+ desc = "#{desc} of #{selector[:consumer]}" if selector[:consumer]
+ elsif selector[:deployed]
+ if selector[:environment]
+ desc = "currently deployed to #{selector[:environment]}"
+ else
+ desc = "currently deployed"
+ end
+ desc = "#{selector[:consumer]} #{desc}" if selector[:consumer]
+ elsif selector[:released]
+ if selector[:environment]
+ desc = "currently released to #{selector[:environment]}"
+ else
+ desc = "currently released"
+ end
+ desc = "#{selector[:consumer]} #{desc}" if selector[:consumer]
+ elsif selector[:deployedOrReleased]
+ if selector[:environment]
+ desc = "currently deployed or released to #{selector[:environment]}"
+ else
+ desc = "currently deployed or released"
+ end
+ desc = "#{selector[:consumer]} #{desc}" if selector[:consumer]
+ elsif selector[:environment]
+ desc = "currently in #{selector[:environment]}"
+ desc = "#{selector[:consumer]} #{desc}" if selector[:consumer]
+ elsif selector[:matchingBranch]
+ desc = "matching current branch"
+ desc = "#{desc} for #{selector[:consumer]}" if selector[:consumer]
+ elsif selector[:matchingTag]
+ desc = "matching tag"
+ desc = "#{desc} for #{selector[:consumer]}" if selector[:consumer]
+ else
+ desc = selector.to_s
+ end
+
+ fallback = selector[:fallback] || selector[:fallbackTag]
+ desc = "#{desc} (or #{fallback} if not found)" if fallback
+
+ desc
+ end.join(", ")
+ if options[:include_wip_pacts_since]
+ desc = "#{desc}, work in progress pacts created after #{options[:include_wip_pacts_since]}"
+ end
+ message << "#{desc}"
+ end
+ message
+ end
+ end
+ end
+end
diff --git a/lib/pact/provider/configuration/dsl.rb b/lib/pact/provider/configuration/dsl.rb
index 82e1ab8f..51b7f8f2 100644
--- a/lib/pact/provider/configuration/dsl.rb
+++ b/lib/pact/provider/configuration/dsl.rb
@@ -1,4 +1,5 @@
require 'pact/provider/configuration/service_provider_dsl'
+require 'pact/provider/configuration/message_provider_dsl'
module Pact
@@ -8,6 +9,10 @@ module DSL
def service_provider name, &block
Configuration::ServiceProviderDSL.build(name, &block)
end
+
+ def message_provider name, &block
+ Configuration::MessageProviderDSL.build(name, &block)
+ end
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/pact/provider/configuration/message_provider_dsl.rb b/lib/pact/provider/configuration/message_provider_dsl.rb
new file mode 100644
index 00000000..0a886ae1
--- /dev/null
+++ b/lib/pact/provider/configuration/message_provider_dsl.rb
@@ -0,0 +1,59 @@
+require 'pact/provider/configuration/service_provider_dsl'
+
+module Pact
+ module Provider
+ module Configuration
+ class MessageProviderDSL < ServiceProviderDSL
+ class RackToMessageAdapter
+ def initialize(message_builder)
+ @message_builder = message_builder
+ end
+
+ def call(env)
+ request_body_json = JSON.parse(env['rack.input'].read)
+ contents = @message_builder.call(request_body_json['description'])
+ [200, {"Content-Type" => "application/json"}, [{ contents: contents }.to_json]]
+ end
+ end
+
+ def initialize name
+ super
+ @mapper_block = lambda { |args| }
+ end
+
+ dsl do
+ def app &block
+ self.app_block = block
+ end
+
+ def app_version application_version
+ self.application_version = application_version
+ end
+
+ def app_version_tags tags
+ self.tags = tags
+ end
+
+ def publish_verification_results publish_verification_results
+ self.publish_verification_results = publish_verification_results
+ Pact::RSpec.with_rspec_2 do
+ Pact.configuration.error_stream.puts "WARN: Publishing of verification results is currently not supported with rspec 2. If you would like this functionality, please feel free to submit a PR!"
+ end
+ end
+
+ def honours_pact_with consumer_name, options = {}, &block
+ create_pact_verification consumer_name, options, &block
+ end
+
+ def honours_pacts_from_pact_broker &block
+ create_pact_verification_from_broker &block
+ end
+
+ def builder &block
+ self.app_block = lambda { RackToMessageAdapter.new(block) }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pact/provider/configuration/pact_verification_from_broker.rb b/lib/pact/provider/configuration/pact_verification_from_broker.rb
index c2778723..9a1a6346 100644
--- a/lib/pact/provider/configuration/pact_verification_from_broker.rb
+++ b/lib/pact/provider/configuration/pact_verification_from_broker.rb
@@ -1,7 +1,8 @@
require 'pact/shared/dsl'
require 'pact/provider/world'
-require 'pact/pact_broker/fetch_pacts'
+require 'pact/pact_broker/fetch_pact_uris_for_verification'
require 'pact/errors'
+require 'pact/utils/string'
module Pact
module Provider
@@ -14,11 +15,16 @@ class PactVerificationFromBroker
# in parent scope, it will clash with these ones,
# so put an underscore in front of the name to be safer.
- attr_accessor :_provider_name, :_pact_broker_base_url, :_consumer_version_tags, :_basic_auth_options, :_verbose
+ attr_accessor :_provider_name, :_pact_broker_base_url, :_consumer_version_tags, :_provider_version_branch, :_provider_version_tags, :_basic_auth_options, :_enable_pending, :_include_wip_pacts_since, :_verbose, :_consumer_version_selectors
- def initialize(provider_name)
+ def initialize(provider_name, provider_version_branch, provider_version_tags)
@_provider_name = provider_name
+ @_provider_version_branch = provider_version_branch
+ @_provider_version_tags = provider_version_tags
@_consumer_version_tags = []
+ @_consumer_version_selectors = []
+ @_enable_pending = false
+ @_include_wip_pacts_since = nil
@_verbose = false
end
@@ -32,6 +38,22 @@ def consumer_version_tags consumer_version_tags
self._consumer_version_tags = *consumer_version_tags
end
+ def consumer_version_selectors consumer_version_selectors
+ self._consumer_version_selectors = *consumer_version_selectors
+ end
+
+ def enable_pending enable_pending
+ self._enable_pending = enable_pending
+ end
+
+ def include_wip_pacts_since since
+ self._include_wip_pacts_since = if since.respond_to?(:xmlschema)
+ since.xmlschema
+ else
+ since
+ end
+ end
+
def verbose verbose
self._verbose = verbose
end
@@ -45,10 +67,50 @@ def finalize
private
def create_pact_verification
- fetch_pacts = Pact::PactBroker::FetchPacts.new(_provider_name, _consumer_version_tags, _pact_broker_base_url, _basic_auth_options.merge(verbose: _verbose))
+ fetch_pacts = Pact::PactBroker::FetchPactURIsForVerification.new(
+ _provider_name,
+ consumer_version_selectors,
+ _provider_version_branch,
+ _provider_version_tags,
+ _pact_broker_base_url,
+ _basic_auth_options.merge(verbose: _verbose),
+ { include_pending_status: _enable_pending, include_wip_pacts_since: _include_wip_pacts_since }
+ )
+
Pact.provider_world.add_pact_uri_source fetch_pacts
end
+ def consumer_version_selectors
+ convert_tags_to_selectors + convert_consumer_version_selectors
+ end
+
+ def convert_tags_to_selectors
+ _consumer_version_tags.collect do | tag |
+ if tag.is_a?(Hash)
+ {
+ tag: tag.fetch(:name),
+ latest: !tag[:all],
+ fallbackTag: tag[:fallback]
+ }
+ elsif tag.is_a?(String)
+ {
+ tag: tag,
+ latest: true
+ }
+ else
+ raise Pact::Error.new("The value supplied for consumer_version_tags must be a String or a Hash. Found #{tag.class}")
+ end
+ end
+ end
+
+ def convert_consumer_version_selectors
+ _consumer_version_selectors.collect do | selector |
+ selector.each_with_object({}) do | (key, value), new_selector |
+ new_selector[Pact::Utils::String.camelcase(key.to_s).to_sym] = value
+ end
+ end
+ end
+
def validate
raise Pact::Error.new("Please provide a pact_broker_base_url from which to retrieve the pacts") unless _pact_broker_base_url
end
diff --git a/lib/pact/provider/configuration/service_provider_config.rb b/lib/pact/provider/configuration/service_provider_config.rb
index 2b3068a2..8a89ba5c 100644
--- a/lib/pact/provider/configuration/service_provider_config.rb
+++ b/lib/pact/provider/configuration/service_provider_config.rb
@@ -4,9 +4,11 @@ module Configuration
class ServiceProviderConfig
attr_accessor :application_version
+ attr_reader :branch
- def initialize application_version, tags, publish_verification_results, &app_block
+ def initialize application_version, branch, tags, publish_verification_results, &app_block
@application_version = application_version
+ @branch = branch
@tags = [*tags]
@publish_verification_results = publish_verification_results
@app_block = app_block
diff --git a/lib/pact/provider/configuration/service_provider_dsl.rb b/lib/pact/provider/configuration/service_provider_dsl.rb
index bdd29ce4..fccb45d2 100644
--- a/lib/pact/provider/configuration/service_provider_dsl.rb
+++ b/lib/pact/provider/configuration/service_provider_dsl.rb
@@ -15,7 +15,7 @@ class ServiceProviderDSL
extend Pact::DSL
- attr_accessor :name, :app_block, :application_version, :tags, :publish_verification_results
+ attr_accessor :name, :app_block, :application_version, :branch, :tags, :publish_verification_results
CONFIG_RU_APP = lambda {
unless File.exist? Pact.configuration.config_ru_path
@@ -44,6 +44,10 @@ def app_version_tags tags
self.tags = tags
end
+ def app_version_branch branch
+ self.branch = branch
+ end
+
def publish_verification_results publish_verification_results
self.publish_verification_results = publish_verification_results
Pact::RSpec.with_rspec_2 do
@@ -65,7 +69,7 @@ def create_pact_verification consumer_name, options, &block
end
def create_pact_verification_from_broker(&block)
- PactVerificationFromBroker.build(name, &block)
+ PactVerificationFromBroker.build(name, branch, tags, &block)
end
def finalize
@@ -85,7 +89,7 @@ def application_version_blank?
end
def create_service_provider
- Pact.configuration.provider = ServiceProviderConfig.new(application_version, tags, publish_verification_results, &@app_block)
+ Pact.configuration.provider = ServiceProviderConfig.new(application_version, branch, tags, publish_verification_results, &@app_block)
end
end
end
diff --git a/lib/pact/provider/help/content.rb b/lib/pact/provider/help/content.rb
index 26452ea2..631ef28f 100644
--- a/lib/pact/provider/help/content.rb
+++ b/lib/pact/provider/help/content.rb
@@ -5,8 +5,8 @@ module Provider
module Help
class Content
- def initialize pact_jsons
- @pact_jsons = pact_jsons
+ def initialize pact_sources
+ @pact_sources = pact_sources
end
def text
@@ -15,7 +15,7 @@ def text
private
- attr_reader :pact_jsons
+ attr_reader :pact_sources
def help_text
temp_dir = Pact.configuration.tmp_dir
@@ -28,7 +28,7 @@ def template_string
end
def pact_diffs
- pact_jsons.collect do | pact_json |
+ pact_sources.collect do | pact_json |
PactDiff.call(pact_json)
end.compact.join("\n")
end
diff --git a/lib/pact/provider/help/pact_diff.rb b/lib/pact/provider/help/pact_diff.rb
index cf967a36..f9677890 100644
--- a/lib/pact/provider/help/pact_diff.rb
+++ b/lib/pact/provider/help/pact_diff.rb
@@ -1,26 +1,24 @@
+require 'pact/hal/entity'
+
module Pact
module Provider
module Help
-
class PactDiff
-
class PrintPactDiffError < StandardError; end
- attr_reader :pact_json, :output
+ attr_reader :pact_source, :output
- def initialize pact_json
- @pact_json = pact_json
+ def initialize pact_source
+ @pact_source = pact_source
end
- def self.call pact_json
- new(pact_json).call
+ def self.call pact_source
+ new(pact_source).call
end
def call
begin
- if diff_rel && diff_url
- header + "\n" + get_diff
- end
+ header + "\n" + get_diff
rescue PrintPactDiffError => e
return e.message
end
@@ -32,35 +30,13 @@ def header
"The following changes have been made since the previous distinct version of this pact, and may be responsible for verification failure:\n"
end
- def pact_hash
- @pact_hash ||= json_load(pact_json)
- end
-
- def links
- pact_hash['_links'] || pact_hash['links']
- end
-
- def diff_rel
- return nil unless links
- key = links.keys.find { | key | key =~ /diff/ && key =~ /distinct/ && key =~ /previous/}
- key ? links[key] : nil
- end
-
- def diff_url
- diff_rel['href']
- end
-
def get_diff
begin
- open(diff_url) { | file | file.read }
+ pact_source.hal_entity._link!("pb:diff-previous-distinct").get!(nil, "Accept" => "text/plain").body
rescue StandardError => e
- raise PrintPactDiffError.new("Tried to retrieve diff with previous pact from #{diff_url}, but received response code #{e}.")
+ raise PrintPactDiffError.new("Tried to retrieve diff with previous pact, but received error #{e.class} #{e.message}.")
end
end
-
- def json_load json
- JSON.load(json, nil, { max_nesting: 50 })
- end
end
end
end
diff --git a/lib/pact/provider/help/write.rb b/lib/pact/provider/help/write.rb
index 29e06a95..0931c4dd 100644
--- a/lib/pact/provider/help/write.rb
+++ b/lib/pact/provider/help/write.rb
@@ -9,23 +9,25 @@ class Write
HELP_FILE_NAME = 'help.md'
- def self.call pact_jsons, reports_dir = Pact.configuration.reports_dir
- new(pact_jsons, reports_dir).call
+ def self.call pact_sources, reports_dir = Pact.configuration.reports_dir
+ new(pact_sources, reports_dir).call
end
- def initialize pact_jsons, reports_dir
- @pact_jsons = pact_jsons
+ def initialize pact_sources, reports_dir
+ @pact_sources = pact_sources
@reports_dir = File.expand_path(reports_dir)
end
def call
clean_reports_dir
write
+ rescue StandardError => e
+ Pact.configuration.error_stream.puts("ERROR: Error generating help output - #{e.class} #{e.message} \n" + e.backtrace.join("\n"))
end
private
- attr_reader :reports_dir, :pact_jsons
+ attr_reader :reports_dir, :pact_sources
def clean_reports_dir
raise "Cleaning report dir #{reports_dir} would delete project!" if reports_dir_contains_pwd
@@ -46,9 +48,8 @@ def help_path
end
def help_text
- Content.new(pact_jsons).text
+ Content.new(pact_sources).text
end
-
end
end
end
diff --git a/lib/pact/provider/pact_helper_locator.rb b/lib/pact/provider/pact_helper_locator.rb
index 43f48b5d..d71f936c 100644
--- a/lib/pact/provider/pact_helper_locator.rb
+++ b/lib/pact/provider/pact_helper_locator.rb
@@ -1,22 +1,24 @@
module Pact
module Provider
module PactHelperLocater
- PACT_HELPER_FILE_PATTERNS = [
+ PACT_HELPER_FILE_PATTERNS = [
"spec/**/*service*consumer*/pact_helper.rb",
"spec/**/*consumer*/pact_helper.rb",
"spec/**/pact_helper.rb",
- "**/pact_helper.rb"]
+ "test/**/*service*consumer*/pact_helper.rb",
+ "test/**/*consumer*/pact_helper.rb",
+ "test/**/pact_helper.rb",
+ "**/pact_helper.rb"
+ ]
- NO_PACT_HELPER_FOUND_MSG = "Please create a pact_helper.rb file that can be found using one of the following patterns: #{PACT_HELPER_FILE_PATTERNS.join(", ")}"
-
- def self.pact_helper_path
- pact_helper_search_results = []
- PACT_HELPER_FILE_PATTERNS.find { | pattern | (pact_helper_search_results.concat(Dir.glob(pattern))).any? }
- raise NO_PACT_HELPER_FOUND_MSG if pact_helper_search_results.empty?
- File.join(Dir.pwd, pact_helper_search_results[0])
- end
+ NO_PACT_HELPER_FOUND_MSG = "Please create a pact_helper.rb file that can be found using one of the following patterns: #{PACT_HELPER_FILE_PATTERNS.join(", ")}"
+ def self.pact_helper_path
+ pact_helper_search_results = []
+ PACT_HELPER_FILE_PATTERNS.find { | pattern | (pact_helper_search_results.concat(Dir.glob(pattern))).any? }
+ raise NO_PACT_HELPER_FOUND_MSG if pact_helper_search_results.empty?
+ File.join(Dir.pwd, pact_helper_search_results[0])
+ end
end
end
end
-
diff --git a/lib/pact/provider/pact_source.rb b/lib/pact/provider/pact_source.rb
index 44b3cb33..a1a14e27 100644
--- a/lib/pact/provider/pact_source.rb
+++ b/lib/pact/provider/pact_source.rb
@@ -1,10 +1,13 @@
require 'pact/consumer_contract/pact_file'
+require 'pact/hal/http_client'
+require 'pact/hal/entity'
+require 'pact/consumer_contract'
module Pact
module Provider
class PactSource
- attr_reader :uri
+ attr_reader :uri # PactURI class
def initialize uri
@uri = uri
@@ -17,6 +20,21 @@ def pact_json
def pact_hash
@pact_hash ||= JSON.load(pact_json, nil, { max_nesting: 50 })
end
+
+ def pending?
+ uri.metadata[:pending]
+ end
+
+ def consumer_contract
+ @consumer_contract ||= Pact::ConsumerContract.from_json(pact_json)
+ end
+
+ def hal_entity
+ http_client_keys = [:username, :password, :token]
+ http_client_options = uri.options.reject{ |k, _| !http_client_keys.include?(k) }
+ http_client = Pact::Hal::HttpClient.new(http_client_options)
+ Pact::Hal::Entity.new(uri, pact_hash, http_client)
+ end
end
end
end
diff --git a/lib/pact/provider/pact_spec_runner.rb b/lib/pact/provider/pact_spec_runner.rb
index 7db51560..cc7e50a9 100644
--- a/lib/pact/provider/pact_spec_runner.rb
+++ b/lib/pact/provider/pact_spec_runner.rb
@@ -9,9 +9,9 @@
require 'pact/provider/help/write'
require 'pact/provider/verification_results/publish_all'
require 'pact/provider/rspec/pact_broker_formatter'
-
-require_relative 'rspec'
-
+require 'pact/provider/rspec/json_formatter'
+require 'pact/provider/rspec'
+require 'pact/provider/rspec/calculate_exit_code'
module Pact
module Provider
@@ -75,12 +75,14 @@ def configure_rspec
end
# For the Pact::Provider::RSpec::PactBrokerFormatter
+ Pact.provider_world.verbose = options[:verbose]
Pact.provider_world.pact_sources = pact_sources
jsons = pact_jsons
executing_with_ruby = executing_with_ruby?
config.after(:suite) do | suite |
- Pact::Provider::Help::Write.call(jsons) if executing_with_ruby
+ Pact.provider_world.failed_examples = suite.reporter.failed_examples
+ Pact::Provider::Help::Write.call(Pact.provider_world.pact_sources) if executing_with_ruby
end
end
@@ -91,7 +93,12 @@ def run_specs
::RSpec::Core::CommandLine.new(NoConfigurationOptions.new)
.run(::RSpec.configuration.output_stream, ::RSpec.configuration.error_stream)
end
- options[:ignore_failures] ? 0 : exit_code
+
+ if options[:ignore_failures]
+ 0
+ else
+ Pact::Provider::RSpec::CalculateExitCode.call(pact_sources, Pact.provider_world.failed_examples)
+ end
end
def rspec_runner_options
@@ -118,12 +125,12 @@ def pact_jsons
def initialize_specs
pact_sources.each do | pact_source |
- options = {
- criteria: @options[:criteria],
- ignore_failures: @options[:ignore_failures],
- request_customizer: @options[:request_customizer]
+ spec_options = {
+ criteria: options[:criteria],
+ ignore_failures: options[:ignore_failures],
+ request_customizer: options[:request_customizer]
}
- honour_pactfile pact_source.uri, ordered_pact_json(pact_source.pact_json), options
+ honour_pactfile pact_source, ordered_pact_json(pact_source.pact_json), spec_options
end
end
@@ -134,11 +141,12 @@ def configure_output
output = options[:out] || Pact.configuration.output_stream
if options[:format]
- ::RSpec.configuration.add_formatter options[:format], output
- if !options[:out]
- # Don't want to mess up the JSON parsing with messages to stdout, so send it to stderr
- Pact.configuration.output_stream = Pact.configuration.error_stream
- end
+ formatter = options[:format] == 'json' ? Pact::Provider::RSpec::JsonFormatter : options[:format]
+ # Send formatted output to $stdout for parsing, unless a file is specified
+ output = options[:out] || $stdout
+ ::RSpec.configuration.add_formatter formatter, output
+ # Don't want to mess up the JSON parsing with INFO and DEBUG messages to stdout, so send it to stderr
+ Pact.configuration.output_stream = Pact.configuration.error_stream if !options[:out]
else
# Sometimes the formatter set in the cli.rb get set with an output of StringIO.. don't know why
formatter_class = Pact::RSpec.formatter_class
@@ -147,8 +155,6 @@ def configure_output
end
::RSpec.configuration.full_backtrace = @options[:full_backtrace]
-
- ::RSpec.configuration.failure_color = :yellow if @options[:ignore_failures]
end
def ordered_pact_json(pact_json)
diff --git a/lib/pact/provider/pact_uri.rb b/lib/pact/provider/pact_uri.rb
index c5f36474..56640a6e 100644
--- a/lib/pact/provider/pact_uri.rb
+++ b/lib/pact/provider/pact_uri.rb
@@ -1,21 +1,23 @@
module Pact
module Provider
class PactURI
- attr_reader :uri, :options
+ attr_reader :uri, :options, :metadata
- def initialize (uri, options={})
+ def initialize (uri, options = nil, metadata = nil)
@uri = uri
- @options = options
+ @options = options || {}
+ @metadata = metadata || {} # make sure it's not nil if nil is passed in
end
def == other
other.is_a?(PactURI) &&
uri == other.uri &&
- options == other.options
+ options == other.options &&
+ metadata == other.metadata
end
def basic_auth?
- !!username
+ !!username && !!password
end
def username
@@ -27,12 +29,27 @@ def password
end
def to_s
- if(basic_auth?)
- URI(@uri).tap { |x| x.userinfo="#{username}:*****"}.to_s
+ if basic_auth? && http_or_https_uri?
+ begin
+ URI(@uri).tap { |x| x.userinfo="#{username}:*****"}.to_s
+ rescue URI::InvalidComponentError
+ URI(@uri).tap { |x| x.userinfo="*****:*****"}.to_s
+ end
+ elsif personal_access_token? && http_or_https_uri?
+ URI(@uri).tap { |x| x.userinfo="*****"}.to_s
else
- @uri
+ uri
end
end
+
+ private def personal_access_token?
+ !!username && !password
+ end
+
+ private def http_or_https_uri?
+ uri.start_with?('http://', 'https://')
+ end
+
end
end
end
diff --git a/lib/pact/provider/request.rb b/lib/pact/provider/request.rb
index 3aaf0662..ad4b1998 100644
--- a/lib/pact/provider/request.rb
+++ b/lib/pact/provider/request.rb
@@ -54,7 +54,7 @@ def reified_body
end
def rack_request_header_for header
- with_http_prefix(header.to_s.upcase).gsub('-', '_')
+ with_http_prefix(header.to_s.upcase).tr('-', '_')
end
def rack_request_value_for value
diff --git a/lib/pact/provider/rspec.rb b/lib/pact/provider/rspec.rb
index 9ac48d50..a6e7372c 100644
--- a/lib/pact/provider/rspec.rb
+++ b/lib/pact/provider/rspec.rb
@@ -17,27 +17,43 @@ def app
end
module ClassMethods
+ EMPTY_ARRAY = [].freeze
include ::RSpec::Core::DSL
- def honour_pactfile pact_uri, pact_json, options
- pact_description = options[:ignore_failures] ? "Pending pact" : "pact"
- Pact.configuration.output_stream.puts "INFO: Reading #{pact_description} at #{pact_uri}"
- Pact.configuration.output_stream.puts "INFO: Filtering interactions by: #{options[:criteria]}" if options[:criteria] && options[:criteria].any?
+ def honour_pactfile pact_source, pact_json, options
+ pact_uri = pact_source.uri
+ Pact.configuration.output_stream.puts "INFO: Reading pact at #{pact_uri}"
consumer_contract = Pact::ConsumerContract.from_json(pact_json)
- ::RSpec.describe "Verifying a #{pact_description} between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}", pactfile_uri: pact_uri do
- honour_consumer_contract consumer_contract, options.merge(pact_json: pact_json, pact_uri: pact_uri)
+ suffix = pact_uri.metadata[:pending] ? " [PENDING]": ""
+ example_group_description = "Verifying a pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}#{suffix}"
+ example_group_metadata = { pactfile_uri: pact_uri, pact_criteria: options[:criteria] }
+
+ ::RSpec.describe example_group_description, example_group_metadata do
+ honour_consumer_contract consumer_contract, options.merge(
+ pact_json: pact_json,
+ pact_uri: pact_uri,
+ pact_source: pact_source,
+ consumer_contract: consumer_contract,
+ criteria: options[:criteria]
+ )
end
end
def honour_consumer_contract consumer_contract, options = {}
- describe_consumer_contract consumer_contract, options.merge(consumer: consumer_contract.consumer.name)
+ describe_consumer_contract consumer_contract, options.merge(consumer: consumer_contract.consumer.name, pact_context: InteractionContext.new)
end
private
def describe_consumer_contract consumer_contract, options
- consumer_interactions(consumer_contract, options).each do |interaction|
+ consumer_interactions(consumer_contract, options).tap{ |interactions|
+ if interactions.empty?
+ # If there are no interactions, the documentation formatter never fires to print this out,
+ # so print it out here.
+ Pact.configuration.output_stream.puts "DEBUG: All interactions for #{options[:pact_uri]} have been filtered out by criteria: #{options[:criteria]}" if options[:criteria] && options[:criteria].any?
+ end
+ }.each do |interaction|
describe_interaction_with_provider_state interaction, options
end
end
@@ -46,7 +62,7 @@ def consumer_interactions(consumer_contract, options)
if options[:criteria].nil?
consumer_contract.interactions
else
- consumer_contract.find_interactions options[:criteria]
+ consumer_contract.find_interactions(options[:criteria])
end
end
@@ -74,12 +90,15 @@ def describe_interaction interaction, options
pact_interaction: interaction,
pact_interaction_example_description: interaction_description_for_rerun_command(interaction),
pact_uri: options[:pact_uri],
- pact_ignore_failures: options[:ignore_failures]
+ pact_source: options[:pact_source],
+ pact_ignore_failures: options[:pact_source].pending? || options[:ignore_failures],
+ pact_consumer_contract: options[:consumer_contract]
}
describe description_for(interaction), metadata do
interaction_context = InteractionContext.new
+ pact_context = options[:pact_context]
before do | example |
interaction_context.run_once :before do
diff --git a/lib/pact/provider/rspec/calculate_exit_code.rb b/lib/pact/provider/rspec/calculate_exit_code.rb
new file mode 100644
index 00000000..472d2599
--- /dev/null
+++ b/lib/pact/provider/rspec/calculate_exit_code.rb
@@ -0,0 +1,18 @@
+module Pact
+ module Provider
+ module RSpec
+ module CalculateExitCode
+ def self.call(pact_sources, failed_examples)
+ any_non_pending_failures = pact_sources.any? do |pact_source|
+ if pact_source.pending?
+ nil
+ else
+ failed_examples.select { |e| e.metadata[:pact_source] == pact_source }.any?
+ end
+ end
+ any_non_pending_failures ? 1 : 0
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pact/provider/rspec/formatter_rspec_3.rb b/lib/pact/provider/rspec/formatter_rspec_3.rb
index 00a4ee62..8c3560c8 100644
--- a/lib/pact/provider/rspec/formatter_rspec_3.rb
+++ b/lib/pact/provider/rspec/formatter_rspec_3.rb
@@ -6,13 +6,13 @@
module Pact
module Provider
module RSpec
-
class Formatter < ::RSpec::Core::Formatters::DocumentationFormatter
class NilFormatter < ::RSpec::Core::Formatters::BaseFormatter
Pact::RSpec.with_rspec_3 do
::RSpec::Core::Formatters.register self, :start, :example_group_started, :close
end
+
def dump_summary(summary)
end
end
@@ -24,6 +24,26 @@ def dump_summary(summary)
C = ::Term::ANSIColor
+ def example_group_started(notification)
+ # This is the metadata on the top level "Verifying a pact between X and Y" describe block
+ if @group_level == 0
+ Pact.configuration.output_stream.puts
+ pact_uri = notification.group.metadata[:pactfile_uri]
+ ::RSpec.configuration.failure_color = pact_uri.metadata[:pending] ? :yellow : :red
+
+ if pact_uri.metadata[:notices]
+ pact_uri.metadata[:notices].before_verification_notices_text.each do | text |
+ Pact.configuration.output_stream.puts("DEBUG: #{text}")
+ end
+ end
+
+ criteria = notification.group.metadata[:pact_criteria]
+ Pact.configuration.output_stream.puts "DEBUG: Filtering interactions by: #{criteria}" if criteria && criteria.any?
+ end
+ super
+ end
+
+
def dump_summary(summary)
output.puts "\n" + colorized_totals_line(summary)
return if summary.failure_count == 0
@@ -35,28 +55,26 @@ def dump_summary(summary)
private
def interactions_count(summary)
- summary.examples.collect{ |e| e.metadata[:pact_interaction_example_description] }.uniq.size
+ summary.examples.collect{ |e| interaction_unique_key(e) }.uniq.size
end
def failed_interactions_count(summary)
- summary.failed_examples.collect{ |e| e.metadata[:pact_interaction_example_description] }.uniq.size
+ failed_interaction_examples(summary).size
end
- def ignore_failures?(summary)
- summary.failed_examples.any?{ |e| e.metadata[:pact_ignore_failures] }
+ def pending_interactions_count(summary)
+ pending_interaction_examples(summary).size
end
def failure_title summary
- if ignore_failures?(summary)
- "#{failed_interactions_count(summary)} pending"
- else
- ::RSpec::Core::Formatters::Helpers.pluralize(failed_interactions_count(summary), "failure")
- end
+ ::RSpec::Core::Formatters::Helpers.pluralize(failed_interactions_count(summary), "failure")
end
def totals_line summary
line = ::RSpec::Core::Formatters::Helpers.pluralize(interactions_count(summary), "interaction")
line << ", " << failure_title(summary)
+ pending_count = pending_interactions_count(summary)
+ line << ", " << "#{pending_count} pending" if pending_count > 0
line
end
@@ -69,13 +87,20 @@ def color_for_summary summary
end
def print_rerun_commands summary
- if ignore_failures?(summary)
+ if pending_interactions_count(summary) > 0
+ set_rspec_failure_color(:yellow)
output.puts("\nPending interactions: (Failures listed here are expected and do not affect your suite's status)\n\n")
- else
- output.puts("\nFailed interactions:\n\n")
+ interaction_rerun_commands(pending_interaction_examples(summary)).each do | message |
+ output.puts(message)
+ end
+ set_rspec_failure_color(:red)
end
- interaction_rerun_commands(summary).each do | message |
- output.puts(message)
+
+ if failed_interactions_count(summary) > 0
+ output.puts("\nFailed interactions:\n\n")
+ interaction_rerun_commands(failed_interaction_examples(summary)).each do | message |
+ output.puts(message)
+ end
end
end
@@ -85,25 +110,64 @@ def print_missing_provider_states
end
end
- def interaction_rerun_commands summary
- summary.failed_examples.collect do |example|
+ def pending_interaction_examples(summary)
+ one_failed_example_per_interaction(summary).select do | example |
+ example.metadata[:pactfile_uri].metadata[:pending]
+ end
+ end
+
+ def failed_interaction_examples(summary)
+ one_failed_example_per_interaction(summary).select do | example |
+ !example.metadata[:pactfile_uri].metadata[:pending]
+ end
+ end
+
+ def one_failed_example_per_interaction(summary)
+ summary.failed_examples.group_by{| e| interaction_unique_key(e)}.values.collect(&:first)
+ end
+
+ def interaction_rerun_commands examples
+ examples.collect do |example|
interaction_rerun_command_for example
- end.uniq.compact
+ end.compact.uniq
+ end
+
+ def interaction_unique_key(example)
+ # pending is just to make the counting easier, it isn't required for the unique key
+ {
+ pactfile_uri: example.metadata[:pactfile_uri],
+ index: example.metadata[:pact_interaction].index,
+ }
end
def interaction_rerun_command_for example
example_description = example.metadata[:pact_interaction_example_description]
- if ENV['PACT_INTERACTION_RERUN_COMMAND']
+
+ _id = example.metadata[:pact_interaction]._id
+ index = example.metadata[:pact_interaction].index
+ provider_state = example.metadata[:pact_interaction].provider_state
+ description = example.metadata[:pact_interaction].description
+ pactfile_uri = example.metadata[:pactfile_uri]
+
+ if _id && ENV['PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER']
+ cmd = String.new(ENV['PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER'])
+ cmd.gsub!("", example.metadata[:pactfile_uri].to_s)
+ cmd.gsub!("", "#{_id}")
+ colorizer.wrap("#{cmd} ", ::RSpec.configuration.failure_color) + colorizer.wrap("# #{example_description}", ::RSpec.configuration.detail_color)
+ elsif ENV['PACT_INTERACTION_RERUN_COMMAND']
cmd = String.new(ENV['PACT_INTERACTION_RERUN_COMMAND'])
- provider_state = example.metadata[:pact_interaction].provider_state
- description = example.metadata[:pact_interaction].description
- pactfile_uri = example.metadata[:pactfile_uri]
cmd.gsub!("", pactfile_uri.to_s)
cmd.gsub!("", description)
cmd.gsub!("", "#{provider_state}")
+ cmd.gsub!("", "#{index}")
colorizer.wrap("#{cmd} ", ::RSpec.configuration.failure_color) + colorizer.wrap("# #{example_description}", ::RSpec.configuration.detail_color)
else
- colorizer.wrap("* #{example_description}", ::RSpec.configuration.failure_color)
+ message = if _id
+ "* #{example_description} (to re-run just this interaction, set environment variable PACT_BROKER_INTERACTION_ID=\"#{_id}\")"
+ else
+ "* #{example_description} (to re-run just this interaction, set environment variables PACT_DESCRIPTION=\"#{description}\" PACT_PROVIDER_STATE=\"#{provider_state}\")"
+ end
+ colorizer.wrap(message, ::RSpec.configuration.failure_color)
end
end
@@ -122,6 +186,10 @@ def colorizer
def executing_with_ruby?
ENV['PACT_EXECUTING_LANGUAGE'] == 'ruby'
end
+
+ def set_rspec_failure_color color
+ ::RSpec.configuration.failure_color = color
+ end
end
end
end
diff --git a/lib/pact/provider/rspec/json_formatter.rb b/lib/pact/provider/rspec/json_formatter.rb
new file mode 100644
index 00000000..e774cfdc
--- /dev/null
+++ b/lib/pact/provider/rspec/json_formatter.rb
@@ -0,0 +1,100 @@
+require 'rspec/core/formatters/json_formatter'
+
+module Pact
+ module Provider
+ module RSpec
+ class JsonFormatter < ::RSpec::Core::Formatters::JsonFormatter
+ ::RSpec::Core::Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :seed, :close
+
+ def dump_summary(summary)
+ super(create_custom_summary(summary))
+ output_hash[:summary][:pacts] = pacts(summary)
+ end
+
+ def format_example(example)
+ {
+ :id => example.id,
+ :interaction_index => example.metadata[:pact_interaction].index,
+ :description => example.description,
+ :full_description => example.full_description,
+ :status => calculate_status(example),
+ :file_path => example.metadata[:file_path],
+ :line_number => example.metadata[:line_number],
+ :run_time => example.execution_result.run_time,
+ :mismatches => extract_differences(example),
+ :pact_url => example.metadata[:pact_uri].uri
+ }
+ end
+
+ def stop(notification)
+ output_hash[:examples] = notification.examples.map do |example|
+ format_example(example).tap do |hash|
+ e = example.exception
+ if e
+ hash[:exception] = {
+ class: e.class.name,
+ message: e.message,
+ }
+ # No point providing a backtrace for a mismatch, too much noise
+ if !e.is_a?(::RSpec::Expectations::ExpectationNotMetError)
+ hash[:exception][:backtrace]
+ end
+ end
+ end
+ end
+ end
+
+ def calculate_status(example)
+ if example.execution_result.status == :failed && example.metadata[:pact_ignore_failures]
+ 'pending'
+ else
+ example.execution_result.status.to_s
+ end
+ end
+
+ # There will most likely be only one pact associated with this RSpec execution, because
+ # the most likely user of this formatter is the Go implementation that parses the JSON
+ # and builds Go tests from them.
+ # If the JSON formatter is used by someone else and they have multiple pacts, all the notices
+ # for the pacts will be mushed together in one collection, so it will be hard to know which notice
+ # belongs to which pact.
+ def pacts(summary)
+ unique_pact_metadatas(summary).collect do | example_metadata |
+ pact_uri = example_metadata[:pact_uri]
+ notices = (pact_uri.metadata[:notices] && pact_uri.metadata[:notices].before_verification_notices) || []
+ {
+ notices: notices,
+ url: pact_uri.uri,
+ consumer_name: example_metadata[:pact_consumer_contract].consumer.name,
+ provider_name: example_metadata[:pact_consumer_contract].provider.name,
+ short_description: pact_uri.metadata[:short_description]
+ }
+ end
+ end
+
+ def unique_pact_metadatas(summary)
+ summary.examples.collect(&:metadata).group_by{ | metadata | metadata[:pact_uri].uri }.values.collect(&:first)
+ end
+
+ def create_custom_summary(summary)
+ ::RSpec::Core::Notifications::SummaryNotification.new(
+ summary.duration,
+ summary.examples,
+ summary.examples.select{ | example | example.execution_result.status == :failed && !example.metadata[:pact_ignore_failures] },
+ summary.examples.select{ | example | example.execution_result.status == :failed && example.metadata[:pact_ignore_failures] },
+ summary.load_time,
+ summary.errors_outside_of_examples_count
+ )
+ end
+
+ def extract_differences(example)
+ if example.metadata[:pact_diff]
+ Pact::Matchers::ExtractDiffMessages.call(example.metadata[:pact_diff]).to_a
+ else
+ []
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pact/provider/rspec/matchers.rb b/lib/pact/provider/rspec/matchers.rb
index 23b12b03..6b082693 100644
--- a/lib/pact/provider/rspec/matchers.rb
+++ b/lib/pact/provider/rspec/matchers.rb
@@ -8,7 +8,7 @@ module Pact
module RSpec
module Matchers
module RSpec2Delegator
- # For backwards compatiblity with rspec-2
+ # For backwards compatibility with rspec-2
def method_missing(method, *args, &block)
if method_name == :failure_message_for_should
failure_message method, *args, &block
diff --git a/lib/pact/provider/rspec/pact_broker_formatter.rb b/lib/pact/provider/rspec/pact_broker_formatter.rb
index c2659ddd..e655b8fd 100644
--- a/lib/pact/provider/rspec/pact_broker_formatter.rb
+++ b/lib/pact/provider/rspec/pact_broker_formatter.rb
@@ -25,7 +25,7 @@ def stop(notification)
end
def close(_notification)
- Pact::Provider::VerificationResults::PublishAll.call(Pact.provider_world.pact_sources, output_hash)
+ Pact::Provider::VerificationResults::PublishAll.call(Pact.provider_world.pact_sources, output_hash, { verbose: Pact.provider_world.verbose })
end
private
@@ -66,7 +66,7 @@ def format_example(example)
if example.metadata[:pact_diff]
hash[:differences] = Pact::Matchers::ExtractDiffMessages.call(example.metadata[:pact_diff])
.to_a
- .collect{ | description | {description: description} }
+ .collect{ | description | { description: description } }
end
end
end
diff --git a/lib/pact/provider/test_methods.rb b/lib/pact/provider/test_methods.rb
index 470b2157..f27466af 100644
--- a/lib/pact/provider/test_methods.rb
+++ b/lib/pact/provider/test_methods.rb
@@ -21,7 +21,11 @@ def replay_interaction interaction, request_customizer = nil
logger.info "Sending #{request.method.upcase} request to path: \"#{request.path}\" with headers: #{request.headers}, see debug logs for body"
logger.debug "body :#{request.body}"
- response = self.send(request.method.downcase, *args)
+ response = if self.respond_to?(:custom_request)
+ self.custom_request(request.method.upcase, *args)
+ else
+ self.send(request.method.downcase, *args)
+ end
logger.info "Received response with status: #{response.status}, headers: #{response.headers}, see debug logs for body"
logger.debug "body: #{response.body}"
end
@@ -29,7 +33,9 @@ def replay_interaction interaction, request_customizer = nil
def parse_body_from_response rack_response
case rack_response.headers['Content-Type']
when /json/
- JSON.load(rack_response.body)
+ # For https://github.com/pact-foundation/pact-net/issues/237
+ # Only required for the pact-ruby-standalone ¯\_(ツ)_/¯
+ JSON.load("[#{rack_response.body}]").first
else
rack_response.body
end
@@ -46,7 +52,7 @@ def set_up_provider_states provider_states, consumer, options = {}
def tear_down_provider_states provider_states, consumer, options = {}
# If there are no provider state, execute with an nil state to ensure global and base states are executed
Pact.configuration.provider_state_tear_down.call(nil, consumer, options) if provider_states.nil? || provider_states.empty?
- provider_states.reverse.each do | provider_state |
+ provider_states.reverse_each do | provider_state |
Pact.configuration.provider_state_tear_down.call(provider_state.name, consumer, options.merge(params: provider_state.params))
end
end
diff --git a/lib/pact/provider/verification_results/create.rb b/lib/pact/provider/verification_results/create.rb
index 2ae5ced2..bf296729 100644
--- a/lib/pact/provider/verification_results/create.rb
+++ b/lib/pact/provider/verification_results/create.rb
@@ -28,7 +28,23 @@ def any_failures?
end
def publishable?
- executed_interactions_count == all_interactions_count && all_interactions_count > 0
+ if defined?(@publishable)
+ @publishable
+ else
+ @publishable = pact_source.consumer_contract.interactions.all? do | interaction |
+ examples_for_pact_uri.any?{ |e| example_is_for_interaction?(e, interaction) }
+ end && examples_for_pact_uri.count > 0
+ end
+ end
+
+ def example_is_for_interaction?(example, interaction)
+ # Use the Pact Broker id if supported
+ if interaction._id
+ example[:pact_interaction]._id == interaction._id
+ else
+ # fall back to object equality (based on the field values of the interaction)
+ example[:pact_interaction] == interaction
+ end
end
def examples_for_pact_uri
@@ -39,18 +55,6 @@ def count_failures_for_pact_uri
examples_for_pact_uri.count{ |e| e[:status] != 'passed' }
end
- def executed_interactions_count
- examples_for_pact_uri
- .collect { |e| e[:pact_interaction].object_id }
- .uniq
- .count
- end
-
- def all_interactions_count
- interactions = (pact_source.pact_hash['interactions'] || pact_source.pact_hash['messages'])
- interactions ? interactions.count : 0
- end
-
def test_results_hash_for_pact_uri
{
tests: examples_for_pact_uri.collect{ |e| clean_example(e) },
diff --git a/lib/pact/provider/verification_results/publish.rb b/lib/pact/provider/verification_results/publish.rb
index f0f91c79..59bb47e5 100644
--- a/lib/pact/provider/verification_results/publish.rb
+++ b/lib/pact/provider/verification_results/publish.rb
@@ -16,29 +16,28 @@ class Publish
PUBLISH_RELATION = 'pb:publish-verification-results'.freeze
PROVIDER_RELATION = 'pb:provider'.freeze
VERSION_TAG_RELATION = 'pb:version-tag'.freeze
+ BRANCH_VERSION_RELATION = 'pb:branch-version'.freeze
- def self.call pact_source, verification_result
- new(pact_source, verification_result).call
+ def self.call pact_source, verification_result, options = {}
+ new(pact_source, verification_result, options).call
end
- def initialize pact_source, verification_result
+ def initialize pact_source, verification_result, options = {}
@pact_source = pact_source
@verification_result = verification_result
-
- http_client_options = {}
- if pact_source.uri.basic_auth?
- http_client_options[:username] = pact_source.uri.username
- http_client_options[:password] = pact_source.uri.password
- end
-
- @http_client = Pact::Hal::HttpClient.new(http_client_options)
+ http_client_options = pact_source.uri.options.reject{ |k, v| ![:username, :password, :token].include?(k) }
+ @http_client = Pact::Hal::HttpClient.new(http_client_options.merge(verbose: options[:verbose]))
@pact_entity = Pact::Hal::Entity.new(pact_source.uri, pact_source.pact_hash, http_client)
end
def call
if can_publish_verification_results?
+ create_branch_version_if_configured
tag_versions_if_configured
publish_verification_results
+ true
+ else
+ false
end
end
@@ -75,8 +74,28 @@ def tag_versions_if_configured
end
end
+ def create_branch_version_if_configured
+ if Pact.configuration.provider.branch
+ branch_version_link = provider_entity._link(BRANCH_VERSION_RELATION)
+ if branch_version_link
+ version_number = Pact.configuration.provider.application_version
+ branch = Pact.configuration.provider.branch
+
+ Pact.configuration.output_stream.puts "INFO: Creating #{provider_name} version #{version_number} with branch \"#{branch}\""
+ branch_entity = branch_version_link.expand(
+ version: version_number,
+ branch: branch
+ ).put
+ unless branch_entity.success?
+ raise PublicationError.new("Error returned from tagging request: status=#{branch_entity.response.code} body=#{branch_entity.response.body}")
+ end
+ else
+ raise PublicationError.new("This version of the Pact Broker does not support version branches. Please update to version 2.58.0 or later.")
+ end
+ end
+ end
+
def tag_versions
- provider_entity = pact_entity.get(PROVIDER_RELATION)
tag_link = provider_entity._link(VERSION_TAG_RELATION) || hacky_tag_url(provider_entity)
provider_application_version = Pact.configuration.provider.application_version
@@ -84,7 +103,7 @@ def tag_versions
Pact.configuration.output_stream.puts "INFO: Tagging version #{provider_application_version} of #{provider_name} as #{tag.inspect}"
tag_entity = tag_link.expand(version: provider_application_version, tag: tag).put
unless tag_entity.success?
- raise PublicationError.new("Error returned from tagging request #{tag_entity.response.code} #{tag_entity.response.body}")
+ raise PublicationError.new("Error returned from tagging request: status=#{tag_entity.response.code} body=#{tag_entity.response.body}")
end
end
end
@@ -114,6 +133,10 @@ def consumer_name
def provider_name
pact_source.pact_hash['provider']['name']
end
+
+ def provider_entity
+ @provider_entity ||= pact_entity.get(PROVIDER_RELATION)
+ end
end
end
end
diff --git a/lib/pact/provider/verification_results/publish_all.rb b/lib/pact/provider/verification_results/publish_all.rb
index 61891e1d..94faab99 100644
--- a/lib/pact/provider/verification_results/publish_all.rb
+++ b/lib/pact/provider/verification_results/publish_all.rb
@@ -6,18 +6,24 @@ module Provider
module VerificationResults
class PublishAll
- def self.call pact_sources, test_results_hash
- new(pact_sources, test_results_hash).call
+ def self.call pact_sources, test_results_hash, options = {}
+ new(pact_sources, test_results_hash, options).call
end
- def initialize pact_sources, test_results_hash
+ def initialize pact_sources, test_results_hash, options = {}
@pact_sources = pact_sources
@test_results_hash = test_results_hash
+ @options = options
end
def call
verification_results.collect do | (pact_source, verification_result) |
- Publish.call(pact_source, verification_result)
+ published = false
+ begin
+ published = Publish.call(pact_source, verification_result, { verbose: options[:verbose] })
+ ensure
+ print_after_verification_notices(pact_source, verification_result, published)
+ end
end
end
@@ -29,7 +35,15 @@ def verification_results
end
end
- attr_reader :pact_sources, :test_results_hash
+ def print_after_verification_notices(pact_source, verification_result, published)
+ if pact_source.uri.metadata[:notices]
+ pact_source.uri.metadata[:notices].after_verification_notices_text(verification_result.success, published).each do | text |
+ Pact.configuration.output_stream.puts "DEBUG: #{text}"
+ end
+ end
+ end
+
+ attr_reader :pact_sources, :test_results_hash, :options
end
end
end
diff --git a/lib/pact/provider/verification_results/verification_result.rb b/lib/pact/provider/verification_results/verification_result.rb
index a39f8ade..4e746cb9 100644
--- a/lib/pact/provider/verification_results/verification_result.rb
+++ b/lib/pact/provider/verification_results/verification_result.rb
@@ -4,6 +4,7 @@ module Pact
module Provider
module VerificationResults
class VerificationResult
+ attr_reader :success, :provider_application_version, :test_results_hash
def initialize publishable, success, provider_application_version, test_results_hash
@publishable = publishable
@@ -31,10 +32,6 @@ def to_json(options = {})
def to_s
"[success: #{success}, providerApplicationVersion: #{provider_application_version}]"
end
-
- private
-
- attr_reader :success, :provider_application_version, :test_results_hash
end
end
end
diff --git a/lib/pact/provider/world.rb b/lib/pact/provider/world.rb
index 73b0effd..f6168d36 100644
--- a/lib/pact/provider/world.rb
+++ b/lib/pact/provider/world.rb
@@ -14,7 +14,7 @@ def self.clear_provider_world
module Provider
class World
- attr_accessor :pact_sources
+ attr_accessor :pact_sources, :failed_examples, :verbose
def provider_states
@provider_states_proxy ||= Pact::Provider::State::ProviderStateProxy.new
@@ -43,7 +43,7 @@ def pact_uri_sources
end
def pact_uris_from_pact_uri_sources
- pact_uri_sources.collect{| pact_uri_source| pact_uri_source.call }.flatten
+ pact_uri_sources.collect(&:call).flatten
end
end
end
diff --git a/lib/pact/tasks/task_helper.rb b/lib/pact/tasks/task_helper.rb
index 1daaab0a..3f533d54 100644
--- a/lib/pact/tasks/task_helper.rb
+++ b/lib/pact/tasks/task_helper.rb
@@ -7,6 +7,7 @@ module Pact
module TaskHelper
PACT_INTERACTION_RERUN_COMMAND = "bundle exec rake pact:verify:at[] PACT_DESCRIPTION=\"\" PACT_PROVIDER_STATE=\"\""
+ PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER = "bundle exec rake pact:verify:at[] PACT_BROKER_INTERACTION_ID=\"\""
extend self
@@ -34,6 +35,8 @@ def verify_command pact_helper, pact_uri, rspec_opts, verification_opts
command_parts << "--backtrace" if ENV['BACKTRACE'] == 'true'
command_parts << "--description #{Shellwords.escape(ENV['PACT_DESCRIPTION'])}" if ENV['PACT_DESCRIPTION']
command_parts << "--provider-state #{Shellwords.escape(ENV['PACT_PROVIDER_STATE'])}" if ENV['PACT_PROVIDER_STATE']
+ command_parts << "--pact-broker-interaction-id #{Shellwords.escape(ENV['PACT_BROKER_INTERACTION_ID'])}" if ENV['PACT_BROKER_INTERACTION_ID']
+ command_parts << "--interaction-index #{Shellwords.escape(ENV['PACT_INTERACTION_INDEX'])}" if ENV['PACT_INTERACTION_INDEX']
command_parts.flatten.join(" ")
end
@@ -41,7 +44,9 @@ def execute_cmd command
Pact.configuration.output_stream.puts command
temporarily_set_env_var 'PACT_EXECUTING_LANGUAGE', 'ruby' do
temporarily_set_env_var 'PACT_INTERACTION_RERUN_COMMAND', PACT_INTERACTION_RERUN_COMMAND do
- exit_status = system(command) ? 0 : 1
+ temporarily_set_env_var 'PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER', PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER do
+ exit_status = system(command) ? 0 : 1
+ end
end
end
end
diff --git a/lib/pact/tasks/verification_task.rb b/lib/pact/tasks/verification_task.rb
index 644abf4d..bac650ab 100644
--- a/lib/pact/tasks/verification_task.rb
+++ b/lib/pact/tasks/verification_task.rb
@@ -31,6 +31,7 @@ class VerificationTask < ::Rake::TaskLib
attr_reader :pact_spec_configs
attr_accessor :rspec_opts
attr_accessor :ignore_failures
+ attr_accessor :_pact_helper
def initialize(name)
@rspec_opts = nil
@@ -41,6 +42,10 @@ def initialize(name)
rake_task
end
+ def pact_helper(pact_helper)
+ @pact_spec_configs << { pact_helper: pact_helper }
+ end
+
def uri(uri, options = {})
@pact_spec_configs << {uri: uri, pact_helper: options[:pact_helper]}
end
@@ -82,6 +87,7 @@ def rake_task
Pact::TaskHelper.handle_verification_failure do
exit_statuses.count{ | status | status != 0 }
end
+
end
end
end
diff --git a/lib/pact/utils/string.rb b/lib/pact/utils/string.rb
new file mode 100644
index 00000000..ddead806
--- /dev/null
+++ b/lib/pact/utils/string.rb
@@ -0,0 +1,35 @@
+# Can't use refinements because of Travelling Ruby
+
+module Pact
+ module Utils
+ module String
+
+ extend self
+
+ # ripped from rubyworks/facets, thank you
+ def camelcase(string, *separators)
+ case separators.first
+ when Symbol, TrueClass, FalseClass, NilClass
+ first_letter = separators.shift
+ end
+
+ separators = ['_', '\s'] if separators.empty?
+
+ str = string.dup
+
+ separators.each do |s|
+ str = str.gsub(/(?:#{s}+)([a-z])/){ $1.upcase }
+ end
+
+ case first_letter
+ when :upper, true
+ str = str.gsub(/(\A|\s)([a-z])/){ $1 + $2.upcase }
+ when :lower, false
+ str = str.gsub(/(\A|\s)([A-Z])/){ $1 + $2.downcase }
+ end
+
+ str
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/lib/pact/version.rb b/lib/pact/version.rb
index 13fbfa62..7d65286b 100644
--- a/lib/pact/version.rb
+++ b/lib/pact/version.rb
@@ -1,4 +1,4 @@
# Remember to bump pact-provider-proxy when this changes major version
module Pact
- VERSION = "1.37.0"
+ VERSION = "1.61.0"
end
diff --git a/pact.gemspec b/pact.gemspec
index cdb486fd..b49fd3ce 100644
--- a/pact.gemspec
+++ b/pact.gemspec
@@ -1,5 +1,4 @@
-# -*- encoding: utf-8 -*-
-lib = File.expand_path('../lib', __FILE__)
+lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'pact/version'
@@ -15,30 +14,35 @@ Gem::Specification.new do |gem|
gem.required_ruby_version = '>= 2.0'
gem.files = `git ls-files bin lib pact.gemspec CHANGELOG.md LICENSE.txt`.split($/)
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ["lib"]
gem.license = 'MIT'
- gem.add_runtime_dependency 'randexp', '~> 0.1.7'
- gem.add_runtime_dependency 'rspec', '>=2.14'
- gem.add_runtime_dependency 'rack-test', '~> 0.6', '>= 0.6.3'
- gem.add_runtime_dependency 'thor'
- gem.add_runtime_dependency 'json','> 1.8.5'
- gem.add_runtime_dependency 'webrick'
+ gem.metadata = {
+ 'changelog_uri' => 'https://github.com/pact-foundation/pact-ruby/blob/master/CHANGELOG.md',
+ 'source_code_uri' => 'https://github.com/pact-foundation/pact-ruby',
+ 'bug_tracker_uri' => 'https://github.com/pact-foundation/pact-ruby/issues',
+ 'documentation_uri' => 'https://github.com/pact-foundation/pact-ruby/blob/master/README.md'
+ }
+
+ gem.add_runtime_dependency 'rspec', '~> 3.0'
+ gem.add_runtime_dependency 'rack-test', '>= 0.6.3', '< 2.0.0'
+ gem.add_runtime_dependency 'thor', '>= 0.20', '< 2.0'
+ gem.add_runtime_dependency 'webrick', '~> 1.3'
gem.add_runtime_dependency 'term-ansicolor', '~> 1.0'
- gem.add_runtime_dependency 'pact-support', '~> 1.8'
- gem.add_runtime_dependency 'pact-mock_service', '~> 2.10'
+ gem.add_runtime_dependency 'pact-support', '~> 1.16', '>= 1.16.9'
+ gem.add_runtime_dependency 'pact-mock_service', '~> 3.0', '>= 3.3.1'
- gem.add_development_dependency 'rake', '~> 10.0.3'
+ gem.add_development_dependency 'rake', '~> 13.0'
gem.add_development_dependency 'webmock', '~> 3.0'
- #gem.add_development_dependency 'pry-byebug'
gem.add_development_dependency 'fakefs', '0.5' # 0.6.0 blows up
gem.add_development_dependency 'hashie', '~> 2.0'
- gem.add_development_dependency 'activesupport'
+ gem.add_development_dependency 'activesupport', '~> 5.2'
gem.add_development_dependency 'faraday', '~> 0.13'
- gem.add_development_dependency 'appraisal', '~> 2.2'
gem.add_development_dependency 'conventional-changelog', '~> 1.3'
gem.add_development_dependency 'bump', '~> 0.5'
+ gem.add_development_dependency 'pact-message', '~> 0.8'
+ gem.add_development_dependency 'rspec-its', '~> 1.3'
end
diff --git a/script/release.sh b/script/release.sh
index 8b19ca25..0fee7506 100755
--- a/script/release.sh
+++ b/script/release.sh
@@ -1,9 +1,9 @@
#!/bin/bash
set -e
-
+unset X_PACT_DEVELOPMENT
bundle exec bump ${1:-minor} --no-commit
-bundle exec appraisal update
bundle exec rake generate_changelog
-git add CHANGELOG.md lib/pact/version.rb gemfiles
+git add CHANGELOG.md lib/pact/version.rb
git commit -m "chore(release): version $(ruby -r ./lib/pact/version.rb -e "puts Pact::VERSION")" && git push
-bundle exec rake release
+bundle exec rake tag_for_release
+echo "Releasing from https://travis-ci.org/pact-foundation/pact-ruby"
diff --git a/script/trigger-release.sh b/script/trigger-release.sh
new file mode 100755
index 00000000..086ed39c
--- /dev/null
+++ b/script/trigger-release.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+set -x
+# Script to trigger release of gem via the pact-foundation/release-gem action
+# Requires a Github API token with repo scope stored in the
+# environment variable GITHUB_ACCESS_TOKEN_FOR_PF_RELEASES
+
+: "${GITHUB_ACCESS_TOKEN_FOR_PF_RELEASES:?Please set environment variable GITHUB_ACCESS_TOKEN_FOR_PF_RELEASES}"
+
+if [ -n "$1" ]; then
+ increment="\"${1}\""
+else
+ increment="null"
+fi
+
+repository_slug=$(git remote get-url $(git remote show) | cut -d':' -f2 | sed 's/\.git//')
+
+output=$(curl -v https://api.github.com/repos/${repository_slug}/dispatches \
+ -H 'Accept: application/vnd.github.everest-preview+json' \
+ -H "Authorization: Bearer $GITHUB_ACCESS_TOKEN_FOR_PF_RELEASES" \
+ -d "{\"event_type\": \"release-triggered\", \"client_payload\": {\"increment\": ${increment}}}" 2>&1)
+
+if ! echo "${output}" | grep "HTTP\/.* 204" > /dev/null; then
+ echo "$output" | sed "s/${GITHUB_ACCESS_TOKEN_FOR_PF_RELEASES}/********/g"
+ echo "Failed to trigger release"
+ exit 1
+else
+ echo "Release workflow triggered"
+fi
+
+echo "See https://github.com/${repository_slug}/actions?query=workflow%3A%22Release+gem%22"
diff --git a/spec/features/consumer_with_file_upload_spec.rb b/spec/features/consumer_with_file_upload_spec.rb
index 41687003..c8aa174b 100644
--- a/spec/features/consumer_with_file_upload_spec.rb
+++ b/spec/features/consumer_with_file_upload_spec.rb
@@ -32,12 +32,11 @@
let(:do_request) { connection.post { |req| req.body = payload } }
- describe "when the content matches" do
-
- let(:body) do
- "-------------RubyMultipartPost-05e76cbc2adb42ac40344eb9b35e98bc\r\nContent-Disposition: form-data; name=\"file\"; filename=\"text.txt\"\r\nContent-Length: 14\r\nContent-Type: text/plain\r\nContent-Transfer-Encoding: binary\r\n\r\nThis is a file\r\n-------------RubyMultipartPost-05e76cbc2adb42ac40344eb9b35e98bc--\r\n\r\n"
- end
+ let(:body) do
+ "-------------RubyMultipartPost-05e76cbc2adb42ac40344eb9b35e98bc\r\nContent-Disposition: form-data; name=\"file\"; filename=\"text.txt\"\r\nContent-Length: 14\r\nContent-Type: text/plain\r\nContent-Transfer-Encoding: binary\r\n\r\n#{File.read(file_to_upload)}\r\n-------------RubyMultipartPost-05e76cbc2adb42ac40344eb9b35e98bc--\r\n"
+ end
+ describe "when the content matches" do
it "returns the mocked response and verification passes" do
file_upload_service.
upon_receiving("a request to upload a file").with({
@@ -60,22 +59,15 @@
end
describe "when the content does not match" do
-
- let(:body) do
- "-------------RubyMultipartPost-05e76cbc2adb42ac40344eb9b35e98bc\r\nContent-Disposition: form-data; name=\"file\"; filename=\"TEXT.txt\"\r\nContent-Length: 14\r\nContent-Type: text/plain\r\nContent-Transfer-Encoding: binary\r\n\r\nThis is a file\r\n-------------RubyMultipartPost-05e76cbc2adb42ac40344eb9b35e98bc--\r\n\r\n"
- end
-
it "the verification fails" do
file_upload_service.
upon_receiving("a request to upload another file").with({
method: :post,
path: '/files',
- query: "foo=bar",
- body: body,
+ body: body.gsub('text.txt', 'wrong.txt'),
headers: {
"Content-Type" => Pact.term(/multipart\/form\-data/, "multipart/form-data; boundary=-----------RubyMultipartPost-05e76cbc2adb42ac40344eb9b35e98bc"),
- "Content-Length" => Pact.like("299"),
- "Missing" => "header"
+ "Content-Length" => Pact.like("299")
}
}).
will_respond_with({
diff --git a/spec/features/foo_bar_spec.rb b/spec/features/foo_bar_spec.rb
index 0ffd6882..ce424d45 100644
--- a/spec/features/foo_bar_spec.rb
+++ b/spec/features/foo_bar_spec.rb
@@ -17,6 +17,7 @@
has_pact_with "Bar" do
mock_service :bar_service do
pact_specification_version "2"
+ host "127.0.0.1"
port 4638
end
end
diff --git a/spec/features/production_spec.rb b/spec/features/production_spec.rb
index ca4ccef8..c2afde97 100644
--- a/spec/features/production_spec.rb
+++ b/spec/features/production_spec.rb
@@ -2,6 +2,7 @@
require 'pact/provider/rspec'
require 'pact/consumer_contract'
require 'features/provider_states/zebras'
+require 'pact/provider/pact_source'
module Pact::Provider
@@ -81,7 +82,10 @@ def call(env)
end
end
- honour_consumer_contract pact
+ pact_uri = Pact::Provider::PactURI.new("http://dummy-uri")
+ pact_source = Pact::Provider::PactSource.new(pact_uri)
+
+ honour_consumer_contract pact, pact_uri: pact_uri, pact_source: pact_source
end
@@ -115,8 +119,10 @@ def call(env)
end
end
+ pact_uri = Pact::Provider::PactURI.new("http://dummy-uri")
+ pact_source = Pact::Provider::PactSource.new(pact_uri)
- honour_consumer_contract consumer_contract
+ honour_consumer_contract consumer_contract, pact_uri: pact_uri, pact_source: pact_source
end
context "that is a string" do
@@ -147,8 +153,10 @@ def call(env)
end
end
+ pact_uri = Pact::Provider::PactURI.new("http://dummy-uri")
+ pact_source = Pact::Provider::PactSource.new(pact_uri)
- honour_consumer_contract consumer_contract
+ honour_consumer_contract consumer_contract, pact_uri: pact_uri, pact_source: pact_source
end
end
diff --git a/spec/integration/publish_verification_spec.rb b/spec/integration/publish_verification_spec.rb
index 0d3f9a4d..0f996b31 100644
--- a/spec/integration/publish_verification_spec.rb
+++ b/spec/integration/publish_verification_spec.rb
@@ -2,7 +2,6 @@
require 'pact/provider/pact_uri'
describe "publishing verifications" do
-
before do
allow(Pact.configuration).to receive(:provider).and_return(provider_configuration)
allow($stdout).to receive(:puts)
@@ -12,17 +11,24 @@
double('provider_configuration',
application_version: '1.2.3',
publish_verification_results?: true,
+ branch: nil,
tags: [])
end
let(:pact_sources) do
- [instance_double('Pact::Provider::PactSource', pact_hash: pact_hash, uri: pact_uri)]
+ [instance_double('Pact::Provider::PactSource', consumer_contract: consumer_contract, pact_hash: pact_hash, uri: pact_uri)]
end
let(:pact_uri) do
- instance_double('Pact::Provider::PactURI', uri: 'pact.json', basic_auth?: false)
+ instance_double('Pact::Provider::PactURI', uri: 'pact.json', options: {}, metadata: metadata)
end
+ let(:consumer_contract) { instance_double('Pact::ConsumerContract', interactions: [pact_interaction])}
+ let(:pact_interaction) { instance_double('Pact::Interaction', _id: "1") }
+
+ let(:metadata) { { notices: notices} }
+ let(:notices) { instance_double('Pact::PactBroker::Notices', after_verification_notices_text: ['hello'] ) }
+
let(:pact_hash) do
{
'interactions' => [{}],
@@ -50,7 +56,8 @@
{
testDescription: '1',
status: 'passed',
- pact_uri: pact_uri
+ pact_uri: pact_uri,
+ pact_interaction: pact_interaction
}
]
}
@@ -59,7 +66,7 @@
subject { Pact::Provider::VerificationResults::PublishAll.call(pact_sources, test_results_hash) }
let!(:request) do
- stub_request(:post, 'http://publish').to_return(status: 200, body: created_verification_body)
+ stub_request(:post, 'http://publish').to_return(status: 200, headers: {'Content-Type' => 'application/hal+json'}, body: created_verification_body)
end
it "publishes the results" do
diff --git a/spec/lib/pact/cli/spec_criteria_spec.rb b/spec/lib/pact/cli/spec_criteria_spec.rb
index c9bcb214..9ba242fe 100644
--- a/spec/lib/pact/cli/spec_criteria_spec.rb
+++ b/spec/lib/pact/cli/spec_criteria_spec.rb
@@ -3,34 +3,41 @@
module Pact
module Cli
describe SpecCriteria do
-
describe "#spec_criteria" do
let(:env_description) { "pact description set in ENV"}
let(:env_provider_state) { "provider state set in ENV"}
- let(:env_criteria){ {:description=>/#{env_description}/, :provider_state=>/#{env_provider_state}/} }
+ let(:env_pact_broker_interaction_id) { "interaction id set in ENV" }
+ let(:interaction_index) { 2 }
+ let(:env_criteria) do
+ {
+ :description=>/#{env_description}/,
+ :provider_state=>/#{env_provider_state}/,
+ :_id => env_pact_broker_interaction_id,
+ :index => interaction_index
+ }
+ end
let(:defaults) { {:description => default_description, :provider_state => default_provider_state} }
let(:subject) { Pact::App.new }
context "when options are defined" do
- before do
-
- allow(ENV).to receive(:[])
- allow(ENV).to receive(:[]).with("PACT_DESCRIPTION").and_return(env_description)
- allow(ENV).to receive(:[]).with("PACT_PROVIDER_STATE").and_return(env_provider_state)
+ let(:options) do
+ {
+ description: env_description,
+ provider_state: env_provider_state,
+ pact_broker_interaction_id: env_pact_broker_interaction_id,
+ interaction_index: interaction_index
+ }
end
- let(:options) { {description: env_description, provider_state: env_provider_state} }
-
it "returns the env vars as regexes" do
expect(Pact::Cli::SpecCriteria.call(options)).to eq(env_criteria)
end
end
context "when ENV variables are not defined" do
-
let(:options) { {} }
it "returns an empty hash" do
@@ -39,8 +46,7 @@ module Cli
end
context "when provider state is an empty string" do
-
- let(:options) { {provider_state: ''} }
+ let(:options) { { provider_state: '' } }
it "returns a nil provider state so that it matches a nil provider state on the interaction" do
expect(Pact::Cli::SpecCriteria.call(options)[:provider_state]).to be_nil
diff --git a/spec/lib/pact/consumer/consumer_contract_builder_spec.rb b/spec/lib/pact/consumer/consumer_contract_builder_spec.rb
index 62e867b8..ebb5789a 100644
--- a/spec/lib/pact/consumer/consumer_contract_builder_spec.rb
+++ b/spec/lib/pact/consumer/consumer_contract_builder_spec.rb
@@ -16,6 +16,7 @@ module Consumer
provider_name: provider_name,
pactfile_write_mode: :overwrite,
port: 2222,
+ host: 'localhost',
pact_dir: pact_dir)
end
@@ -70,6 +71,7 @@ module Consumer
pact_dir: './spec/pacts',
consumer_name: consumer_name,
provider_name: provider_name,
+ host: 'localhost',
port: 1234
)
end
diff --git a/spec/lib/pact/consumer/interaction_builder_spec.rb b/spec/lib/pact/consumer/interaction_builder_spec.rb
index a16ab53c..0927da9e 100644
--- a/spec/lib/pact/consumer/interaction_builder_spec.rb
+++ b/spec/lib/pact/consumer/interaction_builder_spec.rb
@@ -88,6 +88,21 @@ module Consumer
subject.will_respond_with response
end
end
+
+ describe "without_writing_to_pact" do
+ it "sets the write_to_pact key to false on metadata" do
+ mock_metadata = {}
+ expect(interaction).to receive(:metadata).and_return(nil, mock_metadata)
+
+ subject.without_writing_to_pact
+
+ expect(mock_metadata).to eq({ write_to_pact: false })
+ end
+
+ it "returns itself" do
+ expect(subject.without_writing_to_pact).to be(subject)
+ end
+ end
end
end
end
diff --git a/spec/lib/pact/hal/authorization_header_redactor_spec.rb b/spec/lib/pact/hal/authorization_header_redactor_spec.rb
new file mode 100644
index 00000000..1b43eb70
--- /dev/null
+++ b/spec/lib/pact/hal/authorization_header_redactor_spec.rb
@@ -0,0 +1,15 @@
+require 'pact/hal/authorization_header_redactor'
+
+module Pact
+ module Hal
+ describe AuthorizationHeaderRedactor do
+ let(:stream) { StringIO.new }
+ let(:stream_redactor) { AuthorizationHeaderRedactor.new(stream) }
+
+ it "redacts the authorizaton header" do
+ stream_redactor << "\\r\\nAuthorization: Bearer TOKEN\\r\\n"
+ expect(stream.string).to eq "\\r\\nAuthorization: [redacted]\\r\\n"
+ end
+ end
+ end
+end
diff --git a/spec/lib/pact/hal/entity_spec.rb b/spec/lib/pact/hal/entity_spec.rb
index 4eb4744d..9663565f 100644
--- a/spec/lib/pact/hal/entity_spec.rb
+++ b/spec/lib/pact/hal/entity_spec.rb
@@ -9,7 +9,7 @@ module Hal
end
let(:provider_response) do
- double('response', body: provider_hash, success?: true)
+ double('response', body: provider_hash, success?: true, json?: true)
end
let(:provider_hash) do
@@ -42,7 +42,7 @@ module Hal
let(:post_provider) { entity.post("pb:provider", {'some' => 'data'} ) }
it "executes an http request" do
- expect(http_client).to receive(:post).with("http://provider", '{"some":"data"}', {})
+ expect(http_client).to receive(:post).with("http://provider", '{"some":"data"}', {"Accept" => "application/hal+json", "Content-Type" => "application/json"})
post_provider
end
@@ -54,7 +54,7 @@ module Hal
let(:post_provider) { entity._link("pb:version-tag").expand(version: "1", tag: "prod").post({'some' => 'data'} ) }
it "posts to the expanded URL" do
- expect(http_client).to receive(:post).with("http://provider/version/1/tag/prod", '{"some":"data"}', {})
+ expect(http_client).to receive(:post).with("http://provider/version/1/tag/prod", '{"some":"data"}', {"Accept" => "application/hal+json", "Content-Type" => "application/json"})
post_provider
end
end
diff --git a/spec/lib/pact/hal/http_client_spec.rb b/spec/lib/pact/hal/http_client_spec.rb
index de45d6d3..f79f03cb 100644
--- a/spec/lib/pact/hal/http_client_spec.rb
+++ b/spec/lib/pact/hal/http_client_spec.rb
@@ -3,18 +3,17 @@
module Pact
module Hal
describe HttpClient do
-
before do
allow(Retry).to receive(:until_true) { |&block| block.call }
end
- subject { HttpClient.new(username: 'foo', password: 'bar' ) }
+ subject { HttpClient.new(username: 'foo', password: 'bar') }
describe "get" do
let!(:request) do
stub_request(:get, "http://example.org/").
with( headers: {
- 'Accept'=>'application/hal+json',
+ 'Accept'=>'*/*',
'Authorization'=>'Basic Zm9vOmJhcg=='
}).
to_return(status: 200, body: response_body, headers: {'Content-Type' => 'application/json'})
@@ -40,8 +39,38 @@ module Hal
do_get
expect(request).to have_been_made
end
+
+ context "when there are existing params on the URL" do
+ let!(:request) do
+ stub_request(:get, "http://example.org/?foo=hello+world&bar=wiffle&a=b").
+ to_return(status: 200)
+ end
+
+ let(:do_get) { subject.get('http://example.org?foo=bar&a=b', { 'foo' => 'hello world', 'bar' => 'wiffle' }) }
+
+ it "merges them in" do
+ do_get
+ expect(request).to have_been_made
+ end
+ end
end
+ context "with broker token set" do
+ let!(:request) do
+ stub_request(:any, /.*/).
+ with( headers: {
+ 'Authorization'=>'Bearer mytoken123'
+ }).
+ to_return(status: 200, body: response_body, headers: {'Content-Type' => 'application/json'})
+ end
+
+ subject { HttpClient.new(token: 'mytoken123') }
+
+ it "sets a bearer authorization header" do
+ do_get
+ expect(request).to have_been_made
+ end
+ end
it "retries on failure" do
expect(Retry).to receive(:until_true)
@@ -57,9 +86,8 @@ module Hal
let!(:request) do
stub_request(:post, "http://example.org/").
with( headers: {
- 'Accept'=>'application/hal+json',
- 'Authorization'=>'Basic Zm9vOmJhcg==',
- 'Content-Type'=>'application/json'
+ 'Accept'=>'*/*',
+ 'Authorization'=>'Basic Zm9vOmJhcg=='
},
body: request_body).
to_return(status: 200, body: response_body, headers: {'Content-Type' => 'application/json'})
diff --git a/spec/lib/pact/hal/link_spec.rb b/spec/lib/pact/hal/link_spec.rb
index 3b560904..441153f1 100644
--- a/spec/lib/pact/hal/link_spec.rb
+++ b/spec/lib/pact/hal/link_spec.rb
@@ -10,7 +10,7 @@ module Hal
end
let(:response) do
- instance_double('Pact::Hal::HttpClient::Response', success?: success, body: response_body, raw_body: response_body.to_json)
+ instance_double('Pact::Hal::HttpClient::Response', success?: success, body: response_body, raw_body: response_body.to_json, json?: true)
end
let(:success) { true }
@@ -19,9 +19,10 @@ module Hal
instance_double('Pact::Hal::Entity')
end
+ let(:href) { 'http://foo/{bar}' }
let(:attrs) do
{
- 'href' => 'http://foo/{bar}',
+ 'href' => href,
'title' => 'title',
method: :post
}
@@ -78,7 +79,7 @@ module Hal
let(:do_get) { subject.get({ 'foo' => 'bar' }) }
it "executes an HTTP Get request" do
- expect(http_client).to receive(:get).with('http://foo/{bar}', { 'foo' => 'bar' }, {})
+ expect(http_client).to receive(:get).with('http://foo/{bar}', { 'foo' => 'bar' }, { 'Accept' => 'application/hal+json' })
do_get
end
end
@@ -88,12 +89,20 @@ module Hal
context "with custom headers" do
it "executes an HTTP Post request with the custom headers" do
- expect(http_client).to receive(:post).with('http://foo/{bar}', '{"foo":"bar"}', { 'Accept' => 'foo' })
+ expect(http_client).to receive(:post).with('http://foo/{bar}', '{"foo":"bar"}', { 'Accept' => 'foo', 'Content-Type' => 'application/json' })
do_post
end
end
end
+ describe "#with_query" do
+ let(:href) { "http://example.org?a=1&b=2" }
+
+ it "returns a link with the new query merged into the existing query" do
+ expect(subject.with_query("a" => "5", "c" => "3").href).to eq "http://example.org?a=5&b=2&c=3"
+ end
+ end
+
describe "#expand" do
it "returns a duplicate Link with the expanded href" do
expect(subject.expand(bar: 'wiffle').href).to eq "http://foo/wiffle"
diff --git a/spec/lib/pact/pact_broker/fetch_pending_pacts_spec.rb b/spec/lib/pact/pact_broker/fetch_pact_uris_for_verification_spec.rb
similarity index 51%
rename from spec/lib/pact/pact_broker/fetch_pending_pacts_spec.rb
rename to spec/lib/pact/pact_broker/fetch_pact_uris_for_verification_spec.rb
index ea16ae4b..e0690663 100644
--- a/spec/lib/pact/pact_broker/fetch_pending_pacts_spec.rb
+++ b/spec/lib/pact/pact_broker/fetch_pact_uris_for_verification_spec.rb
@@ -1,8 +1,8 @@
-require 'pact/pact_broker/fetch_pending_pacts'
+require 'pact/pact_broker/fetch_pact_uris_for_verification'
module Pact
module PactBroker
- describe FetchPendingPacts do
+ describe FetchPactURIsForVerification do
describe "call" do
before do
allow(Pact.configuration).to receive(:output_stream).and_return(double('output stream').as_null_object)
@@ -11,7 +11,11 @@ module PactBroker
let(:provider) { "Foo"}
let(:broker_base_url) { "http://broker.org" }
let(:http_client_options) { {} }
- subject { FetchPendingPacts.call(provider, broker_base_url, http_client_options)}
+ let(:consumer_version_selectors) { [{ tag: "cmaster", latest: true, fallbackTag: 'blah' }] }
+ let(:provider_version_branch) { "pbranch" }
+ let(:provider_version_tags) { ["pmaster"] }
+
+ subject { FetchPactURIsForVerification.call(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options)}
context "when there is an error retrieving the index resource" do
before do
@@ -32,8 +36,20 @@ module PactBroker
end
end
- context "when the pb:pending-provider-pacts relation does not exist" do
+ context "when a single tag is provided instead of an array" do
+
+ let(:provider_version_tags) { "pmaster" }
+
+ subject { FetchPactURIsForVerification.new(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options)}
+
+ it "wraps an array around it" do
+ expect(subject.provider_version_tags).to eq ["pmaster"]
+ end
+ end
+
+ context "when the beta:provider-pacts-for-verification relation does not exist" do
before do
+ allow(FetchPacts).to receive(:call)
stub_request(:get, "http://broker.org/").to_return(status: 200, body: response_body, headers: response_headers)
end
@@ -44,8 +60,9 @@ module PactBroker
}.to_json
end
- it "returns an empty list" do
- expect(subject).to eq []
+ it "calls the old fetch pacts code" do
+ expect(FetchPacts).to receive(:call).with(provider, [{ name: "cmaster", all: false, fallback: "blah" }], broker_base_url, http_client_options)
+ subject
end
end
end
diff --git a/spec/lib/pact/pact_broker/fetch_pacts_spec.rb b/spec/lib/pact/pact_broker/fetch_pacts_spec.rb
index 096960d0..50d8fc31 100644
--- a/spec/lib/pact/pact_broker/fetch_pacts_spec.rb
+++ b/spec/lib/pact/pact_broker/fetch_pacts_spec.rb
@@ -33,7 +33,7 @@ module PactBroker
context "when there is a HAL relation missing" do
before do
- stub_request(:get, "http://broker.org/").to_return(status: 200, body: {"_links" => {} }.to_json, headers: {})
+ stub_request(:get, "http://broker.org/").to_return(status: 200, body: {"_links" => {} }.to_json, headers: {"Content-Type" => "application/hal+json"})
end
it "raises a Pact::Error" do
diff --git a/spec/lib/pact/pact_broker/notices_spec.rb b/spec/lib/pact/pact_broker/notices_spec.rb
new file mode 100644
index 00000000..31a69e86
--- /dev/null
+++ b/spec/lib/pact/pact_broker/notices_spec.rb
@@ -0,0 +1,58 @@
+require 'pact/pact_broker/notices'
+
+module Pact
+ module PactBroker
+ describe Notices do
+
+ let(:notice_hashes) do
+ [
+ { text: "foo", when: "before_verification" }
+ ]
+ end
+
+ subject(:notices) { Notices.new(notice_hashes) }
+
+ it "behaves like an array" do
+ expect(subject.size).to eq notice_hashes.size
+ end
+
+ describe "before_verification_notices" do
+ let(:notice_hashes) do
+ [
+ { text: "foo", when: "before_verification" },
+ { text: "bar", when: "blah" },
+ ]
+ end
+
+ its(:before_verification_notices_text) { is_expected.to eq [ "foo" ] }
+ end
+
+ describe "after_verification_notices_text" do
+ let(:notice_hashes) do
+ [
+ { text: "foo", when: "after_verification:success_false_published_true" },
+ { text: "bar", when: "blah" },
+ ]
+ end
+
+ subject { notices.after_verification_notices_text(false, true) }
+
+ it { is_expected.to eq [ "foo" ] }
+ end
+
+ describe "after_verification_notices" do
+ let(:notice_hashes) do
+ [
+ { text: "meep", when: "after_verification" },
+ { text: "foo", when: "after_verification:success_false_published_true" },
+ { text: "bar", when: "blah" },
+ ]
+ end
+
+ subject { notices.after_verification_notices(false, true) }
+
+ it { is_expected.to eq [{ text: "meep", when: "after_verification" }, { text: "foo", when: "after_verification" }] }
+ end
+ end
+ end
+end
diff --git a/spec/lib/pact/pact_broker/pact_selection_description_spec.rb b/spec/lib/pact/pact_broker/pact_selection_description_spec.rb
new file mode 100644
index 00000000..cb93143c
--- /dev/null
+++ b/spec/lib/pact/pact_broker/pact_selection_description_spec.rb
@@ -0,0 +1,91 @@
+require 'pact/pact_broker/pact_selection_description'
+
+module Pact
+ module PactBroker
+ describe PactSelectionDescription do
+ include PactSelectionDescription
+
+ describe "#pact_selection_description" do
+ let(:provider) { "Bar" }
+ let(:consumer_version_selectors) { [{ tag: "cmaster", latest: true, fallbackTag: "master" }, { tag: "prod" }] }
+ let(:options) do
+ {
+ include_wip_pacts_since: "2020-01-01"
+ }
+ end
+ let(:broker_base_url) { "http://broker" }
+
+ subject { pact_selection_description(provider, consumer_version_selectors, options, broker_base_url) }
+
+ it { is_expected.to eq "Fetching pacts for Bar from http://broker with the selection criteria: latest for tag cmaster (or master if not found), all for tag prod, work in progress pacts created after 2020-01-01" }
+
+ describe "when consumer selector specifies a consumer name" do
+ let(:consumer_version_selectors) { [{ tag: "cmaster", latest: true, consumer: "Foo" }] }
+
+ it { is_expected.to eq "Fetching pacts for Bar from http://broker with the selection criteria: latest for tag cmaster of Foo, work in progress pacts created after 2020-01-01" }
+ end
+
+ describe "for branch" do
+ let(:consumer_version_selectors) { [{ branch: "feat/x", consumer: "Foo" }] }
+
+ it { is_expected.to include "latest from branch feat/x of Foo" }
+ end
+
+ describe "for main branch" do
+ let(:consumer_version_selectors) { [{ mainBranch: true, consumer: "Foo" }] }
+
+ it { is_expected.to include "latest from main branch of Foo" }
+ end
+
+ describe "for deployedOrReleased" do
+ let(:consumer_version_selectors) { [{ deployedOrReleased: true }] }
+
+ it { is_expected.to include "currently deployed or released" }
+ end
+
+ describe "for released in environment" do
+ let(:consumer_version_selectors) { [{ released: true, environment: "production" }] }
+
+ it { is_expected.to include "currently released to production" }
+ end
+
+ describe "for deployed in environment" do
+ let(:consumer_version_selectors) { [{ deployed: true, environment: "production" }] }
+
+ it { is_expected.to include "currently deployed to production" }
+ end
+
+ describe "for deployedOrReleased in environment" do
+ let(:consumer_version_selectors) { [{ deployedOrReleased: true, environment: "production" }] }
+
+ it { is_expected.to include "currently deployed or released to production" }
+ end
+
+ describe "in environment" do
+ let(:consumer_version_selectors) { [{ environment: "production" }] }
+
+ it { is_expected.to include "in production" }
+ end
+
+ describe "matching branch" do
+ let(:consumer_version_selectors) { [{ matchingBranch: true, consumer: "Foo" }] }
+
+ it { is_expected.to include "matching current branch for Foo" }
+ end
+
+ describe "matching tag" do
+ let(:consumer_version_selectors) { [{ matchingTag: true, consumer: "Foo" }] }
+
+ it { is_expected.to include "matching tag for Foo" }
+ end
+
+ describe "unknown" do
+ let(:consumer_version_selectors) { [{ branchPattern: "*foo" }] }
+
+ it { is_expected.to include "branchPattern" }
+ it { is_expected.to include "*foo" }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/pact/pact_broker_spec.rb b/spec/lib/pact/pact_broker_spec.rb
index f47a5c23..f19f99db 100644
--- a/spec/lib/pact/pact_broker_spec.rb
+++ b/spec/lib/pact/pact_broker_spec.rb
@@ -3,41 +3,22 @@
module Pact
module PactBroker
- describe ".fetch_pact_uris" do
+ describe ".fetch_pact_uris_for_verification" do
before do
- allow(Pact::PactBroker::FetchPacts).to receive(:call).and_return([pact_uri])
+ allow(Pact::PactBroker::FetchPactURIsForVerification).to receive(:call).and_return([pact_uri])
end
let(:pact_uri) { Pact::Provider::PactURI.new("http://pact") }
- subject { Pact::PactBroker.fetch_pact_uris("foo") }
-
- it "calls Pact::PactBroker::FetchPacts" do
- expect(Pact::PactBroker::FetchPacts).to receive(:call).with("foo")
- subject
- end
-
- it "returns a list of string URLs" do
- expect(subject).to eq ["http://pact"]
- end
- end
-
- describe ".fetch_pending_pact_uris" do
- before do
- allow(Pact::PactBroker::FetchPendingPacts).to receive(:call).and_return([pact_uri])
- end
-
- let(:pact_uri) { Pact::Provider::PactURI.new("http://pact") }
-
- subject { Pact::PactBroker.fetch_pending_pact_uris("foo") }
+ subject { Pact::PactBroker.fetch_pact_uris_for_verification("foo") }
it "calls Pact::PactBroker::FetchPendingPacts" do
- expect(Pact::PactBroker::FetchPendingPacts).to receive(:call).with("foo")
+ expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:call).with("foo")
subject
end
- it "returns a list of string URLs" do
- expect(subject).to eq ["http://pact"]
+ it "returns a list of pact uris" do
+ expect(subject).to eq [pact_uri]
end
end
end
diff --git a/spec/lib/pact/provider/configuration/message_provider_dsl_spec.rb b/spec/lib/pact/provider/configuration/message_provider_dsl_spec.rb
new file mode 100644
index 00000000..b41f5895
--- /dev/null
+++ b/spec/lib/pact/provider/configuration/message_provider_dsl_spec.rb
@@ -0,0 +1,198 @@
+require "spec_helper"
+require "pact/provider/configuration/service_provider_dsl"
+require "pact/provider/pact_uri"
+require "pact/pact_broker/fetch_pacts"
+
+module Pact
+ module Provider
+ module Configuration
+ describe MessageProviderDSL do
+ describe "initialize" do
+ context "with an object instead of a block" do
+ subject do
+ described_class.build "name" do
+ app "blah"
+ end
+ end
+
+ it "raises an error" do
+ expect { subject }.to raise_error /wrong number of arguments/
+ end
+ end
+
+ end
+
+ describe "validate" do
+ context "when no name is provided" do
+ subject do
+ described_class.new " " do
+ app { Object.new }
+ end
+ end
+
+ it "raises an error" do
+ expect { subject.send(:validate) }.to raise_error("Please provide a name for the Provider")
+ end
+ end
+
+ context "when nil name is provided" do
+ subject do
+ described_class.new nil do
+ app { Object.new }
+ end
+ end
+
+ it "raises an error" do
+ expect { subject.send(:validate) }.to raise_error(Pact::Provider::Configuration::Error, "Please provide a name for the Provider")
+ end
+ end
+
+ context "when publish_verification_results is true" do
+ context "when no application version is provided" do
+ subject do
+ described_class.build "name" do
+ publish_verification_results true
+ end
+ end
+
+ it "raises an error" do
+ expect { subject.send(:validate) }.to raise_error(Pact::Provider::Configuration::Error, "Please set the app_version when publish_verification_results is true")
+ end
+ end
+
+ context "when an application version is provided" do
+ subject do
+ described_class.build "name" do
+ app_version "1.2.3"
+ publish_verification_results true
+ end
+ end
+
+ it "does not raise an error" do
+ expect { subject.send(:validate) }.to_not raise_error
+ end
+ end
+ end
+ end
+
+ describe "honours_pact_with" do
+ before do
+ Pact.clear_provider_world
+ end
+ let(:pact_url) { "blah" }
+
+ context "with no optional params" do
+ subject do
+ described_class.build "some-provider" do
+ app {}
+ honours_pact_with "some-consumer" do
+ pact_uri pact_url
+ end
+ end
+ end
+
+ it "adds a verification to the Pact.provider_world" do
+ subject
+ pact_uri = Pact::Provider::PactURI.new(pact_url)
+ expect(Pact.provider_world.pact_verifications.first)
+ .to eq(Pact::Provider::PactVerification.new("some-consumer", pact_uri, :head))
+ end
+ end
+
+ context "with all params specified" do
+ let(:pact_uri_options) do
+ {
+ username: "pact_user",
+ password: "pact_pw"
+ }
+ end
+ subject do
+ described_class.build "some-provider" do
+ app {}
+ honours_pact_with "some-consumer", ref: :prod do
+ pact_uri pact_url, pact_uri_options
+ end
+ end
+ end
+
+ it "adds a verification to the Pact.provider_world" do
+ subject
+ pact_uri = Pact::Provider::PactURI.new(pact_url, pact_uri_options)
+ expect(Pact.provider_world.pact_verifications.first)
+ .to eq(Pact::Provider::PactVerification.new("some-consumer", pact_uri , :prod))
+ end
+ end
+ end
+
+ describe "honours_pacts_from_pact_broker" do
+ before do
+ Pact.clear_provider_world
+ end
+ let(:pact_url) { "blah" }
+
+ context "with all params specified" do
+ let(:tag_1) { "master" }
+
+ let(:tag_2) do
+ {
+ name: "tag-name",
+ all: false,
+ fallback: "master"
+ }
+ end
+
+ let(:options) do
+ {
+ pact_broker_base_url: "some-url",
+ consumer_version_tags: [tag_1, tag_2]
+ }
+ end
+
+ subject do
+ described_class.build "some-provider" do
+ app {}
+ app_version_tags ["dev"]
+ honours_pacts_from_pact_broker do
+ end
+ end
+ end
+
+ it "builds a PactVerificationFromBroker" do
+ expect(PactVerificationFromBroker).to receive(:build).with("some-provider", nil, ["dev"])
+ subject
+ end
+ end
+ end
+
+ describe "builder" do
+ context "when builder is initialize with a object instead of a block" do
+ subject do
+ described_class.build "some-provider" do
+ builder "foo"
+ end
+ end
+
+ it "raises an error" do
+ expect { subject }.to raise_error /wrong number of arguments/
+ end
+ end
+ end
+
+ describe "CONFIG_RU_APP" do
+ context "when a config.ru file does not exist" do
+ let(:path_that_does_not_exist) { "./tmp/this/path/does/not/exist/probably" }
+
+ before do
+ allow(Pact.configuration).to receive(:config_ru_path).and_return(path_that_does_not_exist)
+ end
+
+ it "raises an error with some helpful text" do
+ expect { described_class::CONFIG_RU_APP.call }
+ .to raise_error /Could not find config\.ru file.*#{Regexp.escape(path_that_does_not_exist)}/
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/pact/provider/configuration/pact_verification_from_broker_spec.rb b/spec/lib/pact/provider/configuration/pact_verification_from_broker_spec.rb
index 095beed2..79c5c1ae 100644
--- a/spec/lib/pact/provider/configuration/pact_verification_from_broker_spec.rb
+++ b/spec/lib/pact/provider/configuration/pact_verification_from_broker_spec.rb
@@ -6,7 +6,10 @@ module Configuration
describe PactVerificationFromBroker do
describe 'build' do
let(:provider_name) {'provider-name'}
+ let(:provider_version_branch) { 'main' }
+ let(:provider_version_tags) { ['master'] }
let(:base_url) { "http://broker.org" }
+ let(:since) { "2020-01-01" }
let(:basic_auth_options) do
{
username: 'pact_broker_username',
@@ -14,26 +17,39 @@ module Configuration
}
end
let(:tags) { ['master'] }
+ let(:fetch_pacts) { double('FetchPacts') }
before do
- allow(Pact::PactBroker::FetchPacts).to receive(:new).and_return(fetch_pacts)
+ allow(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).and_return(fetch_pacts)
allow(Pact.provider_world).to receive(:add_pact_uri_source)
end
context "with valid values" do
subject do
- PactVerificationFromBroker.build(provider_name) do
+ PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do
pact_broker_base_url base_url, basic_auth_options
consumer_version_tags tags
+ enable_pending true
+ include_wip_pacts_since since
verbose true
end
end
let(:fetch_pacts) { double('FetchPacts') }
- let(:options) { basic_auth_options.merge(verbose: true) }
-
- it "creates a instance of Pact::PactBroker::FetchPacts" do
- expect(Pact::PactBroker::FetchPacts).to receive(:new).with(provider_name, tags, base_url, options)
+ let(:basic_auth_opts) { basic_auth_options.merge(verbose: true) }
+ let(:options) { { include_pending_status: true, include_wip_pacts_since: "2020-01-01" }}
+ let(:consumer_version_selectors) { [ { tag: 'master', latest: true }] }
+
+ it "creates a instance of Pact::PactBroker::FetchPactURIsForVerification" do
+ expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(
+ provider_name,
+ consumer_version_selectors,
+ provider_version_branch,
+ provider_version_tags,
+ base_url,
+ basic_auth_opts,
+ options
+ )
subject
end
@@ -41,17 +57,35 @@ module Configuration
expect(Pact.provider_world).to receive(:add_pact_uri_source).with(fetch_pacts)
subject
end
+
+ context "when since is a Date" do
+ let(:since) { Date.new(2020, 1, 1) }
+
+ it "converts it to a string" do
+ expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(
+ anything,
+ anything,
+ anything,
+ anything,
+ anything,
+ anything,
+ {
+ include_pending_status: true,
+ include_wip_pacts_since: since.xmlschema
+ }
+ )
+ subject
+ end
+ end
end
context "with a missing base url" do
subject do
- PactVerificationFromBroker.build(provider_name) do
+ PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do
end
end
- let(:fetch_pacts) { double('FetchPacts') }
-
it "raises an error" do
expect { subject }.to raise_error Pact::Error, /Please provide a pact_broker_base_url/
end
@@ -59,46 +93,87 @@ module Configuration
context "with a non array object for consumer_version_tags" do
subject do
- PactVerificationFromBroker.build(provider_name) do
+ PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do
pact_broker_base_url base_url
consumer_version_tags "master"
end
end
- let(:fetch_pacts) { double('FetchPacts') }
-
it "coerces the value into an array" do
- expect(Pact::PactBroker::FetchPacts).to receive(:new).with(anything, ["master"], anything, anything)
+ expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [{ tag: "master", latest: true}], anything, anything, anything, anything, anything)
subject
end
end
context "when no consumer_version_tags are provided" do
subject do
- PactVerificationFromBroker.build(provider_name) do
+ PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do
pact_broker_base_url base_url
end
end
- let(:fetch_pacts) { double('FetchPacts') }
+ it "creates an instance of FetchPacts with an empty array for the consumer_version_tags" do
+ expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [], anything, anything, anything, anything, anything)
+ subject
+ end
+ end
+
+ context "when the old format of selector is supplied to the consumer_verison_tags" do
+ let(:tags) { [{ name: 'main', all: true, fallback: 'fallback' }] }
+
+ subject do
+ PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do
+ pact_broker_base_url base_url
+ consumer_version_tags tags
+ end
+ end
- it "creates an instance of FetchPacts with an emtpy array for the consumer_version_tags" do
- expect(Pact::PactBroker::FetchPacts).to receive(:new).with(anything, [], anything, anything)
+ it "converts them to selectors" do
+ expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [{ tag: "main", latest: false, fallbackTag: 'fallback'}], anything, anything, anything, anything, anything)
subject
end
end
- context "when no verbose flag is provided" do
+ context "when an invalid class is used for the consumer_version_tags" do
+ let(:tags) { [true] }
+
subject do
- PactVerificationFromBroker.build(provider_name) do
+ PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do
pact_broker_base_url base_url
+ consumer_version_tags tags
end
end
- let(:fetch_pacts) { double('FetchPacts') }
+ it "raises an error" do
+ expect { subject }.to raise_error Pact::Error, "The value supplied for consumer_version_tags must be a String or a Hash. Found TrueClass"
+ end
+ end
+
+ context "when consumer_version_selectors are provided" do
+ let(:tags) { [{ tag: 'main', latest: true, fallback_tag: 'fallback' }] }
+
+ subject do
+ PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do
+ pact_broker_base_url base_url
+ consumer_version_selectors tags
+ end
+ end
+
+ it "converts the casing of the key names" do
+ expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, [{ tag: "main", latest: true, fallbackTag: 'fallback'}], anything, anything, anything, anything, anything)
+ subject
+ end
+ end
+
+ context "when no verbose flag is provided" do
+ subject do
+ PactVerificationFromBroker.build(provider_name, provider_version_branch, provider_version_tags) do
+ pact_broker_base_url base_url
+ end
+ end
- it "creates an instance of FetchPacts with verbose: false" do
- expect(Pact::PactBroker::FetchPacts).to receive(:new).with(anything, anything, anything, hash_including(verbose: false))
+ it "creates an instance of FetchPactURIsForVerification with verbose: false" do
+ expect(Pact::PactBroker::FetchPactURIsForVerification).to receive(:new).with(anything, anything, anything, anything, anything, hash_including(verbose: false), anything)
subject
end
end
diff --git a/spec/lib/pact/provider/configuration/service_provider_config_spec.rb b/spec/lib/pact/provider/configuration/service_provider_config_spec.rb
index fd5e46cb..2ea62092 100644
--- a/spec/lib/pact/provider/configuration/service_provider_config_spec.rb
+++ b/spec/lib/pact/provider/configuration/service_provider_config_spec.rb
@@ -1,21 +1,18 @@
-require 'spec_helper'
require 'pact/provider/configuration/service_provider_config'
module Pact
module Provider
module Configuration
describe ServiceProviderConfig do
-
describe "app" do
let(:app_block) { ->{ Object.new } }
- subject { ServiceProviderConfig.new("1.2.3'", [], true, &app_block) }
+ subject { ServiceProviderConfig.new("1.2.3'", "main", [], true, &app_block) }
it "should execute the app_block each time" do
expect(subject.app.object_id).to_not equal(subject.app.object_id)
end
-
end
end
end
diff --git a/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb b/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb
index d78b890f..e5b52669 100644
--- a/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb
+++ b/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb
@@ -156,13 +156,15 @@ module Configuration
subject do
ServiceProviderDSL.build 'some-provider' do
app {}
+ app_version_branch 'main'
+ app_version_tags ['dev']
honours_pacts_from_pact_broker do
end
end
end
it 'builds a PactVerificationFromBroker' do
- expect(PactVerificationFromBroker).to receive(:build).with('some-provider')
+ expect(PactVerificationFromBroker).to receive(:build).with('some-provider', 'main', ['dev'])
subject
end
end
diff --git a/spec/lib/pact/provider/help/content_spec.rb b/spec/lib/pact/provider/help/content_spec.rb
index 1aaf17af..928623ba 100644
--- a/spec/lib/pact/provider/help/content_spec.rb
+++ b/spec/lib/pact/provider/help/content_spec.rb
@@ -4,19 +4,17 @@ module Pact
module Provider
module Help
describe Content do
-
describe "#text" do
-
- let(:pact_1_json) { { some: 'json'}.to_json }
- let(:pact_2_json) { { some: 'other json'}.to_json }
- let(:pact_jsons) { [pact_1_json, pact_2_json] }
-
before do
- allow(PactDiff).to receive(:call).with(pact_1_json).and_return('diff 1')
- allow(PactDiff).to receive(:call).with(pact_2_json).and_return(nil)
+ allow(PactDiff).to receive(:call).with(pact_source_1).and_return('diff 1')
+ allow(PactDiff).to receive(:call).with(pact_source_2).and_return(nil)
end
- subject { Content.new(pact_jsons) }
+ let(:pact_source_1) { { some: 'json'}.to_json }
+ let(:pact_source_2) { { some: 'other json'}.to_json }
+ let(:pact_sources) { [pact_source_1, pact_source_2] }
+
+ subject { Content.new(pact_sources) }
it "displays the log path" do
expect(subject.text).to include Pact.configuration.log_path
@@ -29,7 +27,6 @@ module Help
it "displays the diff" do
expect(subject.text).to include 'diff 1'
end
-
end
end
end
diff --git a/spec/lib/pact/provider/help/pact_diff_spec.rb b/spec/lib/pact/provider/help/pact_diff_spec.rb
deleted file mode 100644
index e32a8fa2..00000000
--- a/spec/lib/pact/provider/help/pact_diff_spec.rb
+++ /dev/null
@@ -1,128 +0,0 @@
-require 'pact/provider/help/pact_diff'
-
-module Pact
- module Provider
- module Help
-
- describe PactDiff do
-
- let(:href) { 'http://pact-broker/diff' }
- let(:pact_json) do
- {
- '_links' => {
- 'pb:diff-previous-distinct' => {
- 'href' => href
- }
- }
- }.to_json
- end
-
- let(:output) { subject }
- let(:diff) { {some: 'diff'}.to_json }
- let(:diff_stream) { double('diff stream', read: diff) }
-
- describe ".call" do
-
- before do
- stub_request(:get, "http://pact-broker/diff")
- .to_return(status: 200, body: diff, headers: {})
- end
-
- subject { PactDiff.(pact_json) }
-
- context "when there are no links" do
- let(:pact_json) { {}.to_json }
-
- it "returns nothing" do
- subject
- expect(output).to be_nil
- end
- end
-
- context "when the diff rel is not found" do
- let(:pact_json) do
- {
- '_links' => {}
- }.to_json
- end
-
- it "returns nothing" do
- subject
- expect(output).to be_nil
- end
- end
-
- context "when the diff rel does not have a href" do
- let(:pact_json) do
- {
- '_links' => {
- 'pb:diff-previous-distinct' => {}
- }
- }.to_json
- end
-
- it "returns nothing" do
- subject
- expect(output).to be_nil
- end
- end
-
- context "when the diff rel changes because Beth can't make up her mind" do
- let(:pact_json) do
- {
- '_links' => {
- 'distinct-diff-previous' => {
- 'href' => href
- }
- }
- }.to_json
- end
-
- it "returns something" do
- subject
- expect(output).to_not be_nil
- end
- end
-
- context "when the diff resource exists" do
-
- it "returns a message" do
- subject
- expect(output).to include("The following changes")
- end
-
- it "returns the diff" do
- subject
- expect(output).to include('some')
- expect(output).to include('diff')
- end
- end
-
- context "when the diff resource doesn't exist" do
- before do
- stub_request(:get, "http://pact-broker/diff")
- .to_return(status: 404)
- end
-
- it "returns a warning" do
- subject
- expect(output).to include "Tried to retrieve diff with previous pact from #{href}, but received response code 404"
- end
- end
-
- context "when a redirect is received" do
- before do
- stub_request(:get, "http://pact-broker/diff")
- .to_return(status: 301, body: diff, headers: {})
- end
-
- xit "follows the redirect" do
- subject
- expect(output).to_not include "301"
- end
- end
- end
- end
- end
- end
-end
diff --git a/spec/lib/pact/provider/help/write_spec.rb b/spec/lib/pact/provider/help/write_spec.rb
index 9148c704..4a11a7b6 100644
--- a/spec/lib/pact/provider/help/write_spec.rb
+++ b/spec/lib/pact/provider/help/write_spec.rb
@@ -7,7 +7,7 @@ module Help
describe "#call" do
- let(:pact_jsons) { double('pact jsons') }
+ let(:pact_sources) { double('pact jsons') }
let(:reports_dir) { "./tmp/reports" }
let(:text) { "help text" }
@@ -16,12 +16,12 @@ module Help
allow_any_instance_of(Content).to receive(:text).and_return(text)
end
- subject { Write.call(pact_jsons, reports_dir) }
+ subject { Write.call(pact_sources, reports_dir) }
let(:actual_contents) { File.read(File.join(reports_dir, Write::HELP_FILE_NAME)) }
- it "passes the pact_jsons into the Content" do
- expect(Content).to receive(:new).with(pact_jsons).and_return(double(text: ''))
+ it "passes the pact_sources into the Content" do
+ expect(Content).to receive(:new).with(pact_sources).and_return(double(text: ''))
subject
end
diff --git a/spec/lib/pact/provider/pact_helper_locator_spec.rb b/spec/lib/pact/provider/pact_helper_locator_spec.rb
index 7c39d8f9..72049a08 100644
--- a/spec/lib/pact/provider/pact_helper_locator_spec.rb
+++ b/spec/lib/pact/provider/pact_helper_locator_spec.rb
@@ -20,6 +20,12 @@ def make_pactfile dir
'/spec/serviceconsumers',
'/spec/consumer',
'/spec',
+ '/test/blah/service-consumers',
+ '/test/consumers',
+ '/test/blah/service_consumers',
+ '/test/serviceconsumers',
+ '/test/consumer',
+ '/test',
'/blah',
'/blah/consumer',
''
diff --git a/spec/lib/pact/provider/pact_uri_spec.rb b/spec/lib/pact/provider/pact_uri_spec.rb
index 357e88de..f7b6df6c 100644
--- a/spec/lib/pact/provider/pact_uri_spec.rb
+++ b/spec/lib/pact/provider/pact_uri_spec.rb
@@ -23,13 +23,46 @@
end
describe '#to_s' do
- context 'with userinfo provided' do
+ context 'with basic auth provided' do
let(:password) { 'my_password' }
let(:options) { { username: username, password: password } }
- it 'should include user name and password' do
+ it 'should include user name and and hide password' do
expect(pact_uri.to_s).to eq('http://pact:*****@uri')
end
+
+ context 'when basic auth credentials have been set for a local file (eg. via environment variables, unintentionally)' do
+ let(:uri) { '/some/file thing.json' }
+
+ it 'does not blow up' do
+ expect(pact_uri.to_s).to eq uri
+ end
+ end
+
+ context "with a username that has an @ symbol" do
+ let(:username) { "foo@bar" }
+
+ it 'does not blow up' do
+ expect(pact_uri.to_s).to eq "http://*****:*****@uri"
+ end
+ end
+ end
+
+ context 'with personal access token provided' do
+ let(:pat) { 'should_be_secret' }
+ let(:options) { { username: pat } }
+
+ it 'should hide the pat' do
+ expect(pact_uri.to_s).to eq('http://*****@uri')
+ end
+
+ context 'when pat credentials have been set for a local file (eg. via environment variables, unintentionally)' do
+ let(:uri) { '/some/file thing.json' }
+
+ it 'does not blow up' do
+ expect(pact_uri.to_s).to eq uri
+ end
+ end
end
context 'without userinfo' do
diff --git a/spec/lib/pact/provider/rspec/calculate_exit_code_spec.rb b/spec/lib/pact/provider/rspec/calculate_exit_code_spec.rb
new file mode 100644
index 00000000..4e062442
--- /dev/null
+++ b/spec/lib/pact/provider/rspec/calculate_exit_code_spec.rb
@@ -0,0 +1,56 @@
+require 'pact/provider/rspec/calculate_exit_code'
+
+module Pact
+ module Provider
+ module RSpec
+ module CalculateExitCode
+ describe ".call" do
+ let(:pact_source_1) { double('pact_source_1', pending?: pending_1) }
+ let(:pending_1) { nil }
+ let(:pact_source_2) { double('pact_source_2', pending?: pending_2) }
+ let(:pending_2) { nil }
+ let(:pact_source_3) { double('pact_source_3', pending?: pending_3) }
+ let(:pending_3) { nil }
+ let(:pact_sources) { [pact_source_1, pact_source_2, pact_source_3]}
+
+ let(:failed_examples) { [ example_1, example_2, example_3 ] }
+ let(:example_1) { double('example_1', metadata: { pact_source: pact_source_1 }) }
+ let(:example_2) { double('example_2', metadata: { pact_source: pact_source_1 }) }
+ let(:example_3) { double('example_3', metadata: { pact_source: pact_source_2 }) }
+
+ subject { CalculateExitCode.call(pact_sources, failed_examples ) }
+
+ context "when all pacts are pending" do
+ let(:pending_1) { true }
+ let(:pending_2) { true }
+ let(:pending_3) { true }
+
+ it "returns 0" do
+ expect(subject).to eq 0
+ end
+ end
+
+ context "when a non pending pact has no failures" do
+ let(:pending_1) { true }
+ let(:pending_2) { true }
+ let(:pending_3) { false }
+
+ it "returns 0" do
+ expect(subject).to eq 0
+ end
+ end
+
+ context "when a non pending pact no failures" do
+ let(:pending_1) { true }
+ let(:pending_2) { false }
+ let(:pending_3) { false }
+
+ it "returns 1" do
+ expect(subject).to eq 1
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/pact/provider/rspec/formatter_rspec_3_spec.rb b/spec/lib/pact/provider/rspec/formatter_rspec_3_spec.rb
index 643e3dc1..791bfbf7 100644
--- a/spec/lib/pact/provider/rspec/formatter_rspec_3_spec.rb
+++ b/spec/lib/pact/provider/rspec/formatter_rspec_3_spec.rb
@@ -10,8 +10,10 @@ module Provider
module RSpec
describe Formatter do
- let(:interaction) { InteractionFactory.create 'provider_state' => 'a state', 'description' => 'a description'}
- let(:pactfile_uri) { 'pact_file_uri' }
+ let(:interaction) { InteractionFactory.create 'provider_state' => 'a state', 'description' => 'a description', '_id' => id, 'index' => 2 }
+ let(:interaction_2) { InteractionFactory.create 'provider_state' => 'a state', 'description' => 'a description 2', '_id' => "#{id}2", 'index' => 3 }
+ let(:id) { nil }
+ let(:pactfile_uri) { Pact::Provider::PactURI.new('pact_file_uri') }
let(:description) { 'an interaction' }
let(:pact_json) { {some: 'pact json'}.to_json }
let(:metadata) do
@@ -20,20 +22,22 @@ module RSpec
pactfile_uri: pactfile_uri,
pact_interaction_example_description: description,
pact_json: pact_json,
- pact_ignore_failures: ignore_failures
+ pact_ignore_failures: ignore_failures,
}
end
- let(:metadata_2) { metadata.merge(pact_interaction_example_description: 'another interaction')}
+ let(:metadata_2) { metadata.merge(pact_interaction: interaction_2)}
let(:example) { double("Example", metadata: metadata) }
let(:example_2) { double("Example", metadata: metadata_2) }
let(:failed_examples) { [example, example] }
let(:examples) { [example, example, example_2]}
let(:output) { StringIO.new }
- let(:rerun_command) { "rake pact:verify:at[pact_file_uri] PACT_DESCRIPTION=\"a description\" PACT_PROVIDER_STATE=\"a state\" # an interaction" }
+ let(:rerun_command) { 'PACT_DESCRIPTION="a description" PACT_PROVIDER_STATE="a state" # an interaction' }
+ let(:broker_rerun_command) { "rake pact:verify:at[pact_file_uri] PACT_BROKER_INTERACTION_ID=\"1234\" # an interaction" }
let(:missing_provider_states) { 'missing_provider_states'}
let(:summary) { double("summary", failure_count: 1, failed_examples: failed_examples, examples: examples)}
let(:pact_executing_language) { 'ruby' }
let(:pact_interaction_rerun_command) { Pact::TaskHelper::PACT_INTERACTION_RERUN_COMMAND }
+ let(:pact_interaction_rerun_command_for_broker) { Pact::TaskHelper::PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER }
let(:ignore_failures) { nil }
subject { Formatter.new output }
@@ -43,10 +47,12 @@ module RSpec
before do
allow(ENV).to receive(:[]).with('PACT_INTERACTION_RERUN_COMMAND').and_return(pact_interaction_rerun_command)
allow(ENV).to receive(:[]).with('PACT_EXECUTING_LANGUAGE').and_return(pact_executing_language)
+ allow(ENV).to receive(:[]).with('PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER').and_return(pact_interaction_rerun_command_for_broker)
allow(PrintMissingProviderStates).to receive(:call)
allow(Pact::Provider::Help::PromptText).to receive(:call).and_return("some help")
allow(subject).to receive(:failed_examples).and_return(failed_examples)
allow(Pact.provider_world.provider_states).to receive(:missing_provider_states).and_return(missing_provider_states)
+ allow(subject).to receive(:set_rspec_failure_color)
subject.dump_summary summary
end
@@ -69,15 +75,46 @@ module RSpec
end
end
- context "when PACT_INTERACTION_RERUN_COMMAND is not set" do
+ context "when PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER is set" do
+ context "when the _id is populated" do
+ let(:id) { "1234" }
+
+ it "prints a list of rerun commands" do
+ expect(output_result).to include(broker_rerun_command)
+ end
+
+ it "only prints unique commands" do
+ expect(output_result.scan(broker_rerun_command).size).to eq 1
+ end
+ end
+
+ context "when the _id is not populated" do
+ it "prints a list of rerun commands using the provider state and description" do
+ expect(output_result).to include(rerun_command)
+ end
+ end
+ end
+
+ context "when PACT_INTERACTION_RERUN_COMMAND and PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER are not set" do
let(:pact_interaction_rerun_command) { nil }
+ let(:pact_interaction_rerun_command_for_broker) { nil }
+
+ context "when the _id is populated" do
+ let(:id) { "1234" }
+
+ it "prints a list of failed interactions" do
+ expect(output_result).to include('* an interaction (to re-run just this interaction, set environment variable PACT_BROKER_INTERACTION_ID="1234")')
+ end
+ end
- it "prints a list of failed interactions" do
- expect(output_result).to include("* #{description}\n")
+ context "when the _id is not populated" do
+ it "prints a list of failed interactions" do
+ expect(output_result).to include('* an interaction (to re-run just this interaction, set environment variables PACT_DESCRIPTION="a description" PACT_PROVIDER_STATE="a state")')
+ end
end
it "only prints unique commands" do
- expect(output_result.scan("* #{description}\n").size).to eq 1
+ expect(output_result.scan("* #{description}").size).to eq 1
end
end
@@ -106,7 +143,7 @@ module RSpec
end
context "when ignore_failures is true" do
- let(:ignore_failures) { true }
+ let(:pactfile_uri) { Pact::Provider::PactURI.new('pact_file_uri', {}, { pending: true}) }
it "reports failures as pending" do
expect(output_result).to include("1 pending")
diff --git a/spec/lib/pact/provider/verification_results/create_spec.rb b/spec/lib/pact/provider/verification_results/create_spec.rb
index 55b5e846..7da50fa5 100644
--- a/spec/lib/pact/provider/verification_results/create_spec.rb
+++ b/spec/lib/pact/provider/verification_results/create_spec.rb
@@ -14,21 +14,25 @@ module VerificationResults
double('provider_configuration', application_version: '1.2.3')
end
let(:pact_source_1) do
- instance_double('Pact::Provider::PactSource', uri: pact_uri_1, pact_hash: pact_hash_1)
+ instance_double('Pact::Provider::PactSource', uri: pact_uri_1, consumer_contract: consumer_contract)
end
+ let(:consumer_contract) { instance_double('Pact::ConsumerContract', interactions: interactions)}
+ let(:interactions) { [interaction_1] }
+ let(:interaction_1) { instance_double('Pact::Interaction', _id: "1") }
+ let(:interaction_2) { instance_double('Pact::Interaction', _id: "2") }
let(:pact_uri_1) { instance_double('Pact::Provider::PactURI', uri: URI('foo')) }
let(:pact_uri_2) { instance_double('Pact::Provider::PactURI', uri: URI('bar')) }
let(:example_1) do
{
pact_uri: pact_uri_1,
- pact_interaction: double('interaction'),
+ pact_interaction: interaction_1,
status: 'passed'
}
end
let(:example_2) do
{
pact_uri: pact_uri_2,
- pact_interaction: double('interaction'),
+ pact_interaction: interaction_2,
status: 'passed'
}
end
@@ -37,11 +41,6 @@ module VerificationResults
tests: [example_1, example_2]
}
end
- let(:pact_hash_1) do
- {
- 'interactions' => [{}]
- }
- end
subject { Create.call(pact_source_1, test_results_hash) }
@@ -78,11 +77,9 @@ module VerificationResults
end
context "when not every interaction has been executed" do
- let(:pact_hash_1) do
- {
- 'interactions' => [{}, {}]
- }
- end
+ let(:interaction_3) { instance_double('Pact::Interaction', _id: "3") }
+ let(:interactions) { [interaction_1, interaction_2]}
+
it "sets publishable to false" do
expect(VerificationResult).to receive(:new).with(false, anything, anything, anything)
subject
diff --git a/spec/lib/pact/provider/verification_results/publish_spec.rb b/spec/lib/pact/provider/verification_results/publish_spec.rb
index 15565527..1e5b67ba 100644
--- a/spec/lib/pact/provider/verification_results/publish_spec.rb
+++ b/spec/lib/pact/provider/verification_results/publish_spec.rb
@@ -9,7 +9,8 @@ module VerificationResults
let(:stubbed_publish_verification_url) { 'http://broker/something/provider/Bar/verifications' }
let(:tag_version_url) { 'http://tag-me/{tag}' }
let(:pact_source) { instance_double("Pact::Provider::PactSource", pact_hash: pact_hash, uri: pact_url)}
- let(:pact_url) { instance_double("Pact::Provider::PactURI", basic_auth?: basic_auth, username: 'username', password: 'password')}
+ let(:pact_url) { instance_double("Pact::Provider::PactURI", options: options) }
+ let(:options) { { username: 'username', password: 'password' } }
let(:provider_url) { 'http://provider' }
let(:basic_auth) { false }
let(:pact_hash) do
@@ -47,6 +48,9 @@ module VerificationResults
},
'pb:version-tag' => {
'href' => 'http://provider/version/{version}/tag/{tag}'
+ },
+ 'pb:branch-version' => {
+ 'href' => 'http://provider/branches/{branch}/versions/{version}'
}
}
}.to_json
@@ -64,6 +68,7 @@ module VerificationResults
let(:verification_json) { '{"foo": "bar"}' }
let(:publish_verification_results) { false }
let(:publishable) { true }
+ let(:branch) { nil }
let(:tags) { [] }
let(:verification) do
instance_double("Pact::Verifications::Verification",
@@ -74,15 +79,16 @@ module VerificationResults
end
let(:provider_configuration) do
- double('provider config', publish_verification_results?: publish_verification_results, tags: tags, application_version: '1.2.3')
+ double('provider config', publish_verification_results?: publish_verification_results, branch: branch, tags: tags, application_version: '1.2.3')
end
before do
allow($stdout).to receive(:puts)
allow($stderr).to receive(:puts)
allow(Pact.configuration).to receive(:provider).and_return(provider_configuration)
- stub_request(:post, stubbed_publish_verification_url).to_return(status: 200, body: created_verification_body)
+ stub_request(:post, stubbed_publish_verification_url).to_return(status: 200, headers: { 'Content-Type' => 'application/hal+json'}, body: created_verification_body)
stub_request(:put, 'http://provider/version/1.2.3/tag/foo').to_return(status: 200, headers: { 'Content-Type' => 'application/hal+json'}, body: tag_body)
+ stub_request(:put, "http://provider/branches/main/versions/1.2.3").to_return(status: 200, body: "{}", headers: { 'Content-Type' => 'application/hal+json' })
stub_request(:get, provider_url).to_return(status: 200, headers: { 'Content-Type' => 'application/hal+json'}, body: provider_body)
allow(Retry).to receive(:until_true) { |&block| block.call }
end
@@ -116,6 +122,35 @@ module VerificationResults
end
end
+ context "with a branch" do
+ let(:branch) { "main" }
+
+ it "creates the branch version" do
+ subject
+ expect(WebMock).to have_requested(:put, 'http://provider/branches/main/versions/1.2.3').with(headers: {'Content-Type' => 'application/json'})
+ end
+
+ context "when there is an error creating the branch version" do
+ before do
+ stub_request(:put, "http://provider/branches/main/versions/1.2.3").to_return(status: 500, body: { some: "error" }.to_json, headers: { 'Content-Type' => 'application/hal+json' })
+ end
+
+ it "raises an error" do
+ expect { subject }.to raise_error PublicationError, /500.*some.*error/
+ end
+ end
+
+ context "when the broker does not support creating branch versions" do
+ let(:provider_body) do
+ {}.to_json
+ end
+
+ it "raises an error" do
+ expect { subject }.to raise_error PublicationError, /does not support/
+ end
+ end
+ end
+
context "with tags" do
let(:tags) { ['foo'] }
@@ -154,13 +189,21 @@ module VerificationResults
end
context "when basic auth is configured on the pact URL" do
- let(:basic_auth) { true }
- it "sets the username and password for the pubication URL" do
+ it "sets the username and password for the publication URL" do
subject
expect(WebMock).to have_requested(:post, publish_verification_url).with(basic_auth: ['username', 'password'])
end
end
+ context "when a token is configured on the pact URL" do
+ let(:options) { {token: 'token'} }
+
+ it "sets the authorization header" do
+ subject
+ expect(WebMock).to have_requested(:post, publish_verification_url).with(headers: { 'Authorization' => 'Bearer token'})
+ end
+ end
+
context "when an HTTP error is returned" do
it "raises a PublicationError" do
stub_request(:post, stubbed_publish_verification_url).to_return(status: 500, body: '{}')
@@ -177,7 +220,7 @@ module VerificationResults
context "with https" do
before do
- stub_request(:post, publish_verification_url).to_return(status: 200, body: created_verification_body)
+ stub_request(:post, publish_verification_url).to_return(status: 200, headers: { 'Content-Type' => 'application/json' }, body: created_verification_body)
end
let(:publish_verification_url) { stubbed_publish_verification_url.gsub('http', 'https') }
diff --git a/spec/service_providers/pact_ruby_fetch_pacts_for_verification_test.rb b/spec/service_providers/pact_ruby_fetch_pacts_for_verification_test.rb
new file mode 100644
index 00000000..6260dce9
--- /dev/null
+++ b/spec/service_providers/pact_ruby_fetch_pacts_for_verification_test.rb
@@ -0,0 +1,114 @@
+require_relative 'helper'
+require 'pact/pact_broker/fetch_pact_uris_for_verification'
+
+describe Pact::PactBroker::FetchPactURIsForVerification, pact: true do
+ before do
+ allow($stdout).to receive(:puts)
+ end
+
+ let(:get_headers) { { "Accept" => 'application/hal+json' } }
+ let(:post_headers) do
+ {
+ "Accept" => 'application/hal+json',
+ "Content-Type" => "application/json"
+ }
+ end
+ let(:pacts_for_verification_relation) { Pact::PactBroker::FetchPactURIsForVerification::PACTS_FOR_VERIFICATION_RELATION }
+ let(:body) do
+ {
+ "providerVersionBranch" => "main",
+ "providerVersionTags" => ["pdev"],
+ "consumerVersionSelectors" => [{ "tag" => "cdev", "latest" => true}],
+ "includePendingStatus" => true
+ }
+ end
+ let(:provider_version_branch) { "main" }
+ let(:provider_version_tags) { %w[pdev] }
+ let(:consumer_version_selectors) { [ { tag: "cdev", latest: true }] }
+ let(:options) { { include_pending_status: true }}
+
+ subject { Pact::PactBroker::FetchPactURIsForVerification.call(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, basic_auth_options, options) }
+
+ describe 'fetch pacts' do
+ let(:provider) { 'Bar' }
+ let(:broker_base_url) { pact_broker.mock_service_base_url}
+ let(:basic_auth_options) { { username: 'username', password: 'password' } }
+
+ before do
+ pact_broker
+ .given('the relation for retrieving pacts for verifications exists in the index resource')
+ .upon_receiving('a request for the index resource')
+ .with(
+ method: :get,
+ path: '/',
+ headers: get_headers
+ )
+ .will_respond_with(
+ status: 200,
+ headers: { "Content-Type" => Pact.term("application/hal+json", /hal/) },
+ body: {
+ _links: {
+ pacts_for_verification_relation => {
+ href: Pact.term(
+ generate: broker_base_url + '/pacts/provider/{provider}/for-verification',
+ matcher: %r{/pacts/provider/{provider}/for-verification$}
+ )
+ }
+ }
+ }
+ )
+ end
+
+ context 'retrieving pacts for verification by provider' do
+ before do
+ pact_broker
+ .given('Foo has a pact tagged cdev with provider Bar')
+ .upon_receiving('a request to retrieve the pacts for verification for a provider')
+ .with(
+ method: :post,
+ path: '/pacts/provider/Bar/for-verification',
+ body: body,
+ headers: post_headers
+ )
+ .will_respond_with(
+ status: 200,
+ headers: { "Content-Type" => Pact.term("application/hal+json", /hal/) },
+ body: {
+ "_embedded" => {
+ "pacts" => [{
+ "shortDescription" => "a description",
+ "verificationProperties" => {
+ "pending" => Pact.like(true),
+ "notices" => Pact.each_like("text" => "some text")
+ },
+ '_links' => {
+ "self" => {
+ "href" => Pact.term('http://pact-broker-url-for-foo', %r{http://.*})
+ }
+ }
+ }]
+ }
+ }
+ )
+ end
+
+ let(:expected_metadata) do
+ {
+ pending: true,
+ notices: [
+ text: "some text"
+ ],
+ short_description: "a description"
+ }
+ end
+
+ it 'returns the array of pact urls' do
+ expect(subject).to eq(
+ [
+ Pact::Provider::PactURI.new('http://pact-broker-url-for-foo', basic_auth_options, expected_metadata)
+ ]
+ )
+ end
+ end
+ end
+end
diff --git a/spec/service_providers/pact_ruby_fetch_pacts_test.rb b/spec/service_providers/pact_ruby_fetch_pacts_test.rb
index 65305310..9379eecb 100644
--- a/spec/service_providers/pact_ruby_fetch_pacts_test.rb
+++ b/spec/service_providers/pact_ruby_fetch_pacts_test.rb
@@ -26,6 +26,9 @@
)
.will_respond_with(
status: 200,
+ headers: {
+ 'Content-Type' => Pact.term('application/hal+json', /json/)
+ },
body: {
_links: {
'pb:latest-provider-pacts' => {
@@ -65,6 +68,9 @@
)
.will_respond_with(
status: 200,
+ headers: {
+ 'Content-Type' => Pact.term('application/hal+json', /json/)
+ },
body: {
_links: {
'pb:pacts' => [
@@ -105,6 +111,9 @@
)
.will_respond_with(
status: 200,
+ headers: {
+ 'Content-Type' => Pact.term('application/hal+json', /json/)
+ },
body: {
_links: {
'pb:pacts' => [
@@ -128,6 +137,9 @@
)
.will_respond_with(
status: 200,
+ headers: {
+ 'Content-Type' => Pact.term('application/hal+json', /json/)
+ },
body: {
_links: {
'pb:pacts' => [
@@ -171,6 +183,9 @@
)
.will_respond_with(
status: 200,
+ headers: {
+ 'Content-Type' => Pact.term('application/hal+json', /json/)
+ },
body: {
_links: {
'pb:pacts' => []
@@ -187,6 +202,9 @@
)
.will_respond_with(
status: 200,
+ headers: {
+ 'Content-Type' => Pact.term('application/hal+json', /json/)
+ },
body: {
_links: {
'pb:pacts' => [
@@ -227,6 +245,9 @@
)
.will_respond_with(
status: 200,
+ headers: {
+ 'Content-Type' => Pact.term('application/hal+json', /json/)
+ },
body: {
_links: {
'pb:pacts' => []
@@ -256,6 +277,9 @@
)
.will_respond_with(
status: 200,
+ headers: {
+ 'Content-Type' => Pact.term('application/hal+json', /json/)
+ },
body: {
_links: {
'pb:pacts' => [
@@ -280,6 +304,9 @@
)
.will_respond_with(
status: 200,
+ headers: {
+ 'Content-Type' => Pact.term('application/hal+json', /json/)
+ },
body: {
_links: {
'pb:pacts' => [
@@ -323,6 +350,9 @@
)
.will_respond_with(
status: 200,
+ headers: {
+ 'Content-Type' => Pact.term('application/hal+json', /json/)
+ },
body: {
_links: {
'pb:pacts' => [
diff --git a/spec/service_providers/pact_ruby_fetch_wip_pacts_test.rb b/spec/service_providers/pact_ruby_fetch_wip_pacts_test.rb
deleted file mode 100644
index baa5a47f..00000000
--- a/spec/service_providers/pact_ruby_fetch_wip_pacts_test.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-require_relative 'helper'
-require 'pact/pact_broker/fetch_pending_pacts'
-
-describe Pact::PactBroker::FetchPendingPacts, pact: true do
- before do
- allow($stdout).to receive(:puts)
- end
-
- let(:get_headers) { { Accept: 'application/hal+json' } }
-
- describe 'fetch pacts' do
- let(:provider) { 'provider-1' }
- let(:broker_base_url) { pact_broker.mock_service_base_url + '/' }
- let(:basic_auth_options) { { username: 'foo', password: 'bar' } }
-
- before do
- pact_broker
- .given('the relation for retrieving pending pacts exists in the index resource')
- .upon_receiving('a request for the index resource')
- .with(
- method: :get,
- path: '/',
- headers: get_headers
- )
- .will_respond_with(
- status: 200,
- body: {
- _links: {
- 'beta:pending-provider-pacts' => {
- href: Pact.term(
- generate: broker_base_url + 'pacts/provider/{provider}/pending',
- matcher: %r{/pacts/provider/{provider}/pending$}
- )
- }
- }
- }
- )
- end
-
- context 'retrieving pending pacts by provider' do
- before do
- pact_broker
- .given('consumer-1 has a pending pact with provider provider-1')
- .upon_receiving('a request to retrieve the pending pacts for provider')
- .with(
- method: :get,
- path: '/pacts/provider/provider-1/pending',
- headers: get_headers
- )
- .will_respond_with(
- status: 200,
- body: {
- _links: {
- 'pb:pacts' => [
- {
- href: Pact.term('http://pact-broker-url-for-consumer-1', %r{http://.*})
- }
- ]
- }
- }
- )
- end
-
- it 'returns the array of pact urls' do
- pacts = Pact::PactBroker::FetchPendingPacts.call(provider, broker_base_url, basic_auth_options)
- expect(pacts).to eq(
- [
- Pact::Provider::PactURI.new('http://pact-broker-url-for-consumer-1', basic_auth_options)
- ]
- )
- end
- end
- end
-end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 7951d9e3..9800ca85 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,6 +1,6 @@
require 'rspec'
+require 'rspec/its'
require 'fakefs/spec_helpers'
-require 'rspec'
require 'pact'
require 'webmock/rspec'
require 'support/factories'
@@ -15,7 +15,7 @@
is_jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
RSpec.configure do | config |
- config.include(FakeFS::SpecHelpers, :fakefs => true)
+ config.include(FakeFS::SpecHelpers, fakefs: true)
config.extend Pact::Provider::RSpec::ClassMethods
config.include Pact::Provider::RSpec::InstanceMethods
@@ -24,6 +24,5 @@
if config.respond_to?(:example_status_persistence_file_path=)
config.example_status_persistence_file_path = "./spec/examples.txt"
end
- config.filter_run_excluding :skip_jruby => is_jruby
+ config.filter_run_excluding skip_jruby: is_jruby
end
-
diff --git a/spec/support/bar_pact_helper.rb b/spec/support/bar_pact_helper.rb
index ae03faea..8f1752f9 100644
--- a/spec/support/bar_pact_helper.rb
+++ b/spec/support/bar_pact_helper.rb
@@ -16,6 +16,7 @@ def call env
Pact.service_provider "Bar" do
app { BarApp.new }
app_version '1.2.3'
+ app_version_branch 'master'
app_version_tags ['master']
publish_verification_results true
diff --git a/spec/support/foo-bar-message.json b/spec/support/foo-bar-message.json
new file mode 100644
index 00000000..cca3395e
--- /dev/null
+++ b/spec/support/foo-bar-message.json
@@ -0,0 +1,26 @@
+{
+ "consumer": {
+ "name": "Foo"
+ },
+ "provider": {
+ "name": "Bar"
+ },
+ "messages": [
+ {
+ "description": "a message",
+ "providerStates": [
+ {
+ "name": "a world exists"
+ }
+ ],
+ "contents": {
+ "text": "Hello world"
+ }
+ }
+ ],
+ "metadata": {
+ "pactSpecification": {
+ "version": "2.0.0"
+ }
+ }
+}
diff --git a/spec/support/message_spec_helper.rb b/spec/support/message_spec_helper.rb
new file mode 100644
index 00000000..13a0f046
--- /dev/null
+++ b/spec/support/message_spec_helper.rb
@@ -0,0 +1,41 @@
+require 'pact/message'
+
+# Example data store
+
+class DataStore
+ def self.greeting_recipient= greeting_recipient
+ @greeting_recipient = greeting_recipient
+ end
+
+ def self.greeting_recipient
+ @greeting_recipient
+ end
+end
+
+# Example message producer
+
+class BarProvider
+ def create_message
+ {
+ text: "Hello #{DataStore.greeting_recipient}"
+ }
+ end
+end
+
+# Provider states
+
+Pact.provider_states_for "Foo" do
+ provider_state "a world exists" do
+ set_up do
+ DataStore.greeting_recipient = "world"
+ end
+ end
+end
+
+CONFIG = {
+ "a message" => lambda { BarProvider.new.create_message }
+}
+
+Pact.message_provider "Bar" do
+ builder { |description| CONFIG[description].call }
+end
diff --git a/tasks/foo-bar.rake b/tasks/foo-bar.rake
index 87d4e5db..ebe3129c 100644
--- a/tasks/foo-bar.rake
+++ b/tasks/foo-bar.rake
@@ -1,4 +1,5 @@
require 'pact/tasks/verification_task'
+require 'faraday'
# Use for end to end manual debugging of issues.
BROKER_BASE_URL = 'http://localhost:9292'
@@ -22,6 +23,11 @@ task 'pact:foobar:publish' do
http.request put_request
end
puts response.code unless response.code == '200'
+
+ tag_response = Faraday.put("#{BROKER_BASE_URL}/pacticipants/Foo/versions/1.0.0/tags/dev", nil, { 'Content-Type' => 'application/json' })
+ puts tag_response.status unless tag_response.status == 200
+ tag_response = Faraday.put("#{BROKER_BASE_URL}/pacticipants/Foo/versions/1.0.0/tags/prod", nil, { 'Content-Type' => 'application/json' })
+ puts tag_response.status unless tag_response.status == 200
end
#'./spec/pacts/foo-bar.json'
@@ -35,7 +41,7 @@ Pact::VerificationTask.new('foobar:wip') do | pact |
end
Pact::VerificationTask.new(:foobar_using_broker) do | pact |
- pact.uri "#{BROKER_BASE_URL}/pacts/provider/Bar/consumer/Foo/version/1.0.0", :pact_helper => './spec/support/bar_pact_helper.rb', username: BROKER_USERNAME, password: BROKER_PASSWORD
+ pact.uri nil, :pact_helper => './spec/support/bar_pact_helper.rb', username: BROKER_USERNAME, password: BROKER_PASSWORD
end
Pact::VerificationTask.new('foobar_using_broker:fail') do | pact |
diff --git a/tasks/message-test.rake b/tasks/message-test.rake
new file mode 100644
index 00000000..680dbc0b
--- /dev/null
+++ b/tasks/message-test.rake
@@ -0,0 +1,5 @@
+require 'pact/tasks'
+
+Pact::VerificationTask.new(:message) do | pact |
+ pact.uri 'spec/support/foo-bar-message.json', pact_helper: 'spec/support/message_spec_helper.rb'
+end
diff --git a/tasks/pact-test.rake b/tasks/pact-test.rake
index 86fcaeb4..28247981 100644
--- a/tasks/pact-test.rake
+++ b/tasks/pact-test.rake
@@ -80,6 +80,7 @@ namespace :pact do
Rake::Task['pact:verify:term_v2'].execute
Rake::Task['pact:verify:test_app_with_provider_state_params'].execute
Rake::Task['pact:verify:test_app:wip'].execute
+ Rake::Task['pact:verify:message'].execute
end
desc "All the verification tests with active support loaded"
diff --git a/tasks/release.rake b/tasks/release.rake
index 8f8a634b..422e4d21 100644
--- a/tasks/release.rake
+++ b/tasks/release.rake
@@ -7,3 +7,10 @@ task :generate_changelog do
require 'pact/version'
ConventionalChangelog::Generator.new.generate! version: "v#{Pact::VERSION}"
end
+
+desc 'Tag for release'
+task :tag_for_release do | t, args |
+ command = "git tag -a v#{Pact::VERSION} -m \"chore(release): version #{Pact::VERSION}\" && git push origin v#{Pact::VERSION}"
+ puts command
+ puts `#{command}`
+end
diff --git a/tasks/spec.rake b/tasks/spec.rake
index 3744bbf9..e1a58139 100644
--- a/tasks/spec.rake
+++ b/tasks/spec.rake
@@ -6,3 +6,12 @@ RSpec::Core::RakeTask.new(:spec)
RSpec::Core::RakeTask.new('spec:provider') do | task |
task.pattern = "spec/service_providers/**/*_test.rb"
end
+
+task :set_active_support_on do
+ ENV["LOAD_ACTIVE_SUPPORT"] = 'true'
+end
+
+desc "This is to ensure that the gem still works even when active support JSON is loaded."
+task :spec_with_active_support => [:set_active_support_on] do
+ Rake::Task['spec'].execute
+end