pact 1.0.39 → 1.1.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|