pact 0.1.28 → 1.3.3
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/.gitignore +2 -1
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +374 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +58 -55
- data/README.md +214 -139
- data/Rakefile +4 -28
- data/bin/pact +2 -2
- data/config.ru +3 -0
- data/documentation/README.md +13 -0
- data/documentation/configuration.md +166 -0
- 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 +14 -0
- data/example/animal-service/Gemfile.lock +69 -0
- data/example/animal-service/Rakefile +5 -0
- data/example/animal-service/config.ru +3 -0
- data/example/animal-service/db/animal_db.sqlite3 +0 -0
- data/example/animal-service/lib/animal_service/animal_repository.rb +12 -0
- data/example/animal-service/lib/animal_service/api.rb +28 -0
- data/example/animal-service/lib/animal_service/db.rb +5 -0
- data/example/animal-service/spec/service_consumers/pact_helper.rb +11 -0
- data/example/animal-service/spec/service_consumers/provider_states_for_zoo_app.rb +26 -0
- data/example/zoo-app/Gemfile +12 -0
- data/example/zoo-app/Gemfile.lock +63 -0
- data/example/zoo-app/Rakefile +5 -0
- data/example/zoo-app/doc/pacts/markdown/README.md +3 -0
- data/example/zoo-app/doc/pacts/markdown/Zoo App - Animal Service.md +75 -0
- data/example/zoo-app/lib/zoo_app/animal_service_client.rb +40 -0
- data/example/zoo-app/lib/zoo_app/models/alligator.rb +15 -0
- data/example/zoo-app/spec/pacts/zoo_app-animal_service.json +67 -0
- data/example/zoo-app/spec/service_providers/animal_service_client_spec.rb +71 -0
- data/example/zoo-app/spec/service_providers/pact_helper.rb +15 -0
- data/example/zoo-app/spec/spec_helper.rb +6 -0
- data/lib/pact/cli.rb +138 -0
- data/lib/pact/configuration.rb +169 -28
- data/lib/pact/consumer/app_manager.rb +10 -29
- data/lib/pact/consumer/configuration/configuration_extensions.rb +15 -0
- data/lib/pact/consumer/configuration/dsl.rb +12 -0
- data/lib/pact/consumer/configuration/mock_service.rb +91 -0
- data/lib/pact/consumer/configuration/service_consumer.rb +51 -0
- data/lib/pact/consumer/configuration/service_provider.rb +40 -0
- data/lib/pact/consumer/configuration.rb +11 -0
- data/lib/pact/consumer/consumer_contract_builder.rb +44 -47
- data/lib/pact/consumer/interaction_builder.rb +41 -0
- data/lib/pact/consumer/interactions_filter.rb +48 -0
- data/lib/pact/consumer/mock_service/app.rb +82 -0
- data/lib/pact/consumer/mock_service/interaction_delete.rb +33 -0
- data/lib/pact/consumer/mock_service/interaction_list.rb +76 -0
- data/lib/pact/consumer/mock_service/interaction_mismatch.rb +73 -0
- data/lib/pact/consumer/mock_service/interaction_post.rb +31 -0
- data/lib/pact/consumer/mock_service/interaction_replay.rb +139 -0
- data/lib/pact/consumer/mock_service/log_get.rb +28 -0
- data/lib/pact/consumer/mock_service/missing_interactions_get.rb +30 -0
- data/lib/pact/consumer/mock_service/mock_service_administration_endpoint.rb +31 -0
- data/lib/pact/consumer/mock_service/pact_post.rb +33 -0
- data/lib/pact/consumer/mock_service/rack_request_helper.rb +51 -0
- data/lib/pact/consumer/mock_service/verification_get.rb +68 -0
- data/lib/pact/consumer/mock_service.rb +1 -339
- data/lib/pact/consumer/mock_service_client.rb +65 -0
- data/lib/pact/consumer/mock_service_interaction_expectation.rb +37 -0
- data/lib/pact/consumer/request.rb +27 -0
- data/lib/pact/consumer/rspec.rb +9 -19
- data/lib/pact/consumer/server.rb +90 -0
- data/lib/pact/consumer/spec_hooks.rb +38 -0
- data/lib/pact/consumer/world.rb +37 -0
- data/lib/pact/consumer.rb +10 -7
- data/lib/pact/consumer_contract/consumer_contract.rb +115 -0
- data/lib/pact/consumer_contract/consumer_contract_writer.rb +84 -0
- data/lib/pact/consumer_contract/file_name.rb +19 -0
- data/lib/pact/consumer_contract/headers.rb +51 -0
- data/lib/pact/consumer_contract/interaction.rb +67 -0
- data/lib/pact/consumer_contract/pact_file.rb +24 -0
- data/lib/pact/consumer_contract/request.rb +73 -0
- data/lib/pact/consumer_contract/service_consumer.rb +28 -0
- data/lib/pact/consumer_contract/service_provider.rb +28 -0
- data/lib/pact/consumer_contract.rb +1 -110
- data/lib/pact/doc/doc_file.rb +40 -0
- data/lib/pact/doc/generate.rb +11 -0
- data/lib/pact/doc/generator.rb +82 -0
- data/lib/pact/doc/interaction_view_model.rb +114 -0
- data/lib/pact/doc/markdown/consumer_contract_renderer.rb +56 -0
- data/lib/pact/doc/markdown/generator.rb +26 -0
- data/lib/pact/doc/markdown/index_renderer.rb +41 -0
- data/lib/pact/doc/markdown/interaction.erb +14 -0
- data/lib/pact/doc/markdown/interaction_renderer.rb +38 -0
- data/lib/pact/doc/sort_interactions.rb +17 -0
- data/lib/pact/matchers/actual_type.rb +16 -0
- data/lib/pact/matchers/base_difference.rb +37 -0
- data/lib/pact/matchers/differ.rb +153 -0
- data/lib/pact/matchers/difference.rb +13 -0
- data/lib/pact/matchers/difference_indicator.rb +26 -0
- data/lib/pact/matchers/embedded_diff_formatter.rb +62 -0
- data/lib/pact/matchers/expected_type.rb +35 -0
- data/lib/pact/matchers/index_not_found.rb +15 -0
- data/lib/pact/matchers/list_diff_formatter.rb +101 -0
- data/lib/pact/matchers/matchers.rb +91 -37
- data/lib/pact/matchers/no_diff_indicator.rb +18 -0
- data/lib/pact/matchers/regexp_difference.rb +13 -0
- data/lib/pact/matchers/type_difference.rb +16 -0
- data/lib/pact/matchers/unexpected_index.rb +11 -0
- data/lib/pact/matchers/unexpected_key.rb +11 -0
- data/lib/pact/matchers/unix_diff_formatter.rb +114 -0
- data/lib/pact/project_root.rb +7 -0
- data/lib/pact/provider/configuration/configuration_extension.rb +58 -0
- data/lib/pact/provider/configuration/dsl.rb +13 -0
- data/lib/pact/provider/configuration/pact_verification.rb +46 -0
- data/lib/pact/provider/configuration/service_provider_config.rb +16 -0
- data/lib/pact/provider/configuration/service_provider_dsl.rb +59 -0
- data/lib/pact/provider/configuration.rb +7 -0
- data/lib/pact/provider/context.rb +0 -0
- data/lib/pact/provider/matchers/messages.rb +45 -0
- data/lib/pact/provider/pact_helper_locator.rb +22 -0
- data/lib/pact/provider/pact_spec_runner.rb +123 -0
- data/lib/pact/provider/pact_verification.rb +17 -0
- data/lib/pact/provider/print_missing_provider_states.rb +30 -0
- data/lib/pact/provider/request.rb +70 -0
- data/lib/pact/provider/rspec/backtrace_formatter.rb +43 -0
- data/lib/pact/provider/rspec/custom_options_file +0 -0
- data/lib/pact/provider/rspec/formatter_rspec_2.rb +65 -0
- data/lib/pact/provider/rspec/formatter_rspec_3.rb +96 -0
- data/lib/pact/provider/rspec/matchers.rb +97 -0
- data/lib/pact/provider/rspec.rb +181 -0
- data/lib/pact/provider/state/provider_state.rb +181 -0
- data/lib/pact/provider/state/provider_state_configured_modules.rb +13 -0
- data/lib/pact/provider/state/provider_state_manager.rb +42 -0
- data/lib/pact/provider/state/provider_state_proxy.rb +39 -0
- data/lib/pact/provider/test_methods.rb +47 -0
- data/lib/pact/provider/verification_report.rb +36 -0
- data/lib/pact/provider/world.rb +23 -0
- data/lib/pact/provider.rb +3 -0
- data/lib/pact/reification.rb +6 -4
- data/lib/pact/rspec.rb +53 -0
- data/lib/pact/shared/active_support_support.rb +51 -0
- data/lib/pact/shared/dsl.rb +76 -0
- data/lib/pact/shared/jruby_support.rb +18 -0
- data/lib/pact/shared/json_differ.rb +15 -0
- data/lib/pact/shared/key_not_found.rb +15 -0
- data/lib/pact/shared/null_expectation.rb +31 -0
- data/lib/pact/shared/request.rb +80 -0
- data/lib/pact/shared/text_differ.rb +14 -0
- data/lib/pact/something_like.rb +49 -0
- data/lib/pact/symbolize_keys.rb +12 -0
- data/lib/pact/tasks/task_helper.rb +36 -0
- data/lib/pact/tasks/verification_task.rb +87 -0
- data/lib/pact/tasks.rb +2 -0
- data/lib/pact/templates/provider_state.erb +14 -0
- data/lib/pact/term.rb +47 -2
- data/lib/pact/version.rb +1 -1
- data/lib/pact.rb +1 -1
- data/lib/tasks/pact.rake +25 -2
- data/pact.gemspec +13 -11
- data/{scratchpad.txt → scratchpad.rb} +18 -2
- data/spec/features/consumption_spec.rb +24 -56
- data/spec/features/production_spec.rb +19 -24
- data/spec/features/{producer_states → provider_states}/zebras.rb +3 -3
- data/spec/integration/consumer_spec.rb +212 -0
- data/spec/integration/pact/consumer_configuration_spec.rb +66 -0
- data/spec/integration/pact/provider_configuration_spec.rb +25 -0
- data/spec/lib/pact/cli_spec.rb +47 -0
- data/spec/lib/pact/configuration_spec.rb +269 -7
- data/spec/lib/pact/consumer/app_manager_spec.rb +3 -3
- data/spec/lib/pact/consumer/configuration_spec.rb +57 -0
- data/spec/lib/pact/consumer/consumer_contract_builder_spec.rb +52 -70
- data/spec/lib/pact/consumer/interaction_builder_spec.rb +91 -0
- data/spec/lib/pact/consumer/interactions_spec.rb +64 -0
- data/spec/lib/pact/consumer/mock_service/app_spec.rb +52 -0
- data/spec/lib/pact/consumer/mock_service/interaction_list_spec.rb +78 -0
- data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +70 -0
- data/spec/lib/pact/consumer/mock_service/interaction_replay_spec.rb +12 -0
- data/spec/lib/pact/consumer/mock_service/rack_request_helper_spec.rb +88 -0
- data/spec/lib/pact/consumer/mock_service/verification_get_spec.rb +142 -0
- data/spec/lib/pact/consumer/mock_service_client_spec.rb +88 -0
- data/spec/lib/pact/consumer/mock_service_interaction_expectation_spec.rb +54 -0
- data/spec/lib/pact/consumer/request_spec.rb +24 -0
- data/spec/lib/pact/consumer/service_consumer_spec.rb +1 -1
- data/spec/lib/pact/consumer_contract/active_support_support_spec.rb +58 -0
- data/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +180 -0
- data/spec/lib/pact/consumer_contract/consumer_contract_writer_spec.rb +111 -0
- data/spec/lib/pact/consumer_contract/headers_spec.rb +107 -0
- data/spec/lib/pact/consumer_contract/interaction_spec.rb +107 -0
- data/spec/lib/pact/{request_spec.rb → consumer_contract/request_spec.rb} +80 -67
- data/spec/lib/pact/doc/generator_spec.rb +84 -0
- data/spec/lib/pact/doc/interaction_view_model_spec.rb +132 -0
- data/spec/lib/pact/doc/markdown/consumer_contract_renderer_spec.rb +29 -0
- data/spec/lib/pact/doc/markdown/index_renderer_spec.rb +29 -0
- data/spec/lib/pact/matchers/differ_spec.rb +214 -0
- data/spec/lib/pact/matchers/difference_spec.rb +22 -0
- data/spec/lib/pact/matchers/embedded_diff_formatter_spec.rb +90 -0
- data/spec/lib/pact/matchers/index_not_found_spec.rb +21 -0
- data/spec/lib/pact/matchers/list_diff_formatter_spec.rb +114 -0
- data/spec/lib/pact/matchers/matchers_spec.rb +400 -254
- data/spec/lib/pact/matchers/regexp_difference_spec.rb +20 -0
- data/spec/lib/pact/matchers/type_difference_spec.rb +34 -0
- data/spec/lib/pact/matchers/unexpected_index_spec.rb +20 -0
- data/spec/lib/pact/matchers/unexpected_key_spec.rb +20 -0
- data/spec/lib/pact/matchers/unix_diff_formatter_spec.rb +216 -0
- data/spec/lib/pact/provider/configuration/configuration_extension_spec.rb +30 -0
- data/spec/lib/pact/provider/configuration/pact_verification_spec.rb +43 -0
- data/spec/lib/pact/provider/configuration/service_provider_config_spec.rb +21 -0
- data/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb +108 -0
- data/spec/lib/pact/provider/configuration_spec.rb +50 -0
- data/spec/lib/pact/provider/matchers/messages_spec.rb +116 -0
- data/spec/lib/pact/provider/pact_helper_locator_spec.rb +54 -0
- data/spec/lib/pact/provider/print_missing_provider_states_spec.rb +19 -0
- data/spec/lib/pact/provider/request_spec.rb +78 -0
- data/spec/lib/pact/provider/rspec/formatter_rspec_2_spec.rb +68 -0
- data/spec/lib/pact/provider/rspec/formatter_rspec_3_spec.rb +72 -0
- data/spec/lib/pact/provider/rspec_spec.rb +55 -0
- data/spec/lib/pact/provider/state/provider_state_manager_spec.rb +89 -0
- data/spec/lib/pact/provider/state/provider_state_proxy_spec.rb +80 -0
- data/spec/lib/pact/provider/state/provider_state_spec.rb +213 -0
- data/spec/lib/pact/provider/world_spec.rb +41 -0
- data/spec/lib/pact/reification_spec.rb +24 -0
- data/spec/lib/pact/shared/dsl_spec.rb +86 -0
- data/spec/lib/pact/shared/json_differ_spec.rb +36 -0
- data/spec/lib/pact/shared/key_not_found_spec.rb +20 -0
- data/spec/lib/pact/shared/request_spec.rb +111 -0
- data/spec/lib/pact/shared/text_differ_spec.rb +54 -0
- data/spec/lib/pact/something_like_spec.rb +21 -0
- data/spec/lib/pact/tasks/task_helper_spec.rb +74 -0
- data/spec/lib/pact/tasks/verification_task_spec.rb +75 -0
- data/spec/lib/pact/term_spec.rb +57 -4
- data/spec/pact_specification/compliance-1.0.0.rb +47 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/standalone/consumer_fail_test.rb +55 -0
- data/spec/standalone/consumer_pass_test.rb +51 -0
- data/spec/support/a_consumer-a_producer.json +2 -4
- data/spec/support/a_consumer-a_provider.json +32 -0
- data/spec/support/active_support_if_configured.rb +6 -0
- data/spec/support/app_for_config_ru.rb +4 -0
- data/spec/support/consumer_contract_template.json +24 -0
- data/spec/support/dsl_spec_support.rb +7 -0
- data/spec/support/factories.rb +82 -0
- data/spec/support/generated_index.md +4 -0
- data/spec/support/generated_markdown.md +55 -0
- data/spec/support/interaction_view_model.json +63 -0
- data/spec/support/interaction_view_model_with_terms.json +50 -0
- data/spec/support/markdown_pact.json +48 -0
- data/spec/support/missing_provider_states_output.txt +25 -0
- data/spec/support/options.json +21 -0
- data/spec/support/options_app.rb +15 -0
- data/spec/support/pact_helper.rb +57 -0
- data/spec/support/shared_examples_for_request.rb +94 -0
- data/spec/support/spec_support.rb +20 -0
- data/spec/support/stubbing.json +22 -0
- data/spec/support/stubbing_using_allow.rb +29 -0
- data/spec/support/term.json +48 -0
- data/spec/support/test_app_fail.json +41 -2
- data/spec/support/test_app_pass.json +19 -2
- data/spec/support/test_app_with_right_content_type_differ.json +23 -0
- data/tasks/pact-test.rake +98 -8
- data/tasks/spec.rake +8 -0
- metadata +393 -95
- data/lib/pact/app.rb +0 -32
- data/lib/pact/consumer/configuration_dsl.rb +0 -71
- data/lib/pact/consumer/dsl.rb +0 -98
- data/lib/pact/consumer/interaction.rb +0 -70
- data/lib/pact/consumer/run_condor.rb +0 -4
- data/lib/pact/consumer/run_mock_contract_service.rb +0 -13
- data/lib/pact/consumer/service_consumer.rb +0 -22
- data/lib/pact/consumer/service_producer.rb +0 -23
- data/lib/pact/json_warning.rb +0 -23
- data/lib/pact/producer/configuration_dsl.rb +0 -62
- data/lib/pact/producer/matchers.rb +0 -22
- data/lib/pact/producer/pact_spec_runner.rb +0 -57
- data/lib/pact/producer/producer_state.rb +0 -81
- data/lib/pact/producer/rspec.rb +0 -127
- data/lib/pact/producer/test_methods.rb +0 -89
- data/lib/pact/producer.rb +0 -1
- data/lib/pact/rake_task.rb +0 -64
- data/lib/pact/request.rb +0 -109
- data/lib/pact/verification_task.rb +0 -57
- data/spec/integration/pact/configuration_spec.rb +0 -65
- data/spec/lib/pact/consumer/dsl_spec.rb +0 -52
- data/spec/lib/pact/consumer/interaction_spec.rb +0 -108
- data/spec/lib/pact/consumer/mock_service_spec.rb +0 -147
- data/spec/lib/pact/consumer_contract_spec.rb +0 -125
- data/spec/lib/pact/producer/configuration_dsl_spec.rb +0 -101
- data/spec/lib/pact/producer/producer_state_spec.rb +0 -103
- data/spec/lib/pact/producer/rspec_spec.rb +0 -48
- data/spec/lib/pact/verification_task_spec.rb +0 -64
- data/spec/support/pact_rake_support.rb +0 -41
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
require 'open-uri'
|
|
2
|
+
require 'pact/consumer_contract'
|
|
3
|
+
require 'pact/provider/rspec/matchers'
|
|
4
|
+
require 'pact/provider/test_methods'
|
|
5
|
+
require 'pact/provider/configuration'
|
|
6
|
+
|
|
7
|
+
module Pact
|
|
8
|
+
module Provider
|
|
9
|
+
module RSpec
|
|
10
|
+
|
|
11
|
+
module InstanceMethods
|
|
12
|
+
def app
|
|
13
|
+
Pact.configuration.provider.app
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
module ClassMethods
|
|
18
|
+
|
|
19
|
+
include ::RSpec::Core::DSL
|
|
20
|
+
|
|
21
|
+
def honour_pactfile pactfile_uri, options = {}
|
|
22
|
+
#TODO change puts to use output stream
|
|
23
|
+
puts "Reading pact at #{pactfile_uri}"
|
|
24
|
+
puts "Filtering interactions by: #{options[:criteria]}" if options[:criteria] && options[:criteria].any?
|
|
25
|
+
consumer_contract = Pact::ConsumerContract.from_json(read_pact_from(pactfile_uri, options))
|
|
26
|
+
::RSpec.describe "Verifying a pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}", :pactfile_uri => pactfile_uri do
|
|
27
|
+
honour_consumer_contract consumer_contract, options
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def honour_consumer_contract consumer_contract, options = {}
|
|
32
|
+
describe_consumer_contract consumer_contract, options.merge({:consumer => consumer_contract.consumer.name})
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def describe_consumer_contract consumer_contract, options
|
|
38
|
+
consumer_interactions(consumer_contract, options).each do |interaction|
|
|
39
|
+
describe_interaction_with_provider_state interaction, options
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def consumer_interactions(consumer_contract, options)
|
|
44
|
+
if options[:criteria].nil?
|
|
45
|
+
consumer_contract.interactions
|
|
46
|
+
else
|
|
47
|
+
consumer_contract.find_interactions options[:criteria]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def describe_interaction_with_provider_state interaction, options
|
|
52
|
+
if interaction.provider_state
|
|
53
|
+
describe "Given #{interaction.provider_state}" do
|
|
54
|
+
describe_interaction interaction, options
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
describe_interaction interaction, options
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def describe_interaction interaction, options
|
|
62
|
+
|
|
63
|
+
metadata = {
|
|
64
|
+
:pact => :verify,
|
|
65
|
+
:pact_interaction => interaction,
|
|
66
|
+
:pact_interaction_example_description => interaction_description_for_rerun_command(interaction)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
describe description_for(interaction), metadata do
|
|
70
|
+
|
|
71
|
+
describe "with #{interaction.request.method_and_path}" do
|
|
72
|
+
|
|
73
|
+
interaction_context = InteractionContext.new
|
|
74
|
+
|
|
75
|
+
before do | example |
|
|
76
|
+
interaction_context.run_once :before do
|
|
77
|
+
Pact.configuration.logger.info "Running example '#{Pact::RSpec.full_description(example)}'"
|
|
78
|
+
set_up_provider_state interaction.provider_state, options[:consumer]
|
|
79
|
+
replay_interaction interaction
|
|
80
|
+
interaction_context.last_response = last_response
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
after do
|
|
85
|
+
interaction_context.run_once :after do
|
|
86
|
+
tear_down_provider_state interaction.provider_state, options[:consumer]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe_response Pact::Term.unpack_regexps(interaction.response), interaction_context
|
|
91
|
+
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def describe_response expected_response, interaction_context
|
|
99
|
+
|
|
100
|
+
describe "returns a response which" do
|
|
101
|
+
|
|
102
|
+
include Pact::RSpec::Matchers
|
|
103
|
+
|
|
104
|
+
let(:expected_response_status) { expected_response['status'] }
|
|
105
|
+
let(:expected_response_body) { expected_response['body'] }
|
|
106
|
+
let(:response) { interaction_context.last_response }
|
|
107
|
+
let(:response_status) { response.status }
|
|
108
|
+
let(:response_body) { parse_body_from_response(response) }
|
|
109
|
+
let(:differ) { Pact.configuration.body_differ_for_content_type diff_content_type }
|
|
110
|
+
let(:diff_formatter) { Pact.configuration.diff_formatter_for_content_type diff_content_type }
|
|
111
|
+
let(:expected_content_type) { Pact::Headers.new(expected_response['headers'] || {})['Content-Type'] }
|
|
112
|
+
let(:actual_content_type) { response.headers['Content-Type']}
|
|
113
|
+
let(:diff_content_type) { String === expected_content_type ? expected_content_type : actual_content_type } # expected_content_type may be a Regexp
|
|
114
|
+
let(:diff_options) { { with: differ, diff_formatter: diff_formatter } }
|
|
115
|
+
|
|
116
|
+
if expected_response['status']
|
|
117
|
+
it "has status code #{expected_response['status']}" do
|
|
118
|
+
expect(response_status).to eql expected_response_status
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if expected_response['headers']
|
|
123
|
+
describe "includes headers" do
|
|
124
|
+
expected_response['headers'].each do |name, expected_header_value|
|
|
125
|
+
it "\"#{name}\" with value #{expected_header_value.inspect}" do
|
|
126
|
+
header_value = response.headers[name]
|
|
127
|
+
expect(header_value).to match_header(name, expected_header_value)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if expected_response['body']
|
|
134
|
+
it "has a matching body" do
|
|
135
|
+
expect(response_body).to match_term expected_response_body, diff_options
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def description_for interaction
|
|
142
|
+
interaction.provider_state ? interaction.description : interaction.description.capitalize
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def interaction_description_for_rerun_command interaction
|
|
146
|
+
description_for(interaction).capitalize + ( interaction.provider_state ? " given #{interaction.provider_state}" : "")
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def read_pact_from uri, options = {}
|
|
150
|
+
Pact::PactFile.read(uri, options)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# The "arrange" and "act" parts of the test really only need to be run once,
|
|
156
|
+
# however, stubbing is not supported in before :all, so this is a
|
|
157
|
+
# wee hack to enable before :all like functionality using before :each.
|
|
158
|
+
# In an ideal world, the test setup and execution should be quick enough for
|
|
159
|
+
# the difference between :all and :each to be unnoticable, but the annoying
|
|
160
|
+
# reality is, sometimes it does make a difference. This is for you, V!
|
|
161
|
+
|
|
162
|
+
class InteractionContext
|
|
163
|
+
|
|
164
|
+
attr_accessor :last_response
|
|
165
|
+
|
|
166
|
+
def initialize
|
|
167
|
+
@already_run = []
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def run_once hook
|
|
171
|
+
unless @already_run.include?(hook)
|
|
172
|
+
yield
|
|
173
|
+
@already_run << hook
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
require 'pact/shared/dsl'
|
|
2
|
+
require 'pact/provider/state/provider_state_configured_modules'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module Provider::State
|
|
6
|
+
|
|
7
|
+
BASE_PROVIDER_STATE_NAME = "__base_provider_state__"
|
|
8
|
+
|
|
9
|
+
module DSL
|
|
10
|
+
def provider_state name, &block
|
|
11
|
+
ProviderStates.provider_state(name, &block).register
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def set_up &block
|
|
15
|
+
ProviderStates.base_provider_state.register.register_set_up &block
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def tear_down &block
|
|
19
|
+
ProviderStates.base_provider_state.register_tear_down &block
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def provider_states_for name, &block
|
|
23
|
+
ProviderStates.current_namespaces << name
|
|
24
|
+
instance_eval(&block)
|
|
25
|
+
ProviderStates.current_namespaces.pop
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class ProviderStates
|
|
30
|
+
def self.provider_state name, &block
|
|
31
|
+
ProviderState.build(name, current_namespaces.join('.'), &block)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.base_provider_state
|
|
35
|
+
fullname = namespaced_name BASE_PROVIDER_STATE_NAME, {:for => current_namespaces.first }
|
|
36
|
+
provider_states[fullname] ||
|
|
37
|
+
ProviderState.new(BASE_PROVIDER_STATE_NAME, current_namespaces.join('.'))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.register name, provider_state
|
|
41
|
+
provider_states[name] = provider_state
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.provider_states
|
|
45
|
+
@@provider_states ||= {}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.current_namespaces
|
|
49
|
+
@@current_namespaces ||= []
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.get name, options = {}
|
|
53
|
+
fullname = namespaced_name name, options
|
|
54
|
+
(provider_states[fullname] || provider_states[fullname.to_sym] || provider_states[name])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.get_base opts = {}
|
|
58
|
+
fullname = namespaced_name BASE_PROVIDER_STATE_NAME, opts
|
|
59
|
+
provider_states[fullname] || NoOpProviderState
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.namespaced_name name, options = {}
|
|
63
|
+
fullname = options[:for] ? "#{options[:for]}.#{name}" : name
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class ProviderState
|
|
68
|
+
|
|
69
|
+
attr_accessor :name
|
|
70
|
+
attr_accessor :namespace
|
|
71
|
+
|
|
72
|
+
extend Pact::DSL
|
|
73
|
+
|
|
74
|
+
def initialize name, namespace, &block
|
|
75
|
+
@name = name
|
|
76
|
+
@namespace = namespace
|
|
77
|
+
@set_up_defined = false
|
|
78
|
+
@tear_down_defined = false
|
|
79
|
+
@no_op_defined = false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
dsl do
|
|
83
|
+
def set_up &block
|
|
84
|
+
self.register_set_up &block
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def tear_down &block
|
|
88
|
+
self.register_tear_down &block
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def no_op
|
|
92
|
+
self.register_no_op
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def register
|
|
97
|
+
ProviderStates.register(namespaced(name), self)
|
|
98
|
+
self
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def finalize
|
|
102
|
+
validate
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def register_set_up &block
|
|
106
|
+
@set_up_block = block
|
|
107
|
+
@set_up_defined = true
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def register_tear_down &block
|
|
111
|
+
@tear_down_block = block
|
|
112
|
+
@tear_down_defined = true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def register_no_op
|
|
116
|
+
@no_op_defined = true
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def set_up
|
|
120
|
+
if @set_up_block
|
|
121
|
+
include_provider_state_configured_modules
|
|
122
|
+
instance_eval &@set_up_block
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def tear_down
|
|
127
|
+
if @tear_down_block
|
|
128
|
+
include_provider_state_configured_modules
|
|
129
|
+
instance_eval &@tear_down_block
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
attr_accessor :no_op_defined, :set_up_defined, :tear_down_defined
|
|
136
|
+
|
|
137
|
+
def validate
|
|
138
|
+
if no_op_defined && set_up_defined
|
|
139
|
+
raise error_message_for_extra_block 'set_up'
|
|
140
|
+
elsif no_op_defined && tear_down_defined
|
|
141
|
+
raise error_message_for_extra_block 'tear_down'
|
|
142
|
+
elsif !(no_op_defined || set_up_defined || tear_down_defined)
|
|
143
|
+
raise "Please provide a set_up or tear_down block for provider state \"#{name}\". If there is no data to set up or tear down, you can use \"no_op\" instead."
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def error_message_for_extra_block block_name
|
|
148
|
+
"Provider state \"#{name}\" has been defined as a no_op but it also has a #{block_name} block. Please remove one or the other."
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def namespaced(name)
|
|
152
|
+
if namespace.empty?
|
|
153
|
+
name
|
|
154
|
+
else
|
|
155
|
+
"#{namespace}.#{name}"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def include_provider_state_configured_modules
|
|
160
|
+
# Doing this at runtime means the order of the Pact configuration block
|
|
161
|
+
# and the provider state declarations doesn't matter.
|
|
162
|
+
# Using include ProviderStateConfiguredModules on the class doesn't seem to work -
|
|
163
|
+
# modules dynamically added to ProviderStateConfiguredModules don't seem to be
|
|
164
|
+
# included in the including class.
|
|
165
|
+
self.extend(ProviderStateConfiguredModules) unless self.singleton_class.ancestors.include?(ProviderStateConfiguredModules)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
class NoOpProviderState
|
|
170
|
+
|
|
171
|
+
def self.set_up
|
|
172
|
+
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def self.tear_down
|
|
176
|
+
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Pact
|
|
2
|
+
module Provider::State
|
|
3
|
+
class ProviderStateManager
|
|
4
|
+
|
|
5
|
+
attr_reader :provider_state_name, :consumer
|
|
6
|
+
|
|
7
|
+
def initialize provider_state_name, consumer
|
|
8
|
+
@provider_state_name = provider_state_name
|
|
9
|
+
@consumer = consumer
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def set_up_provider_state
|
|
13
|
+
get_global_base_provider_state.set_up
|
|
14
|
+
get_consumer_base_provider_state.set_up
|
|
15
|
+
if provider_state_name
|
|
16
|
+
get_provider_state.set_up
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def tear_down_provider_state
|
|
21
|
+
if provider_state_name
|
|
22
|
+
get_provider_state.tear_down
|
|
23
|
+
end
|
|
24
|
+
get_consumer_base_provider_state.tear_down
|
|
25
|
+
get_global_base_provider_state.tear_down
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def get_provider_state
|
|
29
|
+
Pact.provider_world.provider_states.get(provider_state_name, :for => consumer)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def get_consumer_base_provider_state
|
|
33
|
+
Pact.provider_world.provider_states.get_base(:for => consumer)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def get_global_base_provider_state
|
|
37
|
+
Pact.provider_world.provider_states.get_base
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Pact
|
|
2
|
+
module Provider::State
|
|
3
|
+
class ProviderStateProxy
|
|
4
|
+
|
|
5
|
+
attr_reader :missing_provider_states
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@missing_provider_states = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def get name, options = {}
|
|
12
|
+
unless provider_state = ProviderStates.get(name, options)
|
|
13
|
+
register_missing_provider_state name, options[:for]
|
|
14
|
+
raise error_message name, options[:for]
|
|
15
|
+
end
|
|
16
|
+
provider_state
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def get_base options = {}
|
|
20
|
+
ProviderStates.get_base options
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def error_message name, consumer
|
|
26
|
+
"Could not find provider state \"#{name}\" for consumer #{consumer}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def register_missing_provider_state name, consumer
|
|
30
|
+
missing_states_for(consumer) << name unless missing_states_for(consumer).include?(name)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def missing_states_for consumer
|
|
34
|
+
@missing_provider_states[consumer] ||= []
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'pact/logging'
|
|
2
|
+
require 'rack/test'
|
|
3
|
+
require 'pact/consumer_contract/interaction'
|
|
4
|
+
require 'pact/provider/state/provider_state'
|
|
5
|
+
require 'pact/provider/state/provider_state_proxy'
|
|
6
|
+
require 'pact/provider/request'
|
|
7
|
+
require 'pact/provider/world'
|
|
8
|
+
require 'pact/provider/state/provider_state_manager'
|
|
9
|
+
|
|
10
|
+
module Pact
|
|
11
|
+
module Provider
|
|
12
|
+
module TestMethods
|
|
13
|
+
|
|
14
|
+
include Pact::Logging
|
|
15
|
+
include Rack::Test::Methods
|
|
16
|
+
|
|
17
|
+
def replay_interaction interaction
|
|
18
|
+
request = Request::Replayable.new(interaction.request)
|
|
19
|
+
args = [request.path, request.body, request.headers]
|
|
20
|
+
|
|
21
|
+
logger.info "Sending #{request.method.upcase} request to path: \"#{request.path}\" with headers: #{request.headers}, see debug logs for body"
|
|
22
|
+
logger.debug "body :#{request.body}"
|
|
23
|
+
response = self.send(request.method.downcase, *args)
|
|
24
|
+
logger.info "Received response with status: #{response.status}, headers: #{response.headers}, see debug logs for body"
|
|
25
|
+
logger.debug "body: #{response.body}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def parse_body_from_response rack_response
|
|
29
|
+
case rack_response.headers['Content-Type']
|
|
30
|
+
when /json/
|
|
31
|
+
JSON.load(rack_response.body)
|
|
32
|
+
else
|
|
33
|
+
rack_response.body
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def set_up_provider_state provider_state_name, consumer
|
|
38
|
+
State::ProviderStateManager.new(provider_state_name, consumer).set_up_provider_state
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def tear_down_provider_state provider_state_name, consumer
|
|
42
|
+
State::ProviderStateManager.new(provider_state_name, consumer).tear_down_provider_state
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'pact/consumer_contract'
|
|
2
|
+
|
|
3
|
+
module Pact::Provider
|
|
4
|
+
class VerificationReport
|
|
5
|
+
|
|
6
|
+
include Pact::FileName
|
|
7
|
+
|
|
8
|
+
def initialize (options)
|
|
9
|
+
@consumer = options[:consumer]
|
|
10
|
+
@provider = options[:provider]
|
|
11
|
+
@result = options[:result]
|
|
12
|
+
@output = options[:output]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_hash
|
|
16
|
+
{
|
|
17
|
+
:consumer => @consumer,
|
|
18
|
+
:provider => @provider,
|
|
19
|
+
:result => @result,
|
|
20
|
+
:output => @output
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def as_json options = {}
|
|
25
|
+
to_hash
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_json(options = {})
|
|
29
|
+
as_json.to_json(options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def report_file_name
|
|
33
|
+
file_name("#{@consumer[:name]}_#{@consumer[:ref]}", "#{@provider[:name]}_#{@provider[:ref]}")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'pact/provider/state/provider_state_proxy'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
|
|
5
|
+
def self.provider_world
|
|
6
|
+
@world ||= Pact::Provider::World.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# internal api, for testing only
|
|
10
|
+
def self.clear_provider_world
|
|
11
|
+
@world = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module Provider
|
|
15
|
+
class World
|
|
16
|
+
|
|
17
|
+
def provider_states
|
|
18
|
+
@provider_states_proxy ||= Pact::Provider::State::ProviderStateProxy.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/pact/reification.rb
CHANGED
|
@@ -4,19 +4,21 @@ module Pact
|
|
|
4
4
|
module Reification
|
|
5
5
|
|
|
6
6
|
def self.from_term(term)
|
|
7
|
-
case
|
|
8
|
-
when
|
|
7
|
+
case term
|
|
8
|
+
when Pact::Term, Regexp, Pact::SomethingLike
|
|
9
9
|
term.generate
|
|
10
|
-
when
|
|
10
|
+
when Hash
|
|
11
11
|
term.inject({}) do |mem, (key,term)|
|
|
12
12
|
mem[key] = from_term(term)
|
|
13
13
|
mem
|
|
14
14
|
end
|
|
15
|
-
when
|
|
15
|
+
when Array
|
|
16
16
|
term.inject([]) do |mem, term|
|
|
17
17
|
mem << from_term(term)
|
|
18
18
|
mem
|
|
19
19
|
end
|
|
20
|
+
when Pact::Request::Base
|
|
21
|
+
from_term(term.to_hash)
|
|
20
22
|
else
|
|
21
23
|
term
|
|
22
24
|
end
|
data/lib/pact/rspec.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'rspec'
|
|
2
|
+
# This is horrible, must work out a better way of doing this
|
|
3
|
+
module Pact
|
|
4
|
+
module RSpec
|
|
5
|
+
|
|
6
|
+
def self.color_enabled?
|
|
7
|
+
if ::RSpec.configuration.respond_to?(:color_enabled?)
|
|
8
|
+
::RSpec.configuration.color_enabled?(::RSpec.configuration.output_stream)
|
|
9
|
+
else
|
|
10
|
+
::RSpec.configuration.color_enabled?
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.formatter_class
|
|
15
|
+
if ::RSpec::Core::Formatters.respond_to?(:register)
|
|
16
|
+
require 'pact/provider/rspec/formatter_rspec_3'
|
|
17
|
+
Pact::Provider::RSpec::Formatter
|
|
18
|
+
else
|
|
19
|
+
require 'pact/provider/rspec/formatter_rspec_2'
|
|
20
|
+
Pact::Provider::RSpec::Formatter2
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.full_description example
|
|
26
|
+
example.respond_to?(:full_description) ? example.full_description : example.example.full_description
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.runner_defined?
|
|
30
|
+
defined?(::RSpec::Core::Runner)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.is_rspec_3
|
|
34
|
+
::RSpec::Core::Formatters.respond_to?(:register)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.is_rspec_2
|
|
38
|
+
!is_rspec_3
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.with_rspec_3
|
|
42
|
+
if is_rspec_3
|
|
43
|
+
yield
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.with_rspec_2
|
|
48
|
+
if is_rspec_2
|
|
49
|
+
yield
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|