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
data/lib/pacto/contract.rb
CHANGED
@@ -1,39 +1,67 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Pacto
|
2
|
-
class Contract
|
3
|
-
|
3
|
+
class Contract < Pacto::Dash
|
4
|
+
property :id
|
5
|
+
property :file
|
6
|
+
property :request, required: true
|
7
|
+
# Although I'd like response to be required, it complicates
|
8
|
+
# the partial contracts used the rake generation task...
|
9
|
+
# yet another reason I'd like to deprecate that feature
|
10
|
+
property :response # , required: true
|
11
|
+
property :values, default: {}
|
12
|
+
# Gotta figure out how to use test doubles w/ coercion
|
13
|
+
coerce_key :request, RequestClause
|
14
|
+
coerce_key :response, ResponseClause
|
15
|
+
property :examples
|
16
|
+
property :name, required: true
|
17
|
+
property :adapter, default: proc { Pacto.configuration.adapter }
|
18
|
+
property :consumer, default: proc { Pacto.configuration.default_consumer }
|
19
|
+
property :provider, default: proc { Pacto.configuration.default_provider }
|
4
20
|
|
5
|
-
def initialize(
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
21
|
+
def initialize(opts)
|
22
|
+
if opts[:file]
|
23
|
+
opts[:file] = Addressable::URI.convert_path(File.expand_path(opts[:file])).to_s
|
24
|
+
opts[:name] ||= opts[:file]
|
25
|
+
end
|
26
|
+
opts[:id] ||= (opts[:summary] || opts[:file])
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def examples?
|
31
|
+
examples && !examples.empty?
|
12
32
|
end
|
13
33
|
|
14
34
|
def stub_contract!(values = {})
|
15
|
-
|
16
|
-
|
35
|
+
self.values = values
|
36
|
+
adapter.stub_request!(self)
|
17
37
|
end
|
18
38
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
validate_consumer request, response, opts
|
39
|
+
def simulate_request
|
40
|
+
pacto_request, pacto_response = execute
|
41
|
+
validate_response pacto_request, pacto_response
|
23
42
|
end
|
24
43
|
|
25
|
-
|
26
|
-
|
44
|
+
# Should this be deprecated?
|
45
|
+
def validate_response(request, response)
|
46
|
+
Pacto::Cops.perform_investigation request, response, self
|
27
47
|
end
|
28
48
|
|
29
49
|
def matches?(request_signature)
|
30
50
|
request_pattern.matches? request_signature
|
31
51
|
end
|
32
52
|
|
33
|
-
|
53
|
+
def request_pattern
|
54
|
+
request.pattern
|
55
|
+
end
|
56
|
+
|
57
|
+
def response_for(pacto_request)
|
58
|
+
provider.response_for self, pacto_request
|
59
|
+
end
|
34
60
|
|
35
|
-
def
|
36
|
-
|
61
|
+
def execute(additional_values = {})
|
62
|
+
# FIXME: Do we really need to store on the Contract, or just as a param for #stub_contact! and #execute?
|
63
|
+
full_values = values.merge(additional_values)
|
64
|
+
consumer.reenact(self, full_values)
|
37
65
|
end
|
38
66
|
end
|
39
67
|
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
class ContractBuilder < Hashie::Dash # rubocop:disable Metrics/ClassLength
|
4
|
+
extend Forwardable
|
5
|
+
attr_accessor :source
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@schema_generator = options[:schema_generator] ||= JSON::SchemaGenerator
|
9
|
+
@filters = options[:filters] ||= Pacto::Generator::Filters.new
|
10
|
+
@data = { request: {}, response: {}, examples: {} }
|
11
|
+
@source = 'Pacto' # Currently used by JSONSchemaGeneator, but not really useful
|
12
|
+
end
|
13
|
+
|
14
|
+
def name=(name)
|
15
|
+
@data[:name] = name
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_example(name, pacto_request, pacto_response)
|
19
|
+
@data[:examples][name] ||= {}
|
20
|
+
@data[:examples][name][:request] = clean(pacto_request.to_hash)
|
21
|
+
@data[:examples][name][:response] = clean(pacto_response.to_hash)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def infer_all
|
26
|
+
# infer_file # The target file is being chosen inferred by the Generator
|
27
|
+
infer_name
|
28
|
+
infer_schemas
|
29
|
+
end
|
30
|
+
|
31
|
+
def infer_name
|
32
|
+
if @data[:examples].empty?
|
33
|
+
@data[:name] = @data[:request][:path] if @data[:request]
|
34
|
+
return self
|
35
|
+
end
|
36
|
+
|
37
|
+
example, hint = example_and_hint
|
38
|
+
@data[:name] = hint.nil? ? PactoRequest.new(example[:request]).uri.path : hint.service_name
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
def infer_schemas
|
43
|
+
return self if @data[:examples].empty?
|
44
|
+
|
45
|
+
# TODO: It'd be awesome if we could infer across all examples
|
46
|
+
example, _hint = example_and_hint
|
47
|
+
sample_request_body = example[:request][:body]
|
48
|
+
sample_response_body = example[:response][:body]
|
49
|
+
@data[:request][:schema] = generate_schema(sample_request_body) if sample_request_body && !sample_request_body.empty?
|
50
|
+
@data[:response][:schema] = generate_schema(sample_response_body) if sample_response_body && !sample_response_body.empty?
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def without_examples
|
55
|
+
@export_examples = false
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def generate_contract(request, response)
|
60
|
+
generate_request(request, response)
|
61
|
+
generate_response(request, response)
|
62
|
+
infer_all
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
def generate_request(request, response)
|
67
|
+
hint = hint_for(request)
|
68
|
+
request = clean(
|
69
|
+
headers: @filters.filter_request_headers(request, response),
|
70
|
+
http_method: request.method,
|
71
|
+
params: request.uri.query_values,
|
72
|
+
path: hint.nil? ? request.uri.path : hint.path
|
73
|
+
)
|
74
|
+
@data[:request] = request
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def generate_response(request, response)
|
79
|
+
response = clean(
|
80
|
+
headers: @filters.filter_response_headers(request, response),
|
81
|
+
status: response.status
|
82
|
+
)
|
83
|
+
@data[:response] = response
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
def build_hash
|
88
|
+
instance_eval(&block) if block_given?
|
89
|
+
@final_data = @data.dup
|
90
|
+
@final_data.delete(:examples) if exclude_examples?
|
91
|
+
clean(@final_data)
|
92
|
+
end
|
93
|
+
|
94
|
+
def build(&block)
|
95
|
+
Contract.new build_hash(&block)
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def example_and_hint
|
101
|
+
example = @data[:examples].values.first
|
102
|
+
example_request = PactoRequest.new example[:request]
|
103
|
+
[example, Pacto::Generator.hint_for(example_request)]
|
104
|
+
end
|
105
|
+
|
106
|
+
def exclude_examples?
|
107
|
+
@export_examples == false
|
108
|
+
end
|
109
|
+
|
110
|
+
def generate_schema(body, generator_options = Pacto.configuration.generator_options)
|
111
|
+
return if body.nil? || body.empty?
|
112
|
+
|
113
|
+
body_schema = @schema_generator.generate @source, body, generator_options
|
114
|
+
MultiJson.load(body_schema)
|
115
|
+
end
|
116
|
+
|
117
|
+
def clean(data)
|
118
|
+
data.delete_if { |_k, v| v.nil? }
|
119
|
+
end
|
120
|
+
|
121
|
+
def hint_for(pacto_request)
|
122
|
+
Pacto::Generator.hint_for(pacto_request)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -1,22 +1,41 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Pacto
|
2
3
|
class ContractFactory
|
3
|
-
|
4
|
+
include Singleton
|
5
|
+
include Logger
|
4
6
|
|
5
|
-
def initialize
|
6
|
-
@
|
7
|
+
def initialize
|
8
|
+
@factories = {}
|
7
9
|
end
|
8
10
|
|
9
|
-
def
|
10
|
-
|
11
|
+
def add_factory(format, factory)
|
12
|
+
@factories[format.to_sym] = factory
|
11
13
|
end
|
12
14
|
|
13
|
-
def
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
Contract
|
15
|
+
def remove_factory(format)
|
16
|
+
@factories.delete format
|
17
|
+
end
|
18
|
+
|
19
|
+
def build(contract_files, host, format = :default)
|
20
|
+
factory = @factories[format.to_sym]
|
21
|
+
fail "No Contract factory registered for #{format}" if factory.nil?
|
22
|
+
|
23
|
+
contract_files.map { |file| factory.build_from_file(file, host) }.flatten
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_contracts(contracts_path, host, format = :default)
|
27
|
+
factory = @factories[format.to_sym]
|
28
|
+
files = factory.files_for(contracts_path)
|
29
|
+
contracts = ContractFactory.build(files, host, format)
|
30
|
+
contracts
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
extend Forwardable
|
35
|
+
def_delegators :instance, *ContractFactory.instance_methods(false)
|
20
36
|
end
|
21
37
|
end
|
22
38
|
end
|
39
|
+
|
40
|
+
require 'pacto/native_contract_factory'
|
41
|
+
require 'pacto/swagger_contract_factory'
|
data/lib/pacto/contract_files.rb
CHANGED
data/lib/pacto/cops.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
module Cops
|
4
|
+
extend Pacto::Resettable
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def reset!
|
8
|
+
@active_cops = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def register_cop(cop)
|
12
|
+
fail TypeError "#{cop} does not respond to investigate" unless cop.respond_to? :investigate
|
13
|
+
registered_cops << cop
|
14
|
+
end
|
15
|
+
|
16
|
+
def registered_cops
|
17
|
+
@registered_cops ||= Set.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def active_cops
|
21
|
+
@active_cops ||= registered_cops.dup
|
22
|
+
end
|
23
|
+
|
24
|
+
def investigate(request_signature, pacto_response)
|
25
|
+
return unless Pacto.validating?
|
26
|
+
|
27
|
+
contract = Pacto.contracts_for(request_signature).first
|
28
|
+
if contract
|
29
|
+
investigation = perform_investigation request_signature, pacto_response, contract
|
30
|
+
else
|
31
|
+
investigation = Investigation.new request_signature, pacto_response
|
32
|
+
end
|
33
|
+
|
34
|
+
Pacto::InvestigationRegistry.instance.register_investigation investigation
|
35
|
+
end
|
36
|
+
|
37
|
+
def perform_investigation(request, response, contract)
|
38
|
+
citations = []
|
39
|
+
active_cops.map do | cop |
|
40
|
+
citations.concat cop.investigate(request, response, contract)
|
41
|
+
end
|
42
|
+
Investigation.new(request, response, contract, citations.compact)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
module Cops
|
4
|
+
class BodyCop
|
5
|
+
KNOWN_CLAUSES = [:request, :response]
|
6
|
+
|
7
|
+
def self.validates(clause)
|
8
|
+
fail ArgumentError, "Unknown clause: #{clause}" unless KNOWN_CLAUSES.include? clause
|
9
|
+
@clause = clause
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.investigate(request, response, contract)
|
13
|
+
# eval "is a security risk" and local_variable_get is ruby 2.1+ only, so...
|
14
|
+
body = { request: request, response: response }[@clause].body
|
15
|
+
schema = contract.send(@clause).schema
|
16
|
+
if schema && !schema.empty?
|
17
|
+
schema['id'] = contract.file unless schema.key? 'id'
|
18
|
+
JSON::Validator.fully_validate(schema, body, version: :draft3)
|
19
|
+
end || []
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,18 +1,10 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Pacto
|
2
|
-
module
|
3
|
-
class
|
4
|
-
def
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def call(env)
|
9
|
-
expected_headers = env[:contract].response.headers
|
10
|
-
actual_headers = env[:actual_response].headers
|
11
|
-
env[:validation_results].concat self.class.validate(expected_headers, actual_headers)
|
12
|
-
@app.call env
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.validate(expected_headers, actual_headers)
|
3
|
+
module Cops
|
4
|
+
class ResponseHeaderCop
|
5
|
+
def self.investigate(_request, response, contract)
|
6
|
+
expected_headers = contract.response.headers
|
7
|
+
actual_headers = response.headers
|
16
8
|
actual_headers = Pacto::Extensions.normalize_header_keys actual_headers
|
17
9
|
headers_to_validate = Pacto::Extensions.normalize_header_keys expected_headers
|
18
10
|
|
@@ -28,7 +20,7 @@ module Pacto
|
|
28
20
|
|
29
21
|
private
|
30
22
|
|
31
|
-
HeaderValidatorMap = Hash.new do |
|
23
|
+
HeaderValidatorMap = Hash.new do |_map, key|
|
32
24
|
proc do |expected_value, actual_value|
|
33
25
|
unless expected_value.eql? actual_value
|
34
26
|
"Invalid response header #{key}: expected #{expected_value.inspect} but received #{actual_value.inspect}"
|
@@ -47,3 +39,5 @@ module Pacto
|
|
47
39
|
end
|
48
40
|
end
|
49
41
|
end
|
42
|
+
|
43
|
+
Pacto::Cops.register_cop Pacto::Cops::ResponseHeaderCop
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
module Cops
|
4
|
+
class ResponseStatusCop
|
5
|
+
def self.investigate(_request, response, contract)
|
6
|
+
expected_status = contract.response.status
|
7
|
+
actual_status = response.status
|
8
|
+
errors = []
|
9
|
+
if expected_status != actual_status
|
10
|
+
errors << "Invalid status: expected #{expected_status} but got #{actual_status}"
|
11
|
+
end
|
12
|
+
errors
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Pacto::Cops.register_cop Pacto::Cops::ResponseStatusCop
|
@@ -1,17 +1,28 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Pacto
|
2
3
|
class Configuration
|
3
|
-
attr_accessor :
|
4
|
-
:contracts_path, :logger, :generator_options
|
4
|
+
attr_accessor :adapter, :strict_matchers,
|
5
|
+
:contracts_path, :logger, :generator_options,
|
6
|
+
:hide_deprecations, :default_consumer, :default_provider,
|
7
|
+
:stenographer_log_file, :color
|
5
8
|
attr_reader :hook
|
6
9
|
|
7
10
|
def initialize
|
8
|
-
@
|
11
|
+
@middleware = Pacto::Core::HTTPMiddleware.new
|
12
|
+
@middleware.add_observer Pacto::Cops, :investigate
|
13
|
+
@generator = Pacto::Generator.contract_generator
|
14
|
+
@middleware.add_observer @generator, :generate
|
15
|
+
@stenographer_log_file ||= File.expand_path('pacto_stenographer.log')
|
16
|
+
@default_consumer = Pacto::Consumer.new
|
17
|
+
@default_provider = Pacto::Provider.new
|
18
|
+
@adapter = Stubs::WebMockAdapter.new(@middleware)
|
9
19
|
@strict_matchers = true
|
10
|
-
@contracts_path =
|
20
|
+
@contracts_path = '.'
|
11
21
|
@logger = Logger::SimpleLogger.instance
|
12
22
|
define_logger_level
|
13
23
|
@hook = Hook.new {}
|
14
|
-
@generator_options = { :
|
24
|
+
@generator_options = { schema_version: 'draft3' }
|
25
|
+
@color = $stdout.tty?
|
15
26
|
end
|
16
27
|
|
17
28
|
def register_hook(hook = nil, &block)
|