pact 1.3.3 → 1.4.0
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 +37 -1
- data/lib/pact/cli.rb +0 -26
- data/lib/pact/consumer/configuration/configuration_extensions.rb +51 -0
- data/lib/pact/consumer/configuration/mock_service.rb +2 -1
- data/lib/pact/consumer/configuration.rb +0 -1
- data/lib/pact/consumer/consumer_contract_builder.rb +3 -3
- data/lib/pact/consumer/interaction_builder.rb +3 -5
- data/lib/pact/doc/interaction_view_model.rb +9 -6
- data/lib/pact/doc/sort_interactions.rb +1 -1
- data/lib/pact/provider/configuration/configuration_extension.rb +0 -9
- data/lib/pact/provider/rspec.rb +11 -9
- data/lib/pact/version.rb +1 -1
- data/lib/pact.rb +6 -5
- data/pact.gemspec +6 -2
- metadata +57 -304
- data/.gitignore +0 -29
- data/.rspec +0 -2
- data/.ruby-version +0 -1
- data/.travis.yml +0 -8
- data/Gemfile +0 -4
- data/Gemfile.lock +0 -86
- data/README.md +0 -313
- data/Rakefile +0 -9
- data/config.ru +0 -3
- data/documentation/README.md +0 -13
- data/documentation/configuration.md +0 -166
- data/documentation/diff_formatter_embedded.png +0 -0
- data/documentation/diff_formatter_list.png +0 -0
- data/documentation/diff_formatter_unix.png +0 -0
- data/example/animal-service/Gemfile +0 -14
- data/example/animal-service/Gemfile.lock +0 -69
- data/example/animal-service/Rakefile +0 -5
- data/example/animal-service/config.ru +0 -3
- data/example/animal-service/db/animal_db.sqlite3 +0 -0
- data/example/animal-service/lib/animal_service/animal_repository.rb +0 -12
- data/example/animal-service/lib/animal_service/api.rb +0 -28
- data/example/animal-service/lib/animal_service/db.rb +0 -5
- data/example/animal-service/spec/service_consumers/pact_helper.rb +0 -11
- data/example/animal-service/spec/service_consumers/provider_states_for_zoo_app.rb +0 -26
- data/example/zoo-app/Gemfile +0 -12
- data/example/zoo-app/Gemfile.lock +0 -63
- data/example/zoo-app/Rakefile +0 -5
- data/example/zoo-app/doc/pacts/markdown/README.md +0 -3
- data/example/zoo-app/doc/pacts/markdown/Zoo App - Animal Service.md +0 -75
- data/example/zoo-app/lib/zoo_app/animal_service_client.rb +0 -40
- data/example/zoo-app/lib/zoo_app/models/alligator.rb +0 -15
- data/example/zoo-app/spec/pacts/zoo_app-animal_service.json +0 -67
- data/example/zoo-app/spec/service_providers/animal_service_client_spec.rb +0 -71
- data/example/zoo-app/spec/service_providers/pact_helper.rb +0 -15
- data/example/zoo-app/spec/spec_helper.rb +0 -6
- data/lib/pact/configuration.rb +0 -195
- data/lib/pact/consumer/app_manager.rb +0 -158
- data/lib/pact/consumer/interactions_filter.rb +0 -48
- data/lib/pact/consumer/mock_service/app.rb +0 -82
- data/lib/pact/consumer/mock_service/interaction_delete.rb +0 -33
- data/lib/pact/consumer/mock_service/interaction_list.rb +0 -76
- data/lib/pact/consumer/mock_service/interaction_mismatch.rb +0 -73
- data/lib/pact/consumer/mock_service/interaction_post.rb +0 -31
- data/lib/pact/consumer/mock_service/interaction_replay.rb +0 -139
- data/lib/pact/consumer/mock_service/log_get.rb +0 -28
- data/lib/pact/consumer/mock_service/missing_interactions_get.rb +0 -30
- data/lib/pact/consumer/mock_service/mock_service_administration_endpoint.rb +0 -31
- data/lib/pact/consumer/mock_service/pact_post.rb +0 -33
- data/lib/pact/consumer/mock_service/rack_request_helper.rb +0 -51
- data/lib/pact/consumer/mock_service/verification_get.rb +0 -68
- data/lib/pact/consumer/mock_service.rb +0 -2
- data/lib/pact/consumer/mock_service_client.rb +0 -65
- data/lib/pact/consumer/mock_service_interaction_expectation.rb +0 -37
- data/lib/pact/consumer/request.rb +0 -27
- data/lib/pact/consumer/server.rb +0 -90
- data/lib/pact/consumer_contract/consumer_contract.rb +0 -115
- data/lib/pact/consumer_contract/consumer_contract_writer.rb +0 -84
- data/lib/pact/consumer_contract/file_name.rb +0 -19
- data/lib/pact/consumer_contract/headers.rb +0 -51
- data/lib/pact/consumer_contract/interaction.rb +0 -67
- data/lib/pact/consumer_contract/pact_file.rb +0 -24
- data/lib/pact/consumer_contract/request.rb +0 -73
- data/lib/pact/consumer_contract/service_consumer.rb +0 -28
- data/lib/pact/consumer_contract/service_provider.rb +0 -28
- data/lib/pact/consumer_contract.rb +0 -1
- data/lib/pact/logging.rb +0 -14
- data/lib/pact/matchers/actual_type.rb +0 -16
- data/lib/pact/matchers/base_difference.rb +0 -37
- data/lib/pact/matchers/differ.rb +0 -153
- data/lib/pact/matchers/difference.rb +0 -13
- data/lib/pact/matchers/difference_indicator.rb +0 -26
- data/lib/pact/matchers/embedded_diff_formatter.rb +0 -62
- data/lib/pact/matchers/expected_type.rb +0 -35
- data/lib/pact/matchers/index_not_found.rb +0 -15
- data/lib/pact/matchers/list_diff_formatter.rb +0 -101
- data/lib/pact/matchers/matchers.rb +0 -139
- data/lib/pact/matchers/no_diff_indicator.rb +0 -18
- data/lib/pact/matchers/regexp_difference.rb +0 -13
- data/lib/pact/matchers/type_difference.rb +0 -16
- data/lib/pact/matchers/unexpected_index.rb +0 -11
- data/lib/pact/matchers/unexpected_key.rb +0 -11
- data/lib/pact/matchers/unix_diff_formatter.rb +0 -114
- data/lib/pact/matchers.rb +0 -1
- data/lib/pact/reification.rb +0 -28
- data/lib/pact/rspec.rb +0 -53
- data/lib/pact/shared/active_support_support.rb +0 -51
- data/lib/pact/shared/dsl.rb +0 -76
- data/lib/pact/shared/jruby_support.rb +0 -18
- data/lib/pact/shared/json_differ.rb +0 -15
- data/lib/pact/shared/key_not_found.rb +0 -15
- data/lib/pact/shared/null_expectation.rb +0 -31
- data/lib/pact/shared/request.rb +0 -80
- data/lib/pact/shared/text_differ.rb +0 -14
- data/lib/pact/something_like.rb +0 -49
- data/lib/pact/symbolize_keys.rb +0 -12
- data/lib/pact/term.rb +0 -85
- data/scratchpad.rb +0 -52
- data/spec/features/consumption_spec.rb +0 -114
- data/spec/features/production_spec.rb +0 -155
- data/spec/features/provider_states/zebras.rb +0 -28
- data/spec/integration/consumer_spec.rb +0 -212
- data/spec/integration/pact/consumer_configuration_spec.rb +0 -66
- data/spec/integration/pact/provider_configuration_spec.rb +0 -25
- data/spec/lib/pact/cli_spec.rb +0 -47
- data/spec/lib/pact/configuration_spec.rb +0 -297
- data/spec/lib/pact/consumer/app_manager_spec.rb +0 -41
- data/spec/lib/pact/consumer/configuration_spec.rb +0 -57
- data/spec/lib/pact/consumer/consumer_contract_builder_spec.rb +0 -69
- data/spec/lib/pact/consumer/interaction_builder_spec.rb +0 -91
- data/spec/lib/pact/consumer/interactions_spec.rb +0 -64
- data/spec/lib/pact/consumer/mock_service/app_spec.rb +0 -52
- data/spec/lib/pact/consumer/mock_service/interaction_list_spec.rb +0 -78
- data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +0 -70
- data/spec/lib/pact/consumer/mock_service/interaction_replay_spec.rb +0 -12
- data/spec/lib/pact/consumer/mock_service/rack_request_helper_spec.rb +0 -88
- data/spec/lib/pact/consumer/mock_service/verification_get_spec.rb +0 -142
- data/spec/lib/pact/consumer/mock_service_client_spec.rb +0 -88
- data/spec/lib/pact/consumer/mock_service_interaction_expectation_spec.rb +0 -54
- data/spec/lib/pact/consumer/request_spec.rb +0 -24
- data/spec/lib/pact/consumer/service_consumer_spec.rb +0 -11
- data/spec/lib/pact/consumer_contract/active_support_support_spec.rb +0 -58
- data/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +0 -180
- data/spec/lib/pact/consumer_contract/consumer_contract_writer_spec.rb +0 -111
- data/spec/lib/pact/consumer_contract/headers_spec.rb +0 -107
- data/spec/lib/pact/consumer_contract/interaction_spec.rb +0 -107
- data/spec/lib/pact/consumer_contract/request_spec.rb +0 -329
- data/spec/lib/pact/doc/generator_spec.rb +0 -84
- data/spec/lib/pact/doc/interaction_view_model_spec.rb +0 -132
- data/spec/lib/pact/doc/markdown/consumer_contract_renderer_spec.rb +0 -29
- data/spec/lib/pact/doc/markdown/index_renderer_spec.rb +0 -29
- data/spec/lib/pact/matchers/differ_spec.rb +0 -214
- data/spec/lib/pact/matchers/difference_spec.rb +0 -22
- data/spec/lib/pact/matchers/embedded_diff_formatter_spec.rb +0 -90
- data/spec/lib/pact/matchers/index_not_found_spec.rb +0 -21
- data/spec/lib/pact/matchers/list_diff_formatter_spec.rb +0 -114
- data/spec/lib/pact/matchers/matchers_spec.rb +0 -500
- data/spec/lib/pact/matchers/regexp_difference_spec.rb +0 -20
- data/spec/lib/pact/matchers/type_difference_spec.rb +0 -34
- data/spec/lib/pact/matchers/unexpected_index_spec.rb +0 -20
- data/spec/lib/pact/matchers/unexpected_key_spec.rb +0 -20
- data/spec/lib/pact/matchers/unix_diff_formatter_spec.rb +0 -216
- data/spec/lib/pact/provider/configuration/configuration_extension_spec.rb +0 -30
- data/spec/lib/pact/provider/configuration/pact_verification_spec.rb +0 -43
- data/spec/lib/pact/provider/configuration/service_provider_config_spec.rb +0 -21
- data/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb +0 -108
- data/spec/lib/pact/provider/configuration_spec.rb +0 -50
- data/spec/lib/pact/provider/matchers/messages_spec.rb +0 -116
- data/spec/lib/pact/provider/pact_helper_locator_spec.rb +0 -54
- data/spec/lib/pact/provider/print_missing_provider_states_spec.rb +0 -19
- data/spec/lib/pact/provider/request_spec.rb +0 -78
- data/spec/lib/pact/provider/rspec/formatter_rspec_2_spec.rb +0 -68
- data/spec/lib/pact/provider/rspec/formatter_rspec_3_spec.rb +0 -72
- data/spec/lib/pact/provider/rspec_spec.rb +0 -55
- data/spec/lib/pact/provider/state/provider_state_manager_spec.rb +0 -89
- data/spec/lib/pact/provider/state/provider_state_proxy_spec.rb +0 -80
- data/spec/lib/pact/provider/state/provider_state_spec.rb +0 -213
- data/spec/lib/pact/provider/world_spec.rb +0 -41
- data/spec/lib/pact/reification_spec.rb +0 -67
- data/spec/lib/pact/shared/dsl_spec.rb +0 -86
- data/spec/lib/pact/shared/json_differ_spec.rb +0 -36
- data/spec/lib/pact/shared/key_not_found_spec.rb +0 -20
- data/spec/lib/pact/shared/request_spec.rb +0 -111
- data/spec/lib/pact/shared/text_differ_spec.rb +0 -54
- data/spec/lib/pact/something_like_spec.rb +0 -21
- data/spec/lib/pact/tasks/task_helper_spec.rb +0 -74
- data/spec/lib/pact/tasks/verification_task_spec.rb +0 -75
- data/spec/lib/pact/term_spec.rb +0 -89
- data/spec/pact_specification/compliance-1.0.0.rb +0 -47
- data/spec/spec_helper.rb +0 -22
- data/spec/standalone/consumer_fail_test.rb +0 -55
- data/spec/standalone/consumer_pass_test.rb +0 -51
- data/spec/support/a_consumer-a_producer.json +0 -32
- data/spec/support/a_consumer-a_provider.json +0 -32
- data/spec/support/active_support_if_configured.rb +0 -6
- data/spec/support/app_for_config_ru.rb +0 -4
- data/spec/support/consumer_contract_template.json +0 -24
- data/spec/support/dsl_spec_support.rb +0 -7
- data/spec/support/factories.rb +0 -82
- data/spec/support/generated_index.md +0 -4
- data/spec/support/generated_markdown.md +0 -55
- data/spec/support/interaction_view_model.json +0 -63
- data/spec/support/interaction_view_model_with_terms.json +0 -50
- data/spec/support/markdown_pact.json +0 -48
- data/spec/support/missing_provider_states_output.txt +0 -25
- data/spec/support/options.json +0 -21
- data/spec/support/options_app.rb +0 -15
- data/spec/support/pact_helper.rb +0 -57
- data/spec/support/shared_examples_for_request.rb +0 -94
- data/spec/support/spec_support.rb +0 -20
- data/spec/support/stubbing.json +0 -22
- data/spec/support/stubbing_using_allow.rb +0 -29
- data/spec/support/term.json +0 -48
- data/spec/support/test_app_fail.json +0 -61
- data/spec/support/test_app_pass.json +0 -38
- data/spec/support/test_app_with_right_content_type_differ.json +0 -23
- data/tasks/pact-test.rake +0 -109
- data/tasks/spec.rake +0 -8
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
require 'pact/consumer/mock_service/rack_request_helper'
|
|
2
|
-
module Pact
|
|
3
|
-
module Consumer
|
|
4
|
-
class MockServiceAdministrationEndpoint
|
|
5
|
-
|
|
6
|
-
attr_accessor :logger, :name
|
|
7
|
-
|
|
8
|
-
def initialize name, logger
|
|
9
|
-
@name = name
|
|
10
|
-
@logger = logger
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
include RackRequestHelper
|
|
14
|
-
|
|
15
|
-
def match? env
|
|
16
|
-
headers_from(env)['X-Pact-Mock-Service'] &&
|
|
17
|
-
env['REQUEST_PATH'] == request_path &&
|
|
18
|
-
env['REQUEST_METHOD'] == request_method
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def request_path
|
|
22
|
-
raise NotImplementedError
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def request_method
|
|
26
|
-
raise NotImplementedError
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
require 'pact/consumer/mock_service/mock_service_administration_endpoint'
|
|
2
|
-
require 'pact/consumer_contract/consumer_contract_writer'
|
|
3
|
-
|
|
4
|
-
module Pact
|
|
5
|
-
module Consumer
|
|
6
|
-
class PactPost < MockServiceAdministrationEndpoint
|
|
7
|
-
|
|
8
|
-
attr_accessor :consumer_contract, :interactions
|
|
9
|
-
|
|
10
|
-
def initialize name, logger, interactions
|
|
11
|
-
super name, logger
|
|
12
|
-
@interactions = interactions
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def request_path
|
|
16
|
-
'/pact'
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def request_method
|
|
20
|
-
'POST'
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def respond env
|
|
24
|
-
consumer_contract_details = JSON.parse(env['rack.input'].string, symbolize_names: true)
|
|
25
|
-
logger.info "Writing pact with details #{consumer_contract_details}"
|
|
26
|
-
consumer_contract_writer = ConsumerContractWriter.new(consumer_contract_details.merge(interactions: interactions), logger)
|
|
27
|
-
json = consumer_contract_writer.write
|
|
28
|
-
|
|
29
|
-
[200, {'Content-Type' =>'application/json'}, [json]]
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
module Pact
|
|
2
|
-
module Consumer
|
|
3
|
-
|
|
4
|
-
module RackRequestHelper
|
|
5
|
-
REQUEST_KEYS = {
|
|
6
|
-
'REQUEST_METHOD' => :method,
|
|
7
|
-
'PATH_INFO' => :path,
|
|
8
|
-
'QUERY_STRING' => :query,
|
|
9
|
-
'rack.input' => :body
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
def params_hash env
|
|
13
|
-
env["QUERY_STRING"].split("&").collect{| param| param.split("=")}.inject({}){|params, param| params[param.first] = URI.decode(param.last); params }
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def request_as_hash_from env
|
|
17
|
-
request = env.inject({}) do |memo, (k, v)|
|
|
18
|
-
request_key = REQUEST_KEYS[k]
|
|
19
|
-
memo[request_key] = v if request_key
|
|
20
|
-
memo
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
request[:headers] = headers_from env
|
|
24
|
-
body_string = request[:body].read
|
|
25
|
-
|
|
26
|
-
if body_string.empty?
|
|
27
|
-
request.delete :body
|
|
28
|
-
else
|
|
29
|
-
body_is_json = request[:headers]['Content-Type'] =~ /json/
|
|
30
|
-
request[:body] = body_is_json ? JSON.parse(body_string) : body_string
|
|
31
|
-
end
|
|
32
|
-
request[:method] = request[:method].downcase
|
|
33
|
-
request
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
private
|
|
37
|
-
|
|
38
|
-
def headers_from env
|
|
39
|
-
headers = env.reject{ |key, value| !(key.start_with?("HTTP") || key == 'CONTENT_TYPE' || key == 'CONTENT_LENGTH')}
|
|
40
|
-
headers.inject({}) do | hash, header |
|
|
41
|
-
hash[standardise_header(header.first)] = header.last
|
|
42
|
-
hash
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def standardise_header header
|
|
47
|
-
header.gsub(/^HTTP_/, '').split("_").collect{|word| word[0] + word[1..-1].downcase}.join("-")
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
require 'pact/consumer/mock_service/mock_service_administration_endpoint'
|
|
2
|
-
|
|
3
|
-
module Pact
|
|
4
|
-
module Consumer
|
|
5
|
-
class VerificationGet < MockServiceAdministrationEndpoint
|
|
6
|
-
|
|
7
|
-
include RackRequestHelper
|
|
8
|
-
attr_accessor :interaction_list, :log_description
|
|
9
|
-
|
|
10
|
-
def initialize name, logger, interaction_list, log_description
|
|
11
|
-
super name, logger
|
|
12
|
-
@interaction_list = interaction_list
|
|
13
|
-
@log_description = log_description
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def request_path
|
|
17
|
-
'/interactions/verification'
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def request_method
|
|
21
|
-
'GET'
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def respond env
|
|
25
|
-
if interaction_list.all_matched?
|
|
26
|
-
logger.info "Verifying - interactions matched for example \"#{example_description(env)}\""
|
|
27
|
-
[200, {'Content-Type' => 'text/plain'}, ['Interactions matched']]
|
|
28
|
-
else
|
|
29
|
-
error_message = FailureMessage.new(interaction_list).to_s
|
|
30
|
-
logger.warn "Verifying - actual interactions do not match expected interactions for example \"#{example_description(env)}\". \n#{error_message}"
|
|
31
|
-
logger.warn error_message
|
|
32
|
-
[500, {'Content-Type' => 'text/plain'}, ["Actual interactions do not match expected interactions for mock #{name}.\n\n#{error_message}See #{log_description} for details."]]
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def example_description env
|
|
37
|
-
params_hash(env)['example_description']
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
class FailureMessage
|
|
41
|
-
|
|
42
|
-
def initialize interaction_list
|
|
43
|
-
@interaction_list = interaction_list
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def to_s
|
|
47
|
-
titles_and_summaries.collect do | title, summaries |
|
|
48
|
-
"#{title}:\n\t#{summaries.join("\n\t")}\n\n" if summaries.any?
|
|
49
|
-
end.compact.join
|
|
50
|
-
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
private
|
|
54
|
-
|
|
55
|
-
attr_reader :interaction_list
|
|
56
|
-
|
|
57
|
-
def titles_and_summaries
|
|
58
|
-
{
|
|
59
|
-
"Incorrect requests" => interaction_list.interaction_mismatches_summaries,
|
|
60
|
-
"Missing requests" => interaction_list.missing_interactions_summaries,
|
|
61
|
-
"Unexpected requests" => interaction_list.unexpected_requests_summaries,
|
|
62
|
-
}
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
require 'net/http'
|
|
2
|
-
require 'pact/consumer/mock_service_interaction_expectation'
|
|
3
|
-
|
|
4
|
-
module Pact
|
|
5
|
-
module Consumer
|
|
6
|
-
class MockServiceClient
|
|
7
|
-
|
|
8
|
-
MOCK_SERVICE_ADMINISTRATON_HEADERS = {'X-Pact-Mock-Service' => 'true'}
|
|
9
|
-
|
|
10
|
-
def initialize port
|
|
11
|
-
@http = Net::HTTP.new('localhost', port)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def verify example_description
|
|
15
|
-
response = http.request_get("/interactions/verification?example_description=#{URI.encode(example_description)}", MOCK_SERVICE_ADMINISTRATON_HEADERS)
|
|
16
|
-
raise "\e[31m#{response.body}\e[m" unless response.is_a? Net::HTTPSuccess
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def log msg
|
|
20
|
-
http.request_get("/log?msg=#{URI.encode(msg)}", MOCK_SERVICE_ADMINISTRATON_HEADERS)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def wait_for_interactions wait_max_seconds, poll_interval
|
|
24
|
-
wait_until_true(wait_max_seconds, poll_interval) do
|
|
25
|
-
response = http.request_get("/interactions/missing", MOCK_SERVICE_ADMINISTRATON_HEADERS)
|
|
26
|
-
JSON.parse(response.body)['size'] == 0
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def clear_interactions example_description
|
|
31
|
-
http.delete("/interactions?example_description=#{URI.encode(example_description)}", MOCK_SERVICE_ADMINISTRATON_HEADERS)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def add_expected_interaction interaction
|
|
35
|
-
response = http.request_post('/interactions', MockServiceInteractionExpectation.new(interaction).to_json, MOCK_SERVICE_ADMINISTRATON_HEADERS.merge("Content-Type" => "application/json"))
|
|
36
|
-
raise "\e[31m#{response.body}\e[m" unless response.is_a? Net::HTTPSuccess
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def self.clear_interactions port, example_description
|
|
40
|
-
Net::HTTP.new("localhost", port).delete("/interactions?example_description=#{URI.encode(example_description)}", MOCK_SERVICE_ADMINISTRATON_HEADERS)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def write_pact pacticipant_details
|
|
44
|
-
response = http.request_post("/pact", pacticipant_details.to_json, MOCK_SERVICE_ADMINISTRATON_HEADERS.merge("Content-Type" => "application/json"))
|
|
45
|
-
raise "\e[31m#{response.body}\e[m" unless response.is_a? Net::HTTPSuccess
|
|
46
|
-
response.body
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
private
|
|
50
|
-
|
|
51
|
-
attr_reader :http
|
|
52
|
-
|
|
53
|
-
#todo: in need a better home (where can we move it?)
|
|
54
|
-
def wait_until_true timeout=3, interval=0.1
|
|
55
|
-
time_limit = Time.now + timeout
|
|
56
|
-
loop do
|
|
57
|
-
result = yield
|
|
58
|
-
return if result || Time.now >= time_limit
|
|
59
|
-
sleep interval
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
@@ -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
|
data/lib/pact/consumer/server.rb
DELETED
|
@@ -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,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
|