pact-mock_service 0.0.1
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.
- checksums.yaml +15 -0
- data/.gitignore +29 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +100 -0
- data/LICENSE.txt +22 -0
- data/README.md +314 -0
- data/Rakefile +6 -0
- data/bin/pact-mock-service +3 -0
- data/lib/pact/consumer/app_manager.rb +159 -0
- data/lib/pact/consumer/interactions_filter.rb +48 -0
- data/lib/pact/consumer/mock_service/app.rb +84 -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 +2 -0
- 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/server.rb +90 -0
- data/lib/pact/consumer_contract/consumer_contract_writer.rb +84 -0
- data/lib/pact/mock_service/cli.rb +49 -0
- data/lib/pact/mock_service/version.rb +5 -0
- data/lib/pact/mock_service.rb +1 -0
- data/pact-mock-service.gemspec +41 -0
- data/spec/lib/pact/consumer/app_manager_spec.rb +42 -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/service_consumer_spec.rb +11 -0
- data/spec/lib/pact/consumer_contract/consumer_contract_writer_spec.rb +111 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/support/a_consumer-a_producer.json +32 -0
- 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/case-insensitive-response-header-matching.json +21 -0
- data/spec/support/case-insensitive-response-header-matching.rb +15 -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 +61 -0
- data/spec/support/test_app_pass.json +38 -0
- data/spec/support/test_app_with_right_content_type_differ.json +23 -0
- data/tasks/spec.rake +6 -0
- metadata +388 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pact/consumer/mock_service/interaction_mismatch'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module Consumer
|
6
|
+
describe InteractionMismatch do
|
7
|
+
let(:content_type) { 'some/content' }
|
8
|
+
let(:actual_request) { instance_double('Pact::Consumer::Request::Actual', :method_and_path => 'GET /path') }
|
9
|
+
let(:expected_request_1) { instance_double('Pact::Request::Expected', :content_type => content_type) }
|
10
|
+
let(:expected_request_2) { instance_double('Pact::Request::Expected', :content_type => content_type) }
|
11
|
+
let(:candidate_1) { instance_double('Pact::Interaction', request: expected_request_1, description_with_provider_state_quoted: "desc 1") }
|
12
|
+
let(:candidate_2) { instance_double('Pact::Interaction', request: expected_request_2, description_with_provider_state_quoted: "desc 2") }
|
13
|
+
let(:candidate_interactions) { [candidate_1, candidate_2] }
|
14
|
+
subject { InteractionMismatch.new(candidate_interactions, actual_request) }
|
15
|
+
let(:diff_1) { {body: 'diff'} }
|
16
|
+
let(:diff_2) { {} }
|
17
|
+
|
18
|
+
before do
|
19
|
+
allow(expected_request_1).to receive(:difference).with(actual_request).and_return(diff_1)
|
20
|
+
allow(expected_request_2).to receive(:difference).with(actual_request).and_return(diff_2)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "short_summary" do
|
24
|
+
it "includes the method and path" do
|
25
|
+
expect(subject.short_summary).to match /GET \/path \(.*\)/
|
26
|
+
end
|
27
|
+
context "when the body does not match" do
|
28
|
+
let(:diff_1) { {body: nil} }
|
29
|
+
|
30
|
+
it "returns a message indicating that the body does not match" do
|
31
|
+
expect(subject.short_summary).to include "(request body did not match)"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
context "when the headers do not match" do
|
35
|
+
let(:diff_1) { {headers: nil} }
|
36
|
+
it "returns a message indicating that the body does not match" do
|
37
|
+
expect(subject.short_summary).to include "(request headers did not match)"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
context "when the headers and body do not match" do
|
41
|
+
let(:diff_1) { {body: nil, headers: nil} }
|
42
|
+
let(:diff_2) { {body: nil, headers: nil} }
|
43
|
+
it "returns a message indicating that the headers and body do not match" do
|
44
|
+
expect(subject.short_summary).to include "(request body and headers did not match)"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "to_s" do
|
50
|
+
let(:expected_message) { "Diff with interaction: desc 1\ndiff 1\nDiff with interaction: desc 2\ndiff 2" }
|
51
|
+
|
52
|
+
let(:diff_formatter) { double("diff_formatter")}
|
53
|
+
before do
|
54
|
+
allow(Pact.configuration).to receive(:diff_formatter_for_content_type).with(content_type).and_return(diff_formatter)
|
55
|
+
allow(diff_formatter).to receive(:call).and_return("diff 1", "diff 2")
|
56
|
+
end
|
57
|
+
|
58
|
+
it "creates diff output using the configured diff_formatter" do
|
59
|
+
expect(diff_formatter).to receive(:call).with(diff_1, colour: false)
|
60
|
+
expect(diff_formatter).to receive(:call).with(diff_2, colour: false)
|
61
|
+
subject.to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
it "includes a diff output in the string output" do
|
65
|
+
expect(subject.to_s).to eq expected_message
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pact/consumer/mock_service/rack_request_helper'
|
3
|
+
|
4
|
+
module Pact::Consumer
|
5
|
+
|
6
|
+
describe RackRequestHelper do
|
7
|
+
class TestSubject
|
8
|
+
include RackRequestHelper
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:rack_env) {
|
12
|
+
{
|
13
|
+
"CONTENT_LENGTH" => "16",
|
14
|
+
"CONTENT_TYPE" => content_type,
|
15
|
+
"GATEWAY_INTERFACE" => "CGI/1.1",
|
16
|
+
"PATH_INFO" => "/donuts",
|
17
|
+
"QUERY_STRING" => "",
|
18
|
+
"REMOTE_ADDR" => "127.0.0.1",
|
19
|
+
"REMOTE_HOST" => "localhost",
|
20
|
+
"REQUEST_METHOD" => "POST",
|
21
|
+
"REQUEST_URI" => "http://localhost:4321/donuts",
|
22
|
+
"SCRIPT_NAME" => "",
|
23
|
+
"SERVER_NAME" => "localhost",
|
24
|
+
"SERVER_PORT" => "4321",
|
25
|
+
"SERVER_PROTOCOL" => "HTTP/1.1",
|
26
|
+
"SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/1.9.3/2013-02-22)",
|
27
|
+
"HTTP_ACCEPT" => "text/plain",
|
28
|
+
"HTTP_USER_AGENT" => "Ruby",
|
29
|
+
"HTTP_HOST" => "localhost:4321",
|
30
|
+
"HTTP_X_SOMETHING" => "1, 2",
|
31
|
+
"rack.version" => [1, 2 ],
|
32
|
+
"rack.input" => StringIO.new(body),
|
33
|
+
"rack.errors" => nil,
|
34
|
+
"rack.multithread" => true,
|
35
|
+
"rack.multiprocess" => false,
|
36
|
+
"rack.run_once" => false,
|
37
|
+
"rack.url_scheme" => "http",
|
38
|
+
"HTTP_VERSION" => "HTTP/1.1",
|
39
|
+
"REQUEST_PATH" => "/donuts"
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
let(:content_type) { "" }
|
44
|
+
let(:body) { '' }
|
45
|
+
|
46
|
+
subject { TestSubject.new }
|
47
|
+
|
48
|
+
let(:expected_request) {
|
49
|
+
{
|
50
|
+
:query => "",
|
51
|
+
:method => "post",
|
52
|
+
:body => expected_body,
|
53
|
+
:path => "/donuts",
|
54
|
+
:headers => {
|
55
|
+
"Content-Type" => content_type,
|
56
|
+
"Content-Length" => "16",
|
57
|
+
"Accept" => "text/plain",
|
58
|
+
"User-Agent" => "Ruby",
|
59
|
+
"Host" => "localhost:4321",
|
60
|
+
"Version" => "HTTP/1.1",
|
61
|
+
"X-Something" => "1, 2"
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
context "with a text body" do
|
67
|
+
let(:content_type) { "application/x-www-form-urlencoded" }
|
68
|
+
let(:body) { 'this is the body' }
|
69
|
+
let(:expected_body) { body }
|
70
|
+
|
71
|
+
it "extracts the body" do
|
72
|
+
expect(subject.request_as_hash_from(rack_env)).to eq expected_request
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "with a json body" do
|
77
|
+
let(:content_type) { "application/json" }
|
78
|
+
let(:body) { '{"a" : "body" }' }
|
79
|
+
let(:expected_body) { {"a" => "body"} }
|
80
|
+
|
81
|
+
it "extracts the body" do
|
82
|
+
expect(subject.request_as_hash_from(rack_env)).to eq expected_request
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pact/consumer/mock_service/verification_get'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module Consumer
|
6
|
+
describe VerificationGet do
|
7
|
+
|
8
|
+
let(:interaction_list) { instance_double("Pact::Consumer::InteractionList")}
|
9
|
+
let(:logger) { double("Logger").as_null_object }
|
10
|
+
let(:log_description) { "/log/pact.log" }
|
11
|
+
|
12
|
+
subject { VerificationGet.new('VerificationGet', logger, interaction_list, log_description) }
|
13
|
+
|
14
|
+
describe "request_path" do
|
15
|
+
it "is /interactions/verification" do
|
16
|
+
expect(subject.request_path).to eq '/interactions/verification'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "request_method" do
|
21
|
+
it "is GET" do
|
22
|
+
expect(subject.request_method).to eq 'GET'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#respond" do
|
27
|
+
let(:env) { {
|
28
|
+
"QUERY_STRING" => "example_description=a description"
|
29
|
+
} }
|
30
|
+
|
31
|
+
before do
|
32
|
+
allow(interaction_list).to receive(:all_matched?).and_return(all_matched)
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:response) { subject.respond env }
|
36
|
+
|
37
|
+
context "when all interactions have been matched" do
|
38
|
+
let(:all_matched) { true }
|
39
|
+
|
40
|
+
it "returns a 200 status" do
|
41
|
+
expect(response.first).to eq 200
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns a Content-Type of text/plain" do
|
45
|
+
expect(response[1]).to eq 'Content-Type' => 'text/plain'
|
46
|
+
end
|
47
|
+
|
48
|
+
it "returns a nice message" do
|
49
|
+
expect(response.last).to eq ['Interactions matched']
|
50
|
+
end
|
51
|
+
|
52
|
+
it "logs the success" do
|
53
|
+
expect(logger).to receive(:info).with(/Verifying - interactions matched.*a description/)
|
54
|
+
response
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when all interactions not been matched" do
|
59
|
+
let(:all_matched) { false }
|
60
|
+
let(:failure_message) { "this is a failure message"}
|
61
|
+
|
62
|
+
before do
|
63
|
+
allow_any_instance_of(VerificationGet::FailureMessage).to receive(:to_s).and_return(failure_message)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "returns a 500 status" do
|
67
|
+
expect(response.first).to eq 500
|
68
|
+
end
|
69
|
+
|
70
|
+
it "returns a Content-Type of text/plain" do
|
71
|
+
expect(response[1]).to eq 'Content-Type' => 'text/plain'
|
72
|
+
end
|
73
|
+
|
74
|
+
it "returns a message" do
|
75
|
+
expect(response.last.first).to include "Actual interactions do not match"
|
76
|
+
expect(response.last.first).to include failure_message
|
77
|
+
expect(response.last.first).to include log_description
|
78
|
+
end
|
79
|
+
|
80
|
+
it "logs the failure message" do
|
81
|
+
expect(logger).to receive(:warn).with(/Verifying - actual interactions do not match/)
|
82
|
+
expect(logger).to receive(:warn).with(failure_message)
|
83
|
+
response
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "FailureMessage" do
|
91
|
+
let(:missing_interactions_summaries) { ["Blah", "Thing"]}
|
92
|
+
let(:interaction_mismatches_summaries) { []}
|
93
|
+
let(:unexpected_requests_summaries) { []}
|
94
|
+
let(:interaction_list) { instance_double("Pact::Consumer::InteractionList") }
|
95
|
+
subject { VerificationGet::FailureMessage.new(interaction_list).to_s }
|
96
|
+
|
97
|
+
before do
|
98
|
+
allow(interaction_list).to receive(:missing_interactions_summaries).and_return(missing_interactions_summaries)
|
99
|
+
allow(interaction_list).to receive(:interaction_mismatches_summaries).and_return(interaction_mismatches_summaries)
|
100
|
+
allow(interaction_list).to receive(:unexpected_requests_summaries).and_return(unexpected_requests_summaries)
|
101
|
+
end
|
102
|
+
|
103
|
+
context "with only a missing interactions" do
|
104
|
+
|
105
|
+
let(:expected_string) { <<-EOS
|
106
|
+
Missing requests:
|
107
|
+
\tBlah
|
108
|
+
\tThing
|
109
|
+
|
110
|
+
EOS
|
111
|
+
}
|
112
|
+
it "only includes missing interactions" do
|
113
|
+
expect(subject).to eq expected_string
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "with missing, mismatches and unexpected interactions" do
|
118
|
+
|
119
|
+
let(:interaction_mismatches_summaries) { ["wiffle"]}
|
120
|
+
let(:unexpected_requests_summaries) { ["moose"]}
|
121
|
+
|
122
|
+
let(:expected_string) { <<-EOS
|
123
|
+
Incorrect requests:
|
124
|
+
\twiffle
|
125
|
+
|
126
|
+
Missing requests:
|
127
|
+
\tBlah
|
128
|
+
\tThing
|
129
|
+
|
130
|
+
Unexpected requests:
|
131
|
+
\tmoose
|
132
|
+
|
133
|
+
EOS
|
134
|
+
}
|
135
|
+
it "includes all the things" do
|
136
|
+
expect(subject).to eq expected_string
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pact/consumer/mock_service_client'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module Consumer
|
6
|
+
describe MockServiceClient do
|
7
|
+
|
8
|
+
subject { MockServiceClient.new(4444) }
|
9
|
+
|
10
|
+
let(:administration_headers) { {'X-Pact-Mock-Service' => 'true'} }
|
11
|
+
|
12
|
+
describe "#add_expected_interaction" do
|
13
|
+
let(:interaction) { InteractionFactory.create }
|
14
|
+
let(:request_body) { MockServiceInteractionExpectation.new(interaction).to_json }
|
15
|
+
|
16
|
+
context "when successful" do
|
17
|
+
let!(:post_interaction) do
|
18
|
+
stub_request(:post, "localhost:4444/interactions").
|
19
|
+
with(body: request_body, headers: administration_headers.merge('Content-Type' => "application/json")).
|
20
|
+
to_return(status: 200)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "sets up the expected interaction on the mock server" do
|
24
|
+
subject.add_expected_interaction interaction
|
25
|
+
expect(post_interaction).to have_been_made
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#verify" do
|
32
|
+
|
33
|
+
context "when all interactions are successfully verified" do
|
34
|
+
|
35
|
+
let!(:get_verification) do
|
36
|
+
stub_request(:get, "localhost:4444/interactions/verification?example_description=some%20example").
|
37
|
+
with(headers: administration_headers).
|
38
|
+
to_return(status: 200)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "does not throw an error" do
|
42
|
+
subject.verify "some example"
|
43
|
+
expect(get_verification).to have_been_made
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe ".clear_interactions" do
|
49
|
+
let!(:delete_verifications) do
|
50
|
+
stub_request(:delete, "localhost:4444/interactions?example_description=some%20example").
|
51
|
+
with(headers: administration_headers).
|
52
|
+
to_return(status: 200)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "deletes the interactions" do
|
56
|
+
MockServiceClient.clear_interactions 4444, "some example"
|
57
|
+
expect(delete_verifications).to have_been_made
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#write_pact" do
|
62
|
+
let(:consumer_contract_details) { {consumer: {name: 'Consumer'}, provider: {name: 'Provider'}, pactfile_write_mode: 'update'} }
|
63
|
+
let(:pact) { {a: 'pact'}.to_json }
|
64
|
+
|
65
|
+
let!(:post_pact) do
|
66
|
+
stub_request(:post, "localhost:4444/pact").
|
67
|
+
with(headers: administration_headers.merge('Content-Type' => "application/json"), body: consumer_contract_details).
|
68
|
+
to_return(status: 200, body: pact)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "deletes the interactions" do
|
72
|
+
expect(subject.write_pact(consumer_contract_details)).to eq pact
|
73
|
+
expect(post_pact).to have_been_made
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#log" do
|
79
|
+
it "sends a log request to the mock server"
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#wait_for_interactions" do
|
83
|
+
it "waits until there are no missing interactions"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pact/consumer/mock_service_interaction_expectation'
|
3
|
+
|
4
|
+
describe Pact::Consumer::MockServiceInteractionExpectation do
|
5
|
+
describe "as_json" do
|
6
|
+
|
7
|
+
let(:options ) { {} }
|
8
|
+
let(:request_as_json) { {a: 'request'} }
|
9
|
+
let(:request) { instance_double('Pact::Request::Expected', :as_json => request_as_json, :options => options)}
|
10
|
+
let(:response) { double('response') }
|
11
|
+
let(:generated_response ) { double('generated_response', :to_json => 'generated_response') }
|
12
|
+
let(:interaction) { instance_double('Pact::Interaction', :description => 'description', :request => request, :response => response, :provider_state => 'some state') }
|
13
|
+
subject { described_class.new(interaction)}
|
14
|
+
let(:expected_hash) { {:response => generated_response, :request => as_json_with_options, :description => '' } }
|
15
|
+
|
16
|
+
before do
|
17
|
+
allow(Pact::Reification).to receive(:from_term).with(response).and_return(generated_response)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "includes the response" do
|
21
|
+
expect(subject.as_json[:response]).to eq response
|
22
|
+
end
|
23
|
+
|
24
|
+
it "includes the options in the request" do
|
25
|
+
expect(subject.as_json[:request]).to eq request_as_json
|
26
|
+
end
|
27
|
+
|
28
|
+
it "includes the provider state" do
|
29
|
+
expect(subject.as_json[:provider_state]).to eq 'some state'
|
30
|
+
end
|
31
|
+
|
32
|
+
it "includes the description" do
|
33
|
+
expect(subject.as_json[:description]).to eq 'description'
|
34
|
+
end
|
35
|
+
|
36
|
+
it "doesn't have any other keys" do
|
37
|
+
expect(subject.as_json.keys).to eq [:description, :provider_state, :request, :response]
|
38
|
+
end
|
39
|
+
|
40
|
+
context "without options" do
|
41
|
+
it "does not include the options key" do
|
42
|
+
expect(subject.as_json.key?(:options)).to be false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "with options" do
|
47
|
+
let(:options) { {:opts => 'blah'} }
|
48
|
+
it "includes the options in the request hash" do
|
49
|
+
expect(subject.as_json[:request][:options]).to eq options
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pact/consumer_contract/consumer_contract_writer'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
|
6
|
+
describe ConsumerContractWriter do
|
7
|
+
|
8
|
+
let(:support_pact_file) { './spec/support/a_consumer-a_provider.json' }
|
9
|
+
let(:consumer_name) { 'a consumer' }
|
10
|
+
let(:provider_name) { 'a provider' }
|
11
|
+
|
12
|
+
before do
|
13
|
+
Pact.clear_configuration
|
14
|
+
allow(Pact.configuration).to receive(:pact_dir).and_return(File.expand_path(tmp_pact_dir))
|
15
|
+
FileUtils.rm_rf tmp_pact_dir
|
16
|
+
FileUtils.mkdir_p tmp_pact_dir
|
17
|
+
FileUtils.cp support_pact_file, "#{tmp_pact_dir}/a_consumer-a_provider.json"
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:existing_interactions) { ConsumerContract.from_json(File.read(support_pact_file)).interactions }
|
21
|
+
let(:new_interactions) { [InteractionFactory.create] }
|
22
|
+
let(:tmp_pact_dir) {"./tmp/pacts"}
|
23
|
+
let(:logger) { double("logger").as_null_object }
|
24
|
+
let(:pactfile_write_mode) {:overwrite}
|
25
|
+
let(:consumer_contract_details) {
|
26
|
+
{
|
27
|
+
consumer: {name: consumer_name},
|
28
|
+
provider: {name: provider_name},
|
29
|
+
pactfile_write_mode: pactfile_write_mode,
|
30
|
+
interactions: new_interactions
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
let(:consumer_contract_writer) { ConsumerContractWriter.new(consumer_contract_details, logger) }
|
35
|
+
|
36
|
+
describe "consumer_contract" do
|
37
|
+
|
38
|
+
let(:subject) { consumer_contract_writer.consumer_contract }
|
39
|
+
|
40
|
+
context "when overwriting pact" do
|
41
|
+
|
42
|
+
it "it uses only the interactions from the current test run" do
|
43
|
+
expect(consumer_contract_writer.consumer_contract.interactions).to eq new_interactions
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when updating pact" do
|
49
|
+
|
50
|
+
let(:pactfile_write_mode) {:update}
|
51
|
+
|
52
|
+
it "merges the interactions from the current test run with the interactions from the existing file" do
|
53
|
+
allow_any_instance_of(ConsumerContractWriter).to receive(:info_and_puts)
|
54
|
+
expect(consumer_contract_writer.consumer_contract.interactions).to eq existing_interactions + new_interactions
|
55
|
+
end
|
56
|
+
|
57
|
+
let(:line0) { /\*/ }
|
58
|
+
let(:line1) { /Updating existing file/ }
|
59
|
+
let(:line2) { /Only interactions defined in this test run will be updated/ }
|
60
|
+
let(:line3) { /As interactions are identified by description and provider state/ }
|
61
|
+
|
62
|
+
it "logs a description message" do
|
63
|
+
expect($stdout).to receive(:puts).with(line0).twice
|
64
|
+
expect($stdout).to receive(:puts).with(line1)
|
65
|
+
expect($stdout).to receive(:puts).with(line2)
|
66
|
+
expect($stdout).to receive(:puts).with(line3)
|
67
|
+
expect(logger).to receive(:info).with(line0).twice
|
68
|
+
expect(logger).to receive(:info).with(line1)
|
69
|
+
expect(logger).to receive(:info).with(line2)
|
70
|
+
expect(logger).to receive(:info).with(line3)
|
71
|
+
consumer_contract_writer.consumer_contract
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "when an error occurs deserializing the existing pactfile" do
|
76
|
+
|
77
|
+
let(:pactfile_write_mode) {:update}
|
78
|
+
let(:error) { RuntimeError.new('some error')}
|
79
|
+
let(:line1) { /Could not load existing consumer contract from .* due to some error/ }
|
80
|
+
let(:line2) {'Creating a new file.'}
|
81
|
+
|
82
|
+
before do
|
83
|
+
allow(ConsumerContract).to receive(:from_json).and_raise(error)
|
84
|
+
allow($stderr).to receive(:puts)
|
85
|
+
allow(logger).to receive(:puts)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "logs the error" do
|
89
|
+
expect($stderr).to receive(:puts).with(line1)
|
90
|
+
expect($stderr).to receive(:puts).with(line2)
|
91
|
+
expect(logger).to receive(:warn).with(line1)
|
92
|
+
expect(logger).to receive(:warn).with(line2)
|
93
|
+
consumer_contract_writer.consumer_contract
|
94
|
+
end
|
95
|
+
|
96
|
+
it "uses the new interactions" do
|
97
|
+
expect(consumer_contract_writer.consumer_contract.interactions).to eq new_interactions
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "write" do
|
103
|
+
it "writes the pact file" do
|
104
|
+
expect_any_instance_of(ConsumerContract).to receive(:update_pactfile)
|
105
|
+
consumer_contract_writer.write
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'fakefs/spec_helpers'
|
3
|
+
require 'rspec'
|
4
|
+
require 'pact/support'
|
5
|
+
require 'webmock/rspec'
|
6
|
+
require 'support/factories'
|
7
|
+
require 'support/spec_support'
|
8
|
+
|
9
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
10
|
+
|
11
|
+
require './spec/support/active_support_if_configured'
|
12
|
+
|
13
|
+
RSpec.configure do | config |
|
14
|
+
config.include(FakeFS::SpecHelpers, :fakefs => true)
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
{
|
2
|
+
"producer": {
|
3
|
+
"name": "an old producer"
|
4
|
+
},
|
5
|
+
"consumer": {
|
6
|
+
"name": "a consumer"
|
7
|
+
},
|
8
|
+
"interactions": [
|
9
|
+
{
|
10
|
+
"description": "request one",
|
11
|
+
"request": {
|
12
|
+
"method": "get",
|
13
|
+
"path": "/path_one"
|
14
|
+
},
|
15
|
+
"response": {
|
16
|
+
},
|
17
|
+
"producer_state": "state one"
|
18
|
+
},
|
19
|
+
{
|
20
|
+
"description": "request two",
|
21
|
+
"request": {
|
22
|
+
"method": "get",
|
23
|
+
"path": "/path_two"
|
24
|
+
},
|
25
|
+
"response": {
|
26
|
+
}
|
27
|
+
}
|
28
|
+
],
|
29
|
+
"metadata": {
|
30
|
+
"pactSpecificationVersion": "1.0"
|
31
|
+
}
|
32
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
{
|
2
|
+
"provider": {
|
3
|
+
"name": "a provider"
|
4
|
+
},
|
5
|
+
"consumer": {
|
6
|
+
"name": "a consumer"
|
7
|
+
},
|
8
|
+
"interactions": [
|
9
|
+
{
|
10
|
+
"description": "request one",
|
11
|
+
"request": {
|
12
|
+
"method": "get",
|
13
|
+
"path": "/path_one"
|
14
|
+
},
|
15
|
+
"response": {
|
16
|
+
},
|
17
|
+
"provider_state": "state one"
|
18
|
+
},
|
19
|
+
{
|
20
|
+
"description": "request two",
|
21
|
+
"request": {
|
22
|
+
"method": "get",
|
23
|
+
"path": "/path_two"
|
24
|
+
},
|
25
|
+
"response": {
|
26
|
+
}
|
27
|
+
}
|
28
|
+
],
|
29
|
+
"metadata": {
|
30
|
+
"pactSpecificationVersion": "1.0"
|
31
|
+
}
|
32
|
+
}
|