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,40 @@
|
|
|
1
|
+
require 'pact/shared/dsl'
|
|
2
|
+
require 'pact/consumer/configuration/mock_service'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module Consumer
|
|
6
|
+
module Configuration
|
|
7
|
+
class ServiceProvider
|
|
8
|
+
|
|
9
|
+
extend Pact::DSL
|
|
10
|
+
|
|
11
|
+
attr_accessor :service, :consumer_name, :name
|
|
12
|
+
|
|
13
|
+
def initialize name, consumer_name
|
|
14
|
+
@name = name
|
|
15
|
+
@service = nil
|
|
16
|
+
@consumer_name = consumer_name
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
dsl do
|
|
20
|
+
def mock_service name, &block
|
|
21
|
+
self.service = MockService.build(name, consumer_name, self.name, &block)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def finalize
|
|
26
|
+
validate
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def validate
|
|
32
|
+
raise "Please configure a service for #{name}" unless service
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'pact/configuration'
|
|
2
|
+
require 'pact/consumer/consumer_contract_builders'
|
|
3
|
+
require 'pact/consumer/consumer_contract_builder'
|
|
4
|
+
require 'pact/consumer/configuration/service_consumer'
|
|
5
|
+
require 'pact/consumer/configuration/service_provider'
|
|
6
|
+
require 'pact/consumer/configuration/dsl'
|
|
7
|
+
require 'pact/consumer/configuration/configuration_extensions'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
Pact.send(:extend, Pact::Consumer::DSL)
|
|
11
|
+
Pact::Configuration.send(:include, Pact::Consumer::Configuration::ConfigurationExtensions)
|
|
@@ -1,78 +1,75 @@
|
|
|
1
1
|
require 'uri'
|
|
2
2
|
require 'json/add/regexp'
|
|
3
|
-
require 'pact/json_warning'
|
|
4
3
|
require 'pact/logging'
|
|
4
|
+
require 'pact/consumer/mock_service_client'
|
|
5
|
+
require 'pact/consumer/interaction_builder'
|
|
5
6
|
|
|
6
7
|
module Pact
|
|
7
8
|
module Consumer
|
|
9
|
+
|
|
8
10
|
class ConsumerContractBuilder
|
|
9
11
|
|
|
10
|
-
include Pact::JsonWarning
|
|
11
12
|
include Pact::Logging
|
|
12
13
|
|
|
13
|
-
attr_reader :
|
|
14
|
-
attr_reader :consumer_contract
|
|
15
|
-
attr_reader :pactfile_write_mode
|
|
14
|
+
attr_reader :consumer_contract, :mock_service_base_url
|
|
16
15
|
|
|
17
16
|
def initialize(attributes)
|
|
18
|
-
@
|
|
19
|
-
@
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
@interaction_builder = nil
|
|
18
|
+
@consumer_contract_details = {
|
|
19
|
+
consumer: {name: attributes[:consumer_name]},
|
|
20
|
+
provider: {name: attributes[:provider_name]},
|
|
21
|
+
pactfile_write_mode: attributes[:pactfile_write_mode].to_s
|
|
22
|
+
}
|
|
23
|
+
@mock_service_client = MockServiceClient.new(attributes[:port])
|
|
24
|
+
@mock_service_base_url = "http://localhost:#{attributes[:port]}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def given(provider_state)
|
|
28
|
+
interaction_builder.given(provider_state)
|
|
30
29
|
end
|
|
31
30
|
|
|
32
|
-
def
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
def upon_receiving(description)
|
|
32
|
+
interaction_builder.upon_receiving(description)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def interaction_builder
|
|
36
|
+
@interaction_builder ||=
|
|
37
|
+
begin
|
|
38
|
+
interaction_builder = InteractionBuilder.new
|
|
39
|
+
interaction_builder.on_interaction_fully_defined do | interaction |
|
|
40
|
+
self.handle_interaction_fully_defined(interaction)
|
|
38
41
|
end
|
|
39
|
-
|
|
42
|
+
interaction_builder
|
|
40
43
|
end
|
|
41
44
|
end
|
|
42
45
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
def verify example_description
|
|
47
|
+
mock_service_client.verify example_description
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def log msg
|
|
51
|
+
mock_service_client.log msg
|
|
46
52
|
end
|
|
47
53
|
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
producer = self
|
|
51
|
-
interaction_builder.on_interaction_fully_defined do | interaction |
|
|
52
|
-
producer.handle_interaction_fully_defined(interaction)
|
|
53
|
-
end
|
|
54
|
-
@interactions["#{description} given #{@producer_state}"] ||= interaction_builder.interaction
|
|
55
|
-
consumer_contract.interactions = @interactions.values
|
|
56
|
-
interaction_builder
|
|
54
|
+
def write_pact
|
|
55
|
+
mock_service_client.write_pact @consumer_contract_details
|
|
57
56
|
end
|
|
58
57
|
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
def wait_for_interactions options = {}
|
|
59
|
+
wait_max_seconds = options.fetch(:wait_max_seconds, 3)
|
|
60
|
+
poll_interval = options.fetch(:poll_interval, 0.1)
|
|
61
|
+
mock_service_client.wait_for_interactions wait_max_seconds, poll_interval
|
|
63
62
|
end
|
|
64
63
|
|
|
65
|
-
def
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
raise response.body unless response.is_a? Net::HTTPSuccess
|
|
64
|
+
def handle_interaction_fully_defined interaction
|
|
65
|
+
mock_service_client.add_expected_interaction interaction #TODO: What will happen if duplicate added?
|
|
66
|
+
self.interaction_builder = nil
|
|
69
67
|
end
|
|
70
68
|
|
|
71
69
|
private
|
|
72
70
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
end
|
|
71
|
+
attr_reader :mock_service_client
|
|
72
|
+
attr_writer :interaction_builder
|
|
76
73
|
|
|
77
74
|
end
|
|
78
75
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'pact/reification'
|
|
3
|
+
require 'pact/consumer_contract/interaction'
|
|
4
|
+
|
|
5
|
+
module Pact
|
|
6
|
+
module Consumer
|
|
7
|
+
class InteractionBuilder
|
|
8
|
+
|
|
9
|
+
attr_reader :interaction
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@interaction = Interaction.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def upon_receiving description
|
|
16
|
+
@interaction.description = description
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def given provider_state
|
|
21
|
+
@interaction.provider_state = provider_state.nil? ? nil : provider_state.to_s
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def with(request_details)
|
|
26
|
+
interaction.request = Pact::Request::Expected.from_hash(request_details)
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def will_respond_with(response)
|
|
31
|
+
interaction.response = response
|
|
32
|
+
@callback.call interaction
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def on_interaction_fully_defined &block
|
|
37
|
+
@callback = block
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#
|
|
2
|
+
# When running in pactfile_write_mode :overwrite, all interactions are cleared from the
|
|
3
|
+
# pact file, and all new interactions should be distinct (unique description and provider state).
|
|
4
|
+
# When running in pactfile_write_mode :update, an interaction with the same description
|
|
5
|
+
# and provider state as an existing one will just overwrite that one interaction.
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
module Pact
|
|
9
|
+
module Consumer
|
|
10
|
+
|
|
11
|
+
#TODO: think of a better word than filter
|
|
12
|
+
class InteractionsFilter
|
|
13
|
+
def initialize interactions = []
|
|
14
|
+
@interactions = interactions
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def index_of interaction
|
|
18
|
+
@interactions.find_index{ |i| i.matches_criteria?(description: interaction.description, provider_state: interaction.provider_state)}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class UpdatableInteractionsFilter < InteractionsFilter
|
|
23
|
+
|
|
24
|
+
def << interaction
|
|
25
|
+
if (ndx = index_of(interaction))
|
|
26
|
+
@interactions[ndx] = interaction
|
|
27
|
+
else
|
|
28
|
+
@interactions << interaction
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class DistinctInteractionsFilter < InteractionsFilter
|
|
35
|
+
|
|
36
|
+
def << interaction
|
|
37
|
+
if (ndx = index_of(interaction))
|
|
38
|
+
if @interactions[ndx] != interaction
|
|
39
|
+
raise "Interaction with same description (#{interaction.description}) and provider state (#{interaction.provider_state}) already exists"
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
@interactions << interaction
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
require 'json'
|
|
3
|
+
require 'logger'
|
|
4
|
+
require 'awesome_print'
|
|
5
|
+
require 'awesome_print/core_ext/logger' #For some reason we get an error indicating that the method 'ap' is private unless we load this specifically
|
|
6
|
+
require 'pact/consumer/request'
|
|
7
|
+
require 'pact/consumer/mock_service/interaction_list'
|
|
8
|
+
require 'pact/consumer/mock_service/interaction_delete'
|
|
9
|
+
require 'pact/consumer/mock_service/interaction_post'
|
|
10
|
+
require 'pact/consumer/mock_service/interaction_replay'
|
|
11
|
+
require 'pact/consumer/mock_service/missing_interactions_get'
|
|
12
|
+
require 'pact/consumer/mock_service/verification_get'
|
|
13
|
+
require 'pact/consumer/mock_service/log_get'
|
|
14
|
+
require 'pact/consumer/mock_service/pact_post'
|
|
15
|
+
|
|
16
|
+
AwesomePrint.defaults = {
|
|
17
|
+
indent: -2,
|
|
18
|
+
plain: true,
|
|
19
|
+
index: false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module Pact
|
|
23
|
+
module Consumer
|
|
24
|
+
|
|
25
|
+
class MockService
|
|
26
|
+
|
|
27
|
+
def initialize options = {}
|
|
28
|
+
log_description = configure_logger options
|
|
29
|
+
interaction_list = InteractionList.new
|
|
30
|
+
|
|
31
|
+
@name = options.fetch(:name, "MockService")
|
|
32
|
+
interactions = []
|
|
33
|
+
@handlers = [
|
|
34
|
+
MissingInteractionsGet.new(@name, @logger, interaction_list),
|
|
35
|
+
VerificationGet.new(@name, @logger, interaction_list, log_description),
|
|
36
|
+
InteractionPost.new(@name, @logger, interaction_list),
|
|
37
|
+
InteractionDelete.new(@name, @logger, interaction_list),
|
|
38
|
+
LogGet.new(@name, @logger),
|
|
39
|
+
PactPost.new(@name, @logger, interactions),
|
|
40
|
+
InteractionReplay.new(@name, @logger, interaction_list, interactions)
|
|
41
|
+
]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def configure_logger options
|
|
45
|
+
options = {log_file: $stdout}.merge options
|
|
46
|
+
log_stream = options[:log_file]
|
|
47
|
+
@logger = Logger.new log_stream
|
|
48
|
+
@logger.level = Pact.configuration.logger.level
|
|
49
|
+
|
|
50
|
+
if log_stream.is_a? File
|
|
51
|
+
File.absolute_path(log_stream).gsub(Dir.pwd + "/", '')
|
|
52
|
+
else
|
|
53
|
+
"standard out/err"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def to_s
|
|
58
|
+
"#{@name} #{super.to_s}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def call env
|
|
62
|
+
response = []
|
|
63
|
+
begin
|
|
64
|
+
relevant_handler = @handlers.detect { |handler| handler.match? env }
|
|
65
|
+
response = relevant_handler.respond env
|
|
66
|
+
rescue StandardError => e
|
|
67
|
+
@logger.error 'Error ocurred in mock service:'
|
|
68
|
+
@logger.ap e, :error
|
|
69
|
+
@logger.ap e.backtrace
|
|
70
|
+
response = [500, {'Content-Type' => 'application/json'}, [{message: e.message, backtrace: e.backtrace}.to_json]]
|
|
71
|
+
rescue Exception => e
|
|
72
|
+
@logger.error 'Exception ocurred in mock service:'
|
|
73
|
+
@logger.ap e, :error
|
|
74
|
+
@logger.ap e.backtrace
|
|
75
|
+
raise e
|
|
76
|
+
end
|
|
77
|
+
response
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'pact/consumer/mock_service/rack_request_helper'
|
|
2
|
+
require 'pact/consumer/mock_service/mock_service_administration_endpoint'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module Consumer
|
|
6
|
+
|
|
7
|
+
class InteractionDelete < MockServiceAdministrationEndpoint
|
|
8
|
+
|
|
9
|
+
include RackRequestHelper
|
|
10
|
+
|
|
11
|
+
attr_accessor :interaction_list
|
|
12
|
+
|
|
13
|
+
def initialize name, logger, interaction_list
|
|
14
|
+
super name, logger
|
|
15
|
+
@interaction_list = interaction_list
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def request_path
|
|
19
|
+
'/interactions'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def request_method
|
|
23
|
+
'DELETE'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def respond env
|
|
27
|
+
interaction_list.clear
|
|
28
|
+
logger.info "Cleared interactions before example \"#{params_hash(env)['example_description']}\""
|
|
29
|
+
[200, {}, ['Deleted interactions']]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Pact
|
|
2
|
+
module Consumer
|
|
3
|
+
class InteractionList
|
|
4
|
+
|
|
5
|
+
attr_reader :interactions
|
|
6
|
+
attr_reader :unexpected_requests
|
|
7
|
+
attr_reader :interaction_mismatches
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
clear
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# For testing, sigh
|
|
14
|
+
def clear
|
|
15
|
+
@interactions = []
|
|
16
|
+
@matched_interactions = []
|
|
17
|
+
@interaction_mismatches = []
|
|
18
|
+
@unexpected_requests = []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add interactions
|
|
22
|
+
@interactions << interactions
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def register_matched interaction
|
|
26
|
+
@matched_interactions << interaction
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def register_unexpected_request request
|
|
30
|
+
@unexpected_requests << request
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def register_interaction_mismatch interaction_mismatch
|
|
34
|
+
@interaction_mismatches << interaction_mismatch
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def all_matched?
|
|
38
|
+
interaction_diffs.empty?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def missing_interactions
|
|
42
|
+
@interactions - @matched_interactions - @interaction_mismatches.collect(&:candidate_interactions).flatten
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def missing_interactions_summaries
|
|
46
|
+
missing_interactions.collect(&:request).collect(&:method_and_path)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def interaction_mismatches_summaries
|
|
50
|
+
interaction_mismatches.collect(&:short_summary)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def unexpected_requests_summaries
|
|
54
|
+
unexpected_requests.collect(&:method_and_path)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def interaction_diffs
|
|
58
|
+
{
|
|
59
|
+
:missing_interactions => missing_interactions_summaries,
|
|
60
|
+
:interaction_mismatches => interaction_mismatches_summaries,
|
|
61
|
+
:unexpected_requests => unexpected_requests_summaries
|
|
62
|
+
}.inject({}) do | hash, pair |
|
|
63
|
+
hash[pair.first] = pair.last if pair.last.any?
|
|
64
|
+
hash
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def find_candidate_interactions actual_request
|
|
69
|
+
interactions.select do | interaction |
|
|
70
|
+
interaction.request.matches_route? actual_request
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Pact
|
|
2
|
+
module Consumer
|
|
3
|
+
|
|
4
|
+
# Presents the differences between an actual request, and a list of
|
|
5
|
+
# expected interactions where the methods and paths match the actual request.
|
|
6
|
+
# This is used to display a helpful message to the user when a request
|
|
7
|
+
# comes in that doesn't match any of the expected interactions.
|
|
8
|
+
class InteractionMismatch
|
|
9
|
+
|
|
10
|
+
attr_accessor :candidate_interactions, :actual_request
|
|
11
|
+
|
|
12
|
+
# Assumes the method and path matches...
|
|
13
|
+
|
|
14
|
+
def initialize candidate_interactions, actual_request
|
|
15
|
+
@candidate_interactions = candidate_interactions
|
|
16
|
+
@actual_request = actual_request
|
|
17
|
+
@candiate_diffs = candidate_interactions.collect{ | candidate_interaction| CandidateDiff.new(candidate_interaction, actual_request)}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_hash
|
|
21
|
+
candiate_diffs.collect(&:to_hash)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def to_s
|
|
25
|
+
candiate_diffs.collect(&:to_s).join("\n")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def short_summary
|
|
29
|
+
mismatched_attributes = candiate_diffs.collect(&:mismatched_attributes).flatten.uniq.join(", ").reverse.sub(",", "dna ").reverse #OMG what a hack!
|
|
30
|
+
actual_request.method_and_path + " (request #{mismatched_attributes} did not match)"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
attr_accessor :candiate_diffs
|
|
36
|
+
|
|
37
|
+
class CandidateDiff
|
|
38
|
+
|
|
39
|
+
attr_accessor :candidate_interaction, :actual_request
|
|
40
|
+
|
|
41
|
+
def initialize candidate_interaction, actual_request
|
|
42
|
+
@candidate_interaction = candidate_interaction
|
|
43
|
+
@actual_request = actual_request
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def mismatched_attributes
|
|
47
|
+
diff.keys
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_hash
|
|
51
|
+
summary = {:description => candidate_interaction.description}
|
|
52
|
+
summary[:provider_state] = candidate_interaction.provider_state if candidate_interaction.provider_state
|
|
53
|
+
summary.merge(diff)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def to_s
|
|
57
|
+
[
|
|
58
|
+
"Diff with interaction: #{candidate_interaction.description_with_provider_state_quoted}",
|
|
59
|
+
diff_formatter.call(diff, {colour: false})
|
|
60
|
+
].join("\n")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def diff_formatter
|
|
64
|
+
Pact.configuration.diff_formatter_for_content_type(candidate_interaction.request.content_type)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def diff
|
|
68
|
+
@diff ||= candidate_interaction.request.difference(actual_request)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'pact/consumer/mock_service/mock_service_administration_endpoint'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module Consumer
|
|
5
|
+
class InteractionPost < MockServiceAdministrationEndpoint
|
|
6
|
+
|
|
7
|
+
attr_accessor :interaction_list
|
|
8
|
+
|
|
9
|
+
def initialize name, logger, interaction_list
|
|
10
|
+
super name, logger
|
|
11
|
+
@interaction_list = interaction_list
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def request_path
|
|
15
|
+
'/interactions'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def request_method
|
|
19
|
+
'POST'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def respond env
|
|
23
|
+
interaction = Interaction.from_hash(JSON.load(env['rack.input'].string))
|
|
24
|
+
interaction_list.add interaction
|
|
25
|
+
logger.info "Registered expected interaction #{interaction.request.method_and_path}"
|
|
26
|
+
logger.debug JSON.pretty_generate JSON.parse(interaction.to_json)
|
|
27
|
+
[200, {}, ['Added interaction']]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|