pacto 0.3.0.pre → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|
+
[](http://badge.fury.io/rb/pacto)
|
|
1
2
|
[](https://travis-ci.org/thoughtworks/pacto)
|
|
2
3
|
[](https://codeclimate.com/github/thoughtworks/pacto)
|
|
3
4
|
[](https://gemnasium.com/thoughtworks/pacto)
|
|
4
5
|
[](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.
|