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
@@ -1,21 +0,0 @@
1
- module PactTaskHelper
2
- def failure_message
3
- redify(
4
- "\n* * * * * * * * * * * * * * * * * * *\n" +
5
- "Provider did not honour pact file.\nSee\n * #{Pact.configuration.log_path}\n * #{Pact.configuration.tmp_dir}\nfor logs and pact files." +
6
- "\n* * * * * * * * * * * * * * * * * * *\n\n"
7
- )
8
- end
9
-
10
- def redify string
11
- "\e[31m#{string}\e[m"
12
- end
13
-
14
- def handle_verification_failure
15
- exit_status = yield
16
- if exit_status != 0
17
- $stderr.puts failure_message
18
- fail
19
- end
20
- end
21
- end
@@ -1,115 +0,0 @@
1
- require_relative 'pact_verification'
2
-
3
- module Pact
4
-
5
- module Provider
6
- module DSL
7
-
8
- #TODO: Move this into a module, out of configuration
9
- module Configuration
10
- def provider= provider
11
- @provider = provider
12
- end
13
-
14
- def provider
15
- if defined? @provider
16
- @provider
17
- else
18
- raise "Please configure your provider. See the Provider section in the README for examples."
19
- end
20
- end
21
-
22
- def add_pact_verification verification
23
- pact_verifications << verification
24
- end
25
- def pact_verifications
26
- @pact_verifications ||= []
27
- end
28
- end
29
-
30
- Pact::Configuration.send(:include, Configuration)
31
-
32
- def service_provider name, &block
33
- service_provider = ServiceProviderDSL.new(name, &block).create_service_provider
34
- Pact.configuration.provider = service_provider
35
- service_provider
36
- end
37
-
38
- class ServiceProviderConfig
39
- attr_accessor :name
40
-
41
- def initialize name, &app_block
42
- @name = name
43
- @app_block = app_block
44
- end
45
-
46
- def app
47
- @app_block.call
48
- end
49
- end
50
-
51
- class VerificationDSL
52
- def initialize consumer_name, options = {}, &block
53
- @consumer_name = consumer_name
54
- @ref = options.fetch(:ref, :head)
55
- @pact_uri = nil
56
- instance_eval(&block)
57
- end
58
-
59
- def pact_uri pact_uri, options = {}
60
- @pact_uri = pact_uri
61
- end
62
-
63
- def task task
64
- @task = task
65
- end
66
-
67
- def create_verification
68
- validate
69
- Pact::Provider::PactVerification.new(@consumer_name, @pact_uri, @ref)
70
- end
71
-
72
- private
73
-
74
- def validate
75
- raise "Please provide a pact_uri for the verification" unless @pact_uri
76
- end
77
-
78
- end
79
-
80
- class ServiceProviderDSL
81
-
82
- def initialize name, &block
83
- @name = name
84
- @app = nil
85
- instance_eval(&block)
86
- end
87
-
88
- def validate
89
- raise "Please provide a name for the Provider" unless @name && !@name.strip.empty?
90
- raise "Please configure an app for the Provider" unless @app_block
91
- end
92
-
93
- def name name
94
- @name = name
95
- end
96
-
97
- def app &block
98
- @app_block = block
99
- end
100
-
101
- def honours_pact_with consumer_name, options = {}, &app_block
102
- verification = VerificationDSL.new(consumer_name, options, &app_block).create_verification
103
- Pact.configuration.add_pact_verification verification
104
- end
105
-
106
- def create_service_provider
107
- validate
108
- ServiceProviderConfig.new(@name, &@app_block)
109
- end
110
- end
111
- end
112
- end
113
- end
114
-
115
- Pact.send(:extend, Pact::Provider::DSL)
data/lib/pact/request.rb DELETED
@@ -1,167 +0,0 @@
1
- require 'pact/matchers'
2
- require 'pact/reification'
3
-
4
- module Pact
5
-
6
- module Request
7
-
8
- class NullExpectation
9
- def to_s
10
- "<No expectation>"
11
- end
12
-
13
- def ==(other_object)
14
- other_object.is_a? NullExpectation
15
- end
16
-
17
- def ===(other_object)
18
- other_object.is_a? NullExpectation
19
- end
20
-
21
- def eql?(other_object)
22
- self == other_object
23
- end
24
-
25
- def hash
26
- 2934820948209428748274238642672
27
- end
28
-
29
- def empty?
30
- true
31
- end
32
-
33
- def nil?
34
- true
35
- end
36
- end
37
-
38
- class Base
39
- include Pact::Matchers
40
- extend Pact::Matchers
41
-
42
- NULL_EXPECTATION = NullExpectation.new
43
-
44
- attr_reader :method, :path, :headers, :body, :query, :options
45
-
46
- def self.from_hash(hash)
47
- sym_hash = symbolize_keys hash
48
- method = sym_hash.fetch(:method)
49
- path = sym_hash.fetch(:path)
50
- query = sym_hash.fetch(:query, NULL_EXPECTATION)
51
- headers = sym_hash.fetch(:headers, NULL_EXPECTATION)
52
- body = sym_hash.fetch(:body, NULL_EXPECTATION)
53
- new(method, path, headers, body, query)
54
- end
55
-
56
- def self.symbolize_keys hash
57
- hash.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
58
- end
59
-
60
- def initialize(method, path, headers, body, query)
61
- @method = method.to_s
62
- @path = path.chomp('/')
63
- @headers = headers
64
- @body = body
65
- @query = query
66
- end
67
-
68
- def to_json(options = {})
69
- as_json.to_json(options)
70
- end
71
-
72
- def as_json
73
- base_json = {
74
- method: method,
75
- path: path,
76
- }
77
-
78
- base_json.merge!(body: body) unless body.is_a? NullExpectation
79
- base_json.merge!(headers: headers) unless headers.is_a? NullExpectation
80
- base_json.merge!(query: query) unless query.is_a? NullExpectation
81
- base_json
82
- end
83
-
84
- def as_json_without_body
85
- keep_keys = [:method, :path, :headers, :query]
86
- as_json.reject{ |key, value| !keep_keys.include? key }
87
- end
88
-
89
- def short_description
90
- "#{method} #{full_path}"
91
- end
92
-
93
- def full_path
94
- fp = ''
95
- if path.empty?
96
- fp << "/"
97
- else
98
- fp << path
99
- end
100
- if query && !query.empty?
101
- fp << ("?" + query)
102
- end
103
- fp
104
- end
105
-
106
- end
107
-
108
- class Expected < Base
109
-
110
- DEFAULT_OPTIONS = {:allow_unexpected_keys => false}.freeze
111
- attr_accessor :description
112
- attr_accessor :options
113
-
114
- def self.from_hash(hash)
115
- request = super
116
- request.description = hash.fetch(:description, nil)
117
- request.options = symbolize_keys(hash).fetch(:options, {})
118
- request
119
- end
120
-
121
- def initialize(method, path, headers, body, query, options = {})
122
- super(method, path, headers, body, query)
123
- @options = options
124
- end
125
-
126
- def match(actual_request)
127
- difference(actual_request).empty?
128
- end
129
-
130
- def matches_route? actual_request
131
- diff({:method => method, :path => path}, {:method => actual_request.method, :path => actual_request.path}).empty?
132
- end
133
-
134
- def difference(actual_request)
135
- request_diff = diff(as_json_without_body, actual_request.as_json_without_body)
136
- unless body.is_a? NullExpectation
137
- request_diff.merge(body_difference(actual_request.body))
138
- else
139
- request_diff
140
- end
141
- end
142
-
143
- def body_difference(actual_body)
144
- diff({:body => body}, {body: actual_body}, allow_unexpected_keys: runtime_options[:allow_unexpected_keys_in_body])
145
- end
146
-
147
- def as_json_with_options
148
- as_json.merge( options.empty? ? {} : { options: options} )
149
- end
150
-
151
- def generated_body
152
- Pact::Reification.from_term(body)
153
- end
154
-
155
- # Don't want to put the default options in the pact json just yet, so calculating these at run time, rather than assigning
156
- # the result to @options
157
- def runtime_options
158
- DEFAULT_OPTIONS.merge(self.class.symbolize_keys(options))
159
- end
160
-
161
- end
162
-
163
- class Actual < Base
164
- end
165
-
166
- end
167
- end
@@ -1,143 +0,0 @@
1
- require 'spec_helper'
2
- require 'pact/consumer/mock_service'
3
-
4
- module Pact::Consumer
5
-
6
- describe InteractionList do
7
- shared_context "unexpected requests and missed interactions" do
8
- let(:expected_call) { {request: 'blah'} }
9
- let(:unexpected_call) { Pact::Request::Actual.from_hash(path: '/path', method: 'get') }
10
- subject {
11
- interactionList = InteractionList.new
12
- interactionList.add expected_call
13
- interactionList.register_unexpected unexpected_call
14
- interactionList
15
- }
16
- end
17
-
18
- shared_context "no unexpected requests or missed interactions exist" do
19
- let(:expected_call) { {request: 'blah'} }
20
- let(:unexpected_call) { Pact::Request::Actual.from_hash(path: '/path', method: 'get') }
21
- subject {
22
- interactionList = InteractionList.new
23
- interactionList.add expected_call
24
- interactionList.register_matched expected_call
25
- interactionList
26
- }
27
- end
28
-
29
- describe "interaction_diffs" do
30
- context "when unexpected requests and missed interactions exist" do
31
- include_context "unexpected requests and missed interactions"
32
- let(:expected) {
33
- {:missing_interactions=>[{:request=>"blah"}], :unexpected_requests=>[{:method=>"get", :path=>"/path"}]}
34
- }
35
- it "returns the unexpected requests and missed interactions" do
36
- expect(subject.interaction_diffs).to eq expected
37
- end
38
- end
39
-
40
- context "when no unexpected requests or missed interactions exist" do
41
- include_context "no unexpected requests or missed interactions exist"
42
- let(:expected) {
43
- {}
44
- }
45
- it "returns an empty hash" do
46
- expect(subject.interaction_diffs).to eq expected
47
- end
48
- end
49
- end
50
-
51
- describe "all_matched?" do
52
- context "when unexpected requests or missed interactions exist" do
53
- include_context "unexpected requests and missed interactions"
54
- it "returns false" do
55
- expect(subject.all_matched?).to be_false
56
- end
57
- end
58
- context "when unexpected requests or missed interactions do not exist" do
59
- include_context "no unexpected requests or missed interactions exist"
60
- it "returns false" do
61
- expect(subject.all_matched?).to be_true
62
- end
63
- end
64
- end
65
- end
66
-
67
- describe RequestExtractor do
68
- class TestSubject
69
- include RequestExtractor
70
- end
71
-
72
- let(:rack_env) {
73
- {
74
- "CONTENT_LENGTH" => "16",
75
- "CONTENT_TYPE" => content_type,
76
- "GATEWAY_INTERFACE" => "CGI/1.1",
77
- "PATH_INFO" => "/donuts",
78
- "QUERY_STRING" => "",
79
- "REMOTE_ADDR" => "127.0.0.1",
80
- "REMOTE_HOST" => "localhost",
81
- "REQUEST_METHOD" => "POST",
82
- "REQUEST_URI" => "http://localhost:4321/donuts",
83
- "SCRIPT_NAME" => "",
84
- "SERVER_NAME" => "localhost",
85
- "SERVER_PORT" => "4321",
86
- "SERVER_PROTOCOL" => "HTTP/1.1",
87
- "SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/1.9.3/2013-02-22)",
88
- "HTTP_ACCEPT" => "text/plain",
89
- "HTTP_USER_AGENT" => "Ruby",
90
- "HTTP_HOST" => "localhost:4321",
91
- "rack.version" => [1, 2 ],
92
- "rack.input" => StringIO.new(body),
93
- "rack.errors" => nil,
94
- "rack.multithread" => true,
95
- "rack.multiprocess" => false,
96
- "rack.run_once" => false,
97
- "rack.url_scheme" => "http",
98
- "HTTP_VERSION" => "HTTP/1.1",
99
- "REQUEST_PATH" => "/donuts"
100
- }
101
- }
102
-
103
- subject { TestSubject.new }
104
-
105
- let(:expected_request) {
106
- {
107
- "query" => "",
108
- "method" => "post",
109
- "body" => expected_body,
110
- "path" => "/donuts",
111
- "headers" => {
112
- "Content-Type" => content_type,
113
- "Accept" => "text/plain",
114
- "User-Agent" => "Ruby",
115
- "Host" => "localhost:4321",
116
- "Version" => "HTTP/1.1"
117
- }
118
- }
119
- }
120
-
121
- context "with a text body" do
122
- let(:content_type) { "application/x-www-form-urlencoded" }
123
- let(:body) { 'this is the body' }
124
- let(:expected_body) { body }
125
-
126
- it "extracts the body" do
127
- expect(subject.request_from(rack_env)).to eq expected_request
128
- end
129
- end
130
-
131
- context "with a json body" do
132
- let(:content_type) { "application/json" }
133
- let(:body) { '{"a" : "body" }' }
134
- let(:expected_body) { {"a" => "body"} }
135
-
136
- it "extracts the body" do
137
- expect(subject.request_from(rack_env)).to eq expected_request
138
- end
139
- end
140
-
141
-
142
- end
143
- end