pact 1.3.3 → 1.4.0.rc2

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.
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,158 +0,0 @@
1
- require 'thwait'
2
-
3
- require 'net/http'
4
- require 'uri'
5
- require 'find_a_port'
6
- require 'pact/logging'
7
- require 'pact/consumer/server'
8
- require 'singleton'
9
-
10
- module Pact
11
- module Consumer
12
- class AppManager
13
-
14
- include Pact::Logging
15
-
16
- include Singleton
17
-
18
- def initialize
19
- @apps_spawned = false
20
- @app_registrations = []
21
- end
22
-
23
- def register_mock_service_for name, url
24
- uri = URI(url)
25
- raise "Currently only http is supported" unless uri.scheme == 'http'
26
- raise "Currently only services on localhost are supported" unless uri.host == 'localhost'
27
-
28
- register(MockService.new(log_file: create_log_file(name), name: name), uri.port)
29
- end
30
-
31
- def register(app, port = FindAPort.available_port)
32
- existing = existing_app_on_port port
33
- raise "Port #{port} is already being used by #{existing}" if existing and not existing == app
34
- app_registration = register_app app, port
35
- app_registration.spawn if @apps_spawned
36
- port
37
- end
38
-
39
- def existing_app_on_port port
40
- app_registration = @app_registrations.find { |app_registration| app_registration.port == port }
41
- app_registration ? app_registration.app : nil
42
- end
43
-
44
- def app_registered_on?(port)
45
- app_registrations.any? { |app_registration| app_registration.port == port }
46
- end
47
-
48
- def ports_of_mock_services
49
- app_registrations.find_all(&:is_a_mock_service?).collect(&:port)
50
- end
51
-
52
- def kill_all
53
- app_registrations.find_all(&:spawned?).collect(&:kill)
54
- @apps_spawned = false
55
- end
56
-
57
- def clear_all
58
- kill_all
59
- @app_registrations = []
60
- end
61
-
62
- def spawn_all
63
- app_registrations.find_all(&:not_spawned?).collect(&:spawn)
64
- @apps_spawned = true
65
- end
66
-
67
- private
68
-
69
- def create_log_file service_name
70
- FileUtils::mkdir_p Pact.configuration.log_dir
71
- log = File.open(log_file_path(service_name), 'w')
72
- log.sync = true
73
- log
74
- end
75
-
76
- def log_file_path service_name
77
- File.join(Pact.configuration.log_dir, "#{log_file_name(service_name)}.log")
78
- end
79
-
80
- def log_file_name service_name
81
- lower_case_name = service_name.downcase.gsub(/\s+/, '_')
82
- if lower_case_name.include?('_service')
83
- lower_case_name.gsub('_service', '_mock_service')
84
- else
85
- lower_case_name + '_mock_service'
86
- end
87
- end
88
-
89
- def app_registrations
90
- @app_registrations
91
- end
92
-
93
- def register_app app, port
94
- app_registration = AppRegistration.new :app => app, :port => port
95
- app_registrations << app_registration
96
- app_registration
97
- end
98
- end
99
-
100
- class AppRegistration
101
- include Pact::Logging
102
- attr_accessor :port
103
- attr_accessor :app
104
- attr_accessor :pid
105
-
106
- def initialize opts
107
- @max_wait = 10
108
- @port = opts[:port]
109
- @pid = opts[:pid]
110
- @app = opts[:app]
111
- end
112
-
113
- def kill
114
- # TODO: need to work out how to kill
115
- # logger.info "Killing #{self}"
116
- # Process.kill(9, pid)
117
- # Process.wait(pid)
118
- # self.pid = nil
119
- self.pid = nil
120
- end
121
-
122
- def not_spawned?
123
- !spawned?
124
- end
125
-
126
- def spawned?
127
- self.pid != nil
128
- end
129
-
130
- def is_a_mock_service?
131
- app.is_a? MockService
132
- end
133
-
134
- def to_s
135
- "#{app} on port #{port}" + (@pid ? " with pid #{pid}" : "")
136
- end
137
-
138
- def spawn
139
- logger.info "Starting app #{self}..."
140
- Pact::Server.new(app, port).boot
141
- self.pid = 'unknown'
142
- logger.info "Started with pid #{pid}"
143
- end
144
-
145
- def wait_until
146
- waited = 0
147
- wait_time = 0.1
148
- while waited < @max_wait do
149
- break if yield
150
- sleep wait_time
151
- waited += wait_time
152
- raise "Waited longer than #{@max_wait} seconds" if waited >= @max_wait
153
- end
154
- end
155
-
156
- end
157
- end
158
- end
@@ -1,48 +0,0 @@
1
- #
2
- # When running in pactfile_write_mode :overwrite, all interactions are cleared from the
3
- # pact file, and all new interactions should be distinct (unique description and provider state).
4
- # When running in pactfile_write_mode :update, an interaction with the same description
5
- # and provider state as an existing one will just overwrite that one interaction.
6
- #
7
-
8
- module Pact
9
- module Consumer
10
-
11
- #TODO: think of a better word than filter
12
- class InteractionsFilter
13
- def initialize interactions = []
14
- @interactions = interactions
15
- end
16
-
17
- def index_of interaction
18
- @interactions.find_index{ |i| i.matches_criteria?(description: interaction.description, provider_state: interaction.provider_state)}
19
- end
20
- end
21
-
22
- class UpdatableInteractionsFilter < InteractionsFilter
23
-
24
- def << interaction
25
- if (ndx = index_of(interaction))
26
- @interactions[ndx] = interaction
27
- else
28
- @interactions << interaction
29
- end
30
- end
31
-
32
- end
33
-
34
- class DistinctInteractionsFilter < InteractionsFilter
35
-
36
- def << interaction
37
- if (ndx = index_of(interaction))
38
- if @interactions[ndx] != interaction
39
- raise "Interaction with same description (#{interaction.description}) and provider state (#{interaction.provider_state}) already exists"
40
- end
41
- else
42
- @interactions << interaction
43
- end
44
- end
45
- end
46
-
47
- end
48
- end
@@ -1,2 +0,0 @@
1
- require 'pact/consumer/mock_service/app'
2
-
@@ -1,82 +0,0 @@
1
- require 'uri'
2
- require 'json'
3
- require 'logger'
4
- require 'awesome_print'
5
- require 'awesome_print/core_ext/logger' #For some reason we get an error indicating that the method 'ap' is private unless we load this specifically
6
- require 'pact/consumer/request'
7
- require 'pact/consumer/mock_service/interaction_list'
8
- require 'pact/consumer/mock_service/interaction_delete'
9
- require 'pact/consumer/mock_service/interaction_post'
10
- require 'pact/consumer/mock_service/interaction_replay'
11
- require 'pact/consumer/mock_service/missing_interactions_get'
12
- require 'pact/consumer/mock_service/verification_get'
13
- require 'pact/consumer/mock_service/log_get'
14
- require 'pact/consumer/mock_service/pact_post'
15
-
16
- AwesomePrint.defaults = {
17
- indent: -2,
18
- plain: true,
19
- index: false
20
- }
21
-
22
- module Pact
23
- module Consumer
24
-
25
- class MockService
26
-
27
- def initialize options = {}
28
- log_description = configure_logger options
29
- interaction_list = InteractionList.new
30
-
31
- @name = options.fetch(:name, "MockService")
32
- interactions = []
33
- @handlers = [
34
- MissingInteractionsGet.new(@name, @logger, interaction_list),
35
- VerificationGet.new(@name, @logger, interaction_list, log_description),
36
- InteractionPost.new(@name, @logger, interaction_list),
37
- InteractionDelete.new(@name, @logger, interaction_list),
38
- LogGet.new(@name, @logger),
39
- PactPost.new(@name, @logger, interactions),
40
- InteractionReplay.new(@name, @logger, interaction_list, interactions)
41
- ]
42
- end
43
-
44
- def configure_logger options
45
- options = {log_file: $stdout}.merge options
46
- log_stream = options[:log_file]
47
- @logger = Logger.new log_stream
48
- @logger.level = Pact.configuration.logger.level
49
-
50
- if log_stream.is_a? File
51
- File.absolute_path(log_stream).gsub(Dir.pwd + "/", '')
52
- else
53
- "standard out/err"
54
- end
55
- end
56
-
57
- def to_s
58
- "#{@name} #{super.to_s}"
59
- end
60
-
61
- def call env
62
- response = []
63
- begin
64
- relevant_handler = @handlers.detect { |handler| handler.match? env }
65
- response = relevant_handler.respond env
66
- rescue StandardError => e
67
- @logger.error 'Error ocurred in mock service:'
68
- @logger.ap e, :error
69
- @logger.ap e.backtrace
70
- response = [500, {'Content-Type' => 'application/json'}, [{message: e.message, backtrace: e.backtrace}.to_json]]
71
- rescue Exception => e
72
- @logger.error 'Exception ocurred in mock service:'
73
- @logger.ap e, :error
74
- @logger.ap e.backtrace
75
- raise e
76
- end
77
- response
78
- end
79
-
80
- end
81
- end
82
- end
@@ -1,33 +0,0 @@
1
- require 'pact/consumer/mock_service/rack_request_helper'
2
- require 'pact/consumer/mock_service/mock_service_administration_endpoint'
3
-
4
- module Pact
5
- module Consumer
6
-
7
- class InteractionDelete < MockServiceAdministrationEndpoint
8
-
9
- include RackRequestHelper
10
-
11
- attr_accessor :interaction_list
12
-
13
- def initialize name, logger, interaction_list
14
- super name, logger
15
- @interaction_list = interaction_list
16
- end
17
-
18
- def request_path
19
- '/interactions'
20
- end
21
-
22
- def request_method
23
- 'DELETE'
24
- end
25
-
26
- def respond env
27
- interaction_list.clear
28
- logger.info "Cleared interactions before example \"#{params_hash(env)['example_description']}\""
29
- [200, {}, ['Deleted interactions']]
30
- end
31
- end
32
- end
33
- end
@@ -1,76 +0,0 @@
1
- module Pact
2
- module Consumer
3
- class InteractionList
4
-
5
- attr_reader :interactions
6
- attr_reader :unexpected_requests
7
- attr_reader :interaction_mismatches
8
-
9
- def initialize
10
- clear
11
- end
12
-
13
- # For testing, sigh
14
- def clear
15
- @interactions = []
16
- @matched_interactions = []
17
- @interaction_mismatches = []
18
- @unexpected_requests = []
19
- end
20
-
21
- def add interactions
22
- @interactions << interactions
23
- end
24
-
25
- def register_matched interaction
26
- @matched_interactions << interaction
27
- end
28
-
29
- def register_unexpected_request request
30
- @unexpected_requests << request
31
- end
32
-
33
- def register_interaction_mismatch interaction_mismatch
34
- @interaction_mismatches << interaction_mismatch
35
- end
36
-
37
- def all_matched?
38
- interaction_diffs.empty?
39
- end
40
-
41
- def missing_interactions
42
- @interactions - @matched_interactions - @interaction_mismatches.collect(&:candidate_interactions).flatten
43
- end
44
-
45
- def missing_interactions_summaries
46
- missing_interactions.collect(&:request).collect(&:method_and_path)
47
- end
48
-
49
- def interaction_mismatches_summaries
50
- interaction_mismatches.collect(&:short_summary)
51
- end
52
-
53
- def unexpected_requests_summaries
54
- unexpected_requests.collect(&:method_and_path)
55
- end
56
-
57
- def interaction_diffs
58
- {
59
- :missing_interactions => missing_interactions_summaries,
60
- :interaction_mismatches => interaction_mismatches_summaries,
61
- :unexpected_requests => unexpected_requests_summaries
62
- }.inject({}) do | hash, pair |
63
- hash[pair.first] = pair.last if pair.last.any?
64
- hash
65
- end
66
- end
67
-
68
- def find_candidate_interactions actual_request
69
- interactions.select do | interaction |
70
- interaction.request.matches_route? actual_request
71
- end
72
- end
73
-
74
- end
75
- end
76
- end
@@ -1,73 +0,0 @@
1
- module Pact
2
- module Consumer
3
-
4
- # Presents the differences between an actual request, and a list of
5
- # expected interactions where the methods and paths match the actual request.
6
- # This is used to display a helpful message to the user when a request
7
- # comes in that doesn't match any of the expected interactions.
8
- class InteractionMismatch
9
-
10
- attr_accessor :candidate_interactions, :actual_request
11
-
12
- # Assumes the method and path matches...
13
-
14
- def initialize candidate_interactions, actual_request
15
- @candidate_interactions = candidate_interactions
16
- @actual_request = actual_request
17
- @candiate_diffs = candidate_interactions.collect{ | candidate_interaction| CandidateDiff.new(candidate_interaction, actual_request)}
18
- end
19
-
20
- def to_hash
21
- candiate_diffs.collect(&:to_hash)
22
- end
23
-
24
- def to_s
25
- candiate_diffs.collect(&:to_s).join("\n")
26
- end
27
-
28
- def short_summary
29
- mismatched_attributes = candiate_diffs.collect(&:mismatched_attributes).flatten.uniq.join(", ").reverse.sub(",", "dna ").reverse #OMG what a hack!
30
- actual_request.method_and_path + " (request #{mismatched_attributes} did not match)"
31
- end
32
-
33
- private
34
-
35
- attr_accessor :candiate_diffs
36
-
37
- class CandidateDiff
38
-
39
- attr_accessor :candidate_interaction, :actual_request
40
-
41
- def initialize candidate_interaction, actual_request
42
- @candidate_interaction = candidate_interaction
43
- @actual_request = actual_request
44
- end
45
-
46
- def mismatched_attributes
47
- diff.keys
48
- end
49
-
50
- def to_hash
51
- summary = {:description => candidate_interaction.description}
52
- summary[:provider_state] = candidate_interaction.provider_state if candidate_interaction.provider_state
53
- summary.merge(diff)
54
- end
55
-
56
- def to_s
57
- [
58
- "Diff with interaction: #{candidate_interaction.description_with_provider_state_quoted}",
59
- diff_formatter.call(diff, {colour: false})
60
- ].join("\n")
61
- end
62
-
63
- def diff_formatter
64
- Pact.configuration.diff_formatter_for_content_type(candidate_interaction.request.content_type)
65
- end
66
-
67
- def diff
68
- @diff ||= candidate_interaction.request.difference(actual_request)
69
- end
70
- end
71
- end
72
- end
73
- end