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
@@ -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