pact 1.2.1.rc2 → 1.3.3

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 (57) hide show
  1. data/.travis.yml +1 -1
  2. data/CHANGELOG.md +31 -0
  3. data/Gemfile.lock +6 -6
  4. data/README.md +48 -38
  5. data/documentation/README.md +9 -7
  6. data/lib/pact/configuration.rb +83 -12
  7. data/lib/pact/consumer/consumer_contract_builder.rb +1 -49
  8. data/lib/pact/consumer/mock_service/interaction_mismatch.rb +5 -1
  9. data/lib/pact/consumer/spec_hooks.rb +7 -4
  10. data/lib/pact/consumer/world.rb +12 -0
  11. data/lib/pact/consumer_contract/headers.rb +51 -0
  12. data/lib/pact/consumer_contract/interaction.rb +1 -1
  13. data/lib/pact/consumer_contract/pact_file.rb +2 -2
  14. data/lib/pact/consumer_contract/request.rb +6 -1
  15. data/lib/pact/matchers/differ.rb +10 -7
  16. data/lib/pact/matchers/unix_diff_formatter.rb +8 -1
  17. data/lib/pact/provider/configuration/service_provider_dsl.rb +6 -1
  18. data/lib/pact/provider/matchers/messages.rb +2 -2
  19. data/lib/pact/provider/pact_spec_runner.rb +9 -1
  20. data/lib/pact/provider/rspec/backtrace_formatter.rb +43 -0
  21. data/lib/pact/provider/rspec/matchers.rb +9 -6
  22. data/lib/pact/provider/rspec.rb +7 -1
  23. data/lib/pact/reification.rb +5 -5
  24. data/lib/pact/shared/json_differ.rb +15 -0
  25. data/lib/pact/shared/request.rb +11 -2
  26. data/lib/pact/shared/text_differ.rb +14 -0
  27. data/lib/pact/version.rb +1 -1
  28. data/pact.gemspec +1 -1
  29. data/spec/lib/pact/configuration_spec.rb +127 -5
  30. data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +8 -5
  31. data/spec/lib/pact/consumer_contract/headers_spec.rb +107 -0
  32. data/spec/lib/pact/consumer_contract/interaction_spec.rb +14 -0
  33. data/spec/lib/pact/consumer_contract/request_spec.rb +9 -0
  34. data/spec/lib/pact/matchers/matchers_spec.rb +35 -0
  35. data/spec/lib/pact/matchers/unix_diff_formatter_spec.rb +9 -8
  36. data/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb +16 -0
  37. data/spec/lib/pact/provider/matchers/messages_spec.rb +5 -4
  38. data/spec/lib/pact/provider/rspec_spec.rb +15 -11
  39. data/spec/lib/pact/reification_spec.rb +12 -0
  40. data/spec/lib/pact/shared/json_differ_spec.rb +36 -0
  41. data/spec/lib/pact/shared/request_spec.rb +25 -1
  42. data/spec/lib/pact/shared/text_differ_spec.rb +54 -0
  43. data/spec/support/options.json +21 -0
  44. data/spec/support/options_app.rb +15 -0
  45. data/spec/support/pact_helper.rb +2 -0
  46. data/spec/support/test_app_with_right_content_type_differ.json +23 -0
  47. data/tasks/pact-test.rake +10 -0
  48. metadata +26 -16
  49. data/documentation/Testing with pact.png +0 -0
  50. data/documentation/best-practices.md +0 -33
  51. data/documentation/development-workflow.md +0 -22
  52. data/documentation/faq.md +0 -81
  53. data/documentation/provider-states.md +0 -167
  54. data/documentation/raq.md +0 -39
  55. data/documentation/terminology.md +0 -25
  56. data/documentation/troubleshooting.md +0 -4
  57. data/documentation/verifying-pacts.md +0 -106
@@ -7,6 +7,9 @@ module Pact
7
7
 
8
8
  describe Interaction do
9
9
 
10
+ let(:request) { {method: 'get', path: 'path'} }
11
+ let(:response) { {} }
12
+
10
13
  describe "==" do
11
14
  subject { InteractionFactory.create }
12
15
  context "when other is the same" do
@@ -39,6 +42,17 @@ module Pact
39
42
  end
40
43
  end
41
44
 
45
+ describe "from_hash" do
46
+ context "when providerState has been used instead of provider_state" do
47
+
48
+ subject { Interaction.from_hash('response' => response, 'request' => request, 'providerState' => 'some state') }
49
+
50
+ it "recognises the provider state" do
51
+ expect(subject.provider_state).to eq 'some state'
52
+ end
53
+ end
54
+ end
55
+
42
56
  describe "to JSON" do
43
57
  let(:request) do
44
58
  {
@@ -76,6 +76,15 @@ module Pact
76
76
  end
77
77
  end
78
78
 
79
+ context "when the methods are the same but different case" do
80
+ let(:expected_method) { 'get' }
81
+ let(:actual_method) { 'GET' }
82
+
83
+ it "matches" do
84
+ expect(subject.matches? actual_request).to be true
85
+ end
86
+ end
87
+
79
88
  context "when the paths are different" do
80
89
  let(:expected_path) { '/foo' }
81
90
  let(:actual_path) { '/bar' }
@@ -1,11 +1,46 @@
1
1
  require 'spec_helper'
2
2
  require 'pact/matchers'
3
+ require 'pact/consumer_contract/headers'
3
4
 
4
5
  module Pact::Matchers
5
6
 
6
7
  describe Pact::Matchers do
7
8
  include Pact::Matchers
8
9
 
10
+ # TODO this is an integration test
11
+ describe 'matching headers' do
12
+ let(:expected) { Pact::Headers.new('Content-Type' => 'application/hippo')}
13
+
14
+ context "when the headers match in a case insensitive way" do
15
+
16
+ context "when the values match" do
17
+ let(:actual) { Pact::Headers.new('CONTENT-TYPE' => 'application/hippo')}
18
+ it "returns an empty diff" do
19
+ expect(diff(expected, actual)).to be_empty
20
+ end
21
+ end
22
+
23
+ context "when the header values do not match" do
24
+ let(:actual) { Pact::Headers.new('CONTENT-TYPE' => 'application/alligator')}
25
+ let(:difference) { {"Content-Type" => Difference.new('application/hippo', 'application/alligator')} }
26
+ it "returns the diff" do
27
+ expect(diff(expected, actual)).to eq difference
28
+ end
29
+ end
30
+ end
31
+
32
+ context "when the headers do not match" do
33
+
34
+ let(:actual) { Pact::Headers.new('Content-Length' => '1')}
35
+ let(:difference) { {"Content-Type" => Difference.new('application/hippo', Pact::KeyNotFound.new)} }
36
+ it "returns a diff" do
37
+ expect(diff(expected, actual)).to eq difference
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
9
44
  describe 'matching with something like' do
10
45
 
11
46
  context 'when the actual is something like the expected' do
@@ -9,6 +9,7 @@ module Pact
9
9
 
10
10
  describe ".call" do
11
11
 
12
+ let(:key_lines_count) { 4 }
12
13
  let(:colour) { false }
13
14
  subject { UnixDiffFormatter.call(diff, {colour: colour}) }
14
15
 
@@ -54,7 +55,7 @@ EOF
54
55
  end
55
56
 
56
57
  it "generates the right number of lines, even with ActiveSupport loaded" do
57
- expect(line_count).to eq 9
58
+ expect(line_count).to eq 9 + key_lines_count
58
59
  end
59
60
 
60
61
  end
@@ -76,7 +77,7 @@ EOF
76
77
  end
77
78
 
78
79
  it "generates the right number of lines, even with ActiveSupport loaded" do
79
- expect(line_count).to eq 9
80
+ expect(line_count).to eq 9 + key_lines_count
80
81
  end
81
82
 
82
83
  end
@@ -98,7 +99,7 @@ EOF
98
99
  end
99
100
 
100
101
  it "generates the right number of lines, even with ActiveSupport loaded" do
101
- expect(line_count).to eq 5
102
+ expect(line_count).to eq 5 + key_lines_count
102
103
  end
103
104
 
104
105
  end
@@ -120,7 +121,7 @@ EOF
120
121
  end
121
122
 
122
123
  it "generates the right number of lines, even with ActiveSupport loaded" do
123
- expect(line_count).to eq 8
124
+ expect(line_count).to eq 8 + key_lines_count
124
125
  end
125
126
  end
126
127
 
@@ -140,7 +141,7 @@ EOF
140
141
  end
141
142
 
142
143
  it "generates the right number of lines, even with ActiveSupport loaded" do
143
- expect(line_count).to eq 8
144
+ expect(line_count).to eq 8 + key_lines_count
144
145
  end
145
146
 
146
147
  end
@@ -159,7 +160,7 @@ EOF
159
160
  end
160
161
 
161
162
  it "generates the right number of lines, even with ActiveSupport loaded" do
162
- expect(line_count).to eq 8
163
+ expect(line_count).to eq 8 + key_lines_count
163
164
  end
164
165
 
165
166
  end
@@ -185,7 +186,7 @@ EOF
185
186
  end
186
187
 
187
188
  it "generates the right number of lines, even with ActiveSupport loaded" do
188
- expect(line_count).to eq 8
189
+ expect(line_count).to eq 8 + key_lines_count
189
190
  end
190
191
 
191
192
  end
@@ -203,7 +204,7 @@ EOF
203
204
  end
204
205
 
205
206
  it "generates the right number of lines, even with ActiveSupport loaded" do
206
- expect(line_count).to eq 11
207
+ expect(line_count).to eq 11 + key_lines_count
207
208
  end
208
209
 
209
210
  end
@@ -84,6 +84,22 @@ module Pact
84
84
  end
85
85
 
86
86
  end
87
+
88
+ describe "CONFIG_RU_APP" do
89
+ context "when a config.ru file does not exist" do
90
+
91
+ let(:path_that_does_not_exist) { './tmp/this/path/does/not/exist/probably' }
92
+
93
+ before do
94
+ allow(Pact.configuration).to receive(:config_ru_path).and_return(path_that_does_not_exist)
95
+ end
96
+
97
+ it "raises an error with some helpful text" do
98
+ expect{ ServiceProviderDSL::CONFIG_RU_APP.call }.to raise_error /Could not find config\.ru file.*#{Regexp.escape(path_that_does_not_exist)}/
99
+ end
100
+
101
+ end
102
+ end
87
103
  end
88
104
 
89
105
  end
@@ -9,6 +9,7 @@ module Pact
9
9
 
10
10
  describe "#match_term_failure_message" do
11
11
 
12
+ let(:diff_formatter) { Pact::Matchers::UnixDiffFormatter }
12
13
  let(:message) { "line1\nline2"}
13
14
  let(:output_message) { "Actual: actual\n\n#{message}"}
14
15
  let(:output_message_with_resets) { "Actual: actual\n\n#{r}line1\n#{r}line2"}
@@ -20,13 +21,13 @@ module Pact
20
21
  let(:message_line_count) { message.split("\n").size }
21
22
 
22
23
  before do
23
- allow(Pact.configuration.diff_formatter).to receive(:call).and_return(message)
24
+ allow(diff_formatter).to receive(:call).and_return(message)
24
25
  end
25
26
 
26
- subject { match_term_failure_message diff, actual, color_enabled }
27
+ subject { match_term_failure_message diff, actual, diff_formatter, color_enabled }
27
28
 
28
- it "creates a message using the configured diff_formatter" do
29
- expect(Pact.configuration.diff_formatter).to receive(:call).with(diff)
29
+ it "creates a message using the diff_formatter" do
30
+ expect(diff_formatter).to receive(:call).with(diff)
30
31
  subject
31
32
  end
32
33
 
@@ -1,51 +1,55 @@
1
- require 'pact/provider/rspec'
2
1
  require 'pact/provider/rspec/matchers'
2
+ require 'pact/shared/json_differ'
3
+ require 'pact/matchers/unix_diff_formatter'
4
+
3
5
 
4
6
  describe "the match_term matcher" do
5
7
 
6
8
  include Pact::RSpec::Matchers
7
9
 
10
+ let(:diff_formatter) { Pact::Matchers::UnixDiffFormatter }
11
+
8
12
  it 'does not match a hash to an array' do
9
- expect({}).to_not match_term([])
13
+ expect({}).to_not match_term([], with: Pact::JsonDiffer, diff_formatter: diff_formatter)
10
14
  end
11
15
 
12
16
  it 'does not match an array to a hash' do
13
- expect([]).to_not match_term({})
17
+ expect([]).to_not match_term({}, with: Pact::JsonDiffer, diff_formatter: diff_formatter)
14
18
  end
15
19
 
16
20
  it 'matches regular expressions' do
17
- expect('blah').to match_term(/[a-z]*/)
21
+ expect('blah').to match_term(/[a-z]*/, with: Pact::JsonDiffer, diff_formatter: diff_formatter)
18
22
  end
19
23
 
20
24
  it 'matches pact terms' do
21
- expect('wootle').to match_term Pact::Term.new(generate:'wootle', matcher:/woot../)
25
+ expect('wootle').to match_term Pact::Term.new(generate:'wootle', matcher:/woot../), with: Pact::JsonDiffer, diff_formatter: diff_formatter
22
26
  end
23
27
 
24
28
  it 'matches all elements of arrays' do
25
- expect(['one', 'two', ['three']]).to match_term [/one/, 'two', [Pact::Term.new(generate:'three', matcher:/thr../)]]
29
+ expect(['one', 'two', ['three']]).to match_term [/one/, 'two', [Pact::Term.new(generate:'three', matcher:/thr../)]], with: Pact::JsonDiffer, diff_formatter: diff_formatter
26
30
  end
27
31
 
28
32
  it 'matches all values of hashes' do
29
- expect({1 => 'one', 2 => 2, 3 => 'three'}).to match_term({1 => /one/, 2 => 2, 3 => Pact::Term.new(generate:'three', matcher:/thr../)})
33
+ expect({1 => 'one', 2 => 2, 3 => 'three'}).to match_term({1 => /one/, 2 => 2, 3 => Pact::Term.new(generate:'three', matcher:/thr../)}, with: Pact::JsonDiffer, diff_formatter: diff_formatter)
30
34
  end
31
35
 
32
36
  it 'matches all other objects using ==' do
33
- expect('wootle').to match_term 'wootle'
37
+ expect('wootle').to match_term 'wootle', with: Pact::JsonDiffer, diff_formatter: diff_formatter
34
38
  end
35
39
 
36
40
  # Note: because a consumer specifies only the keys it cares about, the pact ignores keys that are returned
37
41
  # by the provider, but not are not specified in the pact. This means that any hash will match an
38
42
  # expected empty hash, because there is currently no way for a consumer to expect an absence of keys.
39
43
  it 'is confused by an empty hash' do
40
- expect({:hello => 'everyone'}).to match_term({})
44
+ expect({:hello => 'everyone'}).to match_term({}, with: Pact::JsonDiffer, diff_formatter: diff_formatter)
41
45
  end
42
46
 
43
47
  it 'should not be confused by an empty array' do
44
- expect(['blah']).to_not match_term([])
48
+ expect(['blah']).to_not match_term([], with: Pact::JsonDiffer, diff_formatter: diff_formatter)
45
49
  end
46
50
 
47
51
  it "should allow matches on an array where each item in the array only contains a subset of the actual" do
48
- expect([{name: 'Fred', age: 12}, {name: 'John', age: 13}]).to match_term([{name: 'Fred'}, {name: 'John'}])
52
+ expect([{name: 'Fred', age: 12}, {name: 'John', age: 13}]).to match_term([{name: 'Fred'}, {name: 'John'}], with: Pact::JsonDiffer, diff_formatter: diff_formatter)
49
53
  end
50
54
 
51
55
  end
@@ -51,5 +51,17 @@ module Pact
51
51
 
52
52
  end
53
53
 
54
+ context "when SomethingLike" do
55
+
56
+ let(:request) { Pact::SomethingLike.new({a: 'String'})}
57
+
58
+ subject { Reification.from_term(request)}
59
+
60
+ it "returns the contents of the SomethingLike" do
61
+ expect(subject).to eq({a: 'String'})
62
+ end
63
+
64
+ end
65
+
54
66
  end
55
67
  end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+ require 'pact/shared/json_differ'
3
+
4
+ module Pact
5
+ describe JsonDiffer do
6
+
7
+ describe ".call" do
8
+
9
+ let(:expected) { {'a' => 'b'} }
10
+
11
+ subject { JsonDiffer.call(expected, actual) }
12
+
13
+ context "when the actual is valid JSON" do
14
+
15
+ let(:actual) { {'a' => 'c'} }
16
+ let(:difference) { {'a' => Pact::Matchers::Difference.new('b', 'c')} }
17
+
18
+ context "when the actual does not equal the expected" do
19
+ it "parses the JSON and returns a diff" do
20
+ expect(subject).to eq(difference)
21
+ end
22
+ end
23
+
24
+ context "when the actual equals the expected" do
25
+ let(:actual) { expected }
26
+ it "parses the JSON and returns an empty diff" do
27
+ expect(subject.any?).to be false
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'pact/shared/request'
3
+ require 'pact/shared/key_not_found'
3
4
 
4
5
  module Pact
5
6
 
@@ -10,7 +11,7 @@ module Pact
10
11
  class TestRequest < Base
11
12
 
12
13
  def self.key_not_found
13
- nil
14
+ Pact::KeyNotFound.new
14
15
  end
15
16
 
16
17
  end
@@ -82,6 +83,29 @@ module Pact
82
83
  end
83
84
  end
84
85
 
86
+ describe "#content_type" do
87
+
88
+ subject { TestRequest.new("get", "/something", headers, {} , "") }
89
+ context "when there are no expected headers" do
90
+ let(:headers) { Pact::KeyNotFound.new }
91
+ it "returns nil" do
92
+ expect(subject.send(:content_type)).to be nil
93
+ end
94
+ end
95
+ context "when there is no Content-Type header" do
96
+ let(:headers) { {} }
97
+ it "returns the content-type" do
98
+ expect(subject.send(:content_type)).to be nil
99
+ end
100
+ end
101
+ context "when there is a content-type header (" do
102
+ let(:headers) { {'content-type' => 'blah'} }
103
+ it "returns the content-type" do
104
+ expect(subject.send(:content_type)).to eq 'blah'
105
+ end
106
+ end
107
+ end
108
+
85
109
  end
86
110
  end
87
111
  end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+ require 'pact/shared/text_differ'
3
+
4
+ module Pact
5
+ describe TextDiffer do
6
+
7
+ describe ".call" do
8
+
9
+ subject { TextDiffer.call expected, actual }
10
+
11
+ let(:expected) { "This is the string you are looking for" }
12
+
13
+ context "when the expected and actual are both strings" do
14
+
15
+ context "when they equal each other" do
16
+ let(:actual) { "This is the string you are looking for" }
17
+
18
+ it "returns an empty diff" do
19
+ expect(subject.any?).to be false
20
+ end
21
+ end
22
+
23
+ context "when they don't equal each other" do
24
+ let(:actual) { "This is not the string you are looking for" }
25
+
26
+ it "returns the diff" do
27
+ expect(subject).to eq Pact::Matchers::Difference.new(expected, actual)
28
+ end
29
+ end
30
+ end
31
+
32
+ context "when the actual is not a String" do
33
+ let(:actual) { {some: 'hash'} }
34
+ let(:difference) { Pact::Matchers::Difference.new(expected, actual)}
35
+ it "returns the diff" do
36
+ expect(subject)
37
+ end
38
+ end
39
+
40
+ context "when the expected and actual are both objects" do
41
+ let(:actual) { {some: 'hash', blah: 'blah'} }
42
+ let(:expected) { {some: 'hash'} }
43
+ let(:difference) { Pact::Matchers::Difference.new(expected, actual)}
44
+
45
+ it "returns the diff using the JSON matching logic, allowing extra keys. But should it really if the expected Content-Type isn't actually JSON?" do
46
+ expect(subject)
47
+ end
48
+
49
+ it "potentially should treat both expected and actual as Strings"
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,21 @@
1
+ {
2
+ "consumer": {
3
+ "name": "Consumer"
4
+ },
5
+ "provider": {
6
+ "name": "Provider"
7
+ },
8
+ "interactions": [
9
+ {
10
+ "description": "an OPTIONS request",
11
+ "request": {
12
+ "method": "options",
13
+ "path": "/"
14
+ },
15
+ "response": {
16
+ "status": 200
17
+ },
18
+ "provider_state": null
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,15 @@
1
+ require 'pact/provider/rspec'
2
+
3
+ class App
4
+ def self.call env
5
+ if env['REQUEST_METHOD'] == 'OPTIONS'
6
+ [200, {}, []]
7
+ else
8
+ [500, {}, ["Expected an options request"]]
9
+ end
10
+ end
11
+ end
12
+
13
+ Pact.service_provider 'Provider' do
14
+ app { App }
15
+ end
@@ -11,6 +11,8 @@ module Pact
11
11
  [200, {'Content-Type' => 'application/json'}, [{message: WEATHER[:current_state], :array => [{"foo"=> "blah"}]}.to_json]]
12
12
  elsif env['PATH_INFO'] == '/sometext'
13
13
  [200, {'Content-Type' => 'text/plain'}, ['some text']]
14
+ elsif env['PATH_INFO'] == '/content_type_is_important'
15
+ [200, {'Content-Type' => 'application/json'}, [{message: "A message", note: "This will cause verify to fail if it using the wrong content type differ."}.to_json]]
14
16
  else
15
17
  raise "unexpected path #{env['PATH_INFO']}!!!"
16
18
  end
@@ -0,0 +1,23 @@
1
+ {
2
+ "consumer": {
3
+ "name": "some-test-consumer"
4
+ },
5
+ "provider": {
6
+ "name": "the test app in the pact_helper file"
7
+ },
8
+ "interactions": [
9
+ {
10
+ "description": "a test request expecting an application/json response",
11
+ "request": {
12
+ "method": "get",
13
+ "path": "/content_type_is_important",
14
+ "query": ""
15
+ },
16
+ "response": {
17
+ "status": 200,
18
+ "headers" : {"Content-type": "application/json"},
19
+ "body": {"message" : "A message"}
20
+ }
21
+ }
22
+ ]
23
+ }
data/tasks/pact-test.rake CHANGED
@@ -5,6 +5,10 @@ Pact::VerificationTask.new(:stubbing) do | pact |
5
5
  pact.uri './spec/support/stubbing.json', :pact_helper => './spec/support/stubbing_using_allow.rb'
6
6
  end
7
7
 
8
+ Pact::VerificationTask.new(:options) do | pact |
9
+ pact.uri './spec/support/options.json', :pact_helper => './spec/support/options_app.rb'
10
+ end
11
+
8
12
  Pact::VerificationTask.new(:pass) do | pact |
9
13
  pact.uri './spec/support/test_app_pass.json'
10
14
  end
@@ -30,6 +34,10 @@ Pact::VerificationTask.new('test_app:pass') do | pact |
30
34
  pact.uri './spec/support/test_app_pass.json'
31
35
  end
32
36
 
37
+ Pact::VerificationTask.new('test_app:content_type') do | pact |
38
+ pact.uri './spec/support/test_app_with_right_content_type_differ.json'
39
+ end
40
+
33
41
  Pact::VerificationTask.new('test_app:fail') do | pact |
34
42
  pact.uri './spec/support/test_app_fail.json', pact_helper: './spec/support/pact_helper.rb'
35
43
  end
@@ -45,6 +53,8 @@ namespace :pact do
45
53
  Rake::Task['pact:verify'].execute
46
54
  Rake::Task['pact:verify:test_app:pass'].execute
47
55
  Rake::Task['pact:test:fail'].execute
56
+ Rake::Task['pact:test:pactfile'].execute
57
+ Rake::Task['pact:verify:test_app:content_type'].execute
48
58
  end
49
59
 
50
60
  desc "All the verification tests with active support loaded"