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.
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.