pact 1.0.13 → 1.0.15

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ Do this to generate your change history
2
+
3
+ git log --date=relative --pretty=format:' * %h - %s (%an, %ad)'
4
+
5
+ ### 1.0.15 (22 October 2013)
6
+
7
+ * 6800a58 - Updating README with latest TODOs (Beth, 2 hours ago)
8
+ * 99a6827 - Improving logging in pact:verify. Fixing bug where Pact log level was ignored. (Beth, 3 hours ago)
9
+ * 5434f54 - Updating README with best practice and information on the :pact => :verify metadata. (Beth, 4 hours ago)
10
+ * 16dd2be - Adding :pact => :verify to pact:verify rspec examples for https://github.com/uglyog/pact/issues/3 (Beth, 5 hours ago)
11
+
12
+ ### 1.0.14 (22 October 2013)
13
+
14
+ * 406e746 - Added a template for the provider state when no provider state is found (Beth, 9 minutes ago)
15
+ * 1f58be8 - Adding error messages when set_up or tear_down are not defined, and added no_op as a way to avoid having to use an empty set_up block when there is no data to set up (Beth)
16
+ * 78d3999 - Merge pull request #2 from stuliston/json_warning_minor_refactor (Ronald Holshausen, 18 hours ago)
17
+ * be4a466 - Altering JsonWarning so that it only warns once. Added spec to confirm that's the case. (Stuart Liston, 21 hours ago)
18
+ * 3b11b42 - Fixing the issue where a method defined in global scope could not be accessed in the DSL delegation code (Beth, 11 days ago)
19
+
1
20
  ### 1.0.13 (10 October 2013)
2
21
 
3
22
  * Fixed bug deserialising Pact::SomethingLike [Beth Skurrie]
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pact (1.0.13)
4
+ pact (1.0.15)
5
5
  awesome_print (~> 1.1.0)
6
6
  find_a_port (~> 1.0.1)
7
7
  json
@@ -28,7 +28,7 @@ GEM
28
28
  geminabox-client (0.0.2)
29
29
  multipart-post
30
30
  hashie (2.0.5)
31
- json (1.8.0)
31
+ json (1.8.1)
32
32
  method_source (0.8.2)
33
33
  multipart-post (1.2.0)
34
34
  pry (0.9.12.2)
data/README.md CHANGED
@@ -177,9 +177,7 @@ Pact.provider_states_for 'My Service Consumer' do
177
177
  end
178
178
 
179
179
  provider_state "a thing does not exist" do
180
- set_up do
181
- # Well, probably not much to do here, but you get the picture.
182
- end
180
+ no_op # If there's nothing to do because the state name is more for documentation purposes, you can use no_op to imply this.
183
181
  end
184
182
  end
185
183
 
@@ -225,6 +223,28 @@ end
225
223
  The pact.uri may be a local file system path or a remote URL.
226
224
 
227
225
 
226
+ ## Pact best practices
227
+
228
+ ### Ensure all calls to the provider go through your provider client class
229
+
230
+ Do not hand create any HTTP requests in your consumer app or specs. Testing through your provider client class gives you the assurance that your consumer app will be creating exactly the HTTP requests that you think it should.
231
+
232
+ ### Do not stub your database calls in the provider project
233
+
234
+ This is the best time for you to test your database integration. If you stub your database calls, you are getting little more assurance that the real end-to-end will work than if you'd used a unit test. It's the appropriate time to incur the overhead of a database call.
235
+
236
+ ### Use factories to create your expected models in your consumer project
237
+
238
+ Sure, you've checked that your client deserialises the HTTP response into the object you expect, but then you need to make sure in your other tests where you stub your client that you're stubbing it with a valid object. The best way to do this is to use factories for all your tests.
239
+
240
+ ### Publish your pacts as artifacts on your CI machine
241
+
242
+ Configure the pact_uri in the Pact.service_provider block with the pact artifact URL of your last successful build. This way you're only verifying green builds. No point verifying a broken one.
243
+ (Watch this space - pact-broker coming soon, so we don't have to use messy build box artifact URLs)
244
+
245
+
246
+ ## Advanced
247
+
228
248
  ### Running a standalone mock server
229
249
  A pact service can be run locally and is really useful for debugging purposes.
230
250
 
@@ -233,33 +253,35 @@ A pact service can be run locally and is really useful for debugging purposes.
233
253
  The service prints messages it recieves to stdout which can be really useful
234
254
  when diagnosing issues with pacts.
235
255
 
236
- ## Advanced
237
-
238
256
  ### Notes on pact file write mode
239
257
 
240
258
  By default, the pact file will be overwritten (started from scratch) every time any rspec runs any spec using pacts. This means that if there are interactions that haven't been executed in the most recent rspec run, they are effectively removed from the pact file. If you have long running pact specs (e.g. they are generated using the browser with Capybara) and you are developing both consumer and provider in parallel, or trying to fix a broken interaction, it can be tedius to run all the specs at once. In this scenario, you can set the pactfile_write_mode to :update. This will keep all existing interactions, and update only the changed ones, identified by description and provider state. The down side of this is that if either of those fields change, the old interactions will not be removed from the pact file. As a middle path, you can set pactfile_write_mode to :smart. This will use :overwrite mode when running rake (as determined by a call to system using 'ps') and :update when running an individual spec.
241
259
 
260
+ ### To use RSpec hooks for pact:verify
261
+
262
+ The pact:verify RSpec examples have the metadata `{:pact => :verify}` defined. You can add RSpec hooks using a filter as shown here:
263
+
264
+ ```ruby
265
+ RSpec.configure do | config |
266
+ config.before :each, :pact => :verify do
267
+ # Your code here
268
+ end
269
+ end
270
+ ```
271
+
272
+ See https://www.relishapp.com/rspec/rspec-core/docs/hooks/filters for more information.
273
+
242
274
  ## TODO
243
275
 
244
276
  Short term:
245
- - Rename Pact to ConsumerContract (Done)
246
- - Simplify set up for consumer (Done)
247
- - Move server spawning into to the "at" method (Done)
248
- - automatically register before and after hooks in consumer (Done)
249
- - Provide before and after hooks and a place to define the app for Pact configuration in service provider (remove Rspc from interface of Pact setup) (Done)
250
- - Set_up for state
251
- - Tear_down for state
252
- - Before hook for all
253
- - After hook for all
254
- - Make service provider state lookup try consumer defined state first, then fall back to global one (Done)
255
- - Put service provider and consumer name into pact file (Done)
256
- - Remove consumer name from the rake task, as it should now be able to be determined from the pact file. (Done)
257
- - 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.
277
+ - FIX EXAMPLE!!!
278
+ - Make a pact-broker to store and return all the pacts, removing dependency on the CI box URLs.
279
+ - Provide a better work around for ActiveSupport JSON rubygems hell.
258
280
 
259
281
  Long term:
282
+ - 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.
260
283
  - Decouple Rspec from Pact and make rspec-pact gem for easy integration
261
284
 
262
-
263
285
  ## Contributing
264
286
 
265
287
  1. Fork it
@@ -12,7 +12,7 @@ module Pact
12
12
  attr_writer :pactfile_write_mode
13
13
 
14
14
  def log_path
15
- log_dir + "/pact_gem.log"
15
+ log_dir + "/pact.log"
16
16
  end
17
17
 
18
18
  def pactfile_write_mode
@@ -24,16 +24,7 @@ module Pact
24
24
  class MockService
25
25
 
26
26
  def initialize options = {}
27
- options = {log_file: STDOUT}.merge options
28
- log_stream = options[:log_file]
29
- @logger = Logger.new log_stream
30
-
31
- log_description = if log_stream.is_a? File
32
- File.absolute_path(log_stream).gsub(Dir.pwd + "/", '')
33
- else
34
- "standard out/err"
35
- end
36
-
27
+ log_description = configure_logger options
37
28
  interaction_list = InteractionList.new
38
29
 
39
30
  @name = options.fetch(:name, "MockService")
@@ -47,6 +38,19 @@ module Pact
47
38
  ]
48
39
  end
49
40
 
41
+ def configure_logger options
42
+ options = {log_file: STDOUT}.merge options
43
+ log_stream = options[:log_file]
44
+ @logger = Logger.new log_stream
45
+ @logger.level = Pact.configuration.logger.level
46
+
47
+ if log_stream.is_a? File
48
+ File.absolute_path(log_stream).gsub(Dir.pwd + "/", '')
49
+ else
50
+ "standard out/err"
51
+ end
52
+ end
53
+
50
54
  def to_s
51
55
  "#{@name} #{super.to_s}"
52
56
  end
@@ -4,7 +4,8 @@ require 'json/add/regexp'
4
4
  module Pact
5
5
  module JsonWarning
6
6
  def check_for_active_support_json
7
- @already_warned ||= false
7
+ return if @already_warned
8
+
8
9
  # Active support clobbers the as_json methods defined in the json/add directory of the json gem.
9
10
  # These methods are required to serialize and deserialize the Regexp and Symbol classes properly.
10
11
  # You can potentially fix this by making sure the json gem is required AFTER the active_support/json gem
@@ -16,12 +17,16 @@ module Pact
16
17
  # without breaking the calling code, which may depend on activesupport/json... then please fix this.
17
18
  # Note: we can probably do this in Ruby 2.0 with refinements, but for now, we're all stuck on 1.9 :(
18
19
 
19
- unless @already_warned
20
- unless Regexp.new('').as_json.is_a?(Hash)
21
- Logger.new($stderr).warn("It appears you are using ActiveSupport json in your project. You are now in rubygems hell. Please see Pact::JsonWarning for more info.")
22
- @already_warned = true
23
- end
20
+ if as_json_clobbered?
21
+ Logger.new($stderr).warn("It appears you are using ActiveSupport json in your project. You are now in rubygems hell. Please see Pact::JsonWarning for more info.")
22
+ @already_warned = true
24
23
  end
25
24
  end
25
+
26
+ private
27
+
28
+ def as_json_clobbered?
29
+ !Regexp.new('').as_json.is_a?(Hash)
30
+ end
26
31
  end
27
32
  end
@@ -46,12 +46,17 @@ module Pact
46
46
  def initialize name, namespace, &block
47
47
  @name = name
48
48
  @namespace = namespace
49
+ @set_up_defined = false
50
+ @tear_down_defined = false
51
+ @no_op_defined = false
49
52
  instance_eval(&block)
53
+ validate
50
54
  end
51
55
 
52
56
  def set_up &block
53
57
  if block_given?
54
58
  @set_up_block = block
59
+ @set_up_defined = true
55
60
  elsif @set_up_block
56
61
  instance_eval &@set_up_block
57
62
  end
@@ -60,13 +65,34 @@ module Pact
60
65
  def tear_down &block
61
66
  if block_given?
62
67
  @tear_down_block = block
68
+ @tear_down_defined = true
63
69
  elsif @tear_down_block
64
70
  instance_eval &@tear_down_block
65
71
  end
66
72
  end
67
73
 
74
+ def no_op
75
+ @no_op_defined = true
76
+ end
77
+
68
78
  private
69
79
 
80
+ attr_accessor :no_op_defined, :set_up_defined, :tear_down_defined
81
+
82
+ def validate
83
+ if no_op_defined && set_up_defined
84
+ raise error_message_for_extra_block 'set_up'
85
+ elsif no_op_defined && tear_down_defined
86
+ raise error_message_for_extra_block 'tear_down'
87
+ elsif !(no_op_defined || set_up_defined || tear_down_defined)
88
+ raise "Please provide a set_up or tear_down block for provider state \"#{name}\". If there is no data to set up or tear down, you can use \"no_op\" instead."
89
+ end
90
+ end
91
+
92
+ def error_message_for_extra_block block_name
93
+ "Provider state \"#{name}\" has been defined as a no_op but it also has a #{block_name} block. Please remove one or the other."
94
+ end
95
+
70
96
  def namespaced(name)
71
97
  if namespace.empty?
72
98
  name
@@ -53,7 +53,7 @@ module Pact
53
53
 
54
54
  def describe_interaction interaction, options
55
55
 
56
- describe description_for(interaction) do
56
+ describe description_for(interaction), :pact => :verify do
57
57
 
58
58
  before do
59
59
  set_up_provider_state interaction.provider_state, options[:consumer]
@@ -89,7 +89,6 @@ module Pact
89
89
 
90
90
  if response['body']
91
91
  it "has a matching body" do
92
- logger.debug "Response body is #{last_response.body}"
93
92
  expect(parse_body_from_response(last_response)).to match_term response['body']
94
93
  end
95
94
  end
@@ -126,4 +125,9 @@ RSpec.configure do |config|
126
125
  config.extend Pact::Provider::RSpec::ClassMethods
127
126
  config.include Pact::Provider::RSpec::InstanceMethods
128
127
  config.include Pact::Provider::TestMethods
128
+
129
+ config.before :each, :pact => :verify do | example |
130
+ example_description = "#{example.example.example_group.description} #{example.example.description}"
131
+ Pact.configuration.logger.info "Running example '#{example_description}'"
132
+ end
129
133
  end
@@ -15,8 +15,11 @@ module Pact
15
15
  request = Request::Replayable.new(interaction.request)
16
16
  args = [request.path, request.body, request.headers]
17
17
 
18
- logger.debug "Sending #{request.method} with #{args}"
19
- self.send(request.method, *args)
18
+ logger.info "Sending #{request.method} request to path: \"#{request.path}\" with headers: #{request.headers}, see debug logs for body"
19
+ logger.debug "body :#{request.body}"
20
+ response = self.send(request.method, *args)
21
+ logger.info "Received response with status: #{response.status}, headers: #{response.headers}, see debug logs for body"
22
+ logger.debug "body: #{response.body}"
20
23
  end
21
24
 
22
25
  def parse_body_from_response rack_response
@@ -46,7 +49,15 @@ module Pact
46
49
  error_msg = <<-eos
47
50
  Could not find a provider state named \"#{provider_state_name}\"#{extra}.
48
51
  Have you required the provider states file for this consumer in your pact_helper.rb?
49
- Check the name in the Pact.provider_states_for definition is exactly \"#{consumer}\"
52
+ If you have not yet defined a provider state for \"#{provider_state_name}\", here is a template:
53
+
54
+ Pact.provider_states_for \"#{consumer}\" do
55
+ provider_state \"#{provider_state_name}\" do
56
+ set_up do
57
+ # Your set up code goes here
58
+ end
59
+ end
60
+ end
50
61
  eos
51
62
  raise error_msg
52
63
  end
data/lib/pact/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pact
2
- VERSION = "1.0.13"
2
+ VERSION = "1.0.15"
3
3
  end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ module Pact
4
+
5
+ describe JsonWarning do
6
+
7
+ class TestContract
8
+ include JsonWarning
9
+ end
10
+
11
+ let(:logger) { double }
12
+
13
+ before do
14
+ Logger.stub(new: logger)
15
+ @contract = TestContract.new
16
+ end
17
+
18
+ context 'when as_json has been clobbered' do
19
+ before { @contract.stub(as_json_clobbered?: true) }
20
+
21
+ it 'logs a single warning' do
22
+ logger.should_receive(:warn).once
23
+ @contract.check_for_active_support_json
24
+ @contract.check_for_active_support_json
25
+ end
26
+ end
27
+
28
+ context 'when as_json has NOT been clobbered' do
29
+ before { @contract.stub(as_json_clobbered?: false) }
30
+
31
+ it 'does not log a warning' do
32
+ logger.should_not_receive(:warn)
33
+ @contract.check_for_active_support_json
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -18,6 +18,7 @@ module Pact
18
18
  end
19
19
 
20
20
  Pact.provider_state 'some alligators' do
21
+ no_op
21
22
  end
22
23
 
23
24
  before do
@@ -54,6 +55,43 @@ module Pact
54
55
  end
55
56
  end
56
57
 
58
+ describe 'no_op' do
59
+ context "when a no_op is defined instead of a set_up or tear_down" do
60
+ it "treats set_up and tear_down as empty blocks" do
61
+ Pact.provider_state 'with_no_op' do
62
+ no_op
63
+ end
64
+ ProviderState.get('with_no_op').set_up
65
+ ProviderState.get('with_no_op').tear_down
66
+ end
67
+ end
68
+ context "when a no_op is defined with a set_up" do
69
+ it "raises an error" do
70
+ expect do
71
+ Pact.provider_state 'with_no_op_and_set_up' do
72
+ no_op
73
+ set_up do
74
+
75
+ end
76
+ end.to raise_error(/Provider state \"with_no_op_and_set_up\" has been defined as a no_op but it also has a set_up block. Please remove one or the other./)
77
+ end
78
+ end
79
+ end
80
+ context "when a no_op is defined with a tear_down" do
81
+ it "raises an error" do
82
+ expect do
83
+ Pact.provider_state 'with_no_op_and_set_up' do
84
+ no_op
85
+ tear_down do
86
+
87
+ end
88
+ end.to raise_error(/Provider state \"with_no_op_and_set_up\" has been defined as a no_op but it also has a tear_down block. Please remove one or the other./)
89
+ end
90
+ end
91
+ end
92
+
93
+ end
94
+
57
95
 
58
96
  describe 'namespaced ProviderStates' do
59
97
 
@@ -99,5 +137,25 @@ module Pact
99
137
  end
100
138
  end
101
139
  end
140
+
141
+ describe "invalid provider state" do
142
+ context "when no set_up or tear_down is provided" do
143
+ it "raises an error to prevent someone forgetting about the set_up and putting the set_up code directly in the provider_state block and wasting 20 minutes trying to work out why their provider states aren't working properly" do
144
+ expect do
145
+ Pact.provider_state 'invalid' do
146
+ end
147
+ end.to raise_error(/Please provide a set_up or tear_down block for provider state \"invalid\"/)
148
+ end
149
+ end
150
+ context "when a no_op is defined" do
151
+ it "does not raise an error" do
152
+ expect do
153
+ Pact.provider_state 'valid' do
154
+ no_op
155
+ end
156
+ end.not_to raise_error
157
+ end
158
+ end
159
+ end
102
160
  end
103
161
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pact
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.13
4
+ version: 1.0.15
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2013-10-21 00:00:00.000000000 Z
16
+ date: 2013-10-22 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: randexp
@@ -375,6 +375,7 @@ files:
375
375
  - spec/lib/pact/consumer_contract/consumer_contract_spec.rb
376
376
  - spec/lib/pact/consumer_contract/interaction_spec.rb
377
377
  - spec/lib/pact/consumer_contract/request_spec.rb
378
+ - spec/lib/pact/json_warning_spec.rb
378
379
  - spec/lib/pact/matchers/matchers_spec.rb
379
380
  - spec/lib/pact/provider/configuration_spec.rb
380
381
  - spec/lib/pact/provider/pact_helper_locator_spec.rb
@@ -414,7 +415,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
414
415
  version: '0'
415
416
  segments:
416
417
  - 0
417
- hash: -1516639792059640389
418
+ hash: -222789045808917234
418
419
  required_rubygems_version: !ruby/object:Gem::Requirement
419
420
  none: false
420
421
  requirements:
@@ -423,10 +424,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
423
424
  version: '0'
424
425
  segments:
425
426
  - 0
426
- hash: -1516639792059640389
427
+ hash: -222789045808917234
427
428
  requirements: []
428
429
  rubyforge_project:
429
- rubygems_version: 1.8.25
430
+ rubygems_version: 1.8.23
430
431
  signing_key:
431
432
  specification_version: 3
432
433
  summary: Define a pact between service consumers and providers
@@ -451,6 +452,7 @@ test_files:
451
452
  - spec/lib/pact/consumer_contract/consumer_contract_spec.rb
452
453
  - spec/lib/pact/consumer_contract/interaction_spec.rb
453
454
  - spec/lib/pact/consumer_contract/request_spec.rb
455
+ - spec/lib/pact/json_warning_spec.rb
454
456
  - spec/lib/pact/matchers/matchers_spec.rb
455
457
  - spec/lib/pact/provider/configuration_spec.rb
456
458
  - spec/lib/pact/provider/pact_helper_locator_spec.rb