pact 1.0.9 → 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
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]}"