pacto 0.4.0.rc1 → 0.4.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/Gemfile +2 -7
- data/Rakefile +0 -5
- data/appveyor.yml +12 -0
- data/features/configuration/strict_matchers.feature +4 -4
- data/features/generate/generation.feature +8 -10
- data/features/support/env.rb +3 -7
- data/features/validate/validation.feature +3 -3
- data/lib/pacto.rb +5 -4
- data/lib/pacto/actors/json_generator.rb +1 -1
- data/lib/pacto/body_parsing.rb +42 -0
- data/lib/pacto/consumer/faraday_driver.rb +5 -2
- data/lib/pacto/contract.rb +23 -24
- data/lib/pacto/contract_factory.rb +4 -4
- data/lib/pacto/core/configuration.rb +18 -10
- data/lib/pacto/core/http_middleware.rb +1 -1
- data/lib/pacto/core/pacto_request.rb +3 -15
- data/lib/pacto/core/pacto_response.rb +3 -13
- data/lib/pacto/errors.rb +68 -0
- data/lib/pacto/formats/legacy/contract.rb +49 -0
- data/lib/pacto/formats/legacy/contract_builder.rb +129 -0
- data/lib/pacto/formats/legacy/contract_factory.rb +63 -0
- data/lib/pacto/formats/legacy/contract_generator.rb +77 -0
- data/lib/pacto/formats/legacy/generator/filters.rb +46 -0
- data/lib/pacto/formats/legacy/generator_hint.rb +36 -0
- data/lib/pacto/formats/legacy/request_clause.rb +39 -0
- data/lib/pacto/formats/legacy/response_clause.rb +31 -0
- data/lib/pacto/formats/swagger/contract.rb +86 -0
- data/lib/pacto/formats/swagger/contract_factory.rb +45 -0
- data/lib/pacto/formats/swagger/request_clause.rb +53 -0
- data/lib/pacto/formats/swagger/response_clause.rb +31 -0
- data/lib/pacto/generator.rb +4 -4
- data/lib/pacto/handlers/json_handler.rb +19 -0
- data/lib/pacto/handlers/text_handler.rb +17 -0
- data/lib/pacto/request_clause.rb +9 -19
- data/lib/pacto/response_clause.rb +4 -4
- data/lib/pacto/server.rb +41 -2
- data/lib/pacto/stubs/uri_pattern.rb +5 -5
- data/lib/pacto/stubs/webmock_adapter.rb +4 -1
- data/lib/pacto/test_helper.rb +16 -13
- data/lib/pacto/version.rb +1 -1
- data/pacto-server.gemspec +1 -3
- data/pacto.gemspec +1 -1
- data/sample_apis/user_api.rb +16 -0
- data/samples/contracts/user.json +51 -0
- data/samples/cops.rb +3 -0
- data/samples/server_cli.sh +3 -3
- data/spec/fabricators/contract_fabricator.rb +17 -8
- data/spec/fixtures/{deprecated_contracts → contracts/deprecated}/deprecated_contract.json +2 -2
- data/spec/fixtures/contracts/{contract.json → legacy/contract.json} +0 -0
- data/spec/fixtures/contracts/{contract_with_examples.json → legacy/contract_with_examples.json} +0 -0
- data/spec/fixtures/contracts/{simple_contract.json → legacy/simple_contract.json} +1 -1
- data/spec/fixtures/contracts/{strict_contract.json → legacy/strict_contract.json} +0 -0
- data/spec/fixtures/contracts/{templating_contract.json → legacy/templating_contract.json} +0 -0
- data/spec/fixtures/{swagger → contracts/swagger}/petstore.yaml +0 -0
- data/spec/integration/e2e_spec.rb +6 -12
- data/spec/integration/forensics/integration_matcher_spec.rb +5 -11
- data/spec/integration/rspec_spec.rb +12 -12
- data/spec/integration/templating_spec.rb +1 -1
- data/spec/spec_helper.rb +14 -2
- data/spec/unit/pacto/contract_factory_spec.rb +1 -2
- data/spec/unit/pacto/contract_spec.rb +44 -70
- data/spec/unit/pacto/core/investigation_spec.rb +4 -3
- data/spec/unit/pacto/formats/legacy/contract_builder_spec.rb +93 -0
- data/spec/unit/pacto/formats/legacy/contract_factory_spec.rb +29 -0
- data/spec/unit/pacto/formats/legacy/contract_generator_spec.rb +173 -0
- data/spec/unit/pacto/formats/legacy/contract_spec.rb +41 -0
- data/spec/unit/pacto/formats/legacy/generator/filters_spec.rb +104 -0
- data/spec/unit/pacto/formats/legacy/request_clause_spec.rb +79 -0
- data/spec/unit/pacto/formats/legacy/response_clause_spec.rb +45 -0
- data/spec/unit/pacto/formats/swagger/contract_factory_spec.rb +58 -0
- data/spec/unit/pacto/formats/swagger/contract_spec.rb +47 -0
- data/spec/unit/pacto/investigation_registry_spec.rb +1 -2
- data/spec/unit/pacto/pacto_spec.rb +6 -4
- data/spec/unit/pacto/stubs/uri_pattern_spec.rb +7 -8
- data/spec/unit/pacto/stubs/webmock_adapter_spec.rb +2 -4
- data/tasks/release.rake +1 -1
- metadata +53 -53
- data/lib/pacto/contract_builder.rb +0 -125
- data/lib/pacto/exceptions/invalid_contract.rb +0 -12
- data/lib/pacto/generator/filters.rb +0 -42
- data/lib/pacto/generator/hint.rb +0 -26
- data/lib/pacto/generator/native_contract_generator.rb +0 -74
- data/lib/pacto/native_contract_factory.rb +0 -60
- data/lib/pacto/swagger_contract_factory.rb +0 -90
- data/spec/pacto/dummy_server.rb +0 -4
- data/spec/pacto/dummy_server/dummy.rb +0 -51
- data/spec/pacto/dummy_server/jruby_workaround_helper.rb +0 -23
- data/spec/pacto/dummy_server/playback_servlet.rb +0 -22
- data/spec/unit/pacto/contract_builder_spec.rb +0 -89
- data/spec/unit/pacto/generator/filters_spec.rb +0 -100
- data/spec/unit/pacto/generator/native_contract_generator_spec.rb +0 -171
- data/spec/unit/pacto/native_contract_factory_spec.rb +0 -26
- data/spec/unit/pacto/request_clause_spec.rb +0 -75
- data/spec/unit/pacto/response_clause_spec.rb +0 -41
- data/spec/unit/pacto/server/playback_servlet_spec.rb +0 -27
- data/spec/unit/pacto/swagger_contract_factory_spec.rb +0 -56
@@ -2,11 +2,11 @@
|
|
2
2
|
require 'pacto/rspec'
|
3
3
|
|
4
4
|
describe 'pacto/rspec' do
|
5
|
-
let(:contract_path) { '
|
6
|
-
let(:strict_contract_path) { '
|
5
|
+
let(:contract_path) { contract_file 'simple_contract' }
|
6
|
+
let(:strict_contract_path) { contract_file 'strict_contract' }
|
7
7
|
|
8
8
|
around :each do |example|
|
9
|
-
|
9
|
+
with_pacto(port: 8000) do
|
10
10
|
example.run
|
11
11
|
end
|
12
12
|
end
|
@@ -31,7 +31,7 @@ describe 'pacto/rspec' do
|
|
31
31
|
|
32
32
|
context 'successful investigations' do
|
33
33
|
let(:contracts) do
|
34
|
-
Pacto.load_contracts
|
34
|
+
Pacto.load_contracts contracts_folder, 'http://dummyprovider.com'
|
35
35
|
end
|
36
36
|
|
37
37
|
before(:each) do
|
@@ -43,7 +43,7 @@ describe 'pacto/rspec' do
|
|
43
43
|
contracts.stub_providers(device_id: 42)
|
44
44
|
Pacto.validate!
|
45
45
|
|
46
|
-
Faraday.get('http://dummyprovider.com/hello') do |req|
|
46
|
+
Faraday.get('http://dummyprovider.com/api/hello') do |req|
|
47
47
|
req.headers = { 'Accept' => 'application/json' }
|
48
48
|
end
|
49
49
|
end
|
@@ -54,9 +54,9 @@ describe 'pacto/rspec' do
|
|
54
54
|
expect(Pacto).to_not have_failed_investigations
|
55
55
|
|
56
56
|
# Increasingly strict assertions
|
57
|
-
expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/hello')
|
58
|
-
expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/hello').with(headers: { 'Accept' => 'application/json' })
|
59
|
-
expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/hello').against_contract(/simple_contract.json/)
|
57
|
+
expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/api/hello')
|
58
|
+
expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/api/hello').with(headers: { 'Accept' => 'application/json' })
|
59
|
+
expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/api/hello').against_contract(/simple_contract.json/)
|
60
60
|
end
|
61
61
|
|
62
62
|
it 'supports negative assertions' do
|
@@ -77,11 +77,11 @@ describe 'pacto/rspec' do
|
|
77
77
|
expect_to_raise(/the following requests were not matched.*#{Regexp.quote unmatched_url}/m) { expect(Pacto).to_not have_unmatched_requests }
|
78
78
|
|
79
79
|
# Expected failures
|
80
|
-
expect_to_raise(/no matching request was received/) { expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/hello').with(headers: { 'Accept' => 'text/plain' }) }
|
80
|
+
expect_to_raise(/no matching request was received/) { expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/api/hello').with(headers: { 'Accept' => 'text/plain' }) }
|
81
81
|
# No support for with accepting a block
|
82
|
-
# expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/hello').with { |req| req.body == "abc" }
|
83
|
-
expect_to_raise(/but it was validated against/) { expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/hello').against_contract(/strict_contract.json/) }
|
84
|
-
expect_to_raise(/but it was validated against/) { expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/hello').against_contract('simple_contract.json') }
|
82
|
+
# expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/api/hello').with { |req| req.body == "abc" }
|
83
|
+
expect_to_raise(/but it was validated against/) { expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/api/hello').against_contract(/strict_contract.json/) }
|
84
|
+
expect_to_raise(/but it was validated against/) { expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/api/hello').against_contract('simple_contract.json') }
|
85
85
|
expect_to_raise(/but no matching request was received/) { expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/strict') }
|
86
86
|
end
|
87
87
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
require 'securerandom'
|
3
3
|
|
4
4
|
describe 'Templating' do
|
5
|
-
let(:contract_path) { '
|
5
|
+
let(:contract_path) { contract_file 'templating_contract' }
|
6
6
|
let(:contracts) { Pacto.load_contracts(contract_path, 'http://dummyprovider.com') }
|
7
7
|
|
8
8
|
let(:key) { SecureRandom.hex }
|
data/spec/spec_helper.rb
CHANGED
@@ -3,7 +3,6 @@ require 'coveralls_helper'
|
|
3
3
|
require 'webmock/rspec'
|
4
4
|
require 'pacto'
|
5
5
|
require 'pacto/test_helper'
|
6
|
-
require 'pacto/dummy_server'
|
7
6
|
require 'fabrication'
|
8
7
|
require 'stringio'
|
9
8
|
require 'rspec'
|
@@ -14,7 +13,6 @@ require_relative 'unit/pacto/actor_spec.rb'
|
|
14
13
|
RSpec.configure do |config|
|
15
14
|
config.raise_errors_for_deprecations!
|
16
15
|
config.include Pacto::TestHelper
|
17
|
-
config.include Pacto::DummyServer::JRubyWorkaroundHelper
|
18
16
|
config.expect_with :rspec do |c|
|
19
17
|
c.syntax = :expect
|
20
18
|
end
|
@@ -23,6 +21,20 @@ RSpec.configure do |config|
|
|
23
21
|
end
|
24
22
|
end
|
25
23
|
|
24
|
+
def default_pacto_format
|
25
|
+
ENV['PACTO_DEFAULT_FORMAT'] || 'legacy'
|
26
|
+
end
|
27
|
+
|
28
|
+
def contracts_folder(format = default_pacto_format)
|
29
|
+
"spec/fixtures/contracts/#{format}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def contract_file(name, format = default_pacto_format)
|
33
|
+
file = Dir.glob("#{contracts_folder(format)}/#{name}.*").first
|
34
|
+
fail "Could not find a #{format} contract for #{name}" if file.nil?
|
35
|
+
file
|
36
|
+
end
|
37
|
+
|
26
38
|
def sample_contract
|
27
39
|
# Memoized for test speed
|
28
40
|
@sample_contract ||= Fabricate(:contract)
|
@@ -5,8 +5,7 @@ module Pacto
|
|
5
5
|
describe ContractFactory do
|
6
6
|
let(:host) { 'http://localhost' }
|
7
7
|
let(:contract_name) { 'contract' }
|
8
|
-
let(:
|
9
|
-
let(:contract_path) { File.join(contracts_path, "#{contract_name}.json") }
|
8
|
+
let(:contract_path) { File.join(contracts_folder, "#{contract_name}.json") }
|
10
9
|
let(:contract_files) { [contract_path, contract_path] }
|
11
10
|
subject(:contract_factory) { described_class }
|
12
11
|
|
@@ -1,89 +1,63 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
type: 'object',
|
11
|
-
required: true # , :properties => double('body definition properties')
|
12
|
-
}
|
13
|
-
)
|
14
|
-
end
|
15
|
-
let(:response_clause) do
|
16
|
-
Pacto::ResponseClause.new(status: 200)
|
17
|
-
end
|
18
|
-
let(:adapter) { double 'provider' }
|
19
|
-
let(:file) { 'contract.json' }
|
20
|
-
let(:consumer_driver) { double }
|
21
|
-
let(:provider_actor) { double }
|
2
|
+
RSpec.shared_examples 'a contract' do
|
3
|
+
before do
|
4
|
+
Pacto.configuration.adapter = adapter
|
5
|
+
allow(consumer_driver).to receive(:respond_to?).with(:execute).and_return true
|
6
|
+
allow(provider_actor).to receive(:respond_to?).with(:build_response).and_return true
|
7
|
+
Pacto.configuration.default_consumer.driver = consumer_driver
|
8
|
+
Pacto.configuration.default_provider.actor = provider_actor
|
9
|
+
end
|
22
10
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
response: response_clause,
|
27
|
-
file: file,
|
28
|
-
name: 'sample'
|
29
|
-
)
|
30
|
-
end
|
11
|
+
it 'is a type of Contract' do
|
12
|
+
expect(subject).to be_a_kind_of(Pacto::Contract)
|
13
|
+
end
|
31
14
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
Pacto.configuration.default_consumer.driver = consumer_driver
|
37
|
-
Pacto.configuration.default_provider.actor = provider_actor
|
15
|
+
describe '#stub_contract!' do
|
16
|
+
it 'register a stub for the contract' do
|
17
|
+
expect(adapter).to receive(:stub_request!).with(contract)
|
18
|
+
contract.stub_contract!
|
38
19
|
end
|
20
|
+
end
|
39
21
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
22
|
+
context 'investigations' do
|
23
|
+
let(:request) { Pacto.configuration.default_consumer.build_request contract }
|
24
|
+
let(:fake_response) { Fabricate(:pacto_response) } # double('fake response') }
|
25
|
+
let(:cop) { double 'cop' }
|
26
|
+
let(:investigation_citations) { [double('investigation result')] }
|
46
27
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
28
|
+
before do
|
29
|
+
Pacto::Cops.active_cops.clear
|
30
|
+
Pacto::Cops.active_cops << cop
|
31
|
+
allow(cop).to receive(:investigate).with(an_instance_of(Pacto::PactoRequest), fake_response, contract).and_return investigation_citations
|
32
|
+
end
|
52
33
|
|
34
|
+
describe '#simulate_request' do
|
53
35
|
before do
|
54
|
-
Pacto::
|
55
|
-
Pacto::Cops.active_cops << cop
|
56
|
-
allow(cop).to receive(:investigate).with(an_instance_of(Pacto::PactoRequest), fake_response, contract).and_return investigation_citations
|
36
|
+
allow(consumer_driver).to receive(:execute).with(an_instance_of(Pacto::PactoRequest)).and_return fake_response
|
57
37
|
end
|
58
38
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
it 'generates the response' do
|
65
|
-
expect(consumer_driver).to receive(:execute).with(an_instance_of(Pacto::PactoRequest))
|
66
|
-
contract.simulate_request
|
67
|
-
end
|
39
|
+
it 'generates the response' do
|
40
|
+
expect(consumer_driver).to receive(:execute).with(an_instance_of(Pacto::PactoRequest))
|
41
|
+
contract.simulate_request
|
42
|
+
end
|
68
43
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
44
|
+
it 'returns the result of the validating the generated response' do
|
45
|
+
expect(cop).to receive(:investigate).with(an_instance_of(Pacto::PactoRequest), fake_response, contract)
|
46
|
+
investigation = contract.simulate_request
|
47
|
+
expect(investigation.citations).to eq investigation_citations
|
74
48
|
end
|
75
49
|
end
|
50
|
+
end
|
76
51
|
|
77
|
-
|
78
|
-
|
79
|
-
|
52
|
+
describe '#matches?' do
|
53
|
+
let(:request_pattern) { double('request_pattern') }
|
54
|
+
let(:request_signature) { double('request_signature') }
|
80
55
|
|
81
|
-
|
82
|
-
|
83
|
-
|
56
|
+
it 'delegates to the request pattern' do
|
57
|
+
expect(Pacto::RequestPattern).to receive(:for).and_return(request_pattern)
|
58
|
+
expect(request_pattern).to receive(:matches?).with(request_signature)
|
84
59
|
|
85
|
-
|
86
|
-
end
|
60
|
+
contract.matches?(request_signature)
|
87
61
|
end
|
88
62
|
end
|
89
63
|
end
|
@@ -3,7 +3,8 @@ module Pacto
|
|
3
3
|
describe Investigation do
|
4
4
|
let(:request) { double('request') }
|
5
5
|
let(:response) { double('response') }
|
6
|
-
let(:
|
6
|
+
let(:file) { 'foobar' }
|
7
|
+
let(:contract) { Fabricate(:contract, file: file) }
|
7
8
|
let(:investigation_citations) { [] }
|
8
9
|
let(:investigation_citations_with_errors) { ['an error occurred'] }
|
9
10
|
|
@@ -44,9 +45,9 @@ module Pacto
|
|
44
45
|
end
|
45
46
|
|
46
47
|
it 'returns the contract with an exact string name match' do
|
47
|
-
allow(contract).to receive(:file).and_return('foo')
|
48
48
|
investigation = Pacto::Investigation.new request, response, contract, investigation_citations
|
49
|
-
expect(investigation.against_contract? '
|
49
|
+
expect(investigation.against_contract? 'foobar').to eq(contract)
|
50
|
+
expect(investigation.against_contract? 'foo').to be_nil
|
50
51
|
expect(investigation.against_contract? 'bar').to be_nil
|
51
52
|
end
|
52
53
|
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
module Formats
|
4
|
+
module Legacy
|
5
|
+
describe ContractBuilder do
|
6
|
+
let(:data) { subject.build_hash }
|
7
|
+
describe '#name' do
|
8
|
+
it 'sets the contract name' do
|
9
|
+
subject.name = 'foo'
|
10
|
+
expect(data).to include(name: 'foo')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#add_example' do
|
15
|
+
let(:examples) { subject.build_hash[:examples] }
|
16
|
+
it 'adds named examples to the contract' do
|
17
|
+
subject.add_example 'foo', Fabricate(:pacto_request), Fabricate(:pacto_response)
|
18
|
+
subject.add_example 'bar', Fabricate(:pacto_request), Fabricate(:pacto_response)
|
19
|
+
expect(examples).to be_a(Hash)
|
20
|
+
expect(examples.keys).to include('foo', 'bar')
|
21
|
+
expect(examples['foo'][:response]).to include(status: 200)
|
22
|
+
expect(data)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'without examples' do
|
27
|
+
describe '#infer_schemas' do
|
28
|
+
it 'does not add schemas' do
|
29
|
+
subject.name = 'test'
|
30
|
+
subject.infer_schemas
|
31
|
+
expect(data[:request][:schema]).to be_nil
|
32
|
+
expect(data[:response][:schema]).to be_nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with examples' do
|
38
|
+
before(:each) do
|
39
|
+
subject.add_example 'success', Fabricate(:pacto_request), Fabricate(:pacto_response)
|
40
|
+
subject.add_example 'not found', Fabricate(:pacto_request), Fabricate(:pacto_response)
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#without_examples' do
|
44
|
+
it 'stops the builder from including examples in the final data' do
|
45
|
+
expect(subject.build_hash.keys).to include(:examples)
|
46
|
+
expect(subject.without_examples.build_hash.keys).to_not include(:examples)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#infer_schemas' do
|
51
|
+
it 'adds schemas' do
|
52
|
+
subject.name = 'test'
|
53
|
+
subject.infer_schemas
|
54
|
+
contract = subject.build
|
55
|
+
expect(contract.request.schema).to_not be_nil
|
56
|
+
expect(contract.request.schema).to_not be_nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'generating from interactions' do
|
62
|
+
let(:request) { Fabricate(:pacto_request) }
|
63
|
+
let(:response) { Fabricate(:pacto_response) }
|
64
|
+
let(:data) { subject.generate_response(request, response).build_hash }
|
65
|
+
let(:contract) { subject.generate_contract(request, response).build }
|
66
|
+
|
67
|
+
describe '#generate_response' do
|
68
|
+
it 'sets the response status' do
|
69
|
+
expect(data[:response]).to include(
|
70
|
+
status: 200
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'sets response headers' do
|
75
|
+
expect(data[:response][:headers]).to be_a(Hash)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '#infer_schemas' do
|
80
|
+
it 'sets the schemas based on the examples' do
|
81
|
+
expect(contract.request.schema).to_not be_nil
|
82
|
+
expect(contract.request.schema).to_not be_nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
skip '#add_request_header'
|
88
|
+
skip '#add_response_header'
|
89
|
+
skip '#filter'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Pacto
|
5
|
+
module Formats
|
6
|
+
module Legacy
|
7
|
+
describe ContractFactory do
|
8
|
+
let(:host) { 'http://localhost' }
|
9
|
+
let(:contract_format) { 'legacy' }
|
10
|
+
let(:contract_name) { 'contract' }
|
11
|
+
let(:contract_path) { contract_file(contract_name, contract_format) }
|
12
|
+
subject(:contract_factory) { described_class.new }
|
13
|
+
|
14
|
+
it 'builds a Contract given a JSON file path and a host' do
|
15
|
+
contract = contract_factory.build_from_file(contract_path, host)
|
16
|
+
expect(contract).to be_a(Pacto::Formats::Legacy::Contract)
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'deprecated contracts' do
|
20
|
+
let(:contract_format) { 'deprecated' }
|
21
|
+
let(:contract_name) { 'deprecated_contract' }
|
22
|
+
it 'can no longer be loaded' do
|
23
|
+
expect { contract_factory.build_from_file(contract_path, host) }.to raise_error(/old syntax no longer supported/)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
module Formats
|
4
|
+
module Legacy
|
5
|
+
describe ContractGenerator do
|
6
|
+
let(:record_host) do
|
7
|
+
'http://example.com'
|
8
|
+
end
|
9
|
+
let(:request_clause) { Fabricate(:request_clause, params: { 'api_key' => "<%= ENV['MY_API_KEY'] %>" }) }
|
10
|
+
let(:response_adapter) do
|
11
|
+
Faraday::Response.new(
|
12
|
+
status: 200,
|
13
|
+
response_headers: {
|
14
|
+
'Date' => [Time.now],
|
15
|
+
'Server' => ['Fake Server'],
|
16
|
+
'Content-Type' => ['application/json'],
|
17
|
+
'Vary' => ['User-Agent']
|
18
|
+
},
|
19
|
+
body: 'dummy body' # body is just a string
|
20
|
+
)
|
21
|
+
end
|
22
|
+
let(:filtered_request_headers) { double('filtered_response_headers') }
|
23
|
+
let(:filtered_response_headers) { double('filtered_response_headers') }
|
24
|
+
let(:response_body_schema) { '{"message": "dummy generated schema"}' }
|
25
|
+
let(:version) { 'draft3' }
|
26
|
+
let(:schema_generator) { double('schema_generator') }
|
27
|
+
let(:validator) { double('validator') }
|
28
|
+
let(:filters) { double :filters }
|
29
|
+
let(:consumer) { double 'consumer' }
|
30
|
+
let(:request_file) { 'request.json' }
|
31
|
+
let(:generator) { described_class.new version, schema_generator, validator, filters, consumer }
|
32
|
+
let(:request_contract) do
|
33
|
+
Fabricate(:partial_contract, request: request_clause, file: request_file)
|
34
|
+
end
|
35
|
+
let(:request) do
|
36
|
+
Pacto.configuration.default_consumer.build_request request_contract
|
37
|
+
end
|
38
|
+
|
39
|
+
def pretty(obj)
|
40
|
+
MultiJson.encode(obj, pretty: true).gsub(/^$\n/, '')
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#generate_from_partial_contract' do
|
44
|
+
# TODO: Deprecate partial contracts?
|
45
|
+
let(:generated_contract) { Fabricate(:contract) }
|
46
|
+
before do
|
47
|
+
expect(Pacto).to receive(:load_contract).with(request_file, record_host).and_return request_contract
|
48
|
+
expect(consumer).to receive(:request).with(request_contract).and_return([request, response_adapter])
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'parses the request' do
|
52
|
+
expect(generator).to receive(:save).with(request_file, request, anything)
|
53
|
+
generator.generate_from_partial_contract request_file, record_host
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'fetches a response' do
|
57
|
+
expect(generator).to receive(:save).with(request_file, anything, response_adapter)
|
58
|
+
generator.generate_from_partial_contract request_file, record_host
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'saves the result' do
|
62
|
+
expect(generator).to receive(:save).with(request_file, request, response_adapter).and_return generated_contract
|
63
|
+
expect(generator.generate_from_partial_contract request_file, record_host).to eq(generated_contract)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#save' do
|
68
|
+
before do
|
69
|
+
allow(filters).to receive(:filter_request_headers).with(request, response_adapter).and_return filtered_request_headers
|
70
|
+
allow(filters).to receive(:filter_response_headers).with(request, response_adapter).and_return filtered_response_headers
|
71
|
+
end
|
72
|
+
context 'invalid schema' do
|
73
|
+
it 'raises an error if schema generation fails' do
|
74
|
+
expect(schema_generator).to receive(:generate).and_raise ArgumentError.new('Could not generate schema')
|
75
|
+
expect { generator.save request_file, request, response_adapter }.to raise_error
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'raises an error if the generated contract is invalid' do
|
79
|
+
expect(schema_generator).to receive(:generate).and_return response_body_schema
|
80
|
+
expect(validator).to receive(:validate).and_raise InvalidContract.new('dummy error')
|
81
|
+
expect { generator.save request_file, request, response_adapter }.to raise_error
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'valid schema' do
|
86
|
+
let(:raw_contract) do
|
87
|
+
expect(schema_generator).to receive(:generate).with(request_file, response_adapter.body, Pacto.configuration.generator_options).and_return response_body_schema
|
88
|
+
expect(validator).to receive(:validate).and_return true
|
89
|
+
generator.save request_file, request, response_adapter
|
90
|
+
end
|
91
|
+
subject(:generated_contract) { JSON.parse raw_contract }
|
92
|
+
|
93
|
+
it 'sets the schema to the generated json-schema' do
|
94
|
+
expect(subject['response']['schema']).to eq(JSON.parse response_body_schema)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'sets the request attributes' do
|
98
|
+
generated_request = subject['request']
|
99
|
+
expect(generated_request['params']).to eq(request.uri.query_values)
|
100
|
+
expect(generated_request['path']).to eq(request.uri.path)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'preserves ERB in the request params' do
|
104
|
+
generated_request = subject['request']
|
105
|
+
expect(generated_request['params']).to eq('api_key' => "<%= ENV['MY_API_KEY'] %>")
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'normalizes the request method' do
|
109
|
+
generated_request = subject['request']
|
110
|
+
expect(generated_request['http_method']).to eq(request.method.downcase.to_s)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'sets the response attributes' do
|
114
|
+
generated_response = subject['response']
|
115
|
+
expect(generated_response['status']).to eq(response_adapter.status)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'generates pretty JSON' do
|
119
|
+
expect(raw_contract).to eq(pretty(subject))
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'with hints' do
|
124
|
+
let(:request1) { Fabricate(:pacto_request, host: 'example.com', path: '/album/5/cover') }
|
125
|
+
let(:request2) { Fabricate(:pacto_request, host: 'example.com', path: '/album/7/cover') }
|
126
|
+
let(:response1) { Fabricate(:pacto_response) }
|
127
|
+
let(:response2) { Fabricate(:pacto_response) }
|
128
|
+
let(:contracts_path) { Dir.mktmpdir }
|
129
|
+
|
130
|
+
before(:each) do
|
131
|
+
allow(filters).to receive(:filter_request_headers).with(request1, response1).and_return request1.headers
|
132
|
+
allow(filters).to receive(:filter_response_headers).with(request1, response1).and_return response1.headers
|
133
|
+
allow(filters).to receive(:filter_request_headers).with(request2, response2).and_return request2.headers
|
134
|
+
allow(filters).to receive(:filter_response_headers).with(request2, response2).and_return response2.headers
|
135
|
+
allow(schema_generator).to receive(:generate).with(request_file, response1.body, Pacto.configuration.generator_options).and_return response_body_schema
|
136
|
+
allow(schema_generator).to receive(:generate).with(request_file, response2.body, Pacto.configuration.generator_options).and_return response_body_schema
|
137
|
+
allow(validator).to receive(:validate).twice.and_return true
|
138
|
+
Pacto.configuration.contracts_path = contracts_path
|
139
|
+
Pacto::Generator.configure do |c|
|
140
|
+
c.hint 'Get Album Cover', http_method: :get, host: 'http://example.com', path: '/album/{id}/cover', target_file: 'album_services/get_album_cover.json'
|
141
|
+
end
|
142
|
+
Pacto.generate!
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'names the contract based on the hint' do
|
146
|
+
contract1 = generator.generate request1, response1
|
147
|
+
expect(contract1.name).to eq('Get Album Cover')
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'sets the path to match the hint' do
|
151
|
+
contract1 = generator.generate request1, response1
|
152
|
+
expect(contract1.request.path).to eq('/album/{id}/cover')
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'sets the target file based on the hint' do
|
156
|
+
contract1 = generator.generate request1, response1
|
157
|
+
expected_path = File.expand_path('album_services/get_album_cover.json', contracts_path)
|
158
|
+
real_expected_path = Pathname.new(expected_path).realpath.to_s
|
159
|
+
expected_file_uri = Addressable::URI.convert_path(real_expected_path).to_s
|
160
|
+
expect(contract1.file).to eq(expected_file_uri)
|
161
|
+
end
|
162
|
+
|
163
|
+
xit 'does not create duplicate contracts' do
|
164
|
+
contract1 = generator.generate request1, response1
|
165
|
+
contract2 = generator.generate request2, response2
|
166
|
+
expect(contract1).to eq(contract2)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|