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 ContractRegistry
|
|
3
|
+
def initialize
|
|
4
|
+
@registry = Hash.new { |hash, key| hash[key] = Set.new }
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def [](tag)
|
|
8
|
+
@registry[tag]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def register(contract, *tags)
|
|
12
|
+
tags << :default if tags.empty?
|
|
13
|
+
|
|
14
|
+
tags.each do |tag|
|
|
15
|
+
@registry[tag] << contract
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def use(tag, values = {})
|
|
22
|
+
merged_contracts = @registry[:default] + @registry[tag]
|
|
23
|
+
|
|
24
|
+
fail ArgumentError, "contract \"#{tag}\" not found" if merged_contracts.empty?
|
|
25
|
+
|
|
26
|
+
merged_contracts.each do |contract|
|
|
27
|
+
contract.stub_contract! values
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def contracts_for(request_signature)
|
|
34
|
+
all_contracts.select { |c| c.matches? request_signature }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def all_contracts
|
|
40
|
+
@registry.values.inject(Set.new, :+)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
module Pacto
|
|
2
|
-
class
|
|
2
|
+
class Hook
|
|
3
3
|
def initialize(&block)
|
|
4
|
-
@
|
|
4
|
+
@hook = block
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
def process(contracts, request_signature, response)
|
|
8
|
-
@
|
|
8
|
+
@hook.call contracts, request_signature, response
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Pacto
|
|
2
|
+
class << self
|
|
3
|
+
def generate!
|
|
4
|
+
modes << :generate
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def stop_generating!
|
|
8
|
+
modes.delete :generate
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def generating?
|
|
12
|
+
modes.include? :generate
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def validate!
|
|
16
|
+
modes << :validate
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def stop_validating!
|
|
20
|
+
modes.delete :validate
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def validating?
|
|
24
|
+
modes.include? :validate
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def modes
|
|
30
|
+
@modes ||= []
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Pacto
|
|
2
|
+
class ValidationRegistry
|
|
3
|
+
include Singleton
|
|
4
|
+
attr_reader :validations
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
@validations = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def reset!
|
|
11
|
+
@validations.clear
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def validated?(request_pattern)
|
|
15
|
+
matched_validations = @validations.select do |validation|
|
|
16
|
+
request_pattern.matches? validation.request
|
|
17
|
+
end
|
|
18
|
+
matched_validations unless matched_validations.empty?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def register_validation(validation)
|
|
22
|
+
@validations << validation
|
|
23
|
+
logger.info "Detected #{validation.summary}"
|
|
24
|
+
validation
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def unmatched_validations
|
|
28
|
+
@validations.select do |validation|
|
|
29
|
+
validation.contract.nil?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def failed_validations
|
|
34
|
+
@validations.select do |validation|
|
|
35
|
+
!validation.successful?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def logger
|
|
42
|
+
@logger ||= Logger.instance
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/pacto/erb_processor.rb
CHANGED
data/lib/pacto/extensions.rb
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
module Pacto
|
|
2
2
|
module Extensions
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
# Adapted from Faraday
|
|
4
|
+
HeaderKeyMap = Hash.new do |map, key|
|
|
5
|
+
split_char = key.to_s.include?('-') ? '-' : '_'
|
|
6
|
+
map[key] = key.to_s.split(split_char). # :user_agent => %w(user agent)
|
|
7
|
+
each { |w| w.capitalize! }. # => %w(User Agent)
|
|
8
|
+
join('-') # => "User-Agent"
|
|
9
|
+
end
|
|
10
|
+
HeaderKeyMap[:etag] = 'ETag'
|
|
11
|
+
|
|
12
|
+
def self.normalize_header_keys(headers)
|
|
13
|
+
headers.reduce({}) do |normalized, (key, value)|
|
|
14
|
+
normalized[HeaderKeyMap[key]] = value
|
|
15
|
+
normalized
|
|
6
16
|
end
|
|
17
|
+
end
|
|
7
18
|
|
|
19
|
+
module HashSubsetOf
|
|
20
|
+
# FIXME: Only used by HashMergeProcessor, which I'd like to deprecate
|
|
8
21
|
def normalize_keys
|
|
9
|
-
|
|
22
|
+
reduce({}) do |normalized, (key, value)|
|
|
10
23
|
normalized[key.to_s.downcase] = value
|
|
11
24
|
normalized
|
|
12
25
|
end
|
|
@@ -15,4 +28,5 @@ module Pacto
|
|
|
15
28
|
end
|
|
16
29
|
end
|
|
17
30
|
|
|
31
|
+
# FIXME: Let's not extend Hash...
|
|
18
32
|
Hash.send(:include, Pacto::Extensions::HashSubsetOf)
|
data/lib/pacto/generator.rb
CHANGED
|
@@ -2,73 +2,58 @@ require 'json/schema_generator'
|
|
|
2
2
|
|
|
3
3
|
module Pacto
|
|
4
4
|
class Generator
|
|
5
|
-
attr_accessor :request_headers_to_filter
|
|
6
|
-
attr_accessor :response_headers_to_filter
|
|
7
|
-
|
|
8
|
-
INFORMATIONAL_REQUEST_HEADERS =
|
|
9
|
-
%w{
|
|
10
|
-
content-length
|
|
11
|
-
via
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
INFORMATIONAL_RESPONSE_HEADERS =
|
|
15
|
-
%w{
|
|
16
|
-
server
|
|
17
|
-
date
|
|
18
|
-
content-length
|
|
19
|
-
connection
|
|
20
|
-
}
|
|
21
|
-
|
|
22
5
|
def initialize(schema_version = 'draft3',
|
|
23
6
|
schema_generator = JSON::SchemaGenerator,
|
|
24
|
-
validator = Pacto::MetaSchema.new
|
|
7
|
+
validator = Pacto::MetaSchema.new,
|
|
8
|
+
generator_options = Pacto.configuration.generator_options,
|
|
9
|
+
filters = Pacto::Generator::Filters.new)
|
|
25
10
|
@schema_version = schema_version
|
|
26
11
|
@validator = validator
|
|
27
12
|
@schema_generator = schema_generator
|
|
28
|
-
@
|
|
29
|
-
@
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def generate(request_file, host)
|
|
33
|
-
contract = Pacto.build_from_file request_file, host
|
|
34
|
-
request = contract.request
|
|
35
|
-
response = request.execute
|
|
36
|
-
save(request_file, request, response)
|
|
13
|
+
@generator_options = generator_options
|
|
14
|
+
@filters = filters
|
|
37
15
|
end
|
|
38
16
|
|
|
39
17
|
def save(source, request, response)
|
|
40
|
-
|
|
41
|
-
contract = {
|
|
42
|
-
:request => {
|
|
43
|
-
:headers => filter_request_headers(request.headers),
|
|
44
|
-
:method => request.method,
|
|
45
|
-
:params => request.params,
|
|
46
|
-
:path => request.path
|
|
47
|
-
},
|
|
48
|
-
:response => {
|
|
49
|
-
:headers => filter_response_headers(response.headers),
|
|
50
|
-
:status => response.status,
|
|
51
|
-
:body => MultiJson.load(body_schema)
|
|
52
|
-
}
|
|
53
|
-
}
|
|
18
|
+
contract = generate_contract source, request, response
|
|
54
19
|
pretty_contract = MultiJson.encode(contract, :pretty => true)
|
|
55
20
|
# This is because of a discrepency w/ jruby vs MRI pretty json
|
|
56
|
-
pretty_contract.gsub!
|
|
21
|
+
pretty_contract.gsub!(/^$\n/, '')
|
|
57
22
|
@validator.validate pretty_contract
|
|
58
23
|
pretty_contract
|
|
59
24
|
end
|
|
60
25
|
|
|
61
26
|
private
|
|
62
27
|
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
28
|
+
def generate_contract(source, request, response)
|
|
29
|
+
{
|
|
30
|
+
:request => generate_request(request, response, source),
|
|
31
|
+
:response => generate_response(request, response, source)
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def generate_request(request, response, source)
|
|
36
|
+
{
|
|
37
|
+
:headers => @filters.filter_request_headers(request, response),
|
|
38
|
+
:method => request.method,
|
|
39
|
+
:params => request.params,
|
|
40
|
+
:path => request.path,
|
|
41
|
+
:body => generate_body(source, request.body)
|
|
42
|
+
}.delete_if { |k, v| v.nil? }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def generate_response(request, response, source)
|
|
46
|
+
{
|
|
47
|
+
:headers => @filters.filter_response_headers(request, response),
|
|
48
|
+
:status => response.status,
|
|
49
|
+
:body => generate_body(source, response.body)
|
|
50
|
+
}.delete_if { |k, v| v.nil? }
|
|
67
51
|
end
|
|
68
52
|
|
|
69
|
-
def
|
|
70
|
-
|
|
71
|
-
|
|
53
|
+
def generate_body(source, body)
|
|
54
|
+
if body && !body.empty?
|
|
55
|
+
body_schema = JSON::SchemaGenerator.generate source, body, @generator_options
|
|
56
|
+
MultiJson.load(body_schema)
|
|
72
57
|
end
|
|
73
58
|
end
|
|
74
59
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Pacto
|
|
2
|
+
class Generator
|
|
3
|
+
class Filters
|
|
4
|
+
CONNECTION_CONTROL_HEADERS = %w{
|
|
5
|
+
Via
|
|
6
|
+
Server
|
|
7
|
+
Connection
|
|
8
|
+
Transfer-Encoding
|
|
9
|
+
Content-Length
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
FRESHNESS_HEADERS =
|
|
13
|
+
%w{
|
|
14
|
+
Date
|
|
15
|
+
Last-Modified
|
|
16
|
+
ETag
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
HEADERS_TO_FILTER = CONNECTION_CONTROL_HEADERS + FRESHNESS_HEADERS
|
|
20
|
+
|
|
21
|
+
def filter_request_headers(request, response)
|
|
22
|
+
# FIXME: Do we need to handle all these cases in real situations, or just because of stubbing?
|
|
23
|
+
vary_headers = response.headers['Vary'] || []
|
|
24
|
+
vary_headers = [vary_headers] if vary_headers.is_a? String
|
|
25
|
+
vary_headers = vary_headers.map do |h|
|
|
26
|
+
h.split(',').map(&:strip)
|
|
27
|
+
end.flatten
|
|
28
|
+
|
|
29
|
+
request.headers.select do |header|
|
|
30
|
+
vary_headers.map(&:downcase).include? header.downcase
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def filter_response_headers(request, response)
|
|
35
|
+
Pacto::Extensions.normalize_header_keys(response.headers).reject do |header|
|
|
36
|
+
(HEADERS_TO_FILTER.include? header) || (header.start_with?('X-'))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/pacto/hooks/erb_hook.rb
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
require_relative '../erb_processor'
|
|
2
|
+
|
|
1
3
|
module Pacto
|
|
2
4
|
module Hooks
|
|
3
|
-
class ERBHook < Pacto::
|
|
5
|
+
class ERBHook < Pacto::Hook
|
|
4
6
|
def initialize
|
|
5
7
|
@processor = ERBProcessor.new
|
|
6
8
|
end
|
|
7
9
|
|
|
8
10
|
def process(contracts, request_signature, response)
|
|
9
11
|
bound_values = contracts.empty? ? {} : contracts.first.values
|
|
10
|
-
bound_values.merge!(
|
|
12
|
+
bound_values.merge!(:req => { 'HEADERS' => request_signature.headers})
|
|
11
13
|
response.body = @processor.process response.body, bound_values
|
|
12
14
|
response.body
|
|
13
15
|
end
|
|
14
|
-
|
|
15
16
|
end
|
|
16
17
|
end
|
|
17
18
|
end
|
data/lib/pacto/logger.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
|
|
1
3
|
module Pacto
|
|
2
4
|
class Logger
|
|
3
5
|
include Singleton
|
|
@@ -9,13 +11,13 @@ module Pacto
|
|
|
9
11
|
log ::Logger.new STDOUT
|
|
10
12
|
end
|
|
11
13
|
|
|
12
|
-
def log
|
|
14
|
+
def log(log)
|
|
13
15
|
@log = log
|
|
14
16
|
@log.level = default_level
|
|
15
17
|
@log.progname = 'Pacto'
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
def level=
|
|
20
|
+
def level=(level)
|
|
19
21
|
@log.level = log_levels.fetch(level, default_level)
|
|
20
22
|
end
|
|
21
23
|
|
data/lib/pacto/meta_schema.rb
CHANGED
|
@@ -4,13 +4,15 @@ module Pacto
|
|
|
4
4
|
|
|
5
5
|
def initialize(engine = JSON::Validator)
|
|
6
6
|
@schema = File.join(File.dirname(File.expand_path(__FILE__)), '../../resources/contract_schema.json')
|
|
7
|
+
@base_schema = File.join(File.dirname(File.expand_path(__FILE__)), '../../resources/draft-03.json')
|
|
8
|
+
JSON::Validator.validate!(@base_schema, @schema)
|
|
7
9
|
@engine = engine
|
|
8
10
|
end
|
|
9
11
|
|
|
10
|
-
def validate
|
|
12
|
+
def validate(definition)
|
|
11
13
|
errors = engine.fully_validate(schema, definition, :version => :draft3)
|
|
12
14
|
unless errors.empty?
|
|
13
|
-
|
|
15
|
+
fail InvalidContract, errors
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
end
|
data/lib/pacto/rake_task.rb
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
require 'pacto'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def colorize(*args)
|
|
6
|
-
self
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
|
|
3
|
+
# FIXME: RakeTask is a huge class, refactor this please
|
|
4
|
+
# rubocop:disable ClassLength
|
|
11
5
|
module Pacto
|
|
12
6
|
class RakeTask
|
|
13
7
|
include Rake::DSL
|
|
@@ -29,7 +23,7 @@ module Pacto
|
|
|
29
23
|
desc 'Validates all contracts in a given directory against a given host'
|
|
30
24
|
task :validate, :host, :dir do |t, args|
|
|
31
25
|
if args.to_a.size < 2
|
|
32
|
-
fail 'USAGE: rake pacto:validate[<host>, <contract_dir>]'
|
|
26
|
+
fail Pacto::UI.yellow('USAGE: rake pacto:validate[<host>, <contract_dir>]')
|
|
33
27
|
end
|
|
34
28
|
|
|
35
29
|
validate_contracts(args[:host], args[:dir])
|
|
@@ -40,24 +34,26 @@ module Pacto
|
|
|
40
34
|
desc 'Generates contracts from partial contracts'
|
|
41
35
|
task :generate, :input_dir, :output_dir, :host do |t, args|
|
|
42
36
|
if args.to_a.size < 3
|
|
43
|
-
fail 'USAGE: rake pacto:generate[<request_contract_dir>, <output_dir>, <record_host>]'
|
|
37
|
+
fail Pacto::UI.yellow('USAGE: rake pacto:generate[<request_contract_dir>, <output_dir>, <record_host>]')
|
|
44
38
|
end
|
|
45
39
|
|
|
46
40
|
generate_contracts(args[:input_dir], args[:output_dir], args[:host])
|
|
47
41
|
end
|
|
48
42
|
end
|
|
49
43
|
|
|
44
|
+
# FIXME: meta_validate is a big method =(. Needs refactoring
|
|
45
|
+
# rubocop:disable MethodLength
|
|
50
46
|
def meta_validate
|
|
51
47
|
desc 'Validates a directory of contract definitions'
|
|
52
48
|
task :meta_validate, :dir do |t, args|
|
|
53
49
|
if args.to_a.size < 1
|
|
54
|
-
fail 'USAGE: rake pacto:meta_validate[<contract_dir>]'
|
|
50
|
+
fail Pacto::UI.yellow('USAGE: rake pacto:meta_validate[<contract_dir>]')
|
|
55
51
|
end
|
|
56
52
|
|
|
57
53
|
each_contract(args[:dir]) do |contract_file|
|
|
58
|
-
puts "Validating #{contract_file}"
|
|
59
54
|
fail unless Pacto.validate_contract contract_file
|
|
60
55
|
end
|
|
56
|
+
puts 'All contracts successfully meta-validated'
|
|
61
57
|
end
|
|
62
58
|
end
|
|
63
59
|
|
|
@@ -66,31 +62,36 @@ module Pacto
|
|
|
66
62
|
puts "Validating contracts in directory #{dir} against host #{host}\n\n"
|
|
67
63
|
|
|
68
64
|
total_failed = 0
|
|
69
|
-
|
|
65
|
+
contracts = []
|
|
66
|
+
each_contract(dir) do |contract_file|
|
|
67
|
+
contracts << contract_file
|
|
70
68
|
print "#{contract_file.split('/').last}:"
|
|
71
|
-
contract = Pacto.
|
|
72
|
-
errors = contract.
|
|
69
|
+
contract = Pacto.load_contract(contract_file, host)
|
|
70
|
+
errors = contract.validate_provider
|
|
73
71
|
|
|
74
72
|
if errors.empty?
|
|
75
|
-
puts ' OK!'
|
|
73
|
+
puts Pacto::UI.green(' OK!')
|
|
76
74
|
else
|
|
77
75
|
@exit_with_error = true
|
|
78
76
|
total_failed += 1
|
|
79
|
-
puts ' FAILED!'
|
|
77
|
+
puts Pacto::UI.red(' FAILED!')
|
|
80
78
|
errors.each do |error|
|
|
81
|
-
puts "\t* #{error}"
|
|
79
|
+
puts Pacto::UI.red("\t* #{error}")
|
|
82
80
|
end
|
|
83
81
|
puts ''
|
|
84
82
|
end
|
|
85
83
|
end
|
|
86
84
|
|
|
87
85
|
if @exit_with_error
|
|
88
|
-
fail "#{total_failed} of #{contracts.size} failed. Check output for detailed error messages."
|
|
86
|
+
fail Pacto::UI.red("#{total_failed} of #{contracts.size} failed. Check output for detailed error messages.")
|
|
89
87
|
else
|
|
90
|
-
puts "#{contracts.size} valid contract#{contracts.size > 1 ? 's' : nil}"
|
|
88
|
+
puts Pacto::UI.green("#{contracts.size} valid contract#{contracts.size > 1 ? 's' : nil}")
|
|
91
89
|
end
|
|
92
90
|
end
|
|
91
|
+
# rubocop:enable MethodLength
|
|
93
92
|
|
|
93
|
+
# FIXME: generate_contracts is a big method =(. Needs refactoring
|
|
94
|
+
# rubocop:disable MethodLength
|
|
94
95
|
def generate_contracts(input_dir, output_dir, host)
|
|
95
96
|
WebMock.allow_net_connect!
|
|
96
97
|
generator = Pacto::Generator.new
|
|
@@ -107,16 +108,17 @@ module Pacto
|
|
|
107
108
|
output_file.close
|
|
108
109
|
rescue InvalidContract => e
|
|
109
110
|
failed_contracts << contract_file
|
|
110
|
-
puts e.message
|
|
111
|
+
puts Pacto::UI.red(e.message)
|
|
111
112
|
end
|
|
112
113
|
end
|
|
113
114
|
|
|
114
115
|
if failed_contracts.empty?
|
|
115
|
-
puts 'Successfully generated all contracts'
|
|
116
|
+
puts Pacto::UI.green('Successfully generated all contracts')
|
|
116
117
|
else
|
|
117
|
-
fail "The following contracts could not be generated: #{failed_contracts.join ','}"
|
|
118
|
+
fail Pacto::UI.red("The following contracts could not be generated: #{failed_contracts.join ','}")
|
|
118
119
|
end
|
|
119
120
|
end
|
|
121
|
+
# rubocop:enable MethodLength
|
|
120
122
|
|
|
121
123
|
private
|
|
122
124
|
|
|
@@ -124,8 +126,8 @@ module Pacto
|
|
|
124
126
|
if File.file? dir
|
|
125
127
|
yield dir
|
|
126
128
|
else
|
|
127
|
-
contracts = Dir[File.join(dir, '
|
|
128
|
-
fail "No contracts found in directory #{dir}"
|
|
129
|
+
contracts = Dir[File.join(dir, '**/*{.json.erb,.json}')]
|
|
130
|
+
fail Pacto::UI.yellow("No contracts found in directory #{dir}") if contracts.empty?
|
|
129
131
|
|
|
130
132
|
contracts.sort.each do |contract_file|
|
|
131
133
|
yield contract_file
|
|
@@ -134,5 +136,6 @@ module Pacto
|
|
|
134
136
|
end
|
|
135
137
|
end
|
|
136
138
|
end
|
|
139
|
+
# rubocop:enable ClassLength
|
|
137
140
|
|
|
138
141
|
Pacto::RakeTask.new.install
|