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,27 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
def self.providers
|
4
|
+
@providers ||= {}
|
5
|
+
end
|
6
|
+
|
7
|
+
class Provider
|
8
|
+
include Resettable
|
9
|
+
|
10
|
+
def self.reset!
|
11
|
+
Pacto.providers.clear
|
12
|
+
end
|
13
|
+
|
14
|
+
def actor
|
15
|
+
@actor ||= Pacto::Actors::FromExamples.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def actor=(actor)
|
19
|
+
fail ArgumentError, 'The actor must respond to :build_response' unless actor.respond_to? :build_response
|
20
|
+
@actor = actor
|
21
|
+
end
|
22
|
+
|
23
|
+
def response_for(contract, data = {})
|
24
|
+
actor.build_response contract, data
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/pacto/rake_task.rb
CHANGED
@@ -1,13 +1,25 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'pacto'
|
3
|
+
require 'thor'
|
4
|
+
require 'pacto/cli'
|
5
|
+
require 'pacto/cli/helpers'
|
2
6
|
|
3
7
|
# FIXME: RakeTask is a huge class, refactor this please
|
4
8
|
# rubocop:disable ClassLength
|
5
9
|
module Pacto
|
6
10
|
class RakeTask
|
11
|
+
extend Forwardable
|
12
|
+
include Thor::Actions
|
7
13
|
include Rake::DSL
|
14
|
+
include Pacto::CLI::Helpers
|
8
15
|
|
9
16
|
def initialize
|
10
17
|
@exit_with_error = false
|
18
|
+
@cli = Pacto::CLI::Main.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(task, args, opts = {})
|
22
|
+
Pacto::CLI::Main.new([], opts).public_send(task, *args)
|
11
23
|
end
|
12
24
|
|
13
25
|
def install
|
@@ -21,86 +33,44 @@ module Pacto
|
|
21
33
|
|
22
34
|
def validate_task
|
23
35
|
desc 'Validates all contracts in a given directory against a given host'
|
24
|
-
task :validate, :host, :dir do |
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
validate_contracts(args[:host], args[:dir])
|
36
|
+
task :validate, :host, :dir do |_t, args|
|
37
|
+
opts = args.to_hash
|
38
|
+
dir = opts.delete :dir
|
39
|
+
run(:validate, dir, opts)
|
30
40
|
end
|
31
41
|
end
|
32
42
|
|
33
43
|
def generate_task
|
34
44
|
desc 'Generates contracts from partial contracts'
|
35
|
-
task :generate, :input_dir, :output_dir, :host do |
|
45
|
+
task :generate, :input_dir, :output_dir, :host do |_t, args|
|
36
46
|
if args.to_a.size < 3
|
37
|
-
fail Pacto::UI.
|
47
|
+
fail Pacto::UI.colorize('USAGE: rake pacto:generate[<request_contract_dir>, <output_dir>, <record_host>]', :yellow)
|
38
48
|
end
|
39
49
|
|
40
50
|
generate_contracts(args[:input_dir], args[:output_dir], args[:host])
|
41
51
|
end
|
42
52
|
end
|
43
53
|
|
44
|
-
# FIXME: meta_validate is a big method =(. Needs refactoring
|
45
|
-
# rubocop:disable MethodLength
|
46
54
|
def meta_validate
|
47
55
|
desc 'Validates a directory of contract definitions'
|
48
|
-
task :meta_validate, :dir do |
|
49
|
-
|
50
|
-
fail Pacto::UI.yellow('USAGE: rake pacto:meta_validate[<contract_dir>]')
|
51
|
-
end
|
52
|
-
|
53
|
-
each_contract(args[:dir]) do |contract_file|
|
54
|
-
fail unless Pacto.validate_contract contract_file
|
55
|
-
end
|
56
|
-
puts 'All contracts successfully meta-validated'
|
56
|
+
task :meta_validate, :dir do |_t, args|
|
57
|
+
run(:meta_validate, *args)
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
60
|
-
def validate_contracts(host, dir)
|
61
|
-
WebMock.allow_net_connect!
|
62
|
-
puts "Validating contracts in directory #{dir} against host #{host}\n\n"
|
63
|
-
|
64
|
-
total_failed = 0
|
65
|
-
contracts = []
|
66
|
-
each_contract(dir) do |contract_file|
|
67
|
-
contracts << contract_file
|
68
|
-
print "#{contract_file.split('/').last}:"
|
69
|
-
contract = Pacto.load_contract(contract_file, host)
|
70
|
-
errors = contract.validate_provider
|
71
|
-
|
72
|
-
if errors.empty?
|
73
|
-
puts Pacto::UI.green(' OK!')
|
74
|
-
else
|
75
|
-
@exit_with_error = true
|
76
|
-
total_failed += 1
|
77
|
-
puts Pacto::UI.red(' FAILED!')
|
78
|
-
errors.each do |error|
|
79
|
-
puts Pacto::UI.red("\t* #{error}")
|
80
|
-
end
|
81
|
-
puts ''
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
if @exit_with_error
|
86
|
-
fail Pacto::UI.red("#{total_failed} of #{contracts.size} failed. Check output for detailed error messages.")
|
87
|
-
else
|
88
|
-
puts Pacto::UI.green("#{contracts.size} valid contract#{contracts.size > 1 ? 's' : nil}")
|
89
|
-
end
|
90
|
-
end
|
91
61
|
# rubocop:enable MethodLength
|
92
62
|
|
93
63
|
# FIXME: generate_contracts is a big method =(. Needs refactoring
|
94
64
|
# rubocop:disable MethodLength
|
95
65
|
def generate_contracts(input_dir, output_dir, host)
|
96
66
|
WebMock.allow_net_connect!
|
97
|
-
generator = Pacto::Generator.
|
67
|
+
generator = Pacto::Generator.contract_generator
|
98
68
|
puts "Generating contracts from partial contracts in #{input_dir} and recording to #{output_dir}\n\n"
|
99
69
|
|
100
70
|
failed_contracts = []
|
101
71
|
each_contract(input_dir) do |contract_file|
|
102
72
|
begin
|
103
|
-
contract = generator.
|
73
|
+
contract = generator.generate_from_partial_contract(contract_file, host)
|
104
74
|
output_file = File.expand_path(File.basename(contract_file), output_dir)
|
105
75
|
output_file = File.open(output_file, 'wb')
|
106
76
|
output_file.write contract
|
@@ -108,32 +78,17 @@ module Pacto
|
|
108
78
|
output_file.close
|
109
79
|
rescue InvalidContract => e
|
110
80
|
failed_contracts << contract_file
|
111
|
-
puts Pacto::UI.
|
81
|
+
puts Pacto::UI.colorize(e.message, :red)
|
112
82
|
end
|
113
83
|
end
|
114
84
|
|
115
85
|
if failed_contracts.empty?
|
116
|
-
puts Pacto::UI.
|
86
|
+
puts Pacto::UI.colorize('Successfully generated all contracts', :green)
|
117
87
|
else
|
118
|
-
fail Pacto::UI.
|
88
|
+
fail Pacto::UI.colorize("The following contracts could not be generated: #{failed_contracts.join ','}", :red)
|
119
89
|
end
|
120
90
|
end
|
121
91
|
# rubocop:enable MethodLength
|
122
|
-
|
123
|
-
private
|
124
|
-
|
125
|
-
def each_contract(dir)
|
126
|
-
if File.file? dir
|
127
|
-
yield dir
|
128
|
-
else
|
129
|
-
contracts = Dir[File.join(dir, '**/*{.json.erb,.json}')]
|
130
|
-
fail Pacto::UI.yellow("No contracts found in directory #{dir}") if contracts.empty?
|
131
|
-
|
132
|
-
contracts.sort.each do |contract_file|
|
133
|
-
yield contract_file
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
92
|
end
|
138
93
|
end
|
139
94
|
# rubocop:enable ClassLength
|
data/lib/pacto/request_clause.rb
CHANGED
@@ -1,43 +1,45 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Pacto
|
2
|
-
class RequestClause
|
3
|
-
|
4
|
-
|
3
|
+
class RequestClause < Pacto::Dash
|
4
|
+
include Logger
|
5
|
+
property :host # required?
|
6
|
+
property :http_method, required: true
|
7
|
+
property :schema, default: {}
|
8
|
+
property :path, default: '/'
|
9
|
+
property :headers
|
10
|
+
property :params, default: {}
|
11
|
+
property :request_pattern_provider, default: Pacto::RequestPattern
|
5
12
|
|
6
|
-
def initialize(
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@schema = definition['body'] || {}
|
13
|
+
def initialize(definition)
|
14
|
+
mash = Hashie::Mash.new definition
|
15
|
+
mash['http_method'] = normalize(mash['http_method'])
|
16
|
+
super mash
|
11
17
|
end
|
12
18
|
|
13
|
-
def
|
14
|
-
|
19
|
+
def http_method=(method)
|
20
|
+
normalize(method)
|
15
21
|
end
|
16
22
|
|
17
|
-
def
|
18
|
-
|
23
|
+
def pattern
|
24
|
+
@pattern ||= request_pattern_provider.for(self)
|
19
25
|
end
|
20
26
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
def uri(values = {})
|
28
|
+
values ||= {}
|
29
|
+
uri_template = pattern.uri_template
|
30
|
+
missing_keys = uri_template.keys - values.keys
|
31
|
+
values[:scheme] = 'http' if missing_keys.delete(:scheme)
|
32
|
+
values[:server] = 'localhost' if missing_keys.delete(:server)
|
33
|
+
logger.warn "Missing keys for building a complete URL: #{missing_keys.inspect}" unless missing_keys.empty?
|
34
|
+
Addressable::URI.heuristic_parse(uri_template.expand(values)).tap do |uri|
|
35
|
+
uri.query_values = params unless params.nil? || params.empty?
|
36
|
+
end
|
27
37
|
end
|
28
38
|
|
29
|
-
|
30
|
-
@definition['params'] || {}
|
31
|
-
end
|
39
|
+
private
|
32
40
|
|
33
|
-
def
|
34
|
-
|
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
|
+
def normalize(method)
|
42
|
+
method.to_s.downcase.to_sym
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
@@ -1,8 +1,25 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Pacto
|
2
|
-
class RequestPattern
|
3
|
+
class RequestPattern < WebMock::RequestPattern
|
4
|
+
attr_accessor :uri_template
|
5
|
+
|
3
6
|
def self.for(base_request)
|
4
|
-
|
5
|
-
|
7
|
+
new(base_request.http_method, UriPattern.for(base_request))
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(http_method, uri_template)
|
11
|
+
@uri_template = uri_template
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
string = Pacto::UI.colorize_method(@method_pattern.to_s)
|
17
|
+
string << " #{@uri_pattern}"
|
18
|
+
# WebMock includes this info, but I don't think we should. Pacto should match on URIs only and then validate the rest...
|
19
|
+
# string << " with body #{@body_pattern.to_s}" if @body_pattern
|
20
|
+
# string << " with headers #{@headers_pattern.to_s}" if @headers_pattern
|
21
|
+
# string << " with given block" if @with_block
|
22
|
+
string
|
6
23
|
end
|
7
24
|
end
|
8
25
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Pacto
|
3
|
+
# Included this module so that Pacto::Resettable.reset_all will call your class/module's self.reset! method.
|
4
|
+
module Resettable
|
5
|
+
def self.resettables
|
6
|
+
@resettables ||= []
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.extended(base)
|
10
|
+
resettables << base
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
resettables << base
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.reset_all
|
18
|
+
resettables.each(&:reset!)
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,15 +1,8 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
module Pacto
|
2
|
-
class ResponseClause
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
3
|
+
class ResponseClause < Pacto::Dash
|
4
|
+
property :status
|
5
|
+
property :headers, default: {}
|
6
|
+
property :schema, default: {}
|
14
7
|
end
|
15
8
|
end
|
data/lib/pacto/rspec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
1
2
|
require 'pacto'
|
2
3
|
|
3
4
|
begin
|
@@ -7,40 +8,44 @@ rescue LoadError
|
|
7
8
|
raise 'pacto/rspec requires rspec 2 or later'
|
8
9
|
end
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
require 'pacto/forensics/investigation_filter'
|
12
|
+
require 'pacto/forensics/investigation_matcher'
|
13
|
+
|
14
|
+
RSpec::Matchers.define :have_unmatched_requests do |_method, _uri|
|
12
15
|
match do
|
13
|
-
|
16
|
+
@unmatched_investigations = Pacto::InvestigationRegistry.instance.unmatched_investigations
|
17
|
+
!@unmatched_investigations.empty?
|
14
18
|
end
|
15
19
|
|
16
|
-
|
20
|
+
failure_message do
|
17
21
|
'Expected Pacto to have not matched all requests to a Contract, but all requests were matched.'
|
18
22
|
end
|
19
23
|
|
20
|
-
|
21
|
-
unmatched_requests = @
|
24
|
+
failure_message_when_negated do
|
25
|
+
unmatched_requests = @unmatched_investigations.map(&:request).join("\n ")
|
22
26
|
"Expected Pacto to have matched all requests to a Contract, but the following requests were not matched: \n #{unmatched_requests}"
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
26
|
-
RSpec::Matchers.define :
|
27
|
-
@failed_validations = Pacto::ValidationRegistry.instance.failed_validations
|
30
|
+
RSpec::Matchers.define :have_failed_investigations do |_method, _uri|
|
28
31
|
match do
|
29
|
-
|
32
|
+
@failed_investigations = Pacto::InvestigationRegistry.instance.failed_investigations
|
33
|
+
!@failed_investigations.empty?
|
30
34
|
end
|
31
35
|
|
32
|
-
|
33
|
-
'Expected Pacto to have found
|
36
|
+
failure_message do
|
37
|
+
'Expected Pacto to have found investigation problems, but none were found.'
|
34
38
|
end
|
35
39
|
|
36
|
-
|
37
|
-
"Expected Pacto to have successfully validated all requests, but the following issues were found: #{@
|
40
|
+
failure_message_when_negated do
|
41
|
+
"Expected Pacto to have successfully validated all requests, but the following issues were found: #{@failed_investigations}"
|
38
42
|
end
|
39
43
|
end
|
40
44
|
|
41
45
|
RSpec::Matchers.define :have_validated do |method, uri|
|
42
|
-
@request_pattern = WebMock::RequestPattern.new(method, uri)
|
43
46
|
match do
|
47
|
+
@request_pattern = Pacto::RequestPattern.new(method, uri)
|
48
|
+
@request_pattern.with(@options) if @options
|
44
49
|
validated? @request_pattern
|
45
50
|
end
|
46
51
|
|
@@ -49,51 +54,53 @@ RSpec::Matchers.define :have_validated do |method, uri|
|
|
49
54
|
end
|
50
55
|
|
51
56
|
chain :with do |options|
|
52
|
-
@
|
57
|
+
@options = options
|
53
58
|
end
|
54
59
|
|
55
|
-
def validated?(
|
56
|
-
@
|
57
|
-
validated = !@
|
60
|
+
def validated?(_request_pattern)
|
61
|
+
@matching_investigations = Pacto::InvestigationRegistry.instance.validated? @request_pattern
|
62
|
+
validated = !@matching_investigations.nil?
|
58
63
|
validated && successfully? && contract_matches?
|
59
64
|
end
|
60
65
|
|
61
|
-
def
|
62
|
-
@
|
66
|
+
def investigation_citations
|
67
|
+
@investigation_citations ||= @matching_investigations.map(&:citations).flatten.compact
|
63
68
|
end
|
64
69
|
|
65
70
|
def successfully?
|
66
|
-
@
|
71
|
+
@matching_investigations.map(&:successful?).uniq.eql? [true]
|
67
72
|
end
|
68
73
|
|
69
74
|
def contract_matches?
|
70
75
|
if @contract
|
71
|
-
validated_contracts = @
|
76
|
+
validated_contracts = @matching_investigations.map(&:contract).compact
|
72
77
|
# Is there a better option than case equality for string & regex support?
|
73
|
-
validated_contracts.
|
78
|
+
validated_contracts.any? do |contract|
|
79
|
+
@contract === contract.file || @contract === contract.name # rubocop:disable CaseEquality
|
80
|
+
end
|
74
81
|
else
|
75
82
|
true
|
76
83
|
end
|
77
84
|
end
|
78
85
|
|
79
|
-
|
86
|
+
failure_message do
|
80
87
|
buffer = StringIO.new
|
81
88
|
buffer.puts "expected Pacto to have validated #{@request_pattern}"
|
82
|
-
if @
|
89
|
+
if @matching_investigations.nil? || @matching_investigations.empty?
|
83
90
|
buffer.puts ' but no matching request was received'
|
84
91
|
buffer.puts ' received:'
|
85
92
|
buffer.puts "#{WebMock::RequestRegistry.instance}"
|
86
|
-
elsif @
|
93
|
+
elsif @matching_investigations.map(&:contract).compact.empty?
|
87
94
|
buffer.puts ' but a matching Contract was not found'
|
88
95
|
elsif !successfully?
|
89
|
-
buffer.puts ' but
|
96
|
+
buffer.puts ' but investigation errors were found:'
|
90
97
|
buffer.print ' '
|
91
|
-
buffer.puts
|
92
|
-
#
|
93
|
-
# buffer.puts " #{
|
98
|
+
buffer.puts investigation_citations.join "\n "
|
99
|
+
# investigation_citations.each do |investigation_result|
|
100
|
+
# buffer.puts " #{investigation_result}"
|
94
101
|
# end
|
95
102
|
elsif @contract
|
96
|
-
validated_against = @
|
103
|
+
validated_against = @matching_investigations.map { |v| v.against_contract? @contract }.compact.join ','
|
97
104
|
buffer.puts " against Contract #{@contract}"
|
98
105
|
buffer.puts " but it was validated against #{validated_against}"
|
99
106
|
end
|