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.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rubocop-todo.yml +0 -27
  4. data/.rubocop.yml +9 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +4 -5
  8. data/CONTRIBUTING.md +112 -0
  9. data/Gemfile +5 -0
  10. data/Guardfile +18 -13
  11. data/README.md +157 -101
  12. data/Rakefile +3 -3
  13. data/features/configuration/strict_matchers.feature +97 -0
  14. data/features/evolve/README.md +11 -0
  15. data/features/evolve/existing_services.feature +82 -0
  16. data/features/generate/README.md +5 -0
  17. data/features/generate/generation.feature +28 -0
  18. data/features/steps/pacto_steps.rb +75 -0
  19. data/features/stub/README.md +2 -0
  20. data/features/stub/templates.feature +46 -0
  21. data/features/support/env.rb +11 -5
  22. data/features/validate/README.md +1 -0
  23. data/features/validate/body_only.feature +85 -0
  24. data/features/{journeys/validation.feature → validate/meta_validation.feature} +41 -24
  25. data/features/validate/validation.feature +36 -0
  26. data/lib/pacto.rb +61 -33
  27. data/lib/pacto/contract.rb +18 -15
  28. data/lib/pacto/contract_factory.rb +14 -11
  29. data/lib/pacto/contract_files.rb +17 -0
  30. data/lib/pacto/contract_list.rb +17 -0
  31. data/lib/pacto/contract_validator.rb +29 -0
  32. data/lib/pacto/core/configuration.rb +19 -17
  33. data/lib/pacto/core/contract_registry.rb +43 -0
  34. data/lib/pacto/core/{callback.rb → hook.rb} +3 -3
  35. data/lib/pacto/core/modes.rb +33 -0
  36. data/lib/pacto/core/validation_registry.rb +45 -0
  37. data/lib/pacto/erb_processor.rb +0 -1
  38. data/lib/pacto/extensions.rb +18 -4
  39. data/lib/pacto/generator.rb +34 -49
  40. data/lib/pacto/generator/filters.rb +41 -0
  41. data/lib/pacto/hooks/erb_hook.rb +4 -3
  42. data/lib/pacto/logger.rb +4 -2
  43. data/lib/pacto/meta_schema.rb +4 -2
  44. data/lib/pacto/rake_task.rb +28 -25
  45. data/lib/pacto/request_clause.rb +43 -0
  46. data/lib/pacto/request_pattern.rb +8 -0
  47. data/lib/pacto/response_clause.rb +15 -0
  48. data/lib/pacto/rspec.rb +102 -0
  49. data/lib/pacto/stubs/uri_pattern.rb +23 -0
  50. data/lib/pacto/stubs/webmock_adapter.rb +69 -0
  51. data/lib/pacto/stubs/webmock_helper.rb +71 -0
  52. data/lib/pacto/ui.rb +7 -0
  53. data/lib/pacto/uri.rb +9 -0
  54. data/lib/pacto/validation.rb +57 -0
  55. data/lib/pacto/validators/body_validator.rb +41 -0
  56. data/lib/pacto/validators/request_body_validator.rb +23 -0
  57. data/lib/pacto/validators/response_body_validator.rb +23 -0
  58. data/lib/pacto/validators/response_header_validator.rb +49 -0
  59. data/lib/pacto/validators/response_status_validator.rb +24 -0
  60. data/lib/pacto/version.rb +1 -1
  61. data/pacto.gemspec +33 -29
  62. data/resources/contract_schema.json +8 -176
  63. data/resources/draft-03.json +174 -0
  64. data/spec/integration/data/strict_contract.json +2 -2
  65. data/spec/integration/e2e_spec.rb +22 -31
  66. data/spec/integration/rspec_spec.rb +94 -0
  67. data/spec/integration/templating_spec.rb +9 -12
  68. data/{lib → spec}/pacto/server.rb +0 -0
  69. data/{lib → spec}/pacto/server/dummy.rb +11 -8
  70. data/{lib → spec}/pacto/server/playback_servlet.rb +1 -1
  71. data/spec/spec_helper.rb +2 -0
  72. data/spec/unit/hooks/erb_hook_spec.rb +15 -15
  73. data/spec/unit/pacto/configuration_spec.rb +2 -10
  74. data/spec/unit/pacto/contract_factory_spec.rb +16 -13
  75. data/spec/unit/pacto/contract_files_spec.rb +42 -0
  76. data/spec/unit/pacto/contract_list_spec.rb +35 -0
  77. data/spec/unit/pacto/contract_spec.rb +43 -44
  78. data/spec/unit/pacto/contract_validator_spec.rb +85 -0
  79. data/spec/unit/pacto/core/configuration_spec.rb +4 -11
  80. data/spec/unit/pacto/core/contract_registry_spec.rb +119 -0
  81. data/spec/unit/pacto/core/modes_spec.rb +18 -0
  82. data/spec/unit/pacto/core/validation_registry_spec.rb +76 -0
  83. data/spec/unit/pacto/core/validation_spec.rb +60 -0
  84. data/spec/unit/pacto/extensions_spec.rb +14 -23
  85. data/spec/unit/pacto/generator/filters_spec.rb +99 -0
  86. data/spec/unit/pacto/generator_spec.rb +34 -73
  87. data/spec/unit/pacto/meta_schema_spec.rb +46 -6
  88. data/spec/unit/pacto/pacto_spec.rb +17 -15
  89. data/spec/unit/pacto/{request_spec.rb → request_clause_spec.rb} +32 -44
  90. data/spec/unit/pacto/request_pattern_spec.rb +22 -0
  91. data/spec/unit/pacto/response_clause_spec.rb +54 -0
  92. data/spec/unit/pacto/stubs/uri_pattern_spec.rb +28 -0
  93. data/spec/unit/pacto/stubs/webmock_adapter_spec.rb +205 -0
  94. data/spec/unit/pacto/stubs/webmock_helper_spec.rb +20 -0
  95. data/spec/unit/pacto/uri_spec.rb +20 -0
  96. data/spec/unit/pacto/validators/body_validator_spec.rb +105 -0
  97. data/spec/unit/pacto/validators/response_header_validator_spec.rb +94 -0
  98. data/spec/unit/pacto/validators/response_status_validator_spec.rb +20 -0
  99. metadata +230 -146
  100. data/features/generation/generation.feature +0 -25
  101. data/lib/pacto/core/contract_repository.rb +0 -44
  102. data/lib/pacto/hash_merge_processor.rb +0 -14
  103. data/lib/pacto/request.rb +0 -57
  104. data/lib/pacto/response.rb +0 -63
  105. data/lib/pacto/response_adapter.rb +0 -24
  106. data/lib/pacto/stubs/built_in.rb +0 -57
  107. data/spec/unit/pacto/core/contract_repository_spec.rb +0 -133
  108. data/spec/unit/pacto/hash_merge_processor_spec.rb +0 -20
  109. data/spec/unit/pacto/response_adapter_spec.rb +0 -25
  110. data/spec/unit/pacto/response_spec.rb +0 -201
  111. data/spec/unit/pacto/stubs/built_in_spec.rb +0 -168
@@ -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
@@ -20,4 +20,6 @@ tmp
20
20
  .idea/
21
21
  .floo*
22
22
  .sublime*
23
+ bin/*
24
+ tags
23
25
 
@@ -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
@@ -1 +1,10 @@
1
1
  inherit_from: .rubocop-todo.yml
2
+
3
+ AllCops:
4
+ Includes:
5
+ - Guardfile
6
+ - Rakefile
7
+ - pacto.gemspec
8
+ Excludes:
9
+ - bin/**
10
+ - tmp/**
@@ -0,0 +1 @@
1
+ pacto
@@ -0,0 +1 @@
1
+ 2.1.0
@@ -1,9 +1,8 @@
1
1
  language: ruby
2
+ before_install: gem update --remote bundler
2
3
  rvm:
3
- - 1.9.3
4
+ - 2.1.0
4
5
  - 2.0.0
6
+ - 1.9.3
5
7
  - jruby
6
- - 1.8.7
7
- matrix:
8
- allow_failures:
9
- - rvm: 1.8.7
8
+
@@ -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(%r{.+\.rb$})
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(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
12
- watch('spec/spec_helper.rb') { "spec/unit" }
13
- watch('spec/unit/spec_helper.rb') { "spec/unit" }
14
- watch(%r{^spec/unit/data/.+\.json$}) { "spec/unit" }
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$}) { "spec/integration" }
19
- watch(%r{^lib/.+.rb$}) { "spec/integration" }
20
- watch('spec/spec_helper.rb') { "spec/integration" }
21
- watch('spec/integration/spec_helper.rb') { "spec/integration" }
22
- watch(%r{^spec/integration/data/.+\.json$}) { "spec/integration" }
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(%r{^features/.+\.feature$})
27
- watch(%r{^features/support/.+$}) { 'features' }
28
- watch(%r{^features/step_definitions/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'features' }
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 Ruby implementation of the [Consumer-Driven Contracts](http://martinfowler.com/articles/consumerDrivenContracts.html)
9
- pattern for evolving services. Its main features are:
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
- - A simple language for specifying a contract;
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
- It was developed in a micro-services environment, specifically a RESTful one, so expect it to be opinionated. Although
16
- there is enough functionality implemented to motivate us to open-source this, it is still a work in progress and under active
17
- development. Check the Constraints session for further information on what works and what doesn't.
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
- ## Specifying Contracts
158
+ ## Contracts
20
159
 
21
- A contract specifies a single message exchange between a consumer and a provider. In a RESTful world, this means
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 request has the following attributes:
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
- A response has the following attributes:
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
- Pacto relies on a simple, JSON based language for defining contracts. Below is an example contract for a GET request
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
- The host address is intentionally left out of the request specification so that we can validate a contract against any provider.
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
- ## Validating Contracts
73
-
74
- There are two ways to validate a contract against a provider: through a Rake task or programatically.
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
- 1. Fork it
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.