pacto 0.3.1 → 0.4.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.rubocop.yml +29 -7
- data/.travis.yml +8 -1
- data/CONTRIBUTING.md +3 -6
- data/Gemfile +13 -2
- data/Guardfile +4 -4
- data/Procfile +1 -0
- data/README.md +47 -13
- data/Rakefile +66 -19
- data/TODO.md +33 -10
- data/bin/pacto +4 -0
- data/changelog.md +30 -0
- data/docs/configuration.md +69 -0
- data/docs/consumer.md +18 -0
- data/docs/cops.md +39 -0
- data/docs/forensics.md +66 -0
- data/docs/generation.md +65 -0
- data/docs/rake_tasks.md +10 -0
- data/docs/rspec.md +0 -0
- data/docs/samples.md +133 -0
- data/docs/server.md +34 -0
- data/docs/server_cli.md +18 -0
- data/docs/stenographer.md +20 -0
- data/features/configuration/strict_matchers.feature +10 -10
- data/features/evolve/existing_services.feature +12 -10
- data/features/generate/generation.feature +11 -11
- data/features/steps/pacto_steps.rb +17 -12
- data/features/stub/templates.feature +4 -4
- data/features/support/env.rb +21 -9
- data/features/validate/meta_validation.feature +9 -17
- data/features/validate/validation.feature +5 -6
- data/lib/pacto.rb +41 -33
- data/lib/pacto/actor.rb +5 -0
- data/lib/pacto/actors/from_examples.rb +67 -0
- data/lib/pacto/actors/json_generator.rb +20 -0
- data/lib/pacto/cli.rb +75 -0
- data/lib/pacto/cli/helpers.rb +20 -0
- data/lib/pacto/consumer.rb +80 -0
- data/lib/pacto/consumer/faraday_driver.rb +34 -0
- data/lib/pacto/contract.rb +48 -20
- data/lib/pacto/contract_builder.rb +125 -0
- data/lib/pacto/contract_factory.rb +31 -12
- data/lib/pacto/contract_files.rb +1 -0
- data/lib/pacto/contract_set.rb +12 -0
- data/lib/pacto/cops.rb +46 -0
- data/lib/pacto/cops/body_cop.rb +23 -0
- data/lib/pacto/cops/request_body_cop.rb +10 -0
- data/lib/pacto/cops/response_body_cop.rb +10 -0
- data/lib/pacto/{validators/response_header_validator.rb → cops/response_header_cop.rb} +9 -15
- data/lib/pacto/cops/response_status_cop.rb +18 -0
- data/lib/pacto/core/configuration.rb +16 -5
- data/lib/pacto/core/contract_registry.rb +13 -32
- data/lib/pacto/core/hook.rb +1 -0
- data/lib/pacto/core/http_middleware.rb +23 -0
- data/lib/pacto/core/investigation_registry.rb +60 -0
- data/lib/pacto/core/modes.rb +1 -0
- data/lib/pacto/core/pacto_request.rb +59 -0
- data/lib/pacto/core/pacto_response.rb +41 -0
- data/lib/pacto/dash.rb +9 -0
- data/lib/pacto/erb_processor.rb +1 -0
- data/lib/pacto/exceptions/invalid_contract.rb +1 -0
- data/lib/pacto/extensions.rb +3 -16
- data/lib/pacto/forensics/investigation_filter.rb +90 -0
- data/lib/pacto/forensics/investigation_matcher.rb +80 -0
- data/lib/pacto/generator.rb +31 -53
- data/lib/pacto/generator/filters.rb +8 -7
- data/lib/pacto/generator/hint.rb +26 -0
- data/lib/pacto/generator/native_contract_generator.rb +74 -0
- data/lib/pacto/hooks/erb_hook.rb +2 -1
- data/lib/pacto/investigation.rb +49 -0
- data/lib/pacto/logger.rb +1 -0
- data/lib/pacto/meta_schema.rb +12 -6
- data/lib/pacto/native_contract_factory.rb +60 -0
- data/lib/pacto/observers/stenographer.rb +42 -0
- data/lib/pacto/provider.rb +27 -0
- data/lib/pacto/rake_task.rb +25 -70
- data/lib/pacto/request_clause.rb +31 -29
- data/lib/pacto/request_pattern.rb +20 -3
- data/lib/pacto/resettable.rb +22 -0
- data/lib/pacto/response_clause.rb +5 -12
- data/lib/pacto/rspec.rb +38 -31
- data/lib/pacto/server.rb +4 -0
- data/lib/pacto/stubs/uri_pattern.rb +21 -11
- data/lib/pacto/stubs/webmock_adapter.rb +69 -34
- data/lib/pacto/swagger_contract_factory.rb +90 -0
- data/lib/pacto/test_helper.rb +37 -0
- data/lib/pacto/ui.rb +32 -2
- data/lib/pacto/uri.rb +2 -1
- data/lib/pacto/version.rb +2 -1
- data/pacto-server.gemspec +24 -0
- data/pacto.gemspec +13 -9
- data/resources/contract_schema.json +46 -18
- data/resources/draft-04.json +150 -0
- data/sample_apis/album/cover_api.rb +12 -0
- data/sample_apis/config.ru +25 -0
- data/sample_apis/echo_api.rb +26 -0
- data/sample_apis/files_api.rb +50 -0
- data/sample_apis/hello_api.rb +14 -0
- data/sample_apis/ping_api.rb +11 -0
- data/sample_apis/reverse_api.rb +20 -0
- data/samples/README.md +11 -0
- data/samples/Rakefile +2 -0
- data/samples/configuration.rb +33 -0
- data/samples/consumer.rb +15 -0
- data/samples/contracts/README.md +1 -0
- data/samples/contracts/contract.js +93 -0
- data/samples/contracts/get_album_cover.json +48 -0
- data/samples/contracts/localhost/api/echo.json +37 -0
- data/samples/contracts/localhost/api/ping.json +38 -0
- data/samples/cops.rb +30 -0
- data/samples/forensics.rb +54 -0
- data/samples/generation.rb +48 -0
- data/samples/rake_tasks.sh +7 -0
- data/samples/rspec.rb +1 -0
- data/samples/samples.rb +92 -0
- data/samples/scripts/bootstrap +2 -0
- data/samples/scripts/wrapper +11 -0
- data/samples/server.rb +24 -0
- data/samples/server_cli.sh +12 -0
- data/samples/stenographer.rb +17 -0
- data/spec/coveralls_helper.rb +1 -0
- data/spec/fabricators/contract_fabricator.rb +94 -0
- data/spec/fabricators/http_fabricator.rb +48 -0
- data/spec/fabricators/webmock_fabricator.rb +24 -0
- data/spec/{unit/data → fixtures/contracts}/contract.json +2 -2
- data/spec/fixtures/contracts/contract_with_examples.json +58 -0
- data/spec/{unit/data → fixtures/contracts}/simple_contract.json +5 -3
- data/spec/{integration/data → fixtures/contracts}/strict_contract.json +5 -3
- data/spec/{integration/data → fixtures/contracts}/templating_contract.json +3 -2
- data/spec/{integration/data/simple_contract.json → fixtures/deprecated_contracts/deprecated_contract.json} +2 -1
- data/spec/fixtures/swagger/petstore.yaml +101 -0
- data/spec/integration/e2e_spec.rb +19 -20
- data/spec/integration/forensics/integration_matcher_spec.rb +90 -0
- data/spec/integration/rspec_spec.rb +22 -25
- data/spec/integration/templating_spec.rb +7 -6
- data/spec/pacto/dummy_server.rb +4 -0
- data/spec/pacto/{server → dummy_server}/dummy.rb +7 -6
- data/spec/pacto/dummy_server/jruby_workaround_helper.rb +23 -0
- data/spec/pacto/{server → dummy_server}/playback_servlet.rb +3 -2
- data/spec/spec_helper.rb +16 -7
- data/spec/unit/actors/from_examples_spec.rb +70 -0
- data/spec/unit/actors/json_generator_spec.rb +105 -0
- data/spec/unit/pacto/actor_spec.rb +23 -0
- data/spec/unit/pacto/configuration_spec.rb +7 -6
- data/spec/unit/pacto/consumer/faraday_driver_spec.rb +40 -0
- data/spec/unit/pacto/contract_builder_spec.rb +89 -0
- data/spec/unit/pacto/contract_factory_spec.rb +62 -11
- data/spec/unit/pacto/contract_files_spec.rb +1 -0
- data/spec/unit/pacto/contract_set_spec.rb +36 -0
- data/spec/unit/pacto/contract_spec.rb +51 -39
- data/spec/unit/pacto/cops/body_cop_spec.rb +107 -0
- data/spec/unit/pacto/{validators/response_header_validator_spec.rb → cops/response_header_cop_spec.rb} +30 -19
- data/spec/unit/pacto/cops/response_status_cop_spec.rb +26 -0
- data/spec/unit/pacto/cops_spec.rb +75 -0
- data/spec/unit/pacto/core/configuration_spec.rb +6 -5
- data/spec/unit/pacto/core/contract_registry_spec.rb +16 -83
- data/spec/unit/pacto/core/http_middleware_spec.rb +36 -0
- data/spec/unit/pacto/core/investigation_spec.rb +62 -0
- data/spec/unit/pacto/core/modes_spec.rb +5 -4
- data/spec/unit/pacto/erb_processor_spec.rb +3 -2
- data/spec/unit/pacto/extensions_spec.rb +10 -20
- data/spec/unit/pacto/generator/filters_spec.rb +11 -10
- data/spec/unit/pacto/generator/native_contract_generator_spec.rb +171 -0
- data/spec/unit/{hooks → pacto/hooks}/erb_hook_spec.rb +18 -11
- data/spec/unit/pacto/investigation_registry_spec.rb +77 -0
- data/spec/unit/pacto/logger_spec.rb +6 -5
- data/spec/unit/pacto/meta_schema_spec.rb +5 -4
- data/spec/unit/pacto/native_contract_factory_spec.rb +26 -0
- data/spec/unit/pacto/pacto_spec.rb +13 -28
- data/spec/unit/pacto/request_clause_spec.rb +16 -51
- data/spec/unit/pacto/request_pattern_spec.rb +6 -5
- data/spec/unit/pacto/response_clause_spec.rb +6 -19
- data/spec/unit/pacto/server/playback_servlet_spec.rb +21 -18
- data/spec/unit/pacto/stubs/observers/stenographer_spec.rb +33 -0
- data/spec/unit/pacto/stubs/uri_pattern_spec.rb +39 -11
- data/spec/unit/pacto/stubs/webmock_adapter_spec.rb +67 -117
- data/spec/unit/pacto/swagger_contract_factory_spec.rb +56 -0
- data/spec/unit/pacto/uri_spec.rb +1 -0
- data/tasks/release.rake +57 -0
- metadata +247 -76
- data/.rubocop-todo.yml +0 -24
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/CHANGELOG +0 -12
- data/features/validate/body_only.feature +0 -85
- data/lib/pacto/contract_list.rb +0 -17
- data/lib/pacto/contract_validator.rb +0 -29
- data/lib/pacto/core/validation_registry.rb +0 -40
- data/lib/pacto/stubs/webmock_helper.rb +0 -69
- data/lib/pacto/validation.rb +0 -54
- data/lib/pacto/validators/body_validator.rb +0 -49
- data/lib/pacto/validators/request_body_validator.rb +0 -26
- data/lib/pacto/validators/response_body_validator.rb +0 -26
- data/lib/pacto/validators/response_status_validator.rb +0 -24
- data/spec/pacto/server.rb +0 -2
- data/spec/unit/pacto/contract_list_spec.rb +0 -35
- data/spec/unit/pacto/contract_validator_spec.rb +0 -85
- data/spec/unit/pacto/core/validation_registry_spec.rb +0 -76
- data/spec/unit/pacto/core/validation_spec.rb +0 -60
- data/spec/unit/pacto/generator_spec.rb +0 -132
- data/spec/unit/pacto/stubs/webmock_helper_spec.rb +0 -20
- data/spec/unit/pacto/validators/body_validator_spec.rb +0 -118
- data/spec/unit/pacto/validators/response_status_validator_spec.rb +0 -20
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
module Core
|
4
|
+
describe HTTPMiddleware do
|
5
|
+
subject(:middleware) { Pacto::Core::HTTPMiddleware.new }
|
6
|
+
let(:request) { double }
|
7
|
+
let(:response) { double }
|
8
|
+
|
9
|
+
class FailingObserver
|
10
|
+
def raise_error(_pacto_request, _pacto_response)
|
11
|
+
fail InvalidContract, ['The contract was missing things', 'and stuff']
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#process' do
|
16
|
+
it 'calls registered HTTP observers' do
|
17
|
+
observer1, observer2 = double, double
|
18
|
+
expect(observer1).to receive(:respond_to?).with(:do_something).and_return true
|
19
|
+
expect(observer2).to receive(:respond_to?).with(:do_something_else).and_return true
|
20
|
+
middleware.add_observer(observer1, :do_something)
|
21
|
+
middleware.add_observer(observer2, :do_something_else)
|
22
|
+
expect(observer1).to receive(:do_something).with(request, response)
|
23
|
+
expect(observer2).to receive(:do_something_else).with(request, response)
|
24
|
+
middleware.process request, response
|
25
|
+
end
|
26
|
+
|
27
|
+
pending 'logs rescues and logs failures'
|
28
|
+
pending 'calls the HTTP middleware'
|
29
|
+
pending 'calls the registered hook'
|
30
|
+
pending 'calls generate when generate is enabled'
|
31
|
+
pending 'calls validate when validate mode is enabled'
|
32
|
+
pending 'validates a WebMock request/response pair'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
describe Investigation do
|
4
|
+
let(:request) { double('request') }
|
5
|
+
let(:response) { double('response') }
|
6
|
+
let(:contract) { Fabricate(:contract) }
|
7
|
+
let(:investigation_citations) { [] }
|
8
|
+
let(:investigation_citations_with_errors) { ['an error occurred'] }
|
9
|
+
|
10
|
+
it 'stores the request, response, contract and citations' do
|
11
|
+
investigation = Pacto::Investigation.new request, response, contract, investigation_citations
|
12
|
+
expect(investigation.request).to eq request
|
13
|
+
expect(investigation.response).to eq response
|
14
|
+
expect(investigation.contract).to eq contract
|
15
|
+
expect(investigation.citations).to eq investigation_citations
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'if there were investigation errors' do
|
19
|
+
subject(:investigation) do
|
20
|
+
Pacto::Investigation.new request, response, contract, investigation_citations_with_errors
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#successful?' do
|
24
|
+
it 'returns false' do
|
25
|
+
expect(investigation.successful?).to be_falsey
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'if there were no investigation errors' do
|
31
|
+
subject(:investigation) do
|
32
|
+
Pacto::Investigation.new request, response, contract, investigation_citations
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns false' do
|
36
|
+
expect(investigation.successful?).to be true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#against_contract?' do
|
41
|
+
it 'returns nil if there was no contract' do
|
42
|
+
investigation = Pacto::Investigation.new request, response, nil, investigation_citations
|
43
|
+
expect(investigation.against_contract? 'a').to be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns the contract with an exact string name match' do
|
47
|
+
allow(contract).to receive(:file).and_return('foo')
|
48
|
+
investigation = Pacto::Investigation.new request, response, contract, investigation_citations
|
49
|
+
expect(investigation.against_contract? 'foo').to eq(contract)
|
50
|
+
expect(investigation.against_contract? 'bar').to be_nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns the contract if there is a regex match' do
|
54
|
+
allow(contract).to receive(:file).and_return 'foobar'
|
55
|
+
investigation = Pacto::Investigation.new request, response, contract, investigation_citations
|
56
|
+
expect(investigation.against_contract?(/foo/)).to eq(contract)
|
57
|
+
expect(investigation.against_contract?(/bar/)).to eq(contract)
|
58
|
+
expect(investigation.against_contract?(/baz/)).to be_nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,17 +1,18 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
describe Pacto do
|
2
|
-
modes = %w
|
3
|
+
modes = %w(generate validate)
|
3
4
|
modes.each do |mode|
|
4
5
|
enable_method = "#{mode}!".to_sym # generate!
|
5
6
|
query_method = "#{mode[0..-2]}ing?".to_sym # generating?
|
6
7
|
disable_method = "stop_#{mode[0..-2]}ing!".to_sym # stop_generating!
|
7
8
|
describe ".#{mode}!" do
|
8
9
|
it "tells the provider to enable #{mode} mode" do
|
9
|
-
expect(subject.send query_method).to
|
10
|
+
expect(subject.send query_method).to be_falsey
|
10
11
|
subject.send enable_method
|
11
|
-
expect(subject.send query_method).to
|
12
|
+
expect(subject.send query_method).to be true
|
12
13
|
|
13
14
|
subject.send disable_method
|
14
|
-
expect(subject.send query_method).to
|
15
|
+
expect(subject.send query_method).to be_falsey
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
@@ -1,6 +1,7 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Pacto
|
2
3
|
describe ERBProcessor do
|
3
|
-
subject(:processor) {
|
4
|
+
subject(:processor) { described_class.new }
|
4
5
|
|
5
6
|
describe '#process' do
|
6
7
|
let(:erb) { '2 + 2 = <%= 2 + 2 %>' }
|
@@ -11,7 +12,7 @@ module Pacto
|
|
11
12
|
end
|
12
13
|
|
13
14
|
it 'logs the erb processed' do
|
14
|
-
Pacto.configuration.logger.
|
15
|
+
expect(Pacto.configuration.logger).to receive(:debug).with("Processed contract: \"#{result}\"")
|
15
16
|
processor.process erb
|
16
17
|
end
|
17
18
|
|
@@ -1,24 +1,14 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Pacto
|
2
|
-
|
3
|
-
describe
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
describe '#normalize_header_keys' do
|
14
|
-
it 'matches headers to the style in the RFC documentation' do
|
15
|
-
expect(Pacto::Extensions.normalize_header_keys(:'user-agent' => 'a')).to eq('User-Agent' => 'a') # rubocop:disable SymbolName
|
16
|
-
expect(Pacto::Extensions.normalize_header_keys(:user_agent => 'a')).to eq('User-Agent' => 'a')
|
17
|
-
expect(Pacto::Extensions.normalize_header_keys('User-Agent' => 'a')).to eq('User-Agent' => 'a')
|
18
|
-
expect(Pacto::Extensions.normalize_header_keys('user-agent' => 'a')).to eq('User-Agent' => 'a')
|
19
|
-
expect(Pacto::Extensions.normalize_header_keys('user_agent' => 'a')).to eq('User-Agent' => 'a')
|
20
|
-
expect(Pacto::Extensions.normalize_header_keys('USER_AGENT' => 'a')).to eq('User-Agent' => 'a')
|
21
|
-
end
|
3
|
+
describe Extensions do
|
4
|
+
describe '#normalize_header_keys' do
|
5
|
+
it 'matches headers to the style in the RFC documentation' do
|
6
|
+
expect(Pacto::Extensions.normalize_header_keys(:'user-agent' => 'a')).to eq('User-Agent' => 'a') # rubocop:disable SymbolName
|
7
|
+
expect(Pacto::Extensions.normalize_header_keys(user_agent: 'a')).to eq('User-Agent' => 'a')
|
8
|
+
expect(Pacto::Extensions.normalize_header_keys('User-Agent' => 'a')).to eq('User-Agent' => 'a')
|
9
|
+
expect(Pacto::Extensions.normalize_header_keys('user-agent' => 'a')).to eq('User-Agent' => 'a')
|
10
|
+
expect(Pacto::Extensions.normalize_header_keys('user_agent' => 'a')).to eq('User-Agent' => 'a')
|
11
|
+
expect(Pacto::Extensions.normalize_header_keys('USER_AGENT' => 'a')).to eq('User-Agent' => 'a')
|
22
12
|
end
|
23
13
|
end
|
24
14
|
end
|
@@ -1,22 +1,23 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Pacto
|
2
|
-
|
3
|
+
module Generator
|
3
4
|
describe Filters do
|
4
5
|
let(:record_host) do
|
5
6
|
'http://example.com'
|
6
7
|
end
|
7
8
|
let(:request) do
|
8
9
|
RequestClause.new(
|
9
|
-
record_host,
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
host: record_host,
|
11
|
+
http_method: 'GET',
|
12
|
+
path: '/abcd',
|
13
|
+
headers: {
|
13
14
|
'Server' => ['example.com'],
|
14
15
|
'Connection' => ['Close'],
|
15
16
|
'Content-Length' => [1234],
|
16
17
|
'Via' => ['Some Proxy'],
|
17
18
|
'User-Agent' => ['rspec']
|
18
19
|
},
|
19
|
-
|
20
|
+
params: {
|
20
21
|
'apikey' => "<%= ENV['MY_API_KEY'] %>"
|
21
22
|
}
|
22
23
|
)
|
@@ -24,8 +25,8 @@ module Pacto
|
|
24
25
|
let(:varies) { ['User-Agent'] }
|
25
26
|
let(:response) do
|
26
27
|
Faraday::Response.new(
|
27
|
-
:
|
28
|
-
:
|
28
|
+
status: 200,
|
29
|
+
response_headers: {
|
29
30
|
'Date' => Time.now.rfc2822,
|
30
31
|
'Last-Modified' => Time.now.rfc2822,
|
31
32
|
'ETag' => 'abc123',
|
@@ -33,7 +34,7 @@ module Pacto
|
|
33
34
|
'Content-Type' => ['application/json'],
|
34
35
|
'Vary' => varies
|
35
36
|
},
|
36
|
-
:
|
37
|
+
body: double('dummy body')
|
37
38
|
)
|
38
39
|
end
|
39
40
|
|
@@ -63,7 +64,7 @@ module Pacto
|
|
63
64
|
end
|
64
65
|
context 'as multiple items' do
|
65
66
|
let(:varies) do
|
66
|
-
%w
|
67
|
+
%w(User-Agent Via)
|
67
68
|
end
|
68
69
|
it 'keeps each header' do
|
69
70
|
expect(filtered_request_headers).to include 'user-agent'
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
module Generator
|
4
|
+
describe NativeContractGenerator do
|
5
|
+
let(:record_host) do
|
6
|
+
'http://example.com'
|
7
|
+
end
|
8
|
+
let(:request_clause) { Fabricate(:request_clause, params: { 'api_key' => "<%= ENV['MY_API_KEY'] %>" }) }
|
9
|
+
let(:response_adapter) do
|
10
|
+
Faraday::Response.new(
|
11
|
+
status: 200,
|
12
|
+
response_headers: {
|
13
|
+
'Date' => [Time.now],
|
14
|
+
'Server' => ['Fake Server'],
|
15
|
+
'Content-Type' => ['application/json'],
|
16
|
+
'Vary' => ['User-Agent']
|
17
|
+
},
|
18
|
+
body: 'dummy body' # body is just a string
|
19
|
+
)
|
20
|
+
end
|
21
|
+
let(:filtered_request_headers) { double('filtered_response_headers') }
|
22
|
+
let(:filtered_response_headers) { double('filtered_response_headers') }
|
23
|
+
let(:response_body_schema) { '{"message": "dummy generated schema"}' }
|
24
|
+
let(:version) { 'draft3' }
|
25
|
+
let(:schema_generator) { double('schema_generator') }
|
26
|
+
let(:validator) { double('validator') }
|
27
|
+
let(:filters) { double :filters }
|
28
|
+
let(:consumer) { double 'consumer' }
|
29
|
+
let(:request_file) { 'request.json' }
|
30
|
+
let(:generator) { described_class.new version, schema_generator, validator, filters, consumer }
|
31
|
+
let(:request_contract) do
|
32
|
+
Fabricate(:partial_contract, request: request_clause, file: request_file)
|
33
|
+
end
|
34
|
+
let(:request) do
|
35
|
+
Pacto.configuration.default_consumer.build_request request_contract
|
36
|
+
end
|
37
|
+
|
38
|
+
def pretty(obj)
|
39
|
+
MultiJson.encode(obj, pretty: true).gsub(/^$\n/, '')
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#generate_from_partial_contract' do
|
43
|
+
# TODO: Deprecate partial contracts?
|
44
|
+
let(:generated_contract) { Fabricate(:contract) }
|
45
|
+
before do
|
46
|
+
expect(Pacto).to receive(:load_contract).with(request_file, record_host).and_return request_contract
|
47
|
+
expect(consumer).to receive(:request).with(request_contract).and_return([request, response_adapter])
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'parses the request' do
|
51
|
+
expect(generator).to receive(:save).with(request_file, request, anything)
|
52
|
+
generator.generate_from_partial_contract request_file, record_host
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'fetches a response' do
|
56
|
+
expect(generator).to receive(:save).with(request_file, anything, response_adapter)
|
57
|
+
generator.generate_from_partial_contract request_file, record_host
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'saves the result' do
|
61
|
+
expect(generator).to receive(:save).with(request_file, request, response_adapter).and_return generated_contract
|
62
|
+
expect(generator.generate_from_partial_contract request_file, record_host).to eq(generated_contract)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#save' do
|
67
|
+
before do
|
68
|
+
allow(filters).to receive(:filter_request_headers).with(request, response_adapter).and_return filtered_request_headers
|
69
|
+
allow(filters).to receive(:filter_response_headers).with(request, response_adapter).and_return filtered_response_headers
|
70
|
+
end
|
71
|
+
context 'invalid schema' do
|
72
|
+
it 'raises an error if schema generation fails' do
|
73
|
+
expect(schema_generator).to receive(:generate).and_raise ArgumentError.new('Could not generate schema')
|
74
|
+
expect { generator.save request_file, request, response_adapter }.to raise_error
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'raises an error if the generated contract is invalid' do
|
78
|
+
expect(schema_generator).to receive(:generate).and_return response_body_schema
|
79
|
+
expect(validator).to receive(:validate).and_raise InvalidContract.new('dummy error')
|
80
|
+
expect { generator.save request_file, request, response_adapter }.to raise_error
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'valid schema' do
|
85
|
+
let(:raw_contract) do
|
86
|
+
expect(schema_generator).to receive(:generate).with(request_file, response_adapter.body, Pacto.configuration.generator_options).and_return response_body_schema
|
87
|
+
expect(validator).to receive(:validate).and_return true
|
88
|
+
generator.save request_file, request, response_adapter
|
89
|
+
end
|
90
|
+
subject(:generated_contract) { JSON.parse raw_contract }
|
91
|
+
|
92
|
+
it 'sets the schema to the generated json-schema' do
|
93
|
+
expect(subject['response']['schema']).to eq(JSON.parse response_body_schema)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'sets the request attributes' do
|
97
|
+
generated_request = subject['request']
|
98
|
+
expect(generated_request['params']).to eq(request.uri.query_values)
|
99
|
+
expect(generated_request['path']).to eq(request.uri.path)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'preserves ERB in the request params' do
|
103
|
+
generated_request = subject['request']
|
104
|
+
expect(generated_request['params']).to eq('api_key' => "<%= ENV['MY_API_KEY'] %>")
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'normalizes the request method' do
|
108
|
+
generated_request = subject['request']
|
109
|
+
expect(generated_request['http_method']).to eq(request.method.downcase.to_s)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'sets the response attributes' do
|
113
|
+
generated_response = subject['response']
|
114
|
+
expect(generated_response['status']).to eq(response_adapter.status)
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'generates pretty JSON' do
|
118
|
+
expect(raw_contract).to eq(pretty(subject))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'with hints' do
|
123
|
+
let(:request1) { Fabricate(:pacto_request, host: 'example.com', path: '/album/5/cover') }
|
124
|
+
let(:request2) { Fabricate(:pacto_request, host: 'example.com', path: '/album/7/cover') }
|
125
|
+
let(:response1) { Fabricate(:pacto_response) }
|
126
|
+
let(:response2) { Fabricate(:pacto_response) }
|
127
|
+
let(:contracts_path) { Dir.mktmpdir }
|
128
|
+
|
129
|
+
before(:each) do
|
130
|
+
allow(filters).to receive(:filter_request_headers).with(request1, response1).and_return request1.headers
|
131
|
+
allow(filters).to receive(:filter_response_headers).with(request1, response1).and_return response1.headers
|
132
|
+
allow(filters).to receive(:filter_request_headers).with(request2, response2).and_return request2.headers
|
133
|
+
allow(filters).to receive(:filter_response_headers).with(request2, response2).and_return response2.headers
|
134
|
+
allow(schema_generator).to receive(:generate).with(request_file, response1.body, Pacto.configuration.generator_options).and_return response_body_schema
|
135
|
+
allow(schema_generator).to receive(:generate).with(request_file, response2.body, Pacto.configuration.generator_options).and_return response_body_schema
|
136
|
+
allow(validator).to receive(:validate).twice.and_return true
|
137
|
+
Pacto.configuration.contracts_path = contracts_path
|
138
|
+
Pacto::Generator.configure do |c|
|
139
|
+
c.hint 'Get Album Cover', http_method: :get, host: 'http://example.com', path: '/album/{id}/cover', target_file: 'album_services/get_album_cover.json'
|
140
|
+
end
|
141
|
+
Pacto.generate!
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'names the contract based on the hint' do
|
145
|
+
contract1 = generator.generate request1, response1
|
146
|
+
expect(contract1.name).to eq('Get Album Cover')
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'sets the path to match the hint' do
|
150
|
+
contract1 = generator.generate request1, response1
|
151
|
+
expect(contract1.request.path).to eq('/album/{id}/cover')
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'sets the target file based on the hint' do
|
155
|
+
contract1 = generator.generate request1, response1
|
156
|
+
expected_path = File.expand_path('album_services/get_album_cover.json', contracts_path)
|
157
|
+
real_expected_path = Pathname.new(expected_path).realpath.to_s
|
158
|
+
expected_file_uri = Addressable::URI.convert_path(real_expected_path).to_s
|
159
|
+
expect(contract1.file).to eq(expected_file_uri)
|
160
|
+
end
|
161
|
+
|
162
|
+
xit 'does not create duplicate contracts' do
|
163
|
+
contract1 = generator.generate request1, response1
|
164
|
+
contract2 = generator.generate request2, response2
|
165
|
+
expect(contract1).to eq(contract2)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -1,13 +1,17 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
describe Pacto::Hooks::ERBHook do
|
2
3
|
describe '#process' do
|
3
4
|
let(:req) do
|
4
|
-
OpenStruct.new(:
|
5
|
+
OpenStruct.new(headers: { 'User-Agent' => 'abcd' })
|
5
6
|
end
|
6
7
|
let(:converted_req) do
|
7
|
-
{'HEADERS' => {'User-Agent' => 'abcd'}}
|
8
|
+
{ 'HEADERS' => { 'User-Agent' => 'abcd' } }
|
8
9
|
end
|
9
10
|
let(:res) do
|
10
|
-
|
11
|
+
Pacto::PactoResponse.new(
|
12
|
+
status: 200,
|
13
|
+
body: 'before'
|
14
|
+
)
|
11
15
|
end
|
12
16
|
|
13
17
|
before do
|
@@ -16,7 +20,7 @@ describe Pacto::Hooks::ERBHook do
|
|
16
20
|
context 'no matching contracts' do
|
17
21
|
it 'binds the request' do
|
18
22
|
contracts = Set.new
|
19
|
-
mock_erb(:
|
23
|
+
mock_erb(req: converted_req)
|
20
24
|
described_class.new.process contracts, req, res
|
21
25
|
expect(res.body).to eq('after')
|
22
26
|
end
|
@@ -24,9 +28,9 @@ describe Pacto::Hooks::ERBHook do
|
|
24
28
|
|
25
29
|
context 'one matching contract' do
|
26
30
|
it 'binds the request and the contract\'s values' do
|
27
|
-
contract = OpenStruct.new(:
|
31
|
+
contract = OpenStruct.new(values: { max: 'test' })
|
28
32
|
contracts = Set.new([contract])
|
29
|
-
mock_erb(:
|
33
|
+
mock_erb(req: converted_req, max: 'test')
|
30
34
|
described_class.new.process contracts, req, res
|
31
35
|
expect(res.body).to eq('after')
|
32
36
|
end
|
@@ -34,10 +38,13 @@ describe Pacto::Hooks::ERBHook do
|
|
34
38
|
|
35
39
|
context 'multiple matching contracts' do
|
36
40
|
it 'binds the request and the first contract\'s values' do
|
37
|
-
contract1 = OpenStruct.new(:
|
38
|
-
contract2 = OpenStruct.new(:
|
39
|
-
res =
|
40
|
-
|
41
|
+
contract1 = OpenStruct.new(values: { max: 'test' })
|
42
|
+
contract2 = OpenStruct.new(values: { mob: 'team' })
|
43
|
+
res = Pacto::PactoResponse.new(
|
44
|
+
status: 200,
|
45
|
+
body: 'before'
|
46
|
+
)
|
47
|
+
mock_erb(req: converted_req, max: 'test')
|
41
48
|
contracts = Set.new([contract1, contract2])
|
42
49
|
described_class.new.process contracts, req, res
|
43
50
|
expect(res.body).to eq('after')
|
@@ -46,6 +53,6 @@ describe Pacto::Hooks::ERBHook do
|
|
46
53
|
end
|
47
54
|
|
48
55
|
def mock_erb(hash)
|
49
|
-
Pacto::ERBProcessor.
|
56
|
+
expect_any_instance_of(Pacto::ERBProcessor).to receive(:process).with('before', hash).and_return('after')
|
50
57
|
end
|
51
58
|
end
|