pacto 0.3.0.pre → 0.3.0
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 +7 -0
- data/.gitignore +2 -0
- data/.rubocop-todo.yml +0 -27
- data/.rubocop.yml +9 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -5
- data/CONTRIBUTING.md +112 -0
- data/Gemfile +5 -0
- data/Guardfile +18 -13
- data/README.md +157 -101
- data/Rakefile +3 -3
- data/features/configuration/strict_matchers.feature +97 -0
- data/features/evolve/README.md +11 -0
- data/features/evolve/existing_services.feature +82 -0
- data/features/generate/README.md +5 -0
- data/features/generate/generation.feature +28 -0
- data/features/steps/pacto_steps.rb +75 -0
- data/features/stub/README.md +2 -0
- data/features/stub/templates.feature +46 -0
- data/features/support/env.rb +11 -5
- data/features/validate/README.md +1 -0
- data/features/validate/body_only.feature +85 -0
- data/features/{journeys/validation.feature → validate/meta_validation.feature} +41 -24
- data/features/validate/validation.feature +36 -0
- data/lib/pacto.rb +61 -33
- data/lib/pacto/contract.rb +18 -15
- data/lib/pacto/contract_factory.rb +14 -11
- data/lib/pacto/contract_files.rb +17 -0
- data/lib/pacto/contract_list.rb +17 -0
- data/lib/pacto/contract_validator.rb +29 -0
- data/lib/pacto/core/configuration.rb +19 -17
- data/lib/pacto/core/contract_registry.rb +43 -0
- data/lib/pacto/core/{callback.rb → hook.rb} +3 -3
- data/lib/pacto/core/modes.rb +33 -0
- data/lib/pacto/core/validation_registry.rb +45 -0
- data/lib/pacto/erb_processor.rb +0 -1
- data/lib/pacto/extensions.rb +18 -4
- data/lib/pacto/generator.rb +34 -49
- data/lib/pacto/generator/filters.rb +41 -0
- data/lib/pacto/hooks/erb_hook.rb +4 -3
- data/lib/pacto/logger.rb +4 -2
- data/lib/pacto/meta_schema.rb +4 -2
- data/lib/pacto/rake_task.rb +28 -25
- data/lib/pacto/request_clause.rb +43 -0
- data/lib/pacto/request_pattern.rb +8 -0
- data/lib/pacto/response_clause.rb +15 -0
- data/lib/pacto/rspec.rb +102 -0
- data/lib/pacto/stubs/uri_pattern.rb +23 -0
- data/lib/pacto/stubs/webmock_adapter.rb +69 -0
- data/lib/pacto/stubs/webmock_helper.rb +71 -0
- data/lib/pacto/ui.rb +7 -0
- data/lib/pacto/uri.rb +9 -0
- data/lib/pacto/validation.rb +57 -0
- data/lib/pacto/validators/body_validator.rb +41 -0
- data/lib/pacto/validators/request_body_validator.rb +23 -0
- data/lib/pacto/validators/response_body_validator.rb +23 -0
- data/lib/pacto/validators/response_header_validator.rb +49 -0
- data/lib/pacto/validators/response_status_validator.rb +24 -0
- data/lib/pacto/version.rb +1 -1
- data/pacto.gemspec +33 -29
- data/resources/contract_schema.json +8 -176
- data/resources/draft-03.json +174 -0
- data/spec/integration/data/strict_contract.json +2 -2
- data/spec/integration/e2e_spec.rb +22 -31
- data/spec/integration/rspec_spec.rb +94 -0
- data/spec/integration/templating_spec.rb +9 -12
- data/{lib → spec}/pacto/server.rb +0 -0
- data/{lib → spec}/pacto/server/dummy.rb +11 -8
- data/{lib → spec}/pacto/server/playback_servlet.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/hooks/erb_hook_spec.rb +15 -15
- data/spec/unit/pacto/configuration_spec.rb +2 -10
- data/spec/unit/pacto/contract_factory_spec.rb +16 -13
- data/spec/unit/pacto/contract_files_spec.rb +42 -0
- data/spec/unit/pacto/contract_list_spec.rb +35 -0
- data/spec/unit/pacto/contract_spec.rb +43 -44
- data/spec/unit/pacto/contract_validator_spec.rb +85 -0
- data/spec/unit/pacto/core/configuration_spec.rb +4 -11
- data/spec/unit/pacto/core/contract_registry_spec.rb +119 -0
- data/spec/unit/pacto/core/modes_spec.rb +18 -0
- data/spec/unit/pacto/core/validation_registry_spec.rb +76 -0
- data/spec/unit/pacto/core/validation_spec.rb +60 -0
- data/spec/unit/pacto/extensions_spec.rb +14 -23
- data/spec/unit/pacto/generator/filters_spec.rb +99 -0
- data/spec/unit/pacto/generator_spec.rb +34 -73
- data/spec/unit/pacto/meta_schema_spec.rb +46 -6
- data/spec/unit/pacto/pacto_spec.rb +17 -15
- data/spec/unit/pacto/{request_spec.rb → request_clause_spec.rb} +32 -44
- data/spec/unit/pacto/request_pattern_spec.rb +22 -0
- data/spec/unit/pacto/response_clause_spec.rb +54 -0
- data/spec/unit/pacto/stubs/uri_pattern_spec.rb +28 -0
- data/spec/unit/pacto/stubs/webmock_adapter_spec.rb +205 -0
- data/spec/unit/pacto/stubs/webmock_helper_spec.rb +20 -0
- data/spec/unit/pacto/uri_spec.rb +20 -0
- data/spec/unit/pacto/validators/body_validator_spec.rb +105 -0
- data/spec/unit/pacto/validators/response_header_validator_spec.rb +94 -0
- data/spec/unit/pacto/validators/response_status_validator_spec.rb +20 -0
- metadata +230 -146
- data/features/generation/generation.feature +0 -25
- data/lib/pacto/core/contract_repository.rb +0 -44
- data/lib/pacto/hash_merge_processor.rb +0 -14
- data/lib/pacto/request.rb +0 -57
- data/lib/pacto/response.rb +0 -63
- data/lib/pacto/response_adapter.rb +0 -24
- data/lib/pacto/stubs/built_in.rb +0 -57
- data/spec/unit/pacto/core/contract_repository_spec.rb +0 -133
- data/spec/unit/pacto/hash_merge_processor_spec.rb +0 -20
- data/spec/unit/pacto/response_adapter_spec.rb +0 -25
- data/spec/unit/pacto/response_spec.rb +0 -201
- data/spec/unit/pacto/stubs/built_in_spec.rb +0 -168
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Pacto
|
|
2
|
+
class RequestClause
|
|
3
|
+
attr_reader :host, :method, :schema
|
|
4
|
+
attr_accessor :body
|
|
5
|
+
|
|
6
|
+
def initialize(host, definition)
|
|
7
|
+
@host = host
|
|
8
|
+
@definition = definition
|
|
9
|
+
@method = definition['method'].to_s.downcase.to_sym
|
|
10
|
+
@schema = definition['body'] || {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def uri
|
|
14
|
+
@uri ||= Pacto::URI.for(host, path, params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def body
|
|
18
|
+
JSON::Generator.generate(@definition['body']) if @definition['body']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def path
|
|
22
|
+
@definition['path']
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def headers
|
|
26
|
+
@definition['headers']
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def params
|
|
30
|
+
@definition['params'] || {}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def execute
|
|
34
|
+
conn = Faraday.new(:url => uri.to_s) do |faraday|
|
|
35
|
+
faraday.response :logger if Pacto.configuration.logger.level == :debug
|
|
36
|
+
faraday.adapter Faraday.default_adapter
|
|
37
|
+
end
|
|
38
|
+
conn.send(method) do |req|
|
|
39
|
+
req.headers = headers
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Pacto
|
|
2
|
+
class ResponseClause
|
|
3
|
+
attr_reader :status, :headers, :schema
|
|
4
|
+
|
|
5
|
+
def initialize(definition)
|
|
6
|
+
@status = definition['status']
|
|
7
|
+
@headers = definition['headers']
|
|
8
|
+
@schema = definition['body'] || {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def body
|
|
12
|
+
@body ||= JSON::Generator.generate(schema)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/pacto/rspec.rb
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require 'pacto'
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'rspec/core'
|
|
5
|
+
require 'rspec/expectations'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
raise 'pacto/rspec requires rspec 2 or later'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
RSpec::Matchers.define :have_unmatched_requests do |method, uri|
|
|
11
|
+
@unmatched_validations = Pacto::ValidationRegistry.instance.unmatched_validations
|
|
12
|
+
match do
|
|
13
|
+
!@unmatched_validations.empty?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
failure_message_for_should do
|
|
17
|
+
'Expected Pacto to have not matched all requests to a Contract, but all requests were matched.'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
failure_message_for_should_not do
|
|
21
|
+
unmatched_requests = @unmatched_validations.map(&:request).join("\n ")
|
|
22
|
+
"Expected Pacto to have matched all requests to a Contract, but the following requests were not matched: \n #{unmatched_requests}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
RSpec::Matchers.define :have_failed_validations do |method, uri|
|
|
27
|
+
@failed_validations = Pacto::ValidationRegistry.instance.failed_validations
|
|
28
|
+
match do
|
|
29
|
+
!@failed_validations.empty?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
failure_message_for_should do
|
|
33
|
+
'Expected Pacto to have found validation problems, but none were found.'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
failure_message_for_should_not do
|
|
37
|
+
"Expected Pacto to have successfully validated all requests, but the following issues were found: #{@failed_validations}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
RSpec::Matchers.define :have_validated do |method, uri|
|
|
42
|
+
@request_pattern = WebMock::RequestPattern.new(method, uri)
|
|
43
|
+
match do
|
|
44
|
+
validated? @request_pattern
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
chain :against_contract do |contract|
|
|
48
|
+
@contract = contract
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
chain :with do |options|
|
|
52
|
+
@request_pattern.with options
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def validated?(request_pattern)
|
|
56
|
+
@matching_validations = Pacto::ValidationRegistry.instance.validated? @request_pattern
|
|
57
|
+
validated = !@matching_validations.nil?
|
|
58
|
+
validated && successfully? && contract_matches?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validation_results
|
|
62
|
+
@validation_results ||= @matching_validations.map(&:results).flatten.compact
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def successfully?
|
|
66
|
+
@matching_validations.map(&:successful?).uniq.eql? [true]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def contract_matches?
|
|
70
|
+
if @contract
|
|
71
|
+
validated_contracts = @matching_validations.map(&:contract)
|
|
72
|
+
# Is there a better option than case equality for string & regex support?
|
|
73
|
+
validated_contracts.map(&:file).index { |file| @contract === file } # rubocop:disable CaseEquality
|
|
74
|
+
else
|
|
75
|
+
true
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
failure_message_for_should do
|
|
80
|
+
buffer = StringIO.new
|
|
81
|
+
buffer.puts "expected Pacto to have validated #{@request_pattern}"
|
|
82
|
+
if @matching_validations.nil? || @matching_validations.empty?
|
|
83
|
+
buffer.puts ' but no matching request was received'
|
|
84
|
+
buffer.puts ' received:'
|
|
85
|
+
buffer.puts "#{WebMock::RequestRegistry.instance}"
|
|
86
|
+
elsif @matching_validations.map(&:contract).compact.empty?
|
|
87
|
+
buffer.puts ' but a matching Contract was not found'
|
|
88
|
+
elsif !successfully?
|
|
89
|
+
buffer.puts ' but validation errors were found:'
|
|
90
|
+
buffer.print ' '
|
|
91
|
+
buffer.puts validation_results.join "\n "
|
|
92
|
+
# validation_results.each do |validation_result|
|
|
93
|
+
# buffer.puts " #{validation_result}"
|
|
94
|
+
# end
|
|
95
|
+
elsif @contract
|
|
96
|
+
validated_against = @matching_validations.map { |v| v.against_contract? @contract }.compact.join ','
|
|
97
|
+
buffer.puts " against Contract #{@contract}"
|
|
98
|
+
buffer.puts " but it was validated against #{validated_against}"
|
|
99
|
+
end
|
|
100
|
+
buffer.string
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Pacto
|
|
2
|
+
class UriPattern
|
|
3
|
+
class << self
|
|
4
|
+
def for(request)
|
|
5
|
+
if Pacto.configuration.strict_matchers
|
|
6
|
+
build_strict_uri_pattern(request)
|
|
7
|
+
else
|
|
8
|
+
build_relaxed_uri_pattern(request)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def build_strict_uri_pattern(request)
|
|
13
|
+
"#{request.host}#{request.path}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def build_relaxed_uri_pattern(request)
|
|
17
|
+
path_pattern = request.path.gsub(/\/:\w+/, '/[^\/\?#]+')
|
|
18
|
+
host_pattern = Regexp.quote(request.host)
|
|
19
|
+
/#{host_pattern}#{path_pattern}/
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'pacto/stubs/webmock_helper'
|
|
2
|
+
|
|
3
|
+
module Pacto
|
|
4
|
+
module Stubs
|
|
5
|
+
class WebMockAdapter
|
|
6
|
+
def initialize
|
|
7
|
+
register_hooks
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def stub_request!(request, response)
|
|
11
|
+
uri_pattern = UriPattern.for(request)
|
|
12
|
+
stub = WebMock.stub_request(request.method, uri_pattern)
|
|
13
|
+
|
|
14
|
+
stub.request_pattern.with(strict_details(request)) if Pacto.configuration.strict_matchers
|
|
15
|
+
|
|
16
|
+
stub.to_return(
|
|
17
|
+
:status => response.status,
|
|
18
|
+
:headers => response.headers,
|
|
19
|
+
:body => format_body(response.body)
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def reset!
|
|
24
|
+
WebMock.reset!
|
|
25
|
+
WebMock.reset_callbacks
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def process_hooks(request_signature, response)
|
|
29
|
+
WebMockHelper.generate(request_signature, response) if Pacto.generating?
|
|
30
|
+
|
|
31
|
+
contracts = Pacto.contracts_for request_signature
|
|
32
|
+
Pacto.configuration.hook.process contracts, request_signature, response
|
|
33
|
+
|
|
34
|
+
WebMockHelper.validate(request_signature, response) if Pacto.validating?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def register_hooks
|
|
40
|
+
WebMock.after_request do |request_signature, response|
|
|
41
|
+
process_hooks request_signature, response
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def format_body(body)
|
|
46
|
+
if body.is_a?(Hash) || body.is_a?(Array)
|
|
47
|
+
body.to_json
|
|
48
|
+
else
|
|
49
|
+
body
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def strict_details(request)
|
|
54
|
+
{}.tap do |details|
|
|
55
|
+
unless request.params.empty?
|
|
56
|
+
details[webmock_params_key(request)] = request.params
|
|
57
|
+
end
|
|
58
|
+
unless request.headers.empty?
|
|
59
|
+
details[:headers] = request.headers
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def webmock_params_key(request)
|
|
65
|
+
request.method == :get ? :query : :body
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Pacto
|
|
2
|
+
module Stubs
|
|
3
|
+
class WebMockHelper
|
|
4
|
+
class << self
|
|
5
|
+
def validate(request_signature, response)
|
|
6
|
+
pacto_response = webmock_to_pacto_response(response)
|
|
7
|
+
contract = Pacto.contracts_for(request_signature).first
|
|
8
|
+
validation = Validation.new request_signature, pacto_response, contract
|
|
9
|
+
Pacto::ValidationRegistry.instance.register_validation validation
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def generate(request_signature, response)
|
|
13
|
+
logger.debug("Generating Contract for #{request_signature}, #{response}")
|
|
14
|
+
begin
|
|
15
|
+
contract_file = load_contract_file(request_signature)
|
|
16
|
+
|
|
17
|
+
unless File.exists? contract_file
|
|
18
|
+
generate_contract(request_signature, response, contract_file)
|
|
19
|
+
end
|
|
20
|
+
rescue => e
|
|
21
|
+
logger.error("Error while generating Contract #{contract_file}: #{e.message}")
|
|
22
|
+
logger.error("Backtrace: #{e.backtrace}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def generate_contract(request_signature, response, contract_file)
|
|
29
|
+
uri = URI(request_signature.uri)
|
|
30
|
+
pacto_request = webmock_to_pacto_request(request_signature)
|
|
31
|
+
pacto_response = webmock_to_pacto_response(response)
|
|
32
|
+
generator = Pacto::Generator.new
|
|
33
|
+
FileUtils.mkdir_p(File.dirname contract_file)
|
|
34
|
+
File.write(contract_file, generator.save(uri, pacto_request, pacto_response))
|
|
35
|
+
logger.debug("Generating #{contract_file}")
|
|
36
|
+
|
|
37
|
+
Pacto.load_contract contract_file, uri.host
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def load_contract_file(request_signature)
|
|
41
|
+
uri = URI(request_signature.uri)
|
|
42
|
+
basename = File.basename(uri.path, '.json') + '.json'
|
|
43
|
+
File.join(Pacto.configuration.contracts_path, uri.host, File.dirname(uri.path), basename)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def logger
|
|
47
|
+
@logger ||= Logger.instance
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def webmock_to_pacto_request(webmock_request)
|
|
51
|
+
uri = webmock_request.uri
|
|
52
|
+
Faraday::Request.create webmock_request.method do |req|
|
|
53
|
+
req.path = uri.path
|
|
54
|
+
req.params = uri.query_values
|
|
55
|
+
req.headers = webmock_request.headers
|
|
56
|
+
req.body = webmock_request.body
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def webmock_to_pacto_response(webmock_response)
|
|
61
|
+
status, _description = webmock_response.status
|
|
62
|
+
Faraday::Response.new(
|
|
63
|
+
:status => status,
|
|
64
|
+
:response_headers => webmock_response.headers || {},
|
|
65
|
+
:body => webmock_response.body
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/pacto/ui.rb
ADDED
data/lib/pacto/uri.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Pacto
|
|
2
|
+
class Validation
|
|
3
|
+
attr_reader :request, :response, :contract, :results
|
|
4
|
+
|
|
5
|
+
def initialize(request, response, contract)
|
|
6
|
+
@request = request
|
|
7
|
+
@response = response
|
|
8
|
+
@contract = contract
|
|
9
|
+
validate unless contract.nil?
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def successful?
|
|
13
|
+
@results.nil? || @results.empty?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def against_contract?(contract_pattern)
|
|
17
|
+
unless @contract.nil?
|
|
18
|
+
case contract_pattern
|
|
19
|
+
when String
|
|
20
|
+
@contract if @contract.file.eql? contract_pattern
|
|
21
|
+
when Regexp
|
|
22
|
+
@contract if @contract.file =~ contract_pattern
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def to_s
|
|
28
|
+
contract_name = @contract.nil? ? 'nil' : contract.name
|
|
29
|
+
"""
|
|
30
|
+
Validation:
|
|
31
|
+
\tRequest: #{@request}
|
|
32
|
+
\tContract: #{contract_name}
|
|
33
|
+
\tResults: \n\t\t#{@results.join "\n\t\t"}
|
|
34
|
+
"""
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def summary
|
|
38
|
+
if @contract.nil?
|
|
39
|
+
"Missing contract for services provided by #{@request.uri.host}"
|
|
40
|
+
else
|
|
41
|
+
status = successful? ? 'successful' : 'unsuccessful'
|
|
42
|
+
"#{status} validation of #{@contract.name}"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def logger
|
|
49
|
+
@logger ||= Logger.instance
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def validate
|
|
53
|
+
logger.debug("Validating #{@request}, #{@response} against #{@contract}")
|
|
54
|
+
@results = contract.validate_consumer(@request, @response)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Pacto
|
|
2
|
+
module Validators
|
|
3
|
+
class BodyValidator
|
|
4
|
+
def self.section_name
|
|
5
|
+
fail 'section name should be provided by subclass'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.validate(schema, body)
|
|
9
|
+
if schema
|
|
10
|
+
if schema['type'] && schema['type'] == 'string'
|
|
11
|
+
validate_as_pure_string schema, body.body
|
|
12
|
+
else
|
|
13
|
+
body.respond_to?(:body) ? validate_as_json(schema, body.body) : validate_as_json(schema, body)
|
|
14
|
+
end
|
|
15
|
+
else
|
|
16
|
+
[]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def self.validate_as_pure_string(schema, body)
|
|
23
|
+
errors = []
|
|
24
|
+
if schema['required'] && body.nil?
|
|
25
|
+
errors << "The #{section_name} does not contain a body"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
pattern = schema['pattern']
|
|
29
|
+
if pattern && !(body =~ Regexp.new(pattern))
|
|
30
|
+
errors << "The #{section_name} does not match the pattern #{pattern}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
errors
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.validate_as_json(schema, body)
|
|
37
|
+
JSON::Validator.fully_validate(schema, body, :version => :draft3)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|