pact 1.0.9 → 1.0.10

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 (86) hide show
  1. data/CHANGELOG.md +20 -0
  2. data/Gemfile.lock +3 -3
  3. data/example/animal-service/Gemfile +14 -0
  4. data/example/animal-service/Gemfile.lock +67 -0
  5. data/example/animal-service/Rakefile +3 -0
  6. data/example/animal-service/spec/service_consumers/pact_helper.rb +24 -0
  7. data/example/animal-service/spec/service_consumers/provider_states_for_zoo_app.rb +9 -0
  8. data/example/zoo-app/Gemfile.lock +2 -15
  9. data/example/zoo-app/spec/pacts/zoo_app-animal_service.json +21 -6
  10. data/example/zoo-app/spec/service_providers/animal_service_spec.rb +1 -1
  11. data/lib/pact/consumer.rb +10 -11
  12. data/lib/pact/consumer/app_manager.rb +1 -0
  13. data/lib/pact/consumer/configuration.rb +179 -0
  14. data/lib/pact/consumer/interaction_builder.rb +1 -2
  15. data/lib/pact/consumer/mock_service.rb +1 -370
  16. data/lib/pact/consumer/mock_service/app.rb +70 -0
  17. data/lib/pact/consumer/mock_service/interaction_delete.rb +28 -0
  18. data/lib/pact/consumer/mock_service/interaction_list.rb +57 -0
  19. data/lib/pact/consumer/mock_service/interaction_post.rb +25 -0
  20. data/lib/pact/consumer/mock_service/interaction_replay.rb +126 -0
  21. data/lib/pact/consumer/mock_service/missing_interactions_get.rb +26 -0
  22. data/lib/pact/consumer/mock_service/rack_request_helper.rb +51 -0
  23. data/lib/pact/consumer/mock_service/startup_poll.rb +22 -0
  24. data/lib/pact/consumer/mock_service/verification_get.rb +35 -0
  25. data/lib/pact/consumer/mock_service_client.rb +3 -1
  26. data/lib/pact/consumer/mock_service_interaction_expectation.rb +33 -0
  27. data/lib/pact/consumer/request.rb +27 -0
  28. data/lib/pact/consumer/rspec.rb +1 -4
  29. data/lib/pact/consumer_contract/consumer_contract.rb +11 -8
  30. data/lib/pact/consumer_contract/interaction.rb +9 -22
  31. data/lib/pact/consumer_contract/request.rb +68 -0
  32. data/lib/pact/consumer_contract/service_consumer.rb +6 -2
  33. data/lib/pact/consumer_contract/service_provider.rb +6 -2
  34. data/lib/pact/matchers/index_not_found.rb +20 -0
  35. data/lib/pact/matchers/matchers.rb +14 -28
  36. data/lib/pact/matchers/unexpected_index.rb +17 -0
  37. data/lib/pact/matchers/unexpected_key.rb +17 -0
  38. data/lib/pact/provider.rb +1 -1
  39. data/lib/pact/provider/configuration.rb +129 -0
  40. data/lib/pact/provider/request.rb +59 -0
  41. data/lib/pact/provider/rspec.rb +4 -5
  42. data/lib/pact/provider/test_methods.rb +14 -52
  43. data/lib/pact/shared/dsl.rb +20 -0
  44. data/lib/pact/shared/key_not_found.rb +24 -0
  45. data/lib/pact/shared/null_expectation.rb +31 -0
  46. data/lib/pact/shared/request.rb +68 -0
  47. data/lib/pact/something_like.rb +7 -1
  48. data/lib/pact/symbolize_keys.rb +12 -0
  49. data/lib/pact/tasks.rb +1 -1
  50. data/lib/pact/tasks/task_helper.rb +23 -0
  51. data/lib/pact/{verification_task.rb → tasks/verification_task.rb} +4 -4
  52. data/lib/pact/version.rb +1 -1
  53. data/lib/tasks/pact.rake +5 -4
  54. data/pact.gemspec +1 -1
  55. data/spec/features/consumption_spec.rb +1 -73
  56. data/spec/features/production_spec.rb +3 -0
  57. data/spec/integration/consumer_spec.rb +170 -0
  58. data/spec/integration/pact/consumer_configuration_spec.rb +1 -1
  59. data/spec/lib/pact/consumer/{dsl_spec.rb → configuration_spec.rb} +10 -9
  60. data/spec/lib/pact/consumer/consumer_contract_builder_spec.rb +2 -2
  61. data/spec/lib/pact/consumer/interaction_builder_spec.rb +1 -1
  62. data/spec/lib/pact/consumer/mock_service/interaction_list_spec.rb +66 -0
  63. data/spec/lib/pact/consumer/mock_service/rack_request_helper_spec.rb +82 -0
  64. data/spec/lib/pact/consumer/mock_service_interaction_expectation_spec.rb +54 -0
  65. data/spec/lib/pact/consumer/request_spec.rb +24 -0
  66. data/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +1 -1
  67. data/spec/lib/pact/consumer_contract/interaction_spec.rb +0 -34
  68. data/spec/lib/pact/{request_spec.rb → consumer_contract/request_spec.rb} +45 -121
  69. data/spec/lib/pact/matchers/matchers_spec.rb +29 -13
  70. data/spec/lib/pact/provider/configuration_spec.rb +163 -0
  71. data/spec/lib/pact/provider/request_spec.rb +78 -0
  72. data/spec/lib/pact/provider/test_methods_spec.rb +0 -30
  73. data/spec/lib/pact/verification_task_spec.rb +1 -1
  74. data/spec/spec_helper.rb +3 -0
  75. data/spec/support/factories.rb +5 -1
  76. data/spec/support/pact_helper.rb +6 -0
  77. data/spec/support/shared_examples_for_request.rb +83 -0
  78. data/spec/support/test_app_fail.json +3 -0
  79. data/spec/support/test_app_pass.json +18 -1
  80. metadata +68 -31
  81. data/lib/pact/consumer/dsl.rb +0 -157
  82. data/lib/pact/pact_task_helper.rb +0 -21
  83. data/lib/pact/provider/dsl.rb +0 -115
  84. data/lib/pact/request.rb +0 -167
  85. data/spec/lib/pact/consumer/mock_service_spec.rb +0 -143
  86. data/spec/lib/pact/provider/dsl_spec.rb +0 -179
@@ -0,0 +1,59 @@
1
+ require 'json'
2
+ require 'pact/reification'
3
+ require 'pact/shared/null_expectation'
4
+
5
+ module Pact
6
+ module Provider
7
+ module Request
8
+ class Replayable
9
+
10
+ def initialize expected_request
11
+ @expected_request = expected_request
12
+ end
13
+
14
+ def method
15
+ expected_request.method
16
+ end
17
+
18
+ def path
19
+ expected_request.full_path
20
+ end
21
+
22
+ def body
23
+ case expected_request.body
24
+ when String then expected_request.body
25
+ when NullExpectation then ''
26
+ else
27
+ reified_body
28
+ end
29
+ end
30
+
31
+ def headers
32
+ request_headers = {}
33
+ return request_headers if expected_request.headers.is_a?(Pact::NullExpectation)
34
+ expected_request.headers.each do |key, value|
35
+ key = key.upcase
36
+ if key.match(/CONTENT.TYPE/)
37
+ request_headers['CONTENT_TYPE'] = value
38
+ else
39
+ request_headers['HTTP_' + key.to_s] = value
40
+ end
41
+ end
42
+ request_headers
43
+ end
44
+
45
+ private
46
+ attr_reader :expected_request
47
+
48
+ def reified_body
49
+ rb = Pact::Reification.from_term(expected_request.body)
50
+ if rb.is_a?(String)
51
+ rb
52
+ else
53
+ JSON.dump(rb)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,9 +1,9 @@
1
1
  require 'open-uri'
2
2
  require 'pact/consumer_contract'
3
3
  require 'pact/json_warning'
4
- require_relative 'matchers'
5
- require_relative 'test_methods'
6
- require 'pact/provider/dsl'
4
+ require 'pact/provider/matchers'
5
+ require 'pact/provider/test_methods'
6
+ require 'pact/provider/configuration'
7
7
 
8
8
  module Pact
9
9
  module Provider
@@ -90,7 +90,7 @@ module Pact
90
90
  if response['body']
91
91
  it "has a matching body" do
92
92
  logger.debug "Response body is #{last_response.body}"
93
- expect(parse_entity_from_response(last_response)).to match_term response['body']
93
+ expect(parse_body_from_response(last_response)).to match_term response['body']
94
94
  end
95
95
  end
96
96
  end
@@ -126,5 +126,4 @@ RSpec.configure do |config|
126
126
  config.extend Pact::Provider::RSpec::ClassMethods
127
127
  config.include Pact::Provider::RSpec::InstanceMethods
128
128
  config.include Pact::Provider::TestMethods
129
- config.include Pact::Provider::TestMethods
130
129
  end
@@ -1,7 +1,8 @@
1
1
  require 'pact/logging'
2
2
  require 'rack/test'
3
- require 'pact/reification'
3
+ require 'pact/consumer_contract/interaction'
4
4
  require 'pact/provider/provider_state'
5
+ require 'pact/provider/request'
5
6
 
6
7
  module Pact
7
8
  module Provider
@@ -10,12 +11,20 @@ module Pact
10
11
  include Pact::Logging
11
12
  include Rack::Test::Methods
12
13
 
13
- def parse_entity_from_response response
14
- case response.headers['Content-Type']
14
+ def replay_interaction interaction
15
+ request = Request::Replayable.new(interaction.request)
16
+ args = [request.path, request.body, request.headers]
17
+
18
+ logger.debug "Sending #{request.method} with #{args}"
19
+ self.send(request.method, *args)
20
+ end
21
+
22
+ def parse_body_from_response rack_response
23
+ case rack_response.headers['Content-Type']
15
24
  when /json/
16
- JSON.load(response.body)
25
+ JSON.load(rack_response.body)
17
26
  else
18
- response.body
27
+ rack_response.body
19
28
  end
20
29
  end
21
30
 
@@ -31,53 +40,6 @@ module Pact
31
40
  end
32
41
  end
33
42
 
34
- def replay_interaction interaction
35
- request = interaction.request
36
- path = request_path(request)
37
- args = [path, request_body(request)]
38
-
39
- if !request.headers.nil?
40
- args << request_headers(request)
41
- end
42
-
43
- logger.debug "Sending #{request.method} with #{args}"
44
- self.send(request.method, *args)
45
- end
46
-
47
- private
48
-
49
- def request_headers request
50
- request_headers = {}
51
- request.headers.each do |key, value|
52
- key = key.upcase
53
- if key.match(/CONTENT.TYPE/)
54
- request_headers['CONTENT_TYPE'] = value
55
- else
56
- request_headers['HTTP_' + key.to_s] = value
57
- end
58
- end
59
- request_headers
60
- end
61
-
62
- def request_path request
63
- path = request.path
64
- query = request.query
65
- if query && !query.empty?
66
- query = query.generate if query.kind_of? Pact::Term
67
- path += "?" + query
68
- end
69
- path
70
- end
71
-
72
- def request_body request
73
- body = request.body
74
- if !body.nil? #TODO - write test for this where this is a NullExpectation
75
- body = JSON.dump(Pact::Reification.from_term(body))
76
- else
77
- body = ""
78
- end
79
- end
80
-
81
43
  def get_provider_state provider_state_name, consumer
82
44
  unless provider_state = ProviderState.get(provider_state_name, :for => consumer)
83
45
  extra = consumer ? " for consumer \"#{consumer}\"" : ""
@@ -0,0 +1,20 @@
1
+ require 'delegate'
2
+
3
+ module Pact
4
+ # Ripped from http://blog.joecorcoran.co.uk/2013/09/04/simple-pattern-ruby-dsl
5
+ module DSL
6
+ def build(*args, &block)
7
+ base = self.new(*args)
8
+ delegator_klass = self.const_get('DSLDelegator')
9
+ delegator = delegator_klass.new(base)
10
+ delegator.instance_eval(&block)
11
+ base.finalize
12
+ base
13
+ end
14
+
15
+ def dsl(&block)
16
+ delegator_klass = Class.new(SimpleDelegator, &block)
17
+ self.const_set('DSLDelegator', delegator_klass)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ module Pact
2
+ class KeyNotFound
3
+ def == other
4
+ other.is_a? KeyNotFound
5
+ end
6
+
7
+ def eql? other
8
+ self == other
9
+ end
10
+
11
+ def to_s
12
+ "<key not found>"
13
+ end
14
+
15
+ def to_json options = {}
16
+ to_s
17
+ end
18
+
19
+ def empty?
20
+ true
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,31 @@
1
+ module Pact
2
+ class NullExpectation
3
+ def to_s
4
+ "<No expectation>"
5
+ end
6
+
7
+ def ==(other_object)
8
+ other_object.is_a? NullExpectation
9
+ end
10
+
11
+ def ===(other_object)
12
+ other_object.is_a? NullExpectation
13
+ end
14
+
15
+ def eql?(other_object)
16
+ self == other_object
17
+ end
18
+
19
+ def hash
20
+ 2934820948209428748274238642672
21
+ end
22
+
23
+ def empty?
24
+ true
25
+ end
26
+
27
+ def nil?
28
+ true
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,68 @@
1
+ require 'pact/matchers'
2
+ require 'pact/symbolize_keys'
3
+
4
+ module Pact
5
+
6
+ module Request
7
+
8
+ class Base
9
+ include Pact::Matchers
10
+ include Pact::SymbolizeKeys
11
+ extend Pact::Matchers
12
+
13
+ attr_reader :method, :path, :headers, :body, :query, :options
14
+
15
+ def initialize(method, path, headers, body, query)
16
+ @method = method.to_s
17
+ @path = path.chomp('/')
18
+ @headers = headers
19
+ @body = body
20
+ @query = query
21
+ end
22
+
23
+ def to_json(options = {})
24
+ as_json.to_json(options)
25
+ end
26
+
27
+ def as_json
28
+ base_json = {
29
+ method: method,
30
+ path: path,
31
+ }
32
+
33
+ base_json.merge!(body: body) unless body.is_a? self.class.key_not_found.class
34
+ base_json.merge!(headers: headers) unless headers.is_a? self.class.key_not_found.class
35
+ base_json.merge!(query: query) unless query.is_a? self.class.key_not_found.class
36
+ base_json
37
+ end
38
+
39
+ def method_and_path
40
+ "#{method.upcase} #{path}"
41
+ end
42
+
43
+ def full_path
44
+ fp = ''
45
+ if path.empty?
46
+ fp << "/"
47
+ else
48
+ fp << path
49
+ end
50
+ if query && !query.empty?
51
+ fp << ("?" + (query.kind_of?(Pact::Term) ? query.generate : query))
52
+ end
53
+ fp
54
+ end
55
+
56
+ protected
57
+
58
+ def self.key_not_found
59
+ raise NotImplementedError
60
+ end
61
+
62
+ def as_json_without_body
63
+ keep_keys = [:method, :path, :headers, :query]
64
+ as_json.reject{ |key, value| !keep_keys.include? key }
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,6 +1,12 @@
1
+ require 'pact/symbolize_keys'
1
2
  module Pact
2
3
 
4
+ # Specifies that the actual object should be considered a match if
5
+ # it includes the same keys, and the values of the keys are of the same class.
6
+
3
7
  class SomethingLike
8
+ include SymbolizeKeys
9
+
4
10
  attr_reader :contents
5
11
 
6
12
  def initialize contents
@@ -19,7 +25,7 @@ module Pact
19
25
  end
20
26
 
21
27
  def self.json_create hash
22
- new(hash['contents'])
28
+ new(symbolize_keys(hash))
23
29
  end
24
30
 
25
31
  def generate
@@ -0,0 +1,12 @@
1
+ module Pact
2
+ module SymbolizeKeys
3
+
4
+ def self.included(base)
5
+ base.extend(self)
6
+ end
7
+
8
+ def symbolize_keys hash
9
+ hash.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
10
+ end
11
+ end
12
+ end
data/lib/pact/tasks.rb CHANGED
@@ -1,2 +1,2 @@
1
1
  load File.expand_path('../tasks/pact.rake', File.dirname(__FILE__))
2
- require 'pact/verification_task'
2
+ require 'pact/tasks/verification_task'
@@ -0,0 +1,23 @@
1
+ module Pact
2
+ module TaskHelper
3
+ def failure_message
4
+ redify(
5
+ "\n* * * * * * * * * * * * * * * * * * *\n" +
6
+ "Provider did not honour pact file.\nSee\n * #{Pact.configuration.log_path}\n * #{Pact.configuration.tmp_dir}\nfor logs and pact files." +
7
+ "\n* * * * * * * * * * * * * * * * * * *\n\n"
8
+ )
9
+ end
10
+
11
+ def redify string
12
+ "\e[31m#{string}\e[m"
13
+ end
14
+
15
+ def handle_verification_failure
16
+ exit_status = yield
17
+ if exit_status != 0
18
+ $stderr.puts failure_message
19
+ fail
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,14 +1,14 @@
1
1
  require 'rake/tasklib'
2
2
  require 'pact/provider/pact_spec_runner'
3
- require_relative 'pact_task_helper'
4
- require_relative 'provider/verification_report'
3
+ require 'pact/provider/verification_report'
4
+ require 'pact/tasks/task_helper'
5
5
 
6
6
  =begin
7
7
  To create a rake pact:verify:<something> task
8
8
 
9
9
  Pact::VerificationTask.new(:head) do | pact |
10
10
  pact.uri 'http://master.cd.vpc.realestate.com.au/browse/BIQ-MAS/latestSuccessful/artifact/JOB2/Pacts/mas-contract_transaction_service.json'
11
- pact.uri 'http://master.cd.vpc.realestate.com.au/browse/BIQ-IMAGINARY-CONSUMER/latestSuccessful/artifact/JOB2/Pacts/imaginary_consumer-contract_transaction_service.json'
11
+ pact.uri 'http://master.cd.vpc.realestate.com.au/browse/BIQ-IMAGINARY-CONSUMER/latestSuccessful/artifact/JOB2/Pacts/imaginary_consumer-contract_transaction_service.json'
12
12
  end
13
13
 
14
14
  The pact.uri may be a local file system path or a remote URL.
@@ -33,7 +33,7 @@ module Pact
33
33
  class VerificationTask < ::Rake::TaskLib
34
34
  attr_reader :pact_spec_config
35
35
 
36
- include PactTaskHelper
36
+ include Pact::TaskHelper
37
37
  def initialize(name)
38
38
  @pact_spec_config = []
39
39
  @name = name
data/lib/pact/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pact
2
- VERSION = "1.0.9"
2
+ VERSION = "1.0.10"
3
3
  end
data/lib/tasks/pact.rake CHANGED
@@ -4,14 +4,15 @@ namespace :pact do
4
4
  desc "Verifies the pact files configured in the pact_helper.rb against this service provider."
5
5
  task :verify do
6
6
  require 'pact/provider'
7
- require 'pact/pact_task_helper'
7
+ require 'pact/tasks/task_helper'
8
8
  require 'pact/provider/client_project_pact_helper'
9
9
 
10
- include PactTaskHelper
10
+ include Pact::TaskHelper
11
11
 
12
12
  handle_verification_failure do
13
13
  pact_verifications = Pact.configuration.pact_verifications
14
14
  verification_configs = pact_verifications.collect { | pact_verification | { :uri => pact_verification.uri }}
15
+ raise "Please configure a pact to verify" if verification_configs.empty?
15
16
  Pact::Provider::PactSpecRunner.new(verification_configs).run
16
17
  end
17
18
  end
@@ -19,9 +20,9 @@ namespace :pact do
19
20
  desc "Verifies the pact at the given URI against this service provider."
20
21
  task 'verify:at', :pact_uri do | t, args |
21
22
  require 'pact/provider'
22
- require 'pact/pact_task_helper'
23
+ require 'pact/tasks/task_helper'
23
24
 
24
- include PactTaskHelper
25
+ include Pact::TaskHelper
25
26
 
26
27
  handle_verification_failure do
27
28
  puts "Verifying pact at uri #{args[:pact_uri]}"