pact 1.0.39 → 1.1.0.rc1
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.
- data/CHANGELOG.md +1 -17
- data/Gemfile.lock +2 -2
- data/README.md +18 -27
- data/bethtest.rb +30 -0
- data/documentation/faq.md +6 -36
- data/lib/pact/configuration.rb +0 -4
- data/lib/pact/consumer/consumer_contract_builder.rb +1 -1
- data/lib/pact/consumer_contract/active_support_support.rb +0 -4
- data/lib/pact/matchers/diff_decorator.rb +1 -1
- data/lib/pact/matchers/index_not_found.rb +12 -3
- data/lib/pact/matchers/matchers.rb +2 -1
- data/lib/pact/matchers/nested_json_diff_decorator.rb +48 -0
- data/lib/pact/matchers/plus_minus_diff_decorator.rb +92 -0
- data/lib/pact/matchers/unexpected_index.rb +13 -3
- data/lib/pact/matchers/unexpected_key.rb +12 -3
- data/lib/pact/provider/configuration.rb +31 -0
- data/lib/pact/provider/matchers.rb +4 -4
- data/lib/pact/provider/pact_spec_runner.rb +28 -35
- data/lib/pact/provider/print_missing_provider_states.rb +2 -2
- data/lib/pact/provider/rspec.rb +7 -16
- data/lib/pact/provider/world.rb +0 -6
- data/lib/pact/shared/key_not_found.rb +16 -3
- data/lib/pact/tasks/task_helper.rb +18 -15
- data/lib/pact/templates/provider_state.erb +1 -0
- data/lib/pact/version.rb +1 -1
- data/spec/lib/pact/matchers/matchers_spec.rb +9 -0
- data/spec/lib/pact/matchers/nested_json_diff_decorator_spec.rb +48 -0
- data/spec/lib/pact/matchers/plus_minus_diff_decorator_spec.rb +186 -0
- data/spec/lib/pact/provider/configuration_spec.rb +131 -102
- data/spec/lib/pact/verification_task_spec.rb +10 -0
- data/spec/support/pact_helper.rb +2 -5
- data/spec/support/stubbing.json +1 -1
- data/spec/support/test_app_fail.json +2 -29
- data/spec/support/test_app_pass.json +4 -4
- data/tasks/pact-test.rake +0 -8
- metadata +13 -22
- data/lib/pact/matchers/difference_indicator.rb +0 -26
- data/lib/pact/provider/rspec/formatter.rb +0 -63
- data/lib/pact/provider/rspec/silent_json_formatter.rb +0 -18
- data/spec/lib/pact/matchers/index_not_found_spec.rb +0 -21
- data/spec/lib/pact/matchers/unexpected_index_spec.rb +0 -20
- data/spec/lib/pact/matchers/unexpected_key_spec.rb +0 -20
- data/spec/lib/pact/shared/key_not_found_spec.rb +0 -20
- data/spec/lib/pact/tasks/task_helper_spec.rb +0 -80
@@ -1,11 +1,21 @@
|
|
1
|
-
require 'pact/matchers/difference_indicator'
|
2
|
-
|
3
1
|
module Pact
|
4
|
-
class UnexpectedIndex
|
2
|
+
class UnexpectedIndex
|
3
|
+
|
4
|
+
def == other
|
5
|
+
other.is_a? UnexpectedIndex
|
6
|
+
end
|
5
7
|
|
6
8
|
def to_s
|
7
9
|
'<index not to exist>'
|
8
10
|
end
|
9
11
|
|
12
|
+
def as_json options = {}
|
13
|
+
to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_json opts = {}
|
17
|
+
as_json.to_json options
|
18
|
+
end
|
19
|
+
|
10
20
|
end
|
11
21
|
end
|
@@ -1,11 +1,20 @@
|
|
1
|
-
require 'pact/matchers/difference_indicator'
|
2
|
-
|
3
1
|
module Pact
|
4
|
-
class UnexpectedKey
|
2
|
+
class UnexpectedKey
|
3
|
+
|
4
|
+
def == other
|
5
|
+
other.is_a? UnexpectedKey
|
6
|
+
end
|
5
7
|
|
6
8
|
def to_s
|
7
9
|
'<key not to exist>'
|
8
10
|
end
|
9
11
|
|
12
|
+
def as_json options = {}
|
13
|
+
to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_json opts = {}
|
17
|
+
as_json.to_json options
|
18
|
+
end
|
10
19
|
end
|
11
20
|
end
|
@@ -2,6 +2,10 @@ require 'pact/provider/pact_verification'
|
|
2
2
|
require 'pact/shared/dsl'
|
3
3
|
require 'pact/provider/state/provider_state'
|
4
4
|
require 'pact/provider/state/provider_state_configured_modules'
|
5
|
+
require 'pact/matchers/plus_minus_diff_decorator'
|
6
|
+
require 'pact/matchers/nested_json_diff_decorator'
|
7
|
+
|
8
|
+
# TODO break this class up!
|
5
9
|
|
6
10
|
module Pact
|
7
11
|
|
@@ -19,6 +23,12 @@ module Pact
|
|
19
23
|
module Configuration
|
20
24
|
|
21
25
|
module ConfigurationExtension
|
26
|
+
|
27
|
+
DIFF_FORMATTERS = {
|
28
|
+
:nested_json => Pact::Matchers::NestedJsonDiffDecorator,
|
29
|
+
:plus_and_minus => Pact::Matchers::PlusMinusDiffDecorator
|
30
|
+
}
|
31
|
+
|
22
32
|
def provider= provider
|
23
33
|
@provider = provider
|
24
34
|
end
|
@@ -47,6 +57,27 @@ module Pact
|
|
47
57
|
@config_ru_path = config_ru_path
|
48
58
|
end
|
49
59
|
|
60
|
+
def color_enabled
|
61
|
+
# Can't use ||= when the variable might be false, it will execute the expression if it's false
|
62
|
+
defined?(@color_enabled) ? @color_enabled : true
|
63
|
+
end
|
64
|
+
|
65
|
+
def color_enabled= color_enabled
|
66
|
+
@color_enabled = color_enabled
|
67
|
+
end
|
68
|
+
|
69
|
+
def diff_format= diff_format
|
70
|
+
@diff_format = diff_format
|
71
|
+
end
|
72
|
+
|
73
|
+
def diff_format
|
74
|
+
@diff_format ||= :nested_json
|
75
|
+
end
|
76
|
+
|
77
|
+
def diff_formatter_class
|
78
|
+
DIFF_FORMATTERS.fetch(diff_format)
|
79
|
+
end
|
80
|
+
|
50
81
|
def include mod
|
51
82
|
Pact::Provider::State::ProviderStateConfiguredModules.instance_eval do
|
52
83
|
include mod
|
@@ -1,17 +1,17 @@
|
|
1
1
|
require 'pact/term'
|
2
|
-
require 'pact/consumer_contract/active_support_support'
|
3
2
|
require 'awesome_print'
|
4
3
|
require 'pact/matchers'
|
5
4
|
require 'awesome_print'
|
6
5
|
require 'rspec'
|
6
|
+
require 'pact/matchers/nested_json_diff_decorator'
|
7
|
+
require 'pact/matchers/diff_decorator'
|
7
8
|
|
8
9
|
RSpec::Matchers.define :match_term do |expected|
|
9
10
|
include Pact::Matchers
|
10
|
-
include Pact::ActiveSupportSupport
|
11
11
|
|
12
12
|
match do |actual|
|
13
13
|
if (difference = diff(expected, actual)).any?
|
14
|
-
@
|
14
|
+
@diff_decorator = Pact.configuration.diff_formatter_class.new(difference)
|
15
15
|
false
|
16
16
|
else
|
17
17
|
true
|
@@ -19,7 +19,7 @@ RSpec::Matchers.define :match_term do |expected|
|
|
19
19
|
end
|
20
20
|
|
21
21
|
failure_message_for_should do | actual |
|
22
|
-
|
22
|
+
@diff_decorator.to_s
|
23
23
|
end
|
24
24
|
|
25
25
|
end
|
@@ -4,8 +4,7 @@ require 'rspec/core'
|
|
4
4
|
require 'rspec/core/formatters/documentation_formatter'
|
5
5
|
require 'rspec/core/formatters/json_formatter'
|
6
6
|
require 'pact/provider/pact_helper_locator'
|
7
|
-
require 'pact/provider/
|
8
|
-
require 'pact/provider/rspec/silent_json_formatter'
|
7
|
+
require 'pact/provider/print_missing_provider_states'
|
9
8
|
require_relative 'rspec'
|
10
9
|
|
11
10
|
|
@@ -37,7 +36,6 @@ module Pact
|
|
37
36
|
run_specs
|
38
37
|
ensure
|
39
38
|
::RSpec.reset
|
40
|
-
Pact.clear_world
|
41
39
|
end
|
42
40
|
end
|
43
41
|
|
@@ -45,11 +43,11 @@ module Pact
|
|
45
43
|
|
46
44
|
def require_pact_helper spec_definition
|
47
45
|
if spec_definition[:pact_helper]
|
48
|
-
|
46
|
+
puts "Using #{spec_definition[:pact_helper]}"
|
49
47
|
require spec_definition[:pact_helper]
|
50
48
|
elsif spec_definition[:support_file]
|
51
|
-
|
52
|
-
|
49
|
+
puts "Using #{spec_definition[:support_file]}"
|
50
|
+
$stderr.puts SUPPORT_FILE_DEPRECATION_MESSAGE
|
53
51
|
require spec_definition[:support_file]
|
54
52
|
else
|
55
53
|
require 'pact/provider/client_project_pact_helper'
|
@@ -72,47 +70,42 @@ module Pact
|
|
72
70
|
config = ::RSpec.configuration
|
73
71
|
|
74
72
|
config.color = true
|
75
|
-
config.pattern = "pattern which doesn't match any files"
|
76
|
-
config.backtrace_inclusion_patterns = [/pact\/provider\/rspec/]
|
77
|
-
|
78
73
|
config.extend Pact::Provider::RSpec::ClassMethods
|
79
74
|
config.include Pact::Provider::RSpec::InstanceMethods
|
80
75
|
config.include Pact::Provider::TestMethods
|
76
|
+
config.backtrace_inclusion_patterns = [/pact\/provider\/rspec/]
|
81
77
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
else
|
86
|
-
config.error_stream = Pact.configuration.error_stream
|
87
|
-
config.output_stream = Pact.configuration.output_stream
|
78
|
+
config.before :each, :pact => :verify do | example |
|
79
|
+
example_description = "#{example.example.example_group.description} #{example.example.description}"
|
80
|
+
Pact.configuration.logger.info "Running example '#{example_description}'"
|
88
81
|
end
|
89
82
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
config.before(:suite) do
|
94
|
-
# Preload app before suite so the classes loaded in memory are consistent for
|
95
|
-
# before :each and after :each hooks.
|
96
|
-
# Otherwise the app and all its dependencies are loaded between the first before :each
|
97
|
-
# and the first after :each, leading to inconsistent behaviour
|
98
|
-
# (eg. with database_cleaner transactions)
|
99
|
-
Pact.configuration.provider.app
|
83
|
+
unless options[:silent]
|
84
|
+
config.error_stream = $stderr
|
85
|
+
config.output_stream = $stdout
|
100
86
|
end
|
101
|
-
end
|
102
87
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
exit_code
|
88
|
+
formatter = ::RSpec::Core::Formatters::DocumentationFormatter.new(config.output)
|
89
|
+
@json_formatter = ::RSpec::Core::Formatters::JsonFormatter.new(StringIO.new)
|
90
|
+
reporter = ::RSpec::Core::Reporter.new(formatter, @json_formatter)
|
91
|
+
config.instance_variable_set(:@reporter, reporter)
|
108
92
|
end
|
109
93
|
|
110
|
-
|
111
|
-
|
112
|
-
|
94
|
+
def run_specs
|
95
|
+
config = ::RSpec.configuration
|
96
|
+
world = ::RSpec::world
|
97
|
+
exit_code = config.reporter.report(world.example_count, nil) do |reporter|
|
98
|
+
begin
|
99
|
+
config.run_hook(:before, :suite)
|
100
|
+
world.example_groups.ordered.map {|g| g.run(reporter)}.all? ? 0 : config.failure_exit_code
|
101
|
+
ensure
|
102
|
+
config.run_hook(:after, :suite)
|
103
|
+
end
|
113
104
|
end
|
105
|
+
PrintMissingProviderStates.call Pact.world.provider_states.missing_provider_states
|
106
|
+
@output = @json_formatter.output_hash
|
107
|
+
exit_code
|
114
108
|
end
|
115
|
-
|
116
109
|
end
|
117
110
|
end
|
118
111
|
end
|
@@ -3,9 +3,9 @@ module Pact
|
|
3
3
|
class PrintMissingProviderStates
|
4
4
|
|
5
5
|
# Hash of consumer names to array of names of missing provider states
|
6
|
-
def self.call missing_provider_states
|
6
|
+
def self.call missing_provider_states
|
7
7
|
if missing_provider_states.any?
|
8
|
-
|
8
|
+
puts orangeify(text(missing_provider_states))
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
data/lib/pact/provider/rspec.rb
CHANGED
@@ -19,10 +19,12 @@ module Pact
|
|
19
19
|
include ::RSpec::Core::DSL
|
20
20
|
|
21
21
|
def honour_pactfile pactfile_uri, options = {}
|
22
|
-
puts "Filtering specs by: #{options[:criteria]}" if options[:criteria]
|
22
|
+
puts "Filtering specs by: #{options[:criteria]}" if options[:criteria]
|
23
23
|
consumer_contract = Pact::ConsumerContract.from_json(read_pact_from(pactfile_uri, options))
|
24
|
-
describe "
|
25
|
-
|
24
|
+
describe "A pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}" do
|
25
|
+
describe "in #{pactfile_uri}" do
|
26
|
+
honour_consumer_contract consumer_contract, options
|
27
|
+
end
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
@@ -58,19 +60,12 @@ module Pact
|
|
58
60
|
|
59
61
|
def describe_interaction interaction, options
|
60
62
|
|
61
|
-
|
62
|
-
:pact => :verify,
|
63
|
-
:pact_interaction => interaction,
|
64
|
-
:pact_interaction_example_description => interaction_description_for_rerun_command(interaction)
|
65
|
-
}
|
66
|
-
|
67
|
-
describe description_for(interaction), metadata do
|
63
|
+
describe description_for(interaction), :pact => :verify do
|
68
64
|
|
69
65
|
interaction_context = InteractionContext.new
|
70
66
|
|
71
67
|
before do
|
72
68
|
interaction_context.run_once :before do
|
73
|
-
Pact.configuration.logger.info "Running example '#{self.example.full_description}'"
|
74
69
|
set_up_provider_state interaction.provider_state, options[:consumer]
|
75
70
|
replay_interaction interaction
|
76
71
|
interaction_context.last_response = last_response
|
@@ -124,11 +119,7 @@ module Pact
|
|
124
119
|
end
|
125
120
|
|
126
121
|
def description_for interaction
|
127
|
-
interaction.
|
128
|
-
end
|
129
|
-
|
130
|
-
def interaction_description_for_rerun_command interaction
|
131
|
-
description_for(interaction).capitalize + ( interaction.provider_state ? " given #{interaction.provider_state}" : "")
|
122
|
+
"#{interaction.description} using #{interaction.request.method.upcase} to #{interaction.request.path}"
|
132
123
|
end
|
133
124
|
|
134
125
|
def read_pact_from uri, options = {}
|
data/lib/pact/provider/world.rb
CHANGED
@@ -14,12 +14,6 @@ module Pact
|
|
14
14
|
module Provider
|
15
15
|
class World
|
16
16
|
|
17
|
-
attr_reader :json_formatter_stream
|
18
|
-
|
19
|
-
def initialize
|
20
|
-
@json_formatter_stream = StringIO.new
|
21
|
-
end
|
22
|
-
|
23
17
|
def provider_states
|
24
18
|
@provider_states_proxy ||= Pact::Provider::State::ProviderStateProxy.new
|
25
19
|
end
|
@@ -1,12 +1,25 @@
|
|
1
|
-
require 'pact/matchers/difference_indicator'
|
2
|
-
|
3
1
|
module Pact
|
4
|
-
class KeyNotFound
|
2
|
+
class KeyNotFound
|
3
|
+
def == other
|
4
|
+
other.is_a? KeyNotFound
|
5
|
+
end
|
6
|
+
|
7
|
+
def eql? other
|
8
|
+
self == other
|
9
|
+
end
|
5
10
|
|
6
11
|
def to_s
|
7
12
|
"<key not found>"
|
8
13
|
end
|
9
14
|
|
15
|
+
def as_json options={}
|
16
|
+
to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_json options = {}
|
20
|
+
as_json.to_json options
|
21
|
+
end
|
22
|
+
|
10
23
|
def empty?
|
11
24
|
true
|
12
25
|
end
|
@@ -1,29 +1,32 @@
|
|
1
1
|
module Pact
|
2
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
|
3
10
|
|
4
|
-
|
11
|
+
def redify string
|
12
|
+
"\e[31m#{string}\e[m"
|
13
|
+
end
|
5
14
|
|
6
15
|
def handle_verification_failure
|
7
16
|
exit_status = yield
|
8
|
-
|
17
|
+
if exit_status != 0
|
18
|
+
$stderr.puts failure_message
|
19
|
+
fail
|
20
|
+
end
|
9
21
|
end
|
10
22
|
|
11
23
|
def spec_criteria defaults = {description: nil, provider_state: nil}
|
12
24
|
criteria = {}
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
provider_state = ENV.fetch("PACT_PROVIDER_STATE", defaults[:provider_state])
|
18
|
-
if provider_state
|
19
|
-
if provider_state.length == 0
|
20
|
-
criteria[:provider_state] = nil #Allow PACT_PROVIDER_STATE="" to mean no provider state
|
21
|
-
else
|
22
|
-
criteria[:provider_state] = Regexp.new(provider_state)
|
23
|
-
end
|
25
|
+
[:description, :provider_state].each do | key |
|
26
|
+
value = ENV.fetch("PACT_#{key.to_s.upcase}", defaults[key])
|
27
|
+
criteria[key] = Regexp.new(value) unless value.nil?
|
24
28
|
end
|
25
|
-
|
26
|
-
criteria
|
29
|
+
criteria.any? ? criteria : nil
|
27
30
|
end
|
28
31
|
end
|
29
32
|
end
|
data/lib/pact/version.rb
CHANGED
@@ -158,6 +158,15 @@ module Pact::Matchers
|
|
158
158
|
end
|
159
159
|
end
|
160
160
|
|
161
|
+
context "when the different index is in the middle of an array" do
|
162
|
+
subject { [1,2,3] }
|
163
|
+
let(:actual) { [1,7,3]}
|
164
|
+
let(:difference) { [Pact::Matchers::NO_DIFF_INDICATOR, Difference.new(2, 7), Pact::Matchers::NO_DIFF_INDICATOR] }
|
165
|
+
it 'returns the diff' do
|
166
|
+
expect(diff(subject, actual)).to eq(difference)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
161
170
|
context "when actual array is longer than the expected" do
|
162
171
|
subject { [1] }
|
163
172
|
let(:actual) { [1,2]}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pact/matchers/nested_json_diff_decorator'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module Matchers
|
6
|
+
describe NestedJsonDiffDecorator do
|
7
|
+
|
8
|
+
let(:diff) do
|
9
|
+
{
|
10
|
+
:something => Difference.new({name: 'Fred'}, KeyNotFound.new)
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { NestedJsonDiffDecorator.new(diff) }
|
15
|
+
|
16
|
+
describe "#to_s" do
|
17
|
+
|
18
|
+
context "when color_enabled is true" do
|
19
|
+
it "returns nicely formatted json" do
|
20
|
+
expect(subject.to_s.split("\n").size).to eq 8
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns a string displaying the diff in colour" do
|
24
|
+
expect(subject.to_s).to include NestedJsonDiffDecorator::EXPECTED_COLOURED
|
25
|
+
expect(subject.to_s).to include NestedJsonDiffDecorator::ACTUAL_COLOURED
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when color_enabled is false" do
|
30
|
+
before do
|
31
|
+
Pact.configuration.stub(:color_enabled).and_return(false)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns nicely formatted json" do
|
35
|
+
expect(subject.to_s.split("\n").size).to eq 8
|
36
|
+
end
|
37
|
+
|
38
|
+
it "returns a string displaying the diff without colour" do
|
39
|
+
expect(subject.to_s).to_not include NestedJsonDiffDecorator::EXPECTED_COLOURED
|
40
|
+
expect(subject.to_s).to_not include NestedJsonDiffDecorator::ACTUAL_COLOURED
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|