pact-mock_service 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +29 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +8 -0
  7. data/Gemfile.lock +100 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +314 -0
  10. data/Rakefile +6 -0
  11. data/bin/pact-mock-service +3 -0
  12. data/lib/pact/consumer/app_manager.rb +159 -0
  13. data/lib/pact/consumer/interactions_filter.rb +48 -0
  14. data/lib/pact/consumer/mock_service/app.rb +84 -0
  15. data/lib/pact/consumer/mock_service/interaction_delete.rb +33 -0
  16. data/lib/pact/consumer/mock_service/interaction_list.rb +76 -0
  17. data/lib/pact/consumer/mock_service/interaction_mismatch.rb +73 -0
  18. data/lib/pact/consumer/mock_service/interaction_post.rb +31 -0
  19. data/lib/pact/consumer/mock_service/interaction_replay.rb +139 -0
  20. data/lib/pact/consumer/mock_service/log_get.rb +28 -0
  21. data/lib/pact/consumer/mock_service/missing_interactions_get.rb +30 -0
  22. data/lib/pact/consumer/mock_service/mock_service_administration_endpoint.rb +31 -0
  23. data/lib/pact/consumer/mock_service/pact_post.rb +33 -0
  24. data/lib/pact/consumer/mock_service/rack_request_helper.rb +51 -0
  25. data/lib/pact/consumer/mock_service/verification_get.rb +68 -0
  26. data/lib/pact/consumer/mock_service.rb +2 -0
  27. data/lib/pact/consumer/mock_service_client.rb +65 -0
  28. data/lib/pact/consumer/mock_service_interaction_expectation.rb +37 -0
  29. data/lib/pact/consumer/request.rb +27 -0
  30. data/lib/pact/consumer/server.rb +90 -0
  31. data/lib/pact/consumer_contract/consumer_contract_writer.rb +84 -0
  32. data/lib/pact/mock_service/cli.rb +49 -0
  33. data/lib/pact/mock_service/version.rb +5 -0
  34. data/lib/pact/mock_service.rb +1 -0
  35. data/pact-mock-service.gemspec +41 -0
  36. data/spec/lib/pact/consumer/app_manager_spec.rb +42 -0
  37. data/spec/lib/pact/consumer/mock_service/app_spec.rb +52 -0
  38. data/spec/lib/pact/consumer/mock_service/interaction_list_spec.rb +78 -0
  39. data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +70 -0
  40. data/spec/lib/pact/consumer/mock_service/interaction_replay_spec.rb +12 -0
  41. data/spec/lib/pact/consumer/mock_service/rack_request_helper_spec.rb +88 -0
  42. data/spec/lib/pact/consumer/mock_service/verification_get_spec.rb +142 -0
  43. data/spec/lib/pact/consumer/mock_service_client_spec.rb +88 -0
  44. data/spec/lib/pact/consumer/mock_service_interaction_expectation_spec.rb +54 -0
  45. data/spec/lib/pact/consumer/service_consumer_spec.rb +11 -0
  46. data/spec/lib/pact/consumer_contract/consumer_contract_writer_spec.rb +111 -0
  47. data/spec/spec_helper.rb +16 -0
  48. data/spec/support/a_consumer-a_producer.json +32 -0
  49. data/spec/support/a_consumer-a_provider.json +32 -0
  50. data/spec/support/active_support_if_configured.rb +6 -0
  51. data/spec/support/app_for_config_ru.rb +4 -0
  52. data/spec/support/case-insensitive-response-header-matching.json +21 -0
  53. data/spec/support/case-insensitive-response-header-matching.rb +15 -0
  54. data/spec/support/consumer_contract_template.json +24 -0
  55. data/spec/support/dsl_spec_support.rb +7 -0
  56. data/spec/support/factories.rb +82 -0
  57. data/spec/support/generated_index.md +4 -0
  58. data/spec/support/generated_markdown.md +55 -0
  59. data/spec/support/interaction_view_model.json +63 -0
  60. data/spec/support/interaction_view_model_with_terms.json +50 -0
  61. data/spec/support/markdown_pact.json +48 -0
  62. data/spec/support/missing_provider_states_output.txt +25 -0
  63. data/spec/support/options.json +21 -0
  64. data/spec/support/options_app.rb +15 -0
  65. data/spec/support/pact_helper.rb +57 -0
  66. data/spec/support/shared_examples_for_request.rb +94 -0
  67. data/spec/support/spec_support.rb +20 -0
  68. data/spec/support/stubbing.json +22 -0
  69. data/spec/support/stubbing_using_allow.rb +29 -0
  70. data/spec/support/term.json +48 -0
  71. data/spec/support/test_app_fail.json +61 -0
  72. data/spec/support/test_app_pass.json +38 -0
  73. data/spec/support/test_app_with_right_content_type_differ.json +23 -0
  74. data/tasks/spec.rake +6 -0
  75. metadata +388 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NjM1YTc2NzkzNDg1MjEzZDEwMDMyY2YxZWZlMGE1MjdlYjQyZTI2Nw==
5
+ data.tar.gz: !binary |-
6
+ ZDgyMzllNDQ5MTc1NDlkNjRhZmQyYzU1ZTRiM2M5N2JkYjRkNWUxMA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NmQ3ZmMyNjQ4ZDBmN2U5N2E5OTI5NDVhZWE1MDg3M2FiNzQ4MjlmMWE0MzBl
10
+ YmM3MWU1OWY2NTg4MGRiYWU2OWM4NDZjYjlhNGQwNzRlMDA0M2QyMDk3Nzdj
11
+ NTA4MGI0YzAzOTg5ZWI5OTczNThkNzE3MWJhYmFmZTNjZjQxZGQ=
12
+ data.tar.gz: !binary |-
13
+ MDQxMWZmNGI3YjI5NmM1ZDk4Yzc3YjkyM2U5Y2Q3OGE2NWMwYWE3ZGFkMjVi
14
+ Y2ZlZTU5N2U5ZjM4YTEwMTVmNjFhNmJmNGU3ZTE0NjhjYTI2MzExOTY2ZmVm
15
+ ZGZmYzRjZTdhZjJlZTE5MjA5OGE4NzQxZWU1MzA4YzAzNWU5MTg=
data/.gitignore ADDED
@@ -0,0 +1,29 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ lib/bundler/man
10
+ pkg
11
+ rdoc
12
+ spec/reports
13
+ test/tmp
14
+ test/version_tmp
15
+ tmp
16
+ *.swp
17
+ .bin
18
+ tags
19
+ .rbenv-version
20
+ spec/pacts
21
+ .rvmrc
22
+ *.iml
23
+ .rakeTasks
24
+ *~
25
+ .ruby-gemset
26
+ .ruby-version
27
+ log
28
+ .idea
29
+ reports
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.1
6
+ - jruby-19mode
7
+ - jruby-1.7.11
8
+ - jruby-1.7.12
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ Do this to generate your change history
2
+
3
+ git log --pretty=format:' * %h - %s (%an, %ad)'
4
+
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pact.gemspec
4
+ gemspec
5
+
6
+ if ENV['X_PACT_DEVELOPMENT']
7
+ gem 'pact-support', path: '../pact-support'
8
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,100 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pact-mock_service (0.0.1)
5
+ awesome_print (~> 1.1)
6
+ find_a_port (~> 1.0.1)
7
+ json
8
+ rack
9
+ rack-test (~> 0.6.2)
10
+ rspec (>= 2.14)
11
+ term-ansicolor (~> 1.0)
12
+ thor
13
+ webrick
14
+
15
+ PATH
16
+ remote: ../pact-support
17
+ specs:
18
+ pact-support (0.0.1)
19
+ awesome_print (~> 1.1)
20
+ find_a_port (~> 1.0.1)
21
+ json
22
+ rack-test (~> 0.6.2)
23
+ randexp (~> 0.1.7)
24
+ rspec (>= 2.14)
25
+ term-ansicolor (~> 1.0)
26
+ thor
27
+
28
+ GEM
29
+ remote: https://rubygems.org/
30
+ specs:
31
+ activesupport (4.1.1)
32
+ i18n (~> 0.6, >= 0.6.9)
33
+ json (~> 1.7, >= 1.7.7)
34
+ minitest (~> 5.1)
35
+ thread_safe (~> 0.1)
36
+ tzinfo (~> 1.1)
37
+ addressable (2.3.6)
38
+ awesome_print (1.2.0)
39
+ coderay (1.1.0)
40
+ crack (0.4.2)
41
+ safe_yaml (~> 1.0.0)
42
+ diff-lcs (1.2.5)
43
+ fakefs (0.5.2)
44
+ faraday (0.9.0)
45
+ multipart-post (>= 1.2, < 3)
46
+ find_a_port (1.0.1)
47
+ hashie (2.1.1)
48
+ i18n (0.6.9)
49
+ json (1.8.1)
50
+ method_source (0.8.2)
51
+ minitest (5.3.4)
52
+ multipart-post (2.0.0)
53
+ pry (0.10.0)
54
+ coderay (~> 1.1.0)
55
+ method_source (~> 0.8.1)
56
+ slop (~> 3.4)
57
+ rack (1.5.2)
58
+ rack-test (0.6.2)
59
+ rack (>= 1.0)
60
+ rake (10.0.4)
61
+ randexp (0.1.7)
62
+ rspec (3.1.0)
63
+ rspec-core (~> 3.1.0)
64
+ rspec-expectations (~> 3.1.0)
65
+ rspec-mocks (~> 3.1.0)
66
+ rspec-core (3.1.6)
67
+ rspec-support (~> 3.1.0)
68
+ rspec-expectations (3.1.2)
69
+ diff-lcs (>= 1.2.0, < 2.0)
70
+ rspec-support (~> 3.1.0)
71
+ rspec-mocks (3.1.3)
72
+ rspec-support (~> 3.1.0)
73
+ rspec-support (3.1.2)
74
+ safe_yaml (1.0.3)
75
+ slop (3.5.0)
76
+ term-ansicolor (1.3.0)
77
+ tins (~> 1.0)
78
+ thor (0.19.1)
79
+ thread_safe (0.3.4)
80
+ tins (1.3.3)
81
+ tzinfo (1.2.1)
82
+ thread_safe (~> 0.1)
83
+ webmock (1.18.0)
84
+ addressable (>= 2.3.6)
85
+ crack (>= 0.3.2)
86
+ webrick (1.3.1)
87
+
88
+ PLATFORMS
89
+ ruby
90
+
91
+ DEPENDENCIES
92
+ activesupport
93
+ fakefs (~> 0.4)
94
+ faraday
95
+ hashie (~> 2.0)
96
+ pact-mock_service!
97
+ pact-support!
98
+ pry
99
+ rake (~> 10.0.3)
100
+ webmock (~> 1.18.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 James Fraser, Sergei Matheson, Brent Snook, Ronald Holshausen, Beth Skurrie
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,314 @@
1
+ # Pact
2
+
3
+ Define a pact between service consumers and providers, enabling "consumer driven contract" testing.
4
+
5
+ Pact provides an fluent API for service consumers to define the HTTP requests they will make to a service provider and the HTTP responses they expect back. These expectations are used in the consumer specs to provide a mock service provider. The interactions are recorded, and played back in the service provider specs to ensure the service provider actually does provide the response the consumer expects.
6
+
7
+ This allows testing of both sides of an integration point using fast unit tests.
8
+
9
+ This gem is inspired by the concept of "Consumer driven contracts". See [this article](http://martinfowler.com/articles/consumerDrivenContracts.html) by Martin Fowler for more information.
10
+
11
+ Travis CI Status: [![travis-ci.org Build Status](https://travis-ci.org/realestate-com-au/pact.png)](https://travis-ci.org/realestate-com-au/pact)
12
+
13
+ ## What is it good for?
14
+
15
+ Pact is most valuable for designing and testing integrations where you (or your team/organisation/partner organisation) control the development of both the consumer and the provider. It is fantastic tool for testing intra-organsation microservices.
16
+
17
+ ## What is it not good for?
18
+
19
+ * Performance and load testing.
20
+ * Functional testing of the provider - that is what the provider's own tests should do. Pact is about checking the contents and format of requests and responses.
21
+ * Situations where you cannot load data into the provider without using the API that you're actually testing.
22
+
23
+ ## Features
24
+
25
+ * A service is mocked using an actual process running on a specified port, so javascript clients can be tested as easily as backend clients.
26
+ * "Provider states" (similar to fixtures) allow the same request to be made with a different expected response.
27
+ * Consumers specify only the fields they are interested in, allowing a provider to return more fields without breaking the pact. This allows a provider to have a different pact with a different consumer, and know which fields each cares about in a given response.
28
+ * RSpec and Minitest support for the service consumer codebase.
29
+ * Rake tasks allow pacts to be verified against a service provider codebase.
30
+ * Different versions of a consumer/provider pairs can be easily tested against each other, allowing confidence when deploying new versions of each (see the pact_broker and pact_broker-client gems).
31
+ * Autogenerated API documentation - need we say more?
32
+ * Autogenerated network diagrams with the [Pact Broker](https://github.com/bethesque/pact_broker)
33
+
34
+ ## How does it work?
35
+
36
+ 1. In the specs for the provider facing code in the consumer project, expectations are set up on a mock service provider.
37
+ 1. When the specs are run, the mock service returns the expected responses. The requests, and their expected responses, are then written to a "pact" file.
38
+ 1. The requests in the pact file are later replayed against the provider, and the actual responses are checked to make sure they match the expected responses.
39
+
40
+ ## Why is developing and testing with Pact better than using traditional system integration tests?
41
+
42
+ * Faster execution.
43
+ * Reliable responses from mock service reduce likelihood of flakey tests.
44
+ * Causes of failure are easier to identify as only one component is being tested at a time.
45
+ * Design of service provider is improved by considering first how the data is actually going to be used, rather than how it is most easily retrieved and serialised.
46
+ * No separate integration environment required for automated integration tests - pact tests run in standalone CI builds.
47
+ * Integration flows that would traditionally require running multiple services at the same time can be broken down and each integration point tested separately.
48
+
49
+ ## Contact
50
+
51
+ * Twitter: [@pact_up](https://twitter.com/pact_up)
52
+ * Google users group: https://groups.google.com/forum/#!forum/pact-support
53
+
54
+ ## Installation
55
+
56
+ Put it in your Gemfile. You know how.
57
+
58
+ ## Usage
59
+
60
+ ### Service Consumer project
61
+
62
+ #### 1. Start with your model
63
+
64
+ Imagine a model class that looks something like this. The attributes for a Something live on a remote server, and will need to be retrieved by an HTTP call.
65
+
66
+ ```ruby
67
+ class Something
68
+ attr_reader :name
69
+
70
+ def initialize name
71
+ @name = name
72
+ end
73
+
74
+ def == other
75
+ other.is_a?(Something) && other.name == name
76
+ end
77
+ end
78
+ ```
79
+
80
+ #### 2. Create a skeleton client class
81
+
82
+ Imagine a service provider client class that looks something like this.
83
+
84
+ ```ruby
85
+ require 'httparty'
86
+
87
+ class MyServiceProviderClient
88
+ include HTTParty
89
+ base_uri 'http://my-service'
90
+
91
+ def get_something
92
+ # Yet to be implemented because we're doing Test First Development...
93
+ end
94
+ end
95
+ ```
96
+ #### 3. Configure the mock server
97
+
98
+ The following code will create a mock service on localhost:1234 which will respond to your application's queries over HTTP as if it were the real "My Service Provider" app. It also creates a mock service provider object which you will use to set up your expectations. The method name to access the mock service provider will be what ever name you give as the service argument - in this case "my_service_provider"
99
+
100
+
101
+ ```ruby
102
+ # In /spec/service_providers/pact_helper.rb
103
+
104
+ require 'pact/consumer/rspec'
105
+ # or require 'pact/consumer/minitest' if you are using Minitest
106
+
107
+ Pact.service_consumer "My Service Consumer" do
108
+ has_pact_with "My Service Provider" do
109
+ mock_service :my_service_provider do
110
+ port 1234
111
+ end
112
+ end
113
+ end
114
+ ```
115
+
116
+ #### 4. Write a failing spec for the client
117
+
118
+ ```ruby
119
+ # In /spec/service_providers/my_service_provider_client_spec.rb
120
+
121
+ # When using RSpec, use the metadata `:pact => true` to include all the pact functionality in your spec.
122
+ # When using Minitest, include Pact::Consumer::Minitest in your spec.
123
+
124
+ describe MyServiceProviderClient, :pact => true do
125
+
126
+ before do
127
+ # Configure your client to point to the stub service on localhost using the port you have specified
128
+ MyServiceProviderClient.base_uri 'localhost:1234'
129
+ end
130
+
131
+ subject { MyServiceProviderClient.new }
132
+
133
+ describe "get_something" do
134
+
135
+ before do
136
+ my_service_provider.given("something exists").
137
+ upon_receiving("a request for something").with(method: :get, path: '/something', query: '').
138
+ will_respond_with(
139
+ status: 200,
140
+ headers: {'Content-Type' => 'application/json'},
141
+ body: {name: 'A small something'} )
142
+ end
143
+
144
+ it "returns a Something" do
145
+ expect(subject.get_something).to eq(Something.new('A small something'))
146
+ end
147
+
148
+ end
149
+
150
+ end
151
+ ```
152
+
153
+ #### 5. Run the specs
154
+
155
+ Running the consumer spec will generate a pact file in the configured pact dir (spec/pacts by default).
156
+ Logs will be output to the configured log dir that can be useful when diagnosing problems.
157
+
158
+ Of course, the above specs will fail because the client method is not implemented, so next, implement your client methods.
159
+
160
+ #### 6. Implement the client methods
161
+
162
+ ```ruby
163
+ class MyServiceProviderClient
164
+ include HTTParty
165
+ base_uri 'http://my-service'
166
+
167
+ def get_something
168
+ name = JSON.parse(self.class.get("/something").body)['name']
169
+ Something.new(name)
170
+ end
171
+ end
172
+ ```
173
+
174
+ #### 7. Run the specs again.
175
+
176
+ Green! You now have a pact file that can be used to verify your expectations of the provider project.
177
+
178
+ Now, rinse and repeat for other likely status codes that may be returned. For example, consider how you want your client to respond to a:
179
+ * 404 (return null, or raise an error?)
180
+ * 500 (specifying that the response body should contain an error message, and ensuring that your client logs that error message will make your life much easier when things go wrong)
181
+ * 401/403 if there is authorisation.
182
+
183
+ ### Service Provider project
184
+
185
+ #### 1. Create the skeleton API classes
186
+
187
+ Create your API class using the framework of your choice (the Pact authors have a preference for [Webmachine][webmachine] and [Roar][roar]) - leave the methods unimplemented, we're doing Test First Develoment, remember?
188
+
189
+ #### 2. Tell your provider that it needs to honour the pact file you made earlier
190
+
191
+ Require "pact/tasks" in your Rakefile.
192
+
193
+ ```ruby
194
+ # In Rakefile
195
+ require 'pact/tasks'
196
+ ```
197
+
198
+ Create a `pact_helper.rb` in your service provider project. The recommended place is `spec/service_consumers/pact_helper.rb`.
199
+
200
+ See [Verifying Pacts](https://github.com/realestate-com-au/pact/wiki/Verifying-pacts) and the [Provider](documentation/configuration.md#provider) section of the Configuration documentation for more information.
201
+
202
+ ```ruby
203
+ # In specs/service_consumers/pact_helper.rb
204
+
205
+ require 'pact/provider/rspec'
206
+
207
+ Pact.service_provider "My Service Provider" do
208
+
209
+ honours_pact_with 'My Service Consumer' do
210
+
211
+ # This example points to a local file, however, on a real project with a continuous
212
+ # integration box, you would use a [Pact Broker](https://github.com/bethesque/pact_broker) or publish your pacts as artifacts,
213
+ # and point the pact_uri to the pact published by the last successful build.
214
+
215
+ pact_uri '../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json'
216
+ end
217
+ end
218
+ ```
219
+
220
+ #### 3. Run your failing specs
221
+
222
+ $ rake pact:verify
223
+
224
+ Congratulations! You now have a failing spec to develop against.
225
+
226
+ At this stage, you'll want to be able to run your specs one at a time while you implement each feature. At the bottom of the failed pact:verify output you will see the commands to rerun each failed interaction individually. A command to run just one interaction will look like this:
227
+
228
+ $ rake pact:verify PACT_DESCRIPTION="a request for something" PACT_PROVIDER_STATE="something exists"
229
+
230
+ #### 4. Implement enough to make your first interaction spec pass
231
+
232
+ Rinse and repeat.
233
+
234
+ #### 5. Keep going til you're green
235
+
236
+ Yay! Your provider now honours the pact it has with your consumer. You can now have confidence that your consumer and provider will play nicely together.
237
+
238
+ ### Using provider states
239
+
240
+ Each interaction in a pact is verified in isolation, with no context maintained from the previous interactions. So how do you test a request that requires data to already exist on the provider? Read about provider states [here](https://github.com/realestate-com-au/pact/wiki/Provider-states).
241
+
242
+
243
+ ## Configuration
244
+
245
+ See the [Configuration](/documentation/configuration.md) section of the documentation for options relating to thing like logging, diff formatting, and documentation generation.
246
+
247
+ ## Pact best practices
248
+
249
+ As in all things, there are good ways to implement Pacts, and there are not so good ways. Check out the [Best practices](https://github.com/realestate-com-au/pact/wiki/Best-practices) section of the documentation to make sure you're not Pacting it Wrong.
250
+
251
+ ## Docs
252
+
253
+ * [Example](example)
254
+ * [Configuration](documentation/configuration.md)
255
+ * [Terminology](https://github.com/realestate-com-au/pact/wiki/Terminology)
256
+ * [Provider States](https://github.com/realestate-com-au/pact/wiki/Provider-states)
257
+ * [Verifying pacts](https://github.com/realestate-com-au/pact/wiki/Verifying-pacts)
258
+ * [Sharing pacts between consumer and provider](https://github.com/realestate-com-au/pact/wiki/Sharing-pacts-between-consumer-and-provider)
259
+ * [Using regular expressions with Pact](https://github.com/realestate-com-au/pact/wiki/Using-regular-expressions-with-Pact)
260
+ * [Frequently asked questions](https://github.com/realestate-com-au/pact/wiki/FAQ)
261
+ * [Rarely asked questions](https://github.com/realestate-com-au/pact/wiki/RAQ)
262
+ * [Best practices](https://github.com/realestate-com-au/pact/wiki/Best-practices)
263
+ * [Troubleshooting](https://github.com/realestate-com-au/pact/wiki/Troubleshooting)
264
+ * [Testing with pact diagram](https://github.com/realestate-com-au/pact/wiki/Testing with pact.png)
265
+
266
+ ## Related libraries
267
+
268
+ [Pact Provider Proxy](https://github.com/bethesque/pact-provider-proxy) - Verify a pact against a running server, allowing you to use pacts with a provider of any language.
269
+
270
+ [Pact Broker](https://github.com/bethesque/pact_broker) - A pact repository. Provides endpoints to access published pacts, meaning you don't need to use messy CI URLs in your codebase. Enables cross testing of prod/head versions of your consumer and provider, allowing you to determine whether the head version of one is compatible with the production version of the other. Helps you to answer that ever so important question, "can I deploy without breaking all the things?"
271
+
272
+ [Pact Broker Client](https://github.com/bethesque/pact_broker-client) - Contains rake tasks for publishing pacts to the pact_broker.
273
+
274
+ [Pact JVM](https://github.com/DiUS/pact-jvm) - A Pact implementation for the JVM (Java and Scala). It generates pact files that are compatible with the Ruby implementation.
275
+
276
+ [Pact .NET](https://github.com/SEEK-Jobs/pact-net) - A Pact implementation for .NET.
277
+
278
+ [Shokkenki](https://github.com/brentsnook/shokkenki) - Another Consumer Driven Contract gem written by one of Pact's original authors, Brent Snook. Shokkenki allows matchers to be composed using jsonpath expressions and allows auto-generation of mock response values based on regular expressions.
279
+
280
+ ## Links
281
+
282
+ [Simplifying microservices testing with pacts](http://dius.com.au/2014/05/19/simplifying-micro-service-testing-with-pacts/) - Ron Holshausen (one of the original pact authors)
283
+
284
+ [Pact specification](https://github.com/bethesque/pact-specification)
285
+
286
+ [Integrated tests are a scam](http://vimeo.com/80533536) - J.B. Rainsberger
287
+
288
+ [Consumer Driven Contracts](http://martinfowler.com/articles/consumerDrivenContracts.html) - Ian Robinson
289
+
290
+ [Integration Contract Tests](http://martinfowler.com/bliki/IntegrationContractTest.html) - Martin Fowler
291
+
292
+ ## TODO
293
+
294
+ Short term:
295
+ - Support hash of query params
296
+
297
+ Long term:
298
+ - Provide more flexible matching (eg the keys should match, and the classes of the values should match, but the values of each key do not need to be equal). This is to make the pact verification less brittle.
299
+ - Add XML support
300
+ - Decouple Rspec from Pact and make rspec-pact gem for easy integration
301
+
302
+ ## Contributing
303
+
304
+ 1. Fork it
305
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
306
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
307
+ 4. Push to the branch (`git push origin my-new-feature`)
308
+ 5. Create new Pull Request
309
+
310
+ If you would like to implement pact in another language, please check out the [Pact specification](https://github.com/bethesque/pact-specification) and have a chat to one of us on the [pact-dev Google group](https://groups.google.com/forum/#!forum/pact-dev). The vision is to have a compatible pact implementation in all the commonly used languages, your help would be greatly appreciated!
311
+
312
+ [webmachine]: https://github.com/seancribbs/webmachine-ruby
313
+ [roar]: https://github.com/apotonick/roar
314
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ Dir.glob('./lib/tasks/**/*.rake').each { |task| load task }
4
+ Dir.glob('./tasks/**/*.rake').each { |task| load task }
5
+
6
+
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pact/mock_service/cli'
3
+ Pact::MockService::CLI.start
@@ -0,0 +1,159 @@
1
+ require 'thwait'
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'find_a_port'
6
+ require 'pact/logging'
7
+ require 'pact/consumer/server'
8
+ require 'singleton'
9
+ require 'pact/consumer/mock_service/app'
10
+
11
+ module Pact
12
+ module Consumer
13
+ class AppManager
14
+
15
+ include Pact::Logging
16
+
17
+ include Singleton
18
+
19
+ def initialize
20
+ @apps_spawned = false
21
+ @app_registrations = []
22
+ end
23
+
24
+ def register_mock_service_for name, url
25
+ uri = URI(url)
26
+ raise "Currently only http is supported" unless uri.scheme == 'http'
27
+ raise "Currently only services on localhost are supported" unless uri.host == 'localhost'
28
+
29
+ register(MockService.new(log_file: create_log_file(name), name: name), uri.port)
30
+ end
31
+
32
+ def register(app, port = FindAPort.available_port)
33
+ existing = existing_app_on_port port
34
+ raise "Port #{port} is already being used by #{existing}" if existing and not existing == app
35
+ app_registration = register_app app, port
36
+ app_registration.spawn if @apps_spawned
37
+ port
38
+ end
39
+
40
+ def existing_app_on_port port
41
+ app_registration = @app_registrations.find { |app_registration| app_registration.port == port }
42
+ app_registration ? app_registration.app : nil
43
+ end
44
+
45
+ def app_registered_on?(port)
46
+ app_registrations.any? { |app_registration| app_registration.port == port }
47
+ end
48
+
49
+ def ports_of_mock_services
50
+ app_registrations.find_all(&:is_a_mock_service?).collect(&:port)
51
+ end
52
+
53
+ def kill_all
54
+ app_registrations.find_all(&:spawned?).collect(&:kill)
55
+ @apps_spawned = false
56
+ end
57
+
58
+ def clear_all
59
+ kill_all
60
+ @app_registrations = []
61
+ end
62
+
63
+ def spawn_all
64
+ app_registrations.find_all(&:not_spawned?).collect(&:spawn)
65
+ @apps_spawned = true
66
+ end
67
+
68
+ private
69
+
70
+ def create_log_file service_name
71
+ FileUtils::mkdir_p Pact.configuration.log_dir
72
+ log = File.open(log_file_path(service_name), 'w')
73
+ log.sync = true
74
+ log
75
+ end
76
+
77
+ def log_file_path service_name
78
+ File.join(Pact.configuration.log_dir, "#{log_file_name(service_name)}.log")
79
+ end
80
+
81
+ def log_file_name service_name
82
+ lower_case_name = service_name.downcase.gsub(/\s+/, '_')
83
+ if lower_case_name.include?('_service')
84
+ lower_case_name.gsub('_service', '_mock_service')
85
+ else
86
+ lower_case_name + '_mock_service'
87
+ end
88
+ end
89
+
90
+ def app_registrations
91
+ @app_registrations
92
+ end
93
+
94
+ def register_app app, port
95
+ app_registration = AppRegistration.new :app => app, :port => port
96
+ app_registrations << app_registration
97
+ app_registration
98
+ end
99
+ end
100
+
101
+ class AppRegistration
102
+ include Pact::Logging
103
+ attr_accessor :port
104
+ attr_accessor :app
105
+ attr_accessor :pid
106
+
107
+ def initialize opts
108
+ @max_wait = 10
109
+ @port = opts[:port]
110
+ @pid = opts[:pid]
111
+ @app = opts[:app]
112
+ end
113
+
114
+ def kill
115
+ # TODO: need to work out how to kill
116
+ # logger.info "Killing #{self}"
117
+ # Process.kill(9, pid)
118
+ # Process.wait(pid)
119
+ # self.pid = nil
120
+ self.pid = nil
121
+ end
122
+
123
+ def not_spawned?
124
+ !spawned?
125
+ end
126
+
127
+ def spawned?
128
+ self.pid != nil
129
+ end
130
+
131
+ def is_a_mock_service?
132
+ app.is_a? MockService
133
+ end
134
+
135
+ def to_s
136
+ "#{app} on port #{port}" + (@pid ? " with pid #{pid}" : "")
137
+ end
138
+
139
+ def spawn
140
+ logger.info "Starting app #{self}..."
141
+ Pact::Server.new(app, port).boot
142
+ self.pid = 'unknown'
143
+ logger.info "Started with pid #{pid}"
144
+ end
145
+
146
+ def wait_until
147
+ waited = 0
148
+ wait_time = 0.1
149
+ while waited < @max_wait do
150
+ break if yield
151
+ sleep wait_time
152
+ waited += wait_time
153
+ raise "Waited longer than #{@max_wait} seconds" if waited >= @max_wait
154
+ end
155
+ end
156
+
157
+ end
158
+ end
159
+ end