pact-mock_service 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|