pacto 0.3.0.pre → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rubocop-todo.yml +0 -27
- data/.rubocop.yml +9 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -5
- data/CONTRIBUTING.md +112 -0
- data/Gemfile +5 -0
- data/Guardfile +18 -13
- data/README.md +157 -101
- data/Rakefile +3 -3
- data/features/configuration/strict_matchers.feature +97 -0
- data/features/evolve/README.md +11 -0
- data/features/evolve/existing_services.feature +82 -0
- data/features/generate/README.md +5 -0
- data/features/generate/generation.feature +28 -0
- data/features/steps/pacto_steps.rb +75 -0
- data/features/stub/README.md +2 -0
- data/features/stub/templates.feature +46 -0
- data/features/support/env.rb +11 -5
- data/features/validate/README.md +1 -0
- data/features/validate/body_only.feature +85 -0
- data/features/{journeys/validation.feature → validate/meta_validation.feature} +41 -24
- data/features/validate/validation.feature +36 -0
- data/lib/pacto.rb +61 -33
- data/lib/pacto/contract.rb +18 -15
- data/lib/pacto/contract_factory.rb +14 -11
- data/lib/pacto/contract_files.rb +17 -0
- data/lib/pacto/contract_list.rb +17 -0
- data/lib/pacto/contract_validator.rb +29 -0
- data/lib/pacto/core/configuration.rb +19 -17
- data/lib/pacto/core/contract_registry.rb +43 -0
- data/lib/pacto/core/{callback.rb → hook.rb} +3 -3
- data/lib/pacto/core/modes.rb +33 -0
- data/lib/pacto/core/validation_registry.rb +45 -0
- data/lib/pacto/erb_processor.rb +0 -1
- data/lib/pacto/extensions.rb +18 -4
- data/lib/pacto/generator.rb +34 -49
- data/lib/pacto/generator/filters.rb +41 -0
- data/lib/pacto/hooks/erb_hook.rb +4 -3
- data/lib/pacto/logger.rb +4 -2
- data/lib/pacto/meta_schema.rb +4 -2
- data/lib/pacto/rake_task.rb +28 -25
- data/lib/pacto/request_clause.rb +43 -0
- data/lib/pacto/request_pattern.rb +8 -0
- data/lib/pacto/response_clause.rb +15 -0
- data/lib/pacto/rspec.rb +102 -0
- data/lib/pacto/stubs/uri_pattern.rb +23 -0
- data/lib/pacto/stubs/webmock_adapter.rb +69 -0
- data/lib/pacto/stubs/webmock_helper.rb +71 -0
- data/lib/pacto/ui.rb +7 -0
- data/lib/pacto/uri.rb +9 -0
- data/lib/pacto/validation.rb +57 -0
- data/lib/pacto/validators/body_validator.rb +41 -0
- data/lib/pacto/validators/request_body_validator.rb +23 -0
- data/lib/pacto/validators/response_body_validator.rb +23 -0
- data/lib/pacto/validators/response_header_validator.rb +49 -0
- data/lib/pacto/validators/response_status_validator.rb +24 -0
- data/lib/pacto/version.rb +1 -1
- data/pacto.gemspec +33 -29
- data/resources/contract_schema.json +8 -176
- data/resources/draft-03.json +174 -0
- data/spec/integration/data/strict_contract.json +2 -2
- data/spec/integration/e2e_spec.rb +22 -31
- data/spec/integration/rspec_spec.rb +94 -0
- data/spec/integration/templating_spec.rb +9 -12
- data/{lib → spec}/pacto/server.rb +0 -0
- data/{lib → spec}/pacto/server/dummy.rb +11 -8
- data/{lib → spec}/pacto/server/playback_servlet.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/hooks/erb_hook_spec.rb +15 -15
- data/spec/unit/pacto/configuration_spec.rb +2 -10
- data/spec/unit/pacto/contract_factory_spec.rb +16 -13
- data/spec/unit/pacto/contract_files_spec.rb +42 -0
- data/spec/unit/pacto/contract_list_spec.rb +35 -0
- data/spec/unit/pacto/contract_spec.rb +43 -44
- data/spec/unit/pacto/contract_validator_spec.rb +85 -0
- data/spec/unit/pacto/core/configuration_spec.rb +4 -11
- data/spec/unit/pacto/core/contract_registry_spec.rb +119 -0
- data/spec/unit/pacto/core/modes_spec.rb +18 -0
- data/spec/unit/pacto/core/validation_registry_spec.rb +76 -0
- data/spec/unit/pacto/core/validation_spec.rb +60 -0
- data/spec/unit/pacto/extensions_spec.rb +14 -23
- data/spec/unit/pacto/generator/filters_spec.rb +99 -0
- data/spec/unit/pacto/generator_spec.rb +34 -73
- data/spec/unit/pacto/meta_schema_spec.rb +46 -6
- data/spec/unit/pacto/pacto_spec.rb +17 -15
- data/spec/unit/pacto/{request_spec.rb → request_clause_spec.rb} +32 -44
- data/spec/unit/pacto/request_pattern_spec.rb +22 -0
- data/spec/unit/pacto/response_clause_spec.rb +54 -0
- data/spec/unit/pacto/stubs/uri_pattern_spec.rb +28 -0
- data/spec/unit/pacto/stubs/webmock_adapter_spec.rb +205 -0
- data/spec/unit/pacto/stubs/webmock_helper_spec.rb +20 -0
- data/spec/unit/pacto/uri_spec.rb +20 -0
- data/spec/unit/pacto/validators/body_validator_spec.rb +105 -0
- data/spec/unit/pacto/validators/response_header_validator_spec.rb +94 -0
- data/spec/unit/pacto/validators/response_status_validator_spec.rb +20 -0
- metadata +230 -146
- data/features/generation/generation.feature +0 -25
- data/lib/pacto/core/contract_repository.rb +0 -44
- data/lib/pacto/hash_merge_processor.rb +0 -14
- data/lib/pacto/request.rb +0 -57
- data/lib/pacto/response.rb +0 -63
- data/lib/pacto/response_adapter.rb +0 -24
- data/lib/pacto/stubs/built_in.rb +0 -57
- data/spec/unit/pacto/core/contract_repository_spec.rb +0 -133
- data/spec/unit/pacto/hash_merge_processor_spec.rb +0 -20
- data/spec/unit/pacto/response_adapter_spec.rb +0 -25
- data/spec/unit/pacto/response_spec.rb +0 -201
- data/spec/unit/pacto/stubs/built_in_spec.rb +0 -168
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3465015cc69868938ee729098b3c08df3a0df32a
|
4
|
+
data.tar.gz: 65e2129430e2679ae8b2bc6dc109447a35e3a174
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b14f918661f414d1f2e84b7dc6a95903e18f0ead81eb4f7188eedb9761e3e2aeda225b2c8f0bd97d322593a41b6d71f1d1b99c150f4e155d7995e078137f0bf6
|
7
|
+
data.tar.gz: f030e06b0d84bee8ed495a0f0c9d4434255dec6c7904d5249c62ea5e0a1bffd737e61aaba6b7b3b7e5ca00d8f2e0e507e474ed9f55598af2ab1ee075db4ac7da
|
data/.gitignore
CHANGED
data/.rubocop-todo.yml
CHANGED
@@ -2,18 +2,6 @@
|
|
2
2
|
# The point is for the user to remove these configuration records
|
3
3
|
# one by one as the offences are removed from the code base.
|
4
4
|
|
5
|
-
Blocks:
|
6
|
-
Enabled: false
|
7
|
-
|
8
|
-
CollectionMethods:
|
9
|
-
Enabled: false
|
10
|
-
|
11
|
-
ColonMethodCall:
|
12
|
-
Enabled: false
|
13
|
-
|
14
|
-
DefWithoutParentheses:
|
15
|
-
Enabled: false
|
16
|
-
|
17
5
|
Documentation:
|
18
6
|
Enabled: false
|
19
7
|
|
@@ -32,20 +20,5 @@ IfUnlessModifier:
|
|
32
20
|
LineLength:
|
33
21
|
Enabled: false
|
34
22
|
|
35
|
-
MethodAndVariableSnakeCase:
|
36
|
-
Enabled: false
|
37
|
-
|
38
|
-
MethodLength:
|
39
|
-
Enabled: false
|
40
|
-
|
41
23
|
SpaceInsideHashLiteralBraces:
|
42
24
|
Enabled: false
|
43
|
-
|
44
|
-
SymbolName:
|
45
|
-
Enabled: false
|
46
|
-
|
47
|
-
Void:
|
48
|
-
Enabled: false
|
49
|
-
|
50
|
-
WordArray:
|
51
|
-
Enabled: false
|
data/.rubocop.yml
CHANGED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pacto
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.0
|
data/.travis.yml
CHANGED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
You are welcome to contribute to Pacto and this guide will help you to:
|
4
|
+
|
5
|
+
- [Setup](#setup) all the needed dependencies in order to start hacking.
|
6
|
+
- Follow [conventions](#code-conventions) agreed among the project
|
7
|
+
contributors.
|
8
|
+
- Follow Pacto's suggested [workflow](#workflow).
|
9
|
+
- [Submit](#submit-code) new code to the project.
|
10
|
+
- Run the automated suite of [tests](#run-tests) that is bundled with Pacto.
|
11
|
+
- Find easily code annotations for [technical debt](#technical-debt) (TODOs,
|
12
|
+
FIXMEs, etc)
|
13
|
+
- Be aware of some [troubleshooting tips](#troubleshooting) when running issues
|
14
|
+
with Pacto.
|
15
|
+
|
16
|
+
## <a name="workflow"></a>Development (suggested) workflow
|
17
|
+
|
18
|
+
Pacto comes with [`guard`](https://github.com/guard/guard) enabled, this means
|
19
|
+
that guard will trigger the tests after any change is made on the source code.
|
20
|
+
We try to keep the feedback loop as fast as we can, so you can be able to run
|
21
|
+
all the tests everytime you make a change on the project. If you want to follow
|
22
|
+
this workflow just run:
|
23
|
+
|
24
|
+
`bundle exec guard`
|
25
|
+
|
26
|
+
Guard will run first the static analysis and then will run the unit test related
|
27
|
+
with the file that was changed, later the integration test and last the user
|
28
|
+
journey tests.
|
29
|
+
|
30
|
+
## <a name="submit-code"></a>Submit code
|
31
|
+
|
32
|
+
Any contribution has to come from a Pull Request via GitHub, to do it just
|
33
|
+
follow these steps:
|
34
|
+
|
35
|
+
1. Fork it (`git clone git@github.com:thoughtworks/pacto.git`).
|
36
|
+
2. Create your feature branch (`git checkout -b my-new-feature`).
|
37
|
+
3. Commit your changes (`git commit -am 'Add some feature'`).
|
38
|
+
4. Verify that the tests are passing (`bundle exec rake`).
|
39
|
+
5. Push to the branch (`git push origin my-new-feature`).
|
40
|
+
6. Create new Pull Request.
|
41
|
+
|
42
|
+
## <a name="setup"></a>Setting up
|
43
|
+
|
44
|
+
You will need to have installed:
|
45
|
+
|
46
|
+
- Ruby 1.9.3 or greater installed.
|
47
|
+
- Bundler gem installed (`gem install bundler`).
|
48
|
+
- Install all the dependencies (`bundle install`).
|
49
|
+
|
50
|
+
## <a name="code-conventions"></a>Coding conventions
|
51
|
+
|
52
|
+
### Style guide
|
53
|
+
|
54
|
+
Contributing in a project among several authors could lead to different styles
|
55
|
+
of writting code. In order to create some basic baseline on the source code
|
56
|
+
Pacto comes with an static code analyzer that will enforce the code to follow
|
57
|
+
the [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide). To execute
|
58
|
+
the analyzer just run:
|
59
|
+
|
60
|
+
`bundle exec rubocop`
|
61
|
+
|
62
|
+
### Writing tests
|
63
|
+
|
64
|
+
Pacto unit tests and integration test are written in RSpec and the user journey
|
65
|
+
tests are written in Cucumber. For the RSpec tests we suggest to follow the
|
66
|
+
[Better Specs](http://betterspecs.org/) guideline. For Cucumber test keep in
|
67
|
+
mind that those will be published on
|
68
|
+
[Relish](https://www.relishapp.com/maxlinc/pacto/docs), so try to make them
|
69
|
+
Relish friendly ;).
|
70
|
+
|
71
|
+
## <a name="run-tests"></a>Running tests
|
72
|
+
|
73
|
+
Pacto comes with a set of automated tests. All the tests are runnable via rake
|
74
|
+
tasks:
|
75
|
+
|
76
|
+
- Unit tests (`bundle exec rake unit`).
|
77
|
+
- Integration tests (`bundle exec rake integration`).
|
78
|
+
- User journey tests (`bundle exec rake journey`).
|
79
|
+
|
80
|
+
### Checking that all is green
|
81
|
+
|
82
|
+
To know that both tests and static analysis is working fine you just have to
|
83
|
+
run:
|
84
|
+
|
85
|
+
`bundle exec rake`
|
86
|
+
|
87
|
+
## <a name="technical-debt"></a>Technical Debt
|
88
|
+
|
89
|
+
Some of the code in Pacto is commented with the anotations TODO or
|
90
|
+
FIXME that might point to some potencial technical debt on the source code. If
|
91
|
+
you are interested to list where are all these, just run:
|
92
|
+
|
93
|
+
`bundle exec notes`
|
94
|
+
|
95
|
+
## <a name="troubleshooting"></a>Troubleshooting
|
96
|
+
|
97
|
+
### Debugging pacto
|
98
|
+
|
99
|
+
If you run into some strange behaviour that Pacto might have, you can take
|
100
|
+
advantage of the debugging capabilities of Pacto. Running the tests with the
|
101
|
+
environment variable PACTO_DEBUG=true will show (on the standard output) more
|
102
|
+
details what Pacto is doing behind the scenes.
|
103
|
+
|
104
|
+
### Gemfile.lock
|
105
|
+
|
106
|
+
Because Pacto is a gem we don't include the Gemfile.lock into the repository
|
107
|
+
([here is the reason])(http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/).
|
108
|
+
This could lead to some problems in your daily job as contributor specially
|
109
|
+
when there is an upgrade in any of the gems that Pacto depends upon. That is
|
110
|
+
why we recomend you to remove the Gemfile.lock and generate it
|
111
|
+
(`bundle install`) everytime there are changes on the dependencies.
|
112
|
+
|
data/Gemfile
CHANGED
@@ -2,3 +2,8 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in pacto.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
# This is only used by Relish tests. Putting it here let's travis
|
7
|
+
# pre-install so we can speed up the test with `bundle install --local`,
|
8
|
+
# avoiding Aruba timeouts.
|
9
|
+
gem 'excon'
|
data/Guardfile
CHANGED
@@ -1,5 +1,10 @@
|
|
1
|
+
# vim: syntax=ruby filetype=ruby
|
2
|
+
|
1
3
|
guard :rubocop, all_on_start: false do
|
2
|
-
watch(
|
4
|
+
watch(/.+\.rb$/)
|
5
|
+
watch(/\.gemspec$/)
|
6
|
+
watch('Guardfile')
|
7
|
+
watch('Rakefile')
|
3
8
|
watch('.rubocop.yml') { '.' }
|
4
9
|
watch('.rubocop-todo.yml') { '.' }
|
5
10
|
end
|
@@ -8,23 +13,23 @@ group :tests, halt_on_fail: true do
|
|
8
13
|
guard :rspec do
|
9
14
|
# Unit tests
|
10
15
|
watch(%r{^spec/unit/.+_spec\.rb$})
|
11
|
-
watch(
|
12
|
-
watch('spec/spec_helper.rb') {
|
13
|
-
watch('spec/unit/spec_helper.rb') {
|
14
|
-
watch(%r{^spec/unit/data/.+\.json$}) {
|
16
|
+
watch(/^lib\/(.+)\.rb$/) { |m| 'spec/unit/#{m[1]}_spec.rb' }
|
17
|
+
watch('spec/spec_helper.rb') { 'spec/unit' }
|
18
|
+
watch('spec/unit/spec_helper.rb') { 'spec/unit' }
|
19
|
+
watch(%r{^spec/unit/data/.+\.json$}) { 'spec/unit' }
|
15
20
|
|
16
21
|
# Integration tests
|
17
22
|
watch(%r{^spec/integration/.+_spec\.rb$})
|
18
|
-
watch(%r{^spec/integration/utils/.+\.rb$}) {
|
19
|
-
watch(
|
20
|
-
watch('spec/spec_helper.rb') {
|
21
|
-
watch('spec/integration/spec_helper.rb') {
|
22
|
-
watch(%r{^spec/integration/data/.+\.json$}) {
|
23
|
+
watch(%r{^spec/integration/utils/.+\.rb$}) { 'spec/integration' }
|
24
|
+
watch(/^lib\/.+\.rb$/) { 'spec/integration' }
|
25
|
+
watch('spec/spec_helper.rb') { 'spec/integration' }
|
26
|
+
watch('spec/integration/spec_helper.rb') { 'spec/integration' }
|
27
|
+
watch(%r{^spec/integration/data/.+\.json$}) { 'spec/integration' }
|
23
28
|
end
|
24
29
|
|
25
30
|
guard :cucumber, all_on_start: false do
|
26
|
-
watch(
|
27
|
-
watch(%r{^features/support/.+$})
|
28
|
-
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join(
|
31
|
+
watch(/^features\/.+\.feature$/)
|
32
|
+
watch(%r{^features/support/.+$}) { 'features' }
|
33
|
+
watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join('**/#{m[1]}.feature')][0] || 'features' }
|
29
34
|
end
|
30
35
|
end
|
data/README.md
CHANGED
@@ -1,40 +1,178 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/pacto.png)](http://badge.fury.io/rb/pacto)
|
1
2
|
[![Build Status](https://travis-ci.org/thoughtworks/pacto.png)](https://travis-ci.org/thoughtworks/pacto)
|
2
3
|
[![Code Climate](https://codeclimate.com/github/thoughtworks/pacto.png)](https://codeclimate.com/github/thoughtworks/pacto)
|
3
4
|
[![Dependency Status](https://gemnasium.com/thoughtworks/pacto.png)](https://gemnasium.com/thoughtworks/pacto)
|
4
5
|
[![Coverage Status](https://coveralls.io/repos/thoughtworks/pacto/badge.png)](https://coveralls.io/r/thoughtworks/pacto)
|
5
6
|
|
7
|
+
**If you're viewing this at https://github.com/thoughtworks/pacto,
|
8
|
+
you're reading the documentation for the master branch.
|
9
|
+
[View documentation for the latest release
|
10
|
+
(0.2.5).](https://github.com/thoughtworks/pacto/tree/v0.2.5)**
|
11
|
+
|
6
12
|
# Pacto
|
7
13
|
|
8
|
-
Pacto is a
|
9
|
-
|
14
|
+
Pacto is a judge that arbitrates contract disputes between a **service provider** and one or more **consumers**. In other words, it is a framework for [Integration Contract Testing](http://martinfowler.com/bliki/IntegrationContractTest.html), and helps guide service evolution patterns like [Consumer-Driven Contracts](http://thoughtworks.github.io/pacto/patterns/cdc/) or [Documentation-Driven Contracts](http://thoughtworks.github.io/pacto/patterns/documentation_driven/).
|
15
|
+
|
16
|
+
Pacto considers two major terms in order decide if there has been a breach of contract: the **request clause** and the **response clause**.
|
17
|
+
|
18
|
+
The **request clause** defines what information must be sent by the **consumer** to the **provider** in order to compel them to render a service. The request clause often describes the required HTTP request headers like `Content-Type`, the required parameters, and the required request body (defined in [json-schema](http://json-schema.org/)) when applicable. Providers are not held liable for failing to deliver services for invalid requests.
|
19
|
+
|
20
|
+
The **response clause** defines what information must be returned by the **provider** to the **consumer** in order to successfully complete the transaction. This commonly includes HTTP response headers like `Location` as well as the required response body (also defined in [json-schema](http://json-schema.org/)).
|
21
|
+
|
22
|
+
## Test Doubles
|
23
|
+
|
24
|
+
The consumer may also enter into an agreement with **test doubles** like [WebMock](http://robots.thoughtbot.com/how-to-stub-external-services-in-tests), [vcr](https://github.com/vcr/vcr) or [mountebank](http://www.mbtest.org/). The services delivered by the **test doubles** for the purposes of development and testing will be held to the same conditions as the service the final services rendered by the parent **provider**. This prevents misrepresentation of sample services as realistic, leading to damages during late integration.
|
25
|
+
|
26
|
+
Pacto can provide a [**test double**](#stubbing) if you cannot afford your own.
|
27
|
+
|
28
|
+
## Due Diligence
|
29
|
+
|
30
|
+
Pacto usually makes it clear if the **consumer** or **provider** is at fault, but if a contract is too vague Pacto cannot assign blame, and if it is too specific the matter may become litigious.
|
31
|
+
|
32
|
+
Pacto can provide a [**contract writer**](#generating) that tries to strike a balance, but you may still need to adjust the terms.
|
33
|
+
|
34
|
+
## Implied Terms
|
35
|
+
|
36
|
+
- Pacto only arbitrates contracts for JSON services.
|
37
|
+
- Pacto requires Ruby 1.9.3 or newer, though you can use [Polyglot Testing](http://thoughtworks.github.io/pacto/patterns/polyglot/) techniques to support older Rubies and non-Ruby projects.
|
38
|
+
|
39
|
+
## Roadmap
|
40
|
+
|
41
|
+
- The **test double** provided by Pacto is only semi-competent. It handles simple cases, but we expect to find a more capable replacement in the near future.
|
42
|
+
- Pacto will provide additional Contract Writers for converting from apiblueprint, WADL, or other documentation formats in the future. It's part of our goal to support [Documentation-Driven Contracts](http://thoughtworks.github.io/pacto/patterns/documentation_driven/)
|
43
|
+
- Pacto reserves the right to consider other clauses in the future, like security and compliance to industry specifications.
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
Pacto can perform three activities: generating, validating, or stubbing services. You can do each of these activities against either live or stubbed services.
|
48
|
+
|
49
|
+
### Configuration
|
50
|
+
|
51
|
+
In order to start with Pacto, you just need to require it and optionally customize the default [Configuration](https://www.relishapp.com/maxlinc/pacto/docs/configuration). For example:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
require 'pacto'
|
55
|
+
|
56
|
+
Pacto.configure do |config|
|
57
|
+
config.contracts_path = 'contracts'
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
### Generating
|
62
|
+
|
63
|
+
The easiest way to get started with Pacto is to run a suite of live tests and tell Pacto to generate the contracts:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
Pacto.generate!
|
67
|
+
# run your tests
|
68
|
+
```
|
69
|
+
|
70
|
+
If you're using the same configuration as above, this will produce Contracts in the contracts/ folder.
|
71
|
+
|
72
|
+
We know we cannot generate perfect Contracts, especially if we only have one sample request. So we do our best to give you a good starting point, but you will likely want to customize the contract so the validation is more strict in some places and less strict in others.
|
73
|
+
|
74
|
+
### Contract Lists
|
75
|
+
|
76
|
+
In order to stub or validate or stub a group of contracts you need to create a ContractList.
|
77
|
+
A ContractList represent a collection of endpoints attached to the same host.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
require 'pacto'
|
81
|
+
|
82
|
+
default_contracts = Pacto.load_contracts('contracts/services', 'http://example.com')
|
83
|
+
authentication_contracts = Pacto.load_contracts('contracts/auth', 'http://example.com')
|
84
|
+
legacy_contracts = Pacto.load_contracts('contracts/legacy', 'http://example.com')
|
85
|
+
```
|
86
|
+
|
87
|
+
### Validating
|
88
|
+
|
89
|
+
Once you have a ContractList, you can validate all the contracts against the live host.
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
contracts = Pacto.load_contracts('contracts/services', 'http://example.com')
|
93
|
+
contracts.validate_all
|
94
|
+
```
|
95
|
+
|
96
|
+
This method will hit the real endpoint, with a request created based on the request part of the contract.
|
97
|
+
The response will be compared against the response part of the contract and any structural difference will
|
98
|
+
generate a validation error.
|
99
|
+
|
100
|
+
Running this in your build pipeline will ensure that your contracts actually match the real world, and that
|
101
|
+
you can run your acceptance tests against the contract stubs without worries.
|
102
|
+
|
103
|
+
### Stubbing
|
104
|
+
|
105
|
+
To generate stubs based on a ContractList you can run:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
contracts = Pacto.load_contracts('contracts/services', 'http://example.com')
|
109
|
+
contracts.stub_all
|
110
|
+
```
|
111
|
+
|
112
|
+
This method uses webmock to ensure that whenever you make a request against one of contracts you actually get a stubbed response,
|
113
|
+
based on the default values specified on the contract, instead of hitting the real provider.
|
114
|
+
|
115
|
+
You can override any default value on the contracts by providing a hash of optional values to the stub_all method. This
|
116
|
+
will override the keys for every contract in the list
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
contracts = Pacto.load_contracts('contracts/services', 'http://example.com')
|
120
|
+
contracts.stub_all(request_id: 14, name: "Marcos")
|
121
|
+
```
|
122
|
+
|
123
|
+
## Pacto Server (non-Ruby usage)
|
10
124
|
|
11
|
-
|
12
|
-
- An automated way to validate that a producer meets its consumer's requirements;
|
13
|
-
- An auto-generated stub to be used in the consumer's acceptance tests.
|
125
|
+
It is really easy to embed Pacto inside a small server. We haven't bundled a server inside of Pacto, but check out [pacto-demo](https://github.com/thoughtworks/pacto-demo) to see how easily you can expose Pacto via server.
|
14
126
|
|
15
|
-
|
16
|
-
|
17
|
-
|
127
|
+
That demo lets you easily run a server in several modes:
|
128
|
+
```sh
|
129
|
+
$ bundle exec ruby pacto_server.rb -sv --generate
|
130
|
+
# Runs a server that will generate Contracts for each request received
|
131
|
+
$ bundle exec ruby pacto_server.rb -sv --validate
|
132
|
+
# Runs the server that provides stubs and checks them against Contracts
|
133
|
+
$ bundle exec ruby pacto_server.rb -sv --validate --host http://example.com
|
134
|
+
# Runs the server that acts as a proxy to http://example.com, validating each request/response against a Contract
|
135
|
+
```
|
136
|
+
|
137
|
+
## Rake Tasks
|
138
|
+
|
139
|
+
Pacto includes a few Rake tasks to help with common tasks. If you want to use these tasks, just add this top your Rakefile:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
require 'pacto/rake_task'
|
143
|
+
```
|
144
|
+
|
145
|
+
This should add several new tasks to you project:
|
146
|
+
```sh
|
147
|
+
rake pacto:generate[input_dir,output_dir,host] # Generates contracts from partial contracts
|
148
|
+
rake pacto:meta_validate[dir] # Validates a directory of contract definitions
|
149
|
+
rake pacto:validate[host,dir] # Validates all contracts in a given directory against a given host
|
150
|
+
```
|
151
|
+
|
152
|
+
The pacto:generate task will take partially defined Contracts and generate the missing pieces. See [Generate](https://www.relishapp.com/maxlinc/pacto/docs/generate) for more details.
|
153
|
+
|
154
|
+
The pacto:meta_validate task makes sure that your Contracts are valid. It only checks the Contracts, not the services that implement them.
|
155
|
+
|
156
|
+
The pacto:validate task sends a request to an actual provider and ensures their response complies with the Contract.
|
18
157
|
|
19
|
-
##
|
158
|
+
## Contracts
|
20
159
|
|
21
|
-
|
22
|
-
an HTTP interaction, which is composed of two main parts: a request and a response.
|
160
|
+
Pacto works by associating a service with a Contract. The Contract is a JSON description of the service that uses json-schema to describe the response body. You don't need to write your contracts by hand. In fact, we recommend generating a Contract from your documentation or a service.
|
23
161
|
|
24
|
-
A
|
162
|
+
A contract is composed of a request that has:
|
25
163
|
|
26
164
|
- Method: the method of the HTTP request (e.g. GET, POST, PUT, DELETE);
|
27
165
|
- Path: the relative path (without host) of the provider's endpoint;
|
28
166
|
- Headers: headers sent in the HTTP request;
|
29
167
|
- Params: any data or parameters of the HTTP request (e.g. query string for GET, body for POST).
|
30
168
|
|
31
|
-
|
169
|
+
And a response has that has:
|
32
170
|
|
33
171
|
- Status: the HTTP response status code (e.g. 200, 404, 500);
|
34
172
|
- Headers: the HTTP response headers;
|
35
173
|
- Body: a JSON Schema defining the expected structure of the HTTP response body.
|
36
174
|
|
37
|
-
|
175
|
+
Below is an example contract for a GET request
|
38
176
|
to the /hello_world endpoint of a provider:
|
39
177
|
|
40
178
|
```json
|
@@ -66,94 +204,12 @@ to the /hello_world endpoint of a provider:
|
|
66
204
|
}
|
67
205
|
```
|
68
206
|
|
69
|
-
|
70
|
-
It also reinforces the fact that a contract defines the expectation of a consumer, and not the implementation of any specific provider.
|
207
|
+
## Constraints
|
71
208
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
### Rake Task
|
77
|
-
|
78
|
-
Pacto includes two Rake tasks. In order to use them, include this in your Rakefile:
|
79
|
-
|
80
|
-
```ruby
|
81
|
-
require 'pacto/rake_task'
|
82
|
-
```
|
83
|
-
|
84
|
-
Pacto can validate the contract files:
|
85
|
-
|
86
|
-
```sh
|
87
|
-
$ rake pacto:meta_validate[dir] # Validates a directory of contract definitions
|
88
|
-
```
|
89
|
-
|
90
|
-
Or it can validate contracts against a provider:
|
91
|
-
|
92
|
-
```sh
|
93
|
-
$ rake pacto:validate[host,dir] # Validates all contracts in a given directory against a given host
|
94
|
-
```
|
95
|
-
|
96
|
-
It is recommended that you also include [colorize](https://github.com/fazibear/colorize) to get prettier, colorful output.
|
97
|
-
|
98
|
-
### Programatically
|
99
|
-
|
100
|
-
The easiest way to load a contract from a file and validate it against a host is by using the builder interface:
|
101
|
-
|
102
|
-
```ruby
|
103
|
-
require 'pacto'
|
104
|
-
|
105
|
-
WebMock.allow_net_connect!
|
106
|
-
contract = Pacto.build_from_file('/path/to/contract.json', 'http://dummyprovider.com')
|
107
|
-
contract.validate
|
108
|
-
```
|
109
|
-
|
110
|
-
If you don't want to depend on Pacto to do the request you can also validate a response from a real request:
|
111
|
-
|
112
|
-
```ruby
|
113
|
-
require 'pacto'
|
114
|
-
|
115
|
-
WebMock.allow_net_connect!
|
116
|
-
contract = Pacto.build_from_file('/path/to/contract.json', 'http://dummyprovider.com')
|
117
|
-
# Doing the request with ruby stdlib, you can use your favourite lib to do the request
|
118
|
-
response = Net::HTTP.get_response(URI.parse('http://dummyprovider.com')).body
|
119
|
-
contract.validate response, body_only: true
|
120
|
-
```
|
121
|
-
|
122
|
-
Pacto also has the ability to match a request signature to a contract that is currently in used, via ```Pacto.contract_for request_signature```
|
123
|
-
|
124
|
-
## Auto-Generated Stubs
|
125
|
-
|
126
|
-
Pacto provides an API to be used in the consumer's acceptance tests. It uses a custom JSON Schema parser and generator
|
127
|
-
to generate a valid JSON document as the response body, and relies on [WebMock](https://github.com/bblimke/webmock)
|
128
|
-
to stub any HTTP requests made by your application. Important: the JSON generator is in very early stages and does not work
|
129
|
-
with the entire JSON Schema specification.
|
130
|
-
|
131
|
-
First, register the contracts that are going to be used in the acceptance tests suite. The register_contract method accepts zero or more tags:
|
132
|
-
```ruby
|
133
|
-
require 'pacto'
|
134
|
-
|
135
|
-
contract1 = Pacto.build_from_file('/path/to/contract1.json', 'http://dummyprovider.com')
|
136
|
-
contract2 = Pacto.build_from_file('/path/to/contract2.json', 'http://dummyprovider.com')
|
137
|
-
contract3 = Pacto.build_from_file('/path/to/contract3.json', 'http://dummyprovider.com')
|
138
|
-
Pacto.register_contract(contract1)
|
139
|
-
Pacto.register_contract(contract2, :public_api)
|
140
|
-
Pacto.register_contract(contract3, :public_api, :wip)
|
141
|
-
```
|
142
|
-
Then, in the setup phase of the test, specify which contracts will be used for that test:
|
143
|
-
```ruby
|
144
|
-
Pacto.use('my_tag')
|
145
|
-
```
|
146
|
-
If default values are not specified in the contract's response body, a default value will be automatically generated. It is possible
|
147
|
-
to overwrite those values, however, by passing a second argument:
|
148
|
-
```ruby
|
149
|
-
Pacto.use('my_tag', :value => 'new value')
|
150
|
-
```
|
151
|
-
The values are merged using [hash-deep-merge](https://github.com/Offirmo/hash-deep-merge).
|
209
|
+
- Pacto only works with JSON services
|
210
|
+
- Pacto requires Ruby 1.9.3 or newer (though you can older Rubies or non-Ruby projects with a [Pacto Server](#pacto-server-non-ruby-usage))
|
211
|
+
- Pacto cannot currently specify multiple acceptable status codes (e.g. 200 or 201)
|
152
212
|
|
153
213
|
## Contributing
|
154
214
|
|
155
|
-
|
156
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
157
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
158
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
159
|
-
5. Create new Pull Request
|
215
|
+
Read the CONTRIBUTING.md file.
|