pact 1.0.4 → 1.0.5

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