pact 1.0.4 → 1.0.5

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 CHANGED
@@ -1,3 +1,8 @@
1
+ ### 1.0.5 (6 September 2013)
2
+
3
+ * Added verification reports when running rake pact:verify:xxx [Latheesh Padukana, Beth Skurrie]
4
+ * Changed pact:verify failure message to display in red [Latheesh Padukana, Beth Skurrie]
5
+
1
6
  ### 1.0.4 (6 September 2013)
2
7
 
3
8
  * Added pact/tasks as an easy way to load the rake tasks and classes into the client project [Beth Skurrie]
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pact (1.0.4)
4
+ pact (1.0.5)
5
5
  awesome_print (~> 1.1.0)
6
6
  find_a_port (~> 1.0.1)
7
7
  hashie (~> 2.0)
@@ -3,15 +3,31 @@ require 'pact/logging'
3
3
  require 'pact/json_warning'
4
4
  require 'date'
5
5
  require 'pact/version'
6
+ require 'open-uri'
6
7
  require_relative 'service_consumer'
7
8
  require_relative 'service_provider'
8
9
  require_relative 'interaction'
9
10
 
11
+
12
+
10
13
  module Pact
14
+
15
+ #TODO move to external file for reuse
16
+ module FileName
17
+ def file_name consumer_name, provider_name
18
+ "#{filenamify(consumer_name)}-#{filenamify(provider_name)}.json"
19
+ end
20
+
21
+ def filenamify name
22
+ name.downcase.gsub(/\s/, '_')
23
+ end
24
+ end
25
+
11
26
  class ConsumerContract
12
27
 
13
- include Pact::Logging
14
- include Pact::JsonWarning
28
+ include Logging
29
+ include JsonWarning
30
+ include FileName
15
31
 
16
32
  attr_accessor :interactions
17
33
  attr_accessor :consumer
@@ -37,7 +53,8 @@ module Pact
37
53
  end
38
54
 
39
55
  def to_json(options = {})
40
- as_json(options).to_json(options)
56
+ as_json(options).to_json(
57
+ )
41
58
  end
42
59
 
43
60
  def self.from_hash(obj)
@@ -53,6 +70,10 @@ module Pact
53
70
  from_hash(deserialised_object)
54
71
  end
55
72
 
73
+ def self.from_uri uri
74
+ from_json(open(uri){ |f| f.read })
75
+ end
76
+
56
77
  def self.maintain_backwards_compatiblity_with_producer_keys string
57
78
  string.gsub('"producer":', '"provider":').gsub('"producer_state":', '"provider_state":')
58
79
  end
@@ -78,26 +99,20 @@ module Pact
78
99
  end
79
100
 
80
101
  def pact_file_name
81
- "#{filenamify(consumer.name)}-#{filenamify(provider.name)}.json"
102
+ file_name consumer.name, provider.name
82
103
  end
83
104
 
84
- def pactfile_path
85
- raise 'You must first specify a consumer and service name' unless (consumer && consumer.name && provider && provider.name)
86
- @pactfile_path ||= File.join(Pact.configuration.pact_dir, pact_file_name)
87
- end
88
-
89
- def update_pactfile
90
- logger.debug "Updating pact file for #{provider.name} at #{pactfile_path}"
91
- check_for_active_support_json
92
- File.open(pactfile_path, 'w') do |f|
93
- f.write JSON.pretty_generate(self)
94
- end
95
- end
96
-
97
- private
105
+ def pactfile_path
106
+ raise 'You must first specify a consumer and service name' unless (consumer && consumer.name && provider && provider.name)
107
+ @pactfile_path ||= File.join(Pact.configuration.pact_dir, pact_file_name)
108
+ end
98
109
 
99
- def filenamify name
100
- name.downcase.gsub(/\s/, '_')
110
+ def update_pactfile
111
+ logger.debug "Updating pact file for #{provider.name} at #{pactfile_path}"
112
+ check_for_active_support_json
113
+ File.open(pactfile_path, 'w') do |f|
114
+ f.write JSON.pretty_generate(self)
101
115
  end
116
+ end
102
117
  end
103
118
  end
@@ -1,7 +1,13 @@
1
1
  module PactTaskHelper
2
2
  def failure_message
3
- "\n* * * * * * * * * * * * * * * * * * *\n" +
4
- "Provider did not honour pact file.\nSee\n * #{Pact.configuration.log_path}\n * #{Pact.configuration.tmp_dir}\nfor logs and pact files." +
5
- "\n* * * * * * * * * * * * * * * * * * *\n\n"
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"
6
12
  end
7
13
  end
@@ -2,6 +2,7 @@ require 'open-uri'
2
2
  require 'rspec'
3
3
  require 'rspec/core'
4
4
  require 'rspec/core/formatters/documentation_formatter'
5
+ require 'rspec/core/formatters/json_formatter'
5
6
  require_relative 'rspec'
6
7
 
7
8
 
@@ -9,7 +10,11 @@ module Pact
9
10
  module Provider
10
11
  class PactSpecRunner
11
12
 
12
- extend Pact::Provider::RSpec::ClassMethods
13
+ include Pact::Provider::RSpec::ClassMethods
14
+
15
+ attr_reader :spec_definitions
16
+ attr_reader :options
17
+ attr_reader :output
13
18
 
14
19
  PACT_HELPER_FILE_PATTERNS = [
15
20
  "spec/**/*service*consumer*/pact_helper.rb",
@@ -19,15 +24,21 @@ module Pact
19
24
 
20
25
  NO_PACT_HELPER_FOUND_MSG = "Please create a pact_helper.rb file that can be found using one of the following patterns: #{PACT_HELPER_FILE_PATTERNS.join(", ")}"
21
26
 
22
- def self.run(spec_definitions, options = {})
23
- initialize_specs spec_definitions
24
- configure_rspec options
27
+ def initialize spec_definitions, options = {}
28
+ @spec_definitions = spec_definitions
29
+ @options = options
30
+ @results = nil
31
+ end
32
+
33
+ def run
34
+ initialize_specs
35
+ configure_rspec
25
36
  run_specs
26
37
  end
27
38
 
28
39
  private
29
40
 
30
- def self.require_pact_helper spec_definition
41
+ def require_pact_helper spec_definition
31
42
  if spec_definition[:support_file]
32
43
  require spec_definition[:support_file]
33
44
  else
@@ -35,14 +46,14 @@ module Pact
35
46
  end
36
47
  end
37
48
 
38
- def self.pact_helper_file
49
+ def pact_helper_file
39
50
  pact_helper_search_results = []
40
51
  PACT_HELPER_FILE_PATTERNS.find { | pattern | (pact_helper_search_results.concat(Dir.glob(pattern))).any? }
41
52
  raise NO_PACT_HELPER_FOUND_MSG if pact_helper_search_results.empty?
42
53
  "#{Dir.pwd}/#{pact_helper_search_results[0]}"
43
54
  end
44
55
 
45
- def self.initialize_specs spec_definitions
56
+ def initialize_specs
46
57
  spec_definitions.each do | spec_definition |
47
58
  require_pact_helper spec_definition
48
59
  options = {consumer: spec_definition[:consumer], save_pactfile_to_tmp: true}
@@ -50,7 +61,7 @@ module Pact
50
61
  end
51
62
  end
52
63
 
53
- def self.configure_rspec options
64
+ def configure_rspec
54
65
  config = ::RSpec.configuration
55
66
  config.color = true
56
67
 
@@ -60,14 +71,15 @@ module Pact
60
71
  end
61
72
 
62
73
  formatter = ::RSpec::Core::Formatters::DocumentationFormatter.new(config.output)
63
- reporter = ::RSpec::Core::Reporter.new(formatter)
74
+ @json_formatter = ::RSpec::Core::Formatters::JsonFormatter.new(StringIO.new)
75
+ reporter = ::RSpec::Core::Reporter.new(formatter, @json_formatter)
64
76
  config.instance_variable_set(:@reporter, reporter)
65
77
  end
66
78
 
67
- def self.run_specs
79
+ def run_specs
68
80
  config = ::RSpec.configuration
69
81
  world = ::RSpec::world
70
- config.reporter.report(world.example_count, nil) do |reporter|
82
+ exit_code = config.reporter.report(world.example_count, nil) do |reporter|
71
83
  begin
72
84
  config.run_hook(:before, :suite)
73
85
  world.example_groups.ordered.map {|g| g.run(reporter)}.all? ? 0 : config.failure_exit_code
@@ -75,6 +87,8 @@ module Pact
75
87
  config.run_hook(:after, :suite)
76
88
  end
77
89
  end
90
+ @output = @json_formatter.output_hash
91
+ exit_code
78
92
  end
79
93
  end
80
94
  end
@@ -17,6 +17,8 @@ module Pact
17
17
 
18
18
  module ClassMethods
19
19
 
20
+ include ::RSpec::Core::DSL
21
+
20
22
  include Pact::JsonWarning
21
23
 
22
24
  def honour_pactfile pactfile_uri, options = {}
@@ -0,0 +1,32 @@
1
+ require 'pact/consumer_contract'
2
+
3
+ module Pact::Provider
4
+ class VerificationReport
5
+
6
+ include Pact::FileName
7
+
8
+ def initialize (options)
9
+ @consumer = options[:consumer]
10
+ @provider = options[:provider]
11
+ @result = options[:result]
12
+ @output = options[:output]
13
+ end
14
+
15
+ def as_json
16
+ {
17
+ :consumer => @consumer,
18
+ :provider => @provider,
19
+ :result => @result,
20
+ :output => @output
21
+ }
22
+ end
23
+
24
+ def to_json(options = {})
25
+ as_json.to_json(options)
26
+ end
27
+
28
+ def report_file_name
29
+ file_name("#{@consumer[:name]}_#{@consumer[:ref]}", "#{@provider[:name]}_#{@provider[:ref]}")
30
+ end
31
+ end
32
+ end
@@ -1,6 +1,7 @@
1
1
  require 'rake/tasklib'
2
2
  require 'pact/provider/pact_spec_runner'
3
3
  require_relative 'pact_task_helper'
4
+ require_relative 'provider/verification_report'
4
5
 
5
6
  =begin
6
7
  To create a rake pact:verify:<something> task
@@ -48,12 +49,38 @@ module Pact
48
49
 
49
50
  attr_reader :name
50
51
 
52
+ def parse_pactfile config
53
+ Pact::ConsumerContract.from_uri config[:uri]
54
+ end
55
+
56
+ def publish_report config, output, result
57
+ consumer_contract = parse_pactfile config
58
+ #TODO - when checking out a historical version, provider ref will be prod, however it will think it is head. Fix this!!!!
59
+ report = Provider::VerificationReport.new(
60
+ :result => result,
61
+ :output => output,
62
+ :consumer => {:name => consumer_contract.consumer.name, :ref => name},
63
+ :provider => {:name => consumer_contract.provider.name, :ref => 'head'}
64
+ )
65
+
66
+ FileUtils.mkdir_p "./reports/pact"
67
+ File.open("./reports/pact/#{report.report_file_name}", "w") { |file| file << JSON.pretty_generate(report) }
68
+ end
69
+
51
70
  def define_rake_task
52
71
  namespace :pact do
53
72
  desc "Verify provider against the consumer pacts for #{name}"
54
73
  task "verify:#{name}" do
55
- exit_status = Provider::PactSpecRunner.run(pact_spec_config)
56
- fail failure_message if exit_status != 0
74
+
75
+ exit_statuses = pact_spec_config.collect do | config |
76
+ #TODO: Change this to accept the ConsumerContract that is already parsed, so we don't make the same request twice
77
+ pact_spec_runner = Provider::PactSpecRunner.new([config])
78
+ exit_status = pact_spec_runner.run
79
+ publish_report config, pact_spec_runner.output, exit_status == 0
80
+ exit_status
81
+ end
82
+
83
+ fail failure_message if exit_statuses.any?{ | status | status != 0 }
57
84
  end
58
85
  end
59
86
  end
data/lib/pact/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pact
2
- VERSION = "1.0.4"
2
+ VERSION = "1.0.5"
3
3
  end
@@ -5,7 +5,7 @@ module Pact::Provider
5
5
  describe PactSpecRunner do
6
6
  describe "pact_helper_file", :fakefs => true do
7
7
 
8
- subject { PactSpecRunner.send(:pact_helper_file) }
8
+ subject { PactSpecRunner.new({}).send(:pact_helper_file) }
9
9
 
10
10
  def make_pactfile dir
11
11
  FileUtils.mkdir_p ".#{dir}"
@@ -19,6 +19,16 @@ module Pact
19
19
  end
20
20
  end
21
21
 
22
+ before do
23
+ VerificationTask.any_instance.stub(:publish_report)
24
+ Provider::PactSpecRunner.stub(:new).with(consumer_contract).and_return(pact_spec_runner)
25
+ end
26
+
27
+ let(:pact_spec_runner) { double('PactSpecRunner', :run => exit_code, :output => nil)}
28
+ let(:exit_code) {0}
29
+ let(:consumer_contract) { [ uri: @pact_uri, support_file: nil ] }
30
+
31
+
22
32
  describe '.initialize' do
23
33
  context 'with an explict support_file' do
24
34
  it 'creates the tasks' do
@@ -34,11 +44,9 @@ module Pact
34
44
 
35
45
  describe 'execute' do
36
46
 
37
- let(:consumer_contract) { [ uri: @pact_uri, support_file: nil ] }
38
47
 
39
48
  context "with no explict support file " do
40
49
  it 'verifies the pacts using PactSpecRunner' do
41
- Provider::PactSpecRunner.should_receive(:run).with(consumer_contract).and_return(0)
42
50
  Rake::Task[@task_name].execute
43
51
  end
44
52
  end
@@ -46,17 +54,12 @@ module Pact
46
54
  context "with an explict support_file" do
47
55
  let(:consumer_contract) { [ uri: @pact_uri, support_file: @support_file] }
48
56
  it 'verifies the pacts using PactSpecRunner' do
49
- Provider::PactSpecRunner.should_receive(:run).with(consumer_contract).and_return(0)
50
57
  Rake::Task[@task_name_with_explict_support_file].execute
51
58
  end
52
59
  end
53
60
 
54
61
  context 'when all specs pass' do
55
62
 
56
- before do
57
- Provider::PactSpecRunner.stub(:run).and_return(0)
58
- end
59
-
60
63
  it 'does not raise an exception' do
61
64
  Rake::Task[@task_name].execute
62
65
  end
@@ -64,9 +67,7 @@ module Pact
64
67
 
65
68
  context 'when one or more specs fail' do
66
69
 
67
- before do
68
- Provider::PactSpecRunner.stub(:run).and_return(1)
69
- end
70
+ let(:exit_code) {1}
70
71
 
71
72
  it 'raises an exception' do
72
73
  expect { Rake::Task[@task_name].execute }.to raise_error RuntimeError
data/tasks/pact-test.rake CHANGED
@@ -8,10 +8,10 @@ namespace :pact do
8
8
  puts "Running task pact:tests"
9
9
  # Run these specs silently, otherwise expected failures will be written to stdout and look like unexpected failures.
10
10
 
11
- result = Pact::Provider::PactSpecRunner.run([{ uri: './spec/support/test_app_pass.json' }], silent: silent)
11
+ result = Pact::Provider::PactSpecRunner.new([{ uri: './spec/support/test_app_pass.json' }], silent: silent).run
12
12
  fail 'Expected pact to pass' unless (result == 0)
13
13
 
14
- result = Pact::Provider::PactSpecRunner.run([{ uri: './spec/support/test_app_fail.json', support_file: './spec/support/pact_helper.rb' }], silent: silent)
14
+ result = Pact::Provider::PactSpecRunner.new([{ uri: './spec/support/test_app_fail.json', support_file: './spec/support/pact_helper.rb' }], silent: silent).run
15
15
  fail 'Expected pact to fail' if (result == 0)
16
16
 
17
17
  puts "Task pact:tests completed succesfully."
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pact
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -299,6 +299,7 @@ files:
299
299
  - lib/pact/provider/provider_state.rb
300
300
  - lib/pact/provider/rspec.rb
301
301
  - lib/pact/provider/test_methods.rb
302
+ - lib/pact/provider/verification_report.rb
302
303
  - lib/pact/reification.rb
303
304
  - lib/pact/request.rb
304
305
  - lib/pact/tasks.rb