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,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