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,82 @@
|
|
|
1
|
+
require 'pact/doc/doc_file'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module Doc
|
|
6
|
+
|
|
7
|
+
class Generator
|
|
8
|
+
|
|
9
|
+
def initialize pact_dir, doc_dir, options
|
|
10
|
+
@doc_dir = doc_dir
|
|
11
|
+
@pact_dir = pact_dir
|
|
12
|
+
@consumer_contract_renderer = options[:consumer_contract_renderer]
|
|
13
|
+
@doc_type = options[:doc_type]
|
|
14
|
+
@file_extension = options[:file_extension]
|
|
15
|
+
@index_renderer = options[:index_renderer]
|
|
16
|
+
@index_name = options[:index_name]
|
|
17
|
+
@after = options.fetch(:after, lambda{|pact_dir, target_dir, consumer_contracts| })
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call
|
|
21
|
+
ensure_target_dir_exists_and_is_clean
|
|
22
|
+
write_index if consumer_contracts.any?
|
|
23
|
+
write_doc_files
|
|
24
|
+
perform_after_hook
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
attr_reader :doc_dir, :pact_dir, :consumer_contract_renderer, :doc_type, :file_extension, :index_renderer, :after
|
|
30
|
+
|
|
31
|
+
def write_index
|
|
32
|
+
File.open(index_file_path, "w") { |io| io << index_file_contents }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def index_file_path
|
|
36
|
+
File.join(target_dir, "#{@index_name}#{file_extension}")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def index_file_contents
|
|
40
|
+
index_renderer.call(consumer_contracts.first.consumer.name, index_data)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def index_data
|
|
44
|
+
doc_files.each_with_object({}) do | doc_file, data |
|
|
45
|
+
data[doc_file.title] = doc_file.name
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def write_doc_files
|
|
50
|
+
doc_files.each(&:write)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def doc_files
|
|
54
|
+
consumer_contracts.collect do | consumer_contract |
|
|
55
|
+
DocFile.new(consumer_contract, target_dir, consumer_contract_renderer, file_extension)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def consumer_contracts
|
|
60
|
+
@consumer_contracts ||= begin
|
|
61
|
+
Dir.glob("#{pact_dir}/**").collect do |file|
|
|
62
|
+
Pact::ConsumerContract.from_uri file
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def perform_after_hook
|
|
68
|
+
after.call(pact_dir, target_dir, consumer_contracts)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def ensure_target_dir_exists_and_is_clean
|
|
72
|
+
FileUtils.rm_rf target_dir
|
|
73
|
+
FileUtils.mkdir_p target_dir
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def target_dir
|
|
77
|
+
File.join(doc_dir, doc_type)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require 'pact/shared/active_support_support'
|
|
2
|
+
require 'pact/reification'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module Doc
|
|
6
|
+
class InteractionViewModel
|
|
7
|
+
|
|
8
|
+
include Pact::ActiveSupportSupport
|
|
9
|
+
|
|
10
|
+
def initialize interaction, consumer_contract
|
|
11
|
+
@interaction = interaction
|
|
12
|
+
@consumer_contract = consumer_contract
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def id
|
|
16
|
+
@id ||= begin
|
|
17
|
+
if has_provider_state?
|
|
18
|
+
"#{description} given #{interaction.provider_state}"
|
|
19
|
+
else
|
|
20
|
+
interaction.description
|
|
21
|
+
end.gsub(/\s+/,'_')
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def request_method
|
|
26
|
+
interaction.request.method.upcase
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def request_path
|
|
30
|
+
interaction.request.path
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def response_status
|
|
34
|
+
interaction.response['status']
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def consumer_name
|
|
38
|
+
@consumer_contract.consumer.name
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def provider_name
|
|
42
|
+
@consumer_contract.provider.name
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def has_provider_state?
|
|
46
|
+
@interaction.provider_state && !@interaction.provider_state.empty?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def provider_state start_of_sentence = false
|
|
50
|
+
apply_capitals(@interaction.provider_state.strip, start_of_sentence)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def description start_of_sentence = false
|
|
54
|
+
apply_capitals(@interaction.description.strip, start_of_sentence)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def request
|
|
58
|
+
fix_json_formatting JSON.pretty_generate(clean_request)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def response
|
|
62
|
+
fix_json_formatting JSON.pretty_generate(clean_response)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
attr_reader :interaction, :consumer_contract
|
|
68
|
+
|
|
69
|
+
def clean_request
|
|
70
|
+
ordered_clean_hash Reification.from_term(interaction.request)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def clean_response
|
|
74
|
+
ordered_clean_hash Reification.from_term(interaction.response)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Remove empty body and headers hashes from response, and empty headers from request,
|
|
78
|
+
# as an empty hash means "allow anything" - it's more intuitive and cleaner to just
|
|
79
|
+
# remove the empty hashes from display.
|
|
80
|
+
def ordered_clean_hash source
|
|
81
|
+
ordered_keys.each_with_object({}) do |key, target|
|
|
82
|
+
if source.key? key
|
|
83
|
+
target[key] = source[key] unless value_is_an_empty_hash_that_is_not_request_body(source[key], key)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def value_is_an_empty_hash_that_is_not_request_body value, key
|
|
89
|
+
value.is_a?(Hash) && value.empty? && key != :body
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def ordered_keys
|
|
93
|
+
[:method, :path, :query, :headers, :body, "status", "headers","body"]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def remove_key_if_empty key, hash
|
|
97
|
+
hash.delete(key) if hash[key].is_a?(Hash) && hash[key].empty?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def apply_capitals string, start_of_sentence = false
|
|
101
|
+
start_of_sentence ? capitalize_first_letter(string) : lowercase_first_letter(string)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def capitalize_first_letter string
|
|
105
|
+
string[0].upcase + string[1..-1]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def lowercase_first_letter string
|
|
109
|
+
string[0].downcase + string[1..-1]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'pact/doc/markdown/interaction_renderer'
|
|
2
|
+
require 'pact/doc/sort_interactions'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module Doc
|
|
6
|
+
module Markdown
|
|
7
|
+
class ConsumerContractRenderer
|
|
8
|
+
|
|
9
|
+
def initialize consumer_contract
|
|
10
|
+
@consumer_contract = consumer_contract
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.call consumer_contract
|
|
14
|
+
new(consumer_contract).call
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def call
|
|
18
|
+
title + summaries_title + summaries.join + interactions_title + full_interactions.join
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
attr_reader :consumer_contract
|
|
24
|
+
|
|
25
|
+
def title
|
|
26
|
+
"### A pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}\n\n"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def interaction_renderers
|
|
30
|
+
@interaction_renderers ||= sorted_interactions.collect{|interaction| InteractionRenderer.new interaction, @consumer_contract}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def summaries_title
|
|
34
|
+
"#### Requests from #{consumer_contract.consumer.name} to #{consumer_contract.provider.name}\n\n"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def interactions_title
|
|
38
|
+
"#### Interactions\n\n"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def summaries
|
|
42
|
+
interaction_renderers.collect(&:render_summary)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def full_interactions
|
|
46
|
+
interaction_renderers.collect(&:render_full_interaction)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def sorted_interactions
|
|
50
|
+
SortInteractions.call(consumer_contract.interactions)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'pact/doc/generator'
|
|
2
|
+
require 'pact/doc/markdown/consumer_contract_renderer'
|
|
3
|
+
require 'pact/doc/markdown/index_renderer'
|
|
4
|
+
|
|
5
|
+
module Pact
|
|
6
|
+
module Doc
|
|
7
|
+
module Markdown
|
|
8
|
+
class Generator < Pact::Doc::Generator
|
|
9
|
+
|
|
10
|
+
def initialize pact_dir, doc_dir
|
|
11
|
+
super(pact_dir, doc_dir,
|
|
12
|
+
consumer_contract_renderer: ConsumerContractRenderer,
|
|
13
|
+
doc_type: 'markdown',
|
|
14
|
+
file_extension: '.md',
|
|
15
|
+
index_renderer: IndexRenderer,
|
|
16
|
+
index_name: 'README')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.call pact_dir, doc_dir
|
|
20
|
+
new(pact_dir, doc_dir).call
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Pact
|
|
2
|
+
module Doc
|
|
3
|
+
module Markdown
|
|
4
|
+
class IndexRenderer
|
|
5
|
+
|
|
6
|
+
attr_reader :consumer_name
|
|
7
|
+
attr_reader :docs # Hash of pact title => file_name
|
|
8
|
+
|
|
9
|
+
def initialize consumer_name, docs
|
|
10
|
+
@consumer_name = consumer_name
|
|
11
|
+
@docs = docs
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.call consumer_name, docs
|
|
15
|
+
new(consumer_name, docs).call
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call
|
|
19
|
+
title + "\n\n" + table_of_contents + "\n"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def table_of_contents
|
|
25
|
+
docs.collect do | title, file_name |
|
|
26
|
+
item title, file_name
|
|
27
|
+
end.join("\n")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def title
|
|
31
|
+
"### Pacts for #{consumer_name}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def item title, file_name
|
|
35
|
+
"* [#{title}](#{file_name})"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<a name="<%= interaction.id %>"></a>
|
|
2
|
+
<%= if interaction.has_provider_state?
|
|
3
|
+
"Given **#{interaction.provider_state}**, upon receiving"
|
|
4
|
+
else
|
|
5
|
+
"Upon receiving"
|
|
6
|
+
end
|
|
7
|
+
%> **<%= interaction.description %>** from <%= interaction.consumer_name %>, with
|
|
8
|
+
```json
|
|
9
|
+
<%= interaction.request %>
|
|
10
|
+
```
|
|
11
|
+
<%= interaction.provider_name %> will respond with:
|
|
12
|
+
```json
|
|
13
|
+
<%= interaction.response %>
|
|
14
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'pact/doc/interaction_view_model'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module Doc
|
|
5
|
+
module Markdown
|
|
6
|
+
class InteractionRenderer
|
|
7
|
+
|
|
8
|
+
attr_reader :interaction
|
|
9
|
+
|
|
10
|
+
def initialize interaction, pact
|
|
11
|
+
@interaction = InteractionViewModel.new(interaction, pact)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render_summary
|
|
15
|
+
suffix = interaction.has_provider_state? ? " given #{interaction.provider_state}" : ""
|
|
16
|
+
"* [#{interaction.description(true)}](##{interaction.id})#{suffix}\n\n"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def render_full_interaction
|
|
20
|
+
render('/interaction.erb')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def render template_file
|
|
24
|
+
ERB.new(template_string(template_file)).result(binding)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def template_string(template_file)
|
|
28
|
+
File.read( template_contents(template_file) )
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def template_contents(template_file)
|
|
32
|
+
File.dirname(__FILE__) + template_file
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Pact
|
|
2
|
+
module Doc
|
|
3
|
+
class SortInteractions
|
|
4
|
+
|
|
5
|
+
def self.call interactions
|
|
6
|
+
interactions.sort{|a, b| sortable_id(a) <=> sortable_id(b)}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def self.sortable_id interaction
|
|
12
|
+
"#{interaction.description.downcase} #{interaction.response['status']} #{(interaction.provider_state || '').downcase}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'pact/matchers/expected_type'
|
|
2
|
+
require 'pact/matchers/actual_type'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module Matchers
|
|
6
|
+
class BaseDifference
|
|
7
|
+
|
|
8
|
+
attr_reader :expected, :actual
|
|
9
|
+
|
|
10
|
+
def initialize expected, actual
|
|
11
|
+
@expected = expected
|
|
12
|
+
@actual = actual
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def any?
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def empty?
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_json options = {}
|
|
24
|
+
as_json.to_json(options)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_s
|
|
28
|
+
as_json.to_s
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def == other
|
|
32
|
+
other.class == self.class && other.expected == expected && other.actual == actual
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Ripped from RSpec::Expectations::Differ in rspec/expectations/differ.rb in rspec-expectations 2.14.3
|
|
2
|
+
# Thank you kindly to the original author.
|
|
3
|
+
# Needed to be able to turn the colour off, but can't set RSpec.configuration.color to false
|
|
4
|
+
# once it has been set to true due to a "if bool" at the start of the color= method
|
|
5
|
+
|
|
6
|
+
require 'diff/lcs'
|
|
7
|
+
require 'diff/lcs/hunk'
|
|
8
|
+
require 'pp'
|
|
9
|
+
|
|
10
|
+
module Pact
|
|
11
|
+
module Matchers
|
|
12
|
+
class Differ
|
|
13
|
+
|
|
14
|
+
def initialize(color = false)
|
|
15
|
+
@color = color
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# This is snagged from diff/lcs/ldiff.rb (which is a commandline tool)
|
|
19
|
+
def diff_as_string(input_data_new, input_data_old)
|
|
20
|
+
output = matching_encoding("", input_data_old)
|
|
21
|
+
data_old = input_data_old.split(matching_encoding("\n", input_data_old)).map! { |e| e.chomp }
|
|
22
|
+
data_new = input_data_new.split(matching_encoding("\n", input_data_new)).map! { |e| e.chomp }
|
|
23
|
+
diffs = Diff::LCS.diff(data_old, data_new)
|
|
24
|
+
return output if diffs.empty?
|
|
25
|
+
oldhunk = hunk = nil
|
|
26
|
+
file_length_difference = 0
|
|
27
|
+
diffs.each do |piece|
|
|
28
|
+
begin
|
|
29
|
+
hunk = Diff::LCS::Hunk.new(
|
|
30
|
+
data_old, data_new, piece, context_lines, file_length_difference
|
|
31
|
+
)
|
|
32
|
+
file_length_difference = hunk.file_length_difference
|
|
33
|
+
next unless oldhunk
|
|
34
|
+
# Hunks may overlap, which is why we need to be careful when our
|
|
35
|
+
# diff includes lines of context. Otherwise, we might print
|
|
36
|
+
# redundant lines.
|
|
37
|
+
if (context_lines > 0) and hunk.overlaps?(oldhunk)
|
|
38
|
+
if hunk.respond_to?(:merge)
|
|
39
|
+
# diff-lcs 1.2.x
|
|
40
|
+
hunk.merge(oldhunk)
|
|
41
|
+
else
|
|
42
|
+
# diff-lcs 1.1.3
|
|
43
|
+
hunk.unshift(oldhunk)
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
output << matching_encoding(oldhunk.diff(format).to_s, output)
|
|
47
|
+
end
|
|
48
|
+
ensure
|
|
49
|
+
oldhunk = hunk
|
|
50
|
+
output << matching_encoding("\n", output)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
#Handle the last remaining hunk
|
|
54
|
+
output << matching_encoding(oldhunk.diff(format).to_s,output)
|
|
55
|
+
output << matching_encoding("\n",output)
|
|
56
|
+
color_diff output
|
|
57
|
+
rescue Encoding::CompatibilityError
|
|
58
|
+
if input_data_new.encoding != input_data_old.encoding
|
|
59
|
+
"Could not produce a diff because the encoding of the actual string (#{input_data_old.encoding}) "+
|
|
60
|
+
"differs from the encoding of the expected string (#{input_data_new.encoding})"
|
|
61
|
+
else
|
|
62
|
+
"Could not produce a diff because of the encoding of the string (#{input_data_old.encoding})"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def diff_as_object(actual, expected)
|
|
67
|
+
actual_as_string = object_to_string(actual)
|
|
68
|
+
expected_as_string = object_to_string(expected)
|
|
69
|
+
if diff = diff_as_string(actual_as_string, expected_as_string)
|
|
70
|
+
color_diff diff
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def red(text)
|
|
75
|
+
return text unless @color
|
|
76
|
+
color(text, 31)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def green(text)
|
|
80
|
+
return text unless @color
|
|
81
|
+
color(text, 32)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
protected
|
|
85
|
+
|
|
86
|
+
def format
|
|
87
|
+
:unified
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def context_lines
|
|
91
|
+
3
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def color(text, color_code)
|
|
95
|
+
"\e[#{color_code}m#{text}\e[0m"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def blue(text)
|
|
100
|
+
color(text, 34)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def color_diff(diff)
|
|
104
|
+
return diff unless @color
|
|
105
|
+
|
|
106
|
+
diff.lines.map { |line|
|
|
107
|
+
case line[0].chr
|
|
108
|
+
when "+"
|
|
109
|
+
green line
|
|
110
|
+
when "-"
|
|
111
|
+
red line
|
|
112
|
+
when "@"
|
|
113
|
+
line[1].chr == "@" ? blue(line) : line
|
|
114
|
+
else
|
|
115
|
+
line
|
|
116
|
+
end
|
|
117
|
+
}.join
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def object_to_string(object)
|
|
121
|
+
case object
|
|
122
|
+
when Hash
|
|
123
|
+
object.keys.sort_by { |k| k.to_s }.map do |key|
|
|
124
|
+
pp_key = PP.singleline_pp(key, "")
|
|
125
|
+
pp_value = PP.singleline_pp(object[key], "")
|
|
126
|
+
|
|
127
|
+
# on 1.9.3 PP seems to minimise to US-ASCII, ensure we're matching source encoding
|
|
128
|
+
#
|
|
129
|
+
# note, PP is used to ensure the ordering of the internal values of key/value e.g.
|
|
130
|
+
# <# a: b: c:> not <# c: a: b:>
|
|
131
|
+
matching_encoding("#{pp_key} => #{pp_value}", key.to_s)
|
|
132
|
+
end.join(",\n")
|
|
133
|
+
when String
|
|
134
|
+
object =~ /\n/ ? object : object.inspect
|
|
135
|
+
else
|
|
136
|
+
PP.pp(object,"")
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if String.method_defined?(:encoding)
|
|
141
|
+
def matching_encoding(string, source)
|
|
142
|
+
string.encode(source.encoding)
|
|
143
|
+
end
|
|
144
|
+
else
|
|
145
|
+
def matching_encoding(string, source)
|
|
146
|
+
string
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'pact/shared/active_support_support'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
class DifferenceIndicator
|
|
5
|
+
|
|
6
|
+
include ActiveSupportSupport
|
|
7
|
+
|
|
8
|
+
def == other
|
|
9
|
+
other.class == self.class
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def eql? other
|
|
13
|
+
self == other
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_json options = {}
|
|
17
|
+
remove_unicode as_json.to_json(options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def as_json options = {}
|
|
21
|
+
to_s
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'pact/shared/active_support_support'
|
|
2
|
+
require 'term/ansicolor'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module Matchers
|
|
6
|
+
class EmbeddedDiffFormatter
|
|
7
|
+
|
|
8
|
+
include Pact::ActiveSupportSupport
|
|
9
|
+
C = ::Term::ANSIColor
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
EXPECTED = /"EXPECTED([A-Z_]*)":/
|
|
13
|
+
|
|
14
|
+
ACTUAL = /"ACTUAL([A-Z_]*)":/
|
|
15
|
+
|
|
16
|
+
attr_reader :diff, :colour
|
|
17
|
+
|
|
18
|
+
def initialize diff, options = {}
|
|
19
|
+
@diff = diff
|
|
20
|
+
@colour = options.fetch(:colour, false)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.call diff, options = {colour: Pact.configuration.color_enabled}
|
|
24
|
+
new(diff, options).call
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def call
|
|
28
|
+
to_s
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_hash
|
|
32
|
+
diff
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def to_s
|
|
36
|
+
colourise_message_if_configured fix_json_formatting(diff.to_json)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def colourise_message_if_configured message
|
|
40
|
+
if colour
|
|
41
|
+
colourise_message message
|
|
42
|
+
else
|
|
43
|
+
message
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def colourise_message message
|
|
48
|
+
message.split("\n").collect{| line | colourise(line) }.join("\n")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def colourise line
|
|
52
|
+
line.gsub(EXPECTED){|match| coloured_key match, :red }.gsub(ACTUAL){ | match | coloured_key match, :green }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def coloured_key match, colour
|
|
56
|
+
'"' + C.color(colour, match.downcase.gsub(/^"|":$/,'')) + '":'
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
end
|