pact 1.3.3 → 1.4.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. data/CHANGELOG.md +16 -1
  2. data/Gemfile +5 -0
  3. data/Gemfile.lock +34 -13
  4. data/README.md +3 -2
  5. data/example/animal-service/lib/animal_service/api.rb +1 -0
  6. data/example/zoo-app/lib/zoo_app/animal_service_client.rb +0 -9
  7. data/example/zoo-app/lib/zoo_app/models/alligator.rb +2 -0
  8. data/lib/pact.rb +6 -5
  9. data/lib/pact/cli.rb +0 -26
  10. data/lib/pact/consumer/configuration.rb +0 -1
  11. data/lib/pact/consumer/configuration/configuration_extensions.rb +51 -0
  12. data/lib/pact/consumer/consumer_contract_builder.rb +1 -2
  13. data/lib/pact/consumer/interaction_builder.rb +2 -4
  14. data/lib/pact/doc/interaction_view_model.rb +9 -6
  15. data/lib/pact/doc/sort_interactions.rb +1 -1
  16. data/lib/pact/provider/rspec.rb +11 -9
  17. data/lib/pact/version.rb +1 -1
  18. data/pact.gemspec +5 -0
  19. data/spec/lib/pact/consumer/interaction_builder_spec.rb +4 -8
  20. data/spec/support/case-insensitive-response-header-matching.json +21 -0
  21. data/spec/support/case-insensitive-response-header-matching.rb +15 -0
  22. data/tasks/pact-test.rake +5 -0
  23. metadata +42 -122
  24. data/lib/pact/configuration.rb +0 -195
  25. data/lib/pact/consumer/app_manager.rb +0 -158
  26. data/lib/pact/consumer/interactions_filter.rb +0 -48
  27. data/lib/pact/consumer/mock_service.rb +0 -2
  28. data/lib/pact/consumer/mock_service/app.rb +0 -82
  29. data/lib/pact/consumer/mock_service/interaction_delete.rb +0 -33
  30. data/lib/pact/consumer/mock_service/interaction_list.rb +0 -76
  31. data/lib/pact/consumer/mock_service/interaction_mismatch.rb +0 -73
  32. data/lib/pact/consumer/mock_service/interaction_post.rb +0 -31
  33. data/lib/pact/consumer/mock_service/interaction_replay.rb +0 -139
  34. data/lib/pact/consumer/mock_service/log_get.rb +0 -28
  35. data/lib/pact/consumer/mock_service/missing_interactions_get.rb +0 -30
  36. data/lib/pact/consumer/mock_service/mock_service_administration_endpoint.rb +0 -31
  37. data/lib/pact/consumer/mock_service/pact_post.rb +0 -33
  38. data/lib/pact/consumer/mock_service/rack_request_helper.rb +0 -51
  39. data/lib/pact/consumer/mock_service/verification_get.rb +0 -68
  40. data/lib/pact/consumer/mock_service_client.rb +0 -65
  41. data/lib/pact/consumer/mock_service_interaction_expectation.rb +0 -37
  42. data/lib/pact/consumer/request.rb +0 -27
  43. data/lib/pact/consumer/server.rb +0 -90
  44. data/lib/pact/consumer_contract.rb +0 -1
  45. data/lib/pact/consumer_contract/consumer_contract.rb +0 -115
  46. data/lib/pact/consumer_contract/consumer_contract_writer.rb +0 -84
  47. data/lib/pact/consumer_contract/file_name.rb +0 -19
  48. data/lib/pact/consumer_contract/headers.rb +0 -51
  49. data/lib/pact/consumer_contract/interaction.rb +0 -67
  50. data/lib/pact/consumer_contract/pact_file.rb +0 -24
  51. data/lib/pact/consumer_contract/request.rb +0 -73
  52. data/lib/pact/consumer_contract/service_consumer.rb +0 -28
  53. data/lib/pact/consumer_contract/service_provider.rb +0 -28
  54. data/lib/pact/logging.rb +0 -14
  55. data/lib/pact/matchers.rb +0 -1
  56. data/lib/pact/matchers/actual_type.rb +0 -16
  57. data/lib/pact/matchers/base_difference.rb +0 -37
  58. data/lib/pact/matchers/differ.rb +0 -153
  59. data/lib/pact/matchers/difference.rb +0 -13
  60. data/lib/pact/matchers/difference_indicator.rb +0 -26
  61. data/lib/pact/matchers/embedded_diff_formatter.rb +0 -62
  62. data/lib/pact/matchers/expected_type.rb +0 -35
  63. data/lib/pact/matchers/index_not_found.rb +0 -15
  64. data/lib/pact/matchers/list_diff_formatter.rb +0 -101
  65. data/lib/pact/matchers/matchers.rb +0 -139
  66. data/lib/pact/matchers/no_diff_indicator.rb +0 -18
  67. data/lib/pact/matchers/regexp_difference.rb +0 -13
  68. data/lib/pact/matchers/type_difference.rb +0 -16
  69. data/lib/pact/matchers/unexpected_index.rb +0 -11
  70. data/lib/pact/matchers/unexpected_key.rb +0 -11
  71. data/lib/pact/matchers/unix_diff_formatter.rb +0 -114
  72. data/lib/pact/reification.rb +0 -28
  73. data/lib/pact/rspec.rb +0 -53
  74. data/lib/pact/shared/active_support_support.rb +0 -51
  75. data/lib/pact/shared/dsl.rb +0 -76
  76. data/lib/pact/shared/jruby_support.rb +0 -18
  77. data/lib/pact/shared/json_differ.rb +0 -15
  78. data/lib/pact/shared/key_not_found.rb +0 -15
  79. data/lib/pact/shared/null_expectation.rb +0 -31
  80. data/lib/pact/shared/request.rb +0 -80
  81. data/lib/pact/shared/text_differ.rb +0 -14
  82. data/lib/pact/something_like.rb +0 -49
  83. data/lib/pact/symbolize_keys.rb +0 -12
  84. data/lib/pact/term.rb +0 -85
  85. data/spec/lib/pact/consumer/request_spec.rb +0 -24
  86. data/spec/lib/pact/consumer_contract/active_support_support_spec.rb +0 -58
  87. data/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +0 -180
  88. data/spec/lib/pact/consumer_contract/headers_spec.rb +0 -107
  89. data/spec/lib/pact/consumer_contract/interaction_spec.rb +0 -107
  90. data/spec/lib/pact/consumer_contract/request_spec.rb +0 -329
  91. data/spec/lib/pact/matchers/differ_spec.rb +0 -214
  92. data/spec/lib/pact/matchers/difference_spec.rb +0 -22
  93. data/spec/lib/pact/matchers/embedded_diff_formatter_spec.rb +0 -90
  94. data/spec/lib/pact/matchers/index_not_found_spec.rb +0 -21
  95. data/spec/lib/pact/matchers/list_diff_formatter_spec.rb +0 -114
  96. data/spec/lib/pact/matchers/matchers_spec.rb +0 -500
  97. data/spec/lib/pact/matchers/regexp_difference_spec.rb +0 -20
  98. data/spec/lib/pact/matchers/type_difference_spec.rb +0 -34
  99. data/spec/lib/pact/matchers/unexpected_index_spec.rb +0 -20
  100. data/spec/lib/pact/matchers/unexpected_key_spec.rb +0 -20
  101. data/spec/lib/pact/matchers/unix_diff_formatter_spec.rb +0 -216
  102. data/spec/lib/pact/reification_spec.rb +0 -67
  103. data/spec/lib/pact/shared/dsl_spec.rb +0 -86
  104. data/spec/lib/pact/shared/json_differ_spec.rb +0 -36
  105. data/spec/lib/pact/shared/key_not_found_spec.rb +0 -20
  106. data/spec/lib/pact/shared/request_spec.rb +0 -111
  107. data/spec/lib/pact/shared/text_differ_spec.rb +0 -54
  108. data/spec/lib/pact/something_like_spec.rb +0 -21
  109. data/spec/lib/pact/term_spec.rb +0 -89
  110. data/spec/support/dsl_spec_support.rb +0 -7
@@ -1,37 +0,0 @@
1
- require 'pact/reification'
2
-
3
- # Represents the Interaction in the form required by the MockService
4
- # The json generated will be posted to the MockService to register the expectation
5
- module Pact
6
- module Consumer
7
- class MockServiceInteractionExpectation
8
-
9
-
10
- def initialize interaction
11
- @interaction = interaction
12
- end
13
-
14
- def to_hash
15
- hash = {:description => interaction.description}
16
- hash[:provider_state] = interaction.provider_state if interaction.provider_state
17
- options = interaction.request.options.empty? ? {} : { options: interaction.request.options}
18
- hash[:request] = interaction.request.as_json.merge(options)
19
- hash[:response] = interaction.response
20
- hash
21
- end
22
-
23
- def as_json options = {}
24
- to_hash
25
- end
26
-
27
- def to_json opts = {}
28
- as_json.to_json(opts)
29
- end
30
-
31
- private
32
-
33
- attr_reader :interaction
34
-
35
- end
36
- end
37
- end
@@ -1,27 +0,0 @@
1
- require 'pact/shared/request'
2
- require 'pact/shared/key_not_found'
3
-
4
- module Pact
5
- module Consumer
6
- module Request
7
- class Actual < Pact::Request::Base
8
-
9
- def self.from_hash(hash)
10
- sym_hash = symbolize_keys hash
11
- method = sym_hash.fetch(:method)
12
- path = sym_hash.fetch(:path)
13
- query = sym_hash.fetch(:query)
14
- headers = sym_hash.fetch(:headers)
15
- body = sym_hash.fetch(:body, nil)
16
- new(method, path, headers, body, query)
17
- end
18
-
19
- protected
20
-
21
- def self.key_not_found
22
- nil
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,90 +0,0 @@
1
- require 'uri'
2
- require 'net/http'
3
- require 'rack'
4
-
5
- # Copied shamelessly from Capybara
6
- module Pact
7
- class Server
8
- class Middleware
9
- attr_accessor :error
10
-
11
- def initialize(app)
12
- @app = app
13
- end
14
-
15
- def call(env)
16
- if env["PATH_INFO"] == "/__identify__"
17
- [200, {}, [@app.object_id.to_s]]
18
- else
19
- begin
20
- @app.call(env)
21
- rescue StandardError => e
22
- @error = e unless @error
23
- raise e
24
- end
25
- end
26
- end
27
- end
28
-
29
- class << self
30
- def ports
31
- @ports ||= {}
32
- end
33
- end
34
-
35
- attr_reader :app, :port
36
-
37
- def initialize(app, port)
38
- @app = app
39
- @middleware = Middleware.new(@app)
40
- @server_thread = nil # supress warnings
41
- @port = port
42
- end
43
-
44
- def reset_error!
45
- @middleware.error = nil
46
- end
47
-
48
- def error
49
- @middleware.error
50
- end
51
-
52
- def host
53
- "localhost"
54
- end
55
-
56
- def responsive?
57
- return false if @server_thread && @server_thread.join(0)
58
-
59
- res = Net::HTTP.start(host, @port) { |http| http.get('/__identify__') }
60
-
61
- if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
62
- return res.body == @app.object_id.to_s
63
- end
64
- rescue SystemCallError
65
- return false
66
- end
67
-
68
- def run_default_server(app, port)
69
- require 'rack/handler/webrick'
70
- Rack::Handler::WEBrick.run(app, :Port => port, :AccessLog => [], :Logger => WEBrick::Log::new(nil, 0))
71
- end
72
-
73
- def boot
74
- unless responsive?
75
- Pact::Server.ports[@app.object_id] = @port
76
-
77
- @server_thread = Thread.new do
78
- run_default_server(@middleware, @port)
79
- end
80
-
81
- Timeout.timeout(60) { @server_thread.join(0.1) until responsive? }
82
- end
83
- rescue Timeout::Error
84
- raise "Rack application timed out during boot"
85
- else
86
- self
87
- end
88
-
89
- end
90
- end
@@ -1 +0,0 @@
1
- require 'pact/consumer_contract/consumer_contract'
@@ -1,115 +0,0 @@
1
- require 'pact/logging'
2
- require 'pact/something_like'
3
- require 'pact/symbolize_keys'
4
- require 'pact/term'
5
- require 'pact/version'
6
- require 'pact/shared/active_support_support'
7
- require 'date'
8
- require 'json/add/regexp'
9
- require 'open-uri'
10
- require_relative 'service_consumer'
11
- require_relative 'service_provider'
12
- require_relative 'interaction'
13
- require_relative 'request'
14
- require_relative 'pact_file'
15
- require_relative 'file_name'
16
-
17
-
18
-
19
- module Pact
20
-
21
- class ConsumerContract
22
-
23
- include SymbolizeKeys
24
- include Logging
25
- include FileName
26
- include ActiveSupportSupport
27
-
28
- attr_accessor :interactions
29
- attr_accessor :consumer
30
- attr_accessor :provider
31
-
32
- def initialize(attributes = {})
33
- @interactions = attributes[:interactions] || []
34
- @consumer = attributes[:consumer]
35
- @provider = attributes[:provider]
36
- end
37
-
38
- def to_hash
39
- {
40
- provider: @provider.as_json,
41
- consumer: @consumer.as_json,
42
- interactions: @interactions.collect(&:as_json),
43
- metadata: {
44
- pactSpecificationVersion: "1.0.0"
45
- }
46
- }
47
- end
48
-
49
- def as_json(options = {})
50
- fix_all_the_things to_hash
51
- end
52
-
53
- def to_json(options = {})
54
- as_json.to_json(options)
55
- end
56
-
57
- def self.from_hash(hash)
58
- hash = symbolize_keys(hash)
59
- new({
60
- :interactions => hash[:interactions].collect { |hash| Interaction.from_hash(hash)},
61
- :consumer => ServiceConsumer.from_hash(hash[:consumer]),
62
- :provider => ServiceProvider.from_hash(hash[:provider])
63
- })
64
- end
65
-
66
- def self.from_json string
67
- deserialised_object = JSON.load(maintain_backwards_compatiblity_with_producer_keys(string))
68
- from_hash(deserialised_object)
69
- end
70
-
71
- def self.from_uri uri, options = {}
72
- from_json(Pact::PactFile.read(uri, options))
73
- end
74
-
75
- def self.maintain_backwards_compatiblity_with_producer_keys string
76
- string.gsub('"producer":', '"provider":').gsub('"producer_state":', '"provider_state":')
77
- end
78
-
79
- def find_interaction criteria
80
- interactions = find_interactions criteria
81
- if interactions.size == 0
82
- raise "Could not find interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}."
83
- elsif interactions.size > 1
84
- raise "Found more than 1 interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}."
85
- end
86
- interactions.first
87
- end
88
-
89
- def find_interactions criteria
90
- interactions.select{ | interaction| interaction.matches_criteria?(criteria)}
91
- end
92
-
93
- def each
94
- interactions.each do | interaction |
95
- yield interaction
96
- end
97
- end
98
-
99
- def pact_file_name
100
- file_name consumer.name, provider.name
101
- end
102
-
103
- def pactfile_path
104
- raise 'You must first specify a consumer and service name' unless (consumer && consumer.name && provider && provider.name)
105
- @pactfile_path ||= file_path consumer.name, provider.name
106
- end
107
-
108
- def update_pactfile
109
- logger.debug "Updating pact file for #{provider.name} at #{pactfile_path}"
110
- File.open(pactfile_path, 'w') do |f|
111
- f.write fix_json_formatting(JSON.pretty_generate(self))
112
- end
113
- end
114
- end
115
- end
@@ -1,84 +0,0 @@
1
- require 'pact/consumer_contract'
2
- require 'pact/consumer/interactions_filter'
3
- require 'pact/consumer_contract/file_name'
4
-
5
- module Pact
6
-
7
- class ConsumerContractWriter
8
-
9
- attr_reader :consumer_contract_details, :pactfile_write_mode, :interactions, :logger
10
-
11
- def initialize consumer_contract_details, logger
12
- @logger = logger
13
- @consumer_contract_details = consumer_contract_details
14
- @pactfile_write_mode = consumer_contract_details.fetch(:pactfile_write_mode, :overwrite).to_sym
15
- @interactions = consumer_contract_details.fetch(:interactions)
16
- end
17
-
18
- def consumer_contract
19
- @consumer_contract ||= Pact::ConsumerContract.new(
20
- consumer: ServiceConsumer.new(name: consumer_contract_details[:consumer][:name]),
21
- provider: ServiceProvider.new(name: consumer_contract_details[:provider][:name]),
22
- interactions: interactions_for_new_consumer_contract)
23
- end
24
-
25
- def write
26
- consumer_contract.update_pactfile
27
- consumer_contract.to_json
28
- end
29
-
30
- def interactions_for_new_consumer_contract
31
- if pactfile_write_mode == :update
32
- merged_interactions = existing_interactions
33
- filter = Consumer::UpdatableInteractionsFilter.new(merged_interactions)
34
- interactions.each {|i| filter << i }
35
- merged_interactions
36
- else
37
- interactions
38
- end
39
- end
40
-
41
- def existing_interactions
42
- interactions = []
43
- if pactfile_exists?
44
- begin
45
- interactions = existing_consumer_contract.interactions
46
- info_and_puts "*****************************************************************************"
47
- info_and_puts "Updating existing file .#{pactfile_path.gsub(Dir.pwd, '')} as config.pactfile_write_mode is :update"
48
- info_and_puts "Only interactions defined in this test run will be updated."
49
- info_and_puts "As interactions are identified by description and provider state, pleased note that if either of these have changed, the old interactions won't be removed from the pact file until the specs are next run with :pactfile_write_mode => :overwrite."
50
- info_and_puts "*****************************************************************************"
51
- rescue StandardError => e
52
- warn_and_stderr "Could not load existing consumer contract from #{pactfile_path} due to #{e}"
53
- logger.error e
54
- logger.error e.backtrace
55
- warn_and_stderr "Creating a new file."
56
- end
57
- end
58
- interactions
59
- end
60
-
61
- def pactfile_exists?
62
- File.exist?(pactfile_path)
63
- end
64
-
65
- def pactfile_path
66
- Pact::FileName.file_path consumer_contract_details[:consumer][:name], consumer_contract_details[:provider][:name]
67
- end
68
-
69
- def existing_consumer_contract
70
- Pact::ConsumerContract.from_uri(pactfile_path)
71
- end
72
-
73
- def warn_and_stderr msg
74
- Pact.configuration.error_stream.puts msg
75
- logger.warn msg
76
- end
77
-
78
- def info_and_puts msg
79
- $stdout.puts msg
80
- logger.info msg
81
- end
82
- end
83
-
84
- end
@@ -1,19 +0,0 @@
1
- module Pact
2
-
3
- module FileName
4
-
5
- extend self
6
-
7
- def file_name consumer_name, provider_name
8
- "#{filenamify(consumer_name)}-#{filenamify(provider_name)}.json"
9
- end
10
-
11
- def file_path consumer_name, provider_name, pact_dir = Pact.configuration.pact_dir
12
- File.join(pact_dir, file_name(consumer_name, provider_name))
13
- end
14
-
15
- def filenamify name
16
- name.downcase.gsub(/\s/, '_')
17
- end
18
- end
19
- end
@@ -1,51 +0,0 @@
1
- module Pact
2
-
3
- class DuplicateHeaderError < StandardError; end
4
- class InvalidHeaderNameTypeError < StandardError; end
5
-
6
- class Headers < Hash
7
-
8
- def initialize hash = {}
9
- hash.each_pair do | key, value |
10
- check_for_invalid key
11
- self[find_matching_key(key)] = value
12
- end
13
- self.freeze
14
- end
15
-
16
- def [] key
17
- super(find_matching_key(key))
18
- end
19
-
20
- def fetch *args, &block
21
- args[0] = find_matching_key(args[0]) if args.first
22
- super(*args, &block)
23
- end
24
-
25
- def key? key
26
- super(find_matching_key(key))
27
- end
28
-
29
- alias_method :has_key?, :key?
30
- alias_method :include?, :key?
31
-
32
- private
33
-
34
- def find_matching_key key
35
- key = key.to_s
36
- match = keys.find { |k| k.downcase == key.downcase }
37
- match.nil? ? key : match
38
- end
39
-
40
- def check_for_invalid key
41
- unless (String === key || Symbol === key)
42
- raise InvalidHeaderNameTypeError.new "Header name (#{key}) must be a String or a Symbol."
43
- end
44
- if key? key
45
- raise DuplicateHeaderError.new "Duplicate header found (#{find_matching_key(key)} and #{key}. Please use a comma separated single value when multiple headers with the same name are required."
46
- end
47
- end
48
-
49
- end
50
-
51
- end
@@ -1,67 +0,0 @@
1
- require 'pact/consumer_contract/request'
2
- require 'pact/symbolize_keys'
3
- require 'pact/shared/active_support_support'
4
-
5
- module Pact
6
- class Interaction
7
- include ActiveSupportSupport
8
- include SymbolizeKeys
9
-
10
- attr_accessor :description, :request, :response, :provider_state
11
-
12
- def initialize attributes = {}
13
- @description = attributes[:description]
14
- @request = attributes[:request]
15
- @response = attributes[:response]
16
- @provider_state = attributes[:provider_state] || attributes[:providerState]
17
- end
18
-
19
- def self.from_hash hash
20
- request = Pact::Request::Expected.from_hash(hash['request'])
21
- new(symbolize_keys(hash).merge({request: request}))
22
- end
23
-
24
- def to_hash
25
- hash = { :description => @description }
26
- hash[:provider_state] = @provider_state if @provider_state #Easier to read when provider state at top
27
- hash.merge(:request => @request.as_json, :response => @response)
28
- end
29
-
30
- def as_json options = {}
31
- fix_all_the_things to_hash
32
- end
33
-
34
- def to_json(options = {})
35
- as_json.to_json(options)
36
- end
37
-
38
- def matches_criteria? criteria
39
- criteria.each do | key, value |
40
- unless match_criterion self.send(key.to_s), value
41
- return false
42
- end
43
- end
44
- true
45
- end
46
-
47
- def match_criterion target, criterion
48
- target == criterion || (criterion.is_a?(Regexp) && criterion.match(target))
49
- end
50
-
51
- def == other
52
- other.is_a?(Interaction) && as_json == other.as_json
53
- end
54
-
55
- def eq? other
56
- self == other
57
- end
58
-
59
- def description_with_provider_state_quoted
60
- provider_state ? "\"#{description}\" given \"#{provider_state}\"" : "\"#{description}\""
61
- end
62
-
63
- def to_s
64
- JSON.pretty_generate(self)
65
- end
66
- end
67
- end