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.
Files changed (204) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +29 -7
  4. data/.travis.yml +8 -1
  5. data/CONTRIBUTING.md +3 -6
  6. data/Gemfile +13 -2
  7. data/Guardfile +4 -4
  8. data/Procfile +1 -0
  9. data/README.md +47 -13
  10. data/Rakefile +66 -19
  11. data/TODO.md +33 -10
  12. data/bin/pacto +4 -0
  13. data/changelog.md +30 -0
  14. data/docs/configuration.md +69 -0
  15. data/docs/consumer.md +18 -0
  16. data/docs/cops.md +39 -0
  17. data/docs/forensics.md +66 -0
  18. data/docs/generation.md +65 -0
  19. data/docs/rake_tasks.md +10 -0
  20. data/docs/rspec.md +0 -0
  21. data/docs/samples.md +133 -0
  22. data/docs/server.md +34 -0
  23. data/docs/server_cli.md +18 -0
  24. data/docs/stenographer.md +20 -0
  25. data/features/configuration/strict_matchers.feature +10 -10
  26. data/features/evolve/existing_services.feature +12 -10
  27. data/features/generate/generation.feature +11 -11
  28. data/features/steps/pacto_steps.rb +17 -12
  29. data/features/stub/templates.feature +4 -4
  30. data/features/support/env.rb +21 -9
  31. data/features/validate/meta_validation.feature +9 -17
  32. data/features/validate/validation.feature +5 -6
  33. data/lib/pacto.rb +41 -33
  34. data/lib/pacto/actor.rb +5 -0
  35. data/lib/pacto/actors/from_examples.rb +67 -0
  36. data/lib/pacto/actors/json_generator.rb +20 -0
  37. data/lib/pacto/cli.rb +75 -0
  38. data/lib/pacto/cli/helpers.rb +20 -0
  39. data/lib/pacto/consumer.rb +80 -0
  40. data/lib/pacto/consumer/faraday_driver.rb +34 -0
  41. data/lib/pacto/contract.rb +48 -20
  42. data/lib/pacto/contract_builder.rb +125 -0
  43. data/lib/pacto/contract_factory.rb +31 -12
  44. data/lib/pacto/contract_files.rb +1 -0
  45. data/lib/pacto/contract_set.rb +12 -0
  46. data/lib/pacto/cops.rb +46 -0
  47. data/lib/pacto/cops/body_cop.rb +23 -0
  48. data/lib/pacto/cops/request_body_cop.rb +10 -0
  49. data/lib/pacto/cops/response_body_cop.rb +10 -0
  50. data/lib/pacto/{validators/response_header_validator.rb → cops/response_header_cop.rb} +9 -15
  51. data/lib/pacto/cops/response_status_cop.rb +18 -0
  52. data/lib/pacto/core/configuration.rb +16 -5
  53. data/lib/pacto/core/contract_registry.rb +13 -32
  54. data/lib/pacto/core/hook.rb +1 -0
  55. data/lib/pacto/core/http_middleware.rb +23 -0
  56. data/lib/pacto/core/investigation_registry.rb +60 -0
  57. data/lib/pacto/core/modes.rb +1 -0
  58. data/lib/pacto/core/pacto_request.rb +59 -0
  59. data/lib/pacto/core/pacto_response.rb +41 -0
  60. data/lib/pacto/dash.rb +9 -0
  61. data/lib/pacto/erb_processor.rb +1 -0
  62. data/lib/pacto/exceptions/invalid_contract.rb +1 -0
  63. data/lib/pacto/extensions.rb +3 -16
  64. data/lib/pacto/forensics/investigation_filter.rb +90 -0
  65. data/lib/pacto/forensics/investigation_matcher.rb +80 -0
  66. data/lib/pacto/generator.rb +31 -53
  67. data/lib/pacto/generator/filters.rb +8 -7
  68. data/lib/pacto/generator/hint.rb +26 -0
  69. data/lib/pacto/generator/native_contract_generator.rb +74 -0
  70. data/lib/pacto/hooks/erb_hook.rb +2 -1
  71. data/lib/pacto/investigation.rb +49 -0
  72. data/lib/pacto/logger.rb +1 -0
  73. data/lib/pacto/meta_schema.rb +12 -6
  74. data/lib/pacto/native_contract_factory.rb +60 -0
  75. data/lib/pacto/observers/stenographer.rb +42 -0
  76. data/lib/pacto/provider.rb +27 -0
  77. data/lib/pacto/rake_task.rb +25 -70
  78. data/lib/pacto/request_clause.rb +31 -29
  79. data/lib/pacto/request_pattern.rb +20 -3
  80. data/lib/pacto/resettable.rb +22 -0
  81. data/lib/pacto/response_clause.rb +5 -12
  82. data/lib/pacto/rspec.rb +38 -31
  83. data/lib/pacto/server.rb +4 -0
  84. data/lib/pacto/stubs/uri_pattern.rb +21 -11
  85. data/lib/pacto/stubs/webmock_adapter.rb +69 -34
  86. data/lib/pacto/swagger_contract_factory.rb +90 -0
  87. data/lib/pacto/test_helper.rb +37 -0
  88. data/lib/pacto/ui.rb +32 -2
  89. data/lib/pacto/uri.rb +2 -1
  90. data/lib/pacto/version.rb +2 -1
  91. data/pacto-server.gemspec +24 -0
  92. data/pacto.gemspec +13 -9
  93. data/resources/contract_schema.json +46 -18
  94. data/resources/draft-04.json +150 -0
  95. data/sample_apis/album/cover_api.rb +12 -0
  96. data/sample_apis/config.ru +25 -0
  97. data/sample_apis/echo_api.rb +26 -0
  98. data/sample_apis/files_api.rb +50 -0
  99. data/sample_apis/hello_api.rb +14 -0
  100. data/sample_apis/ping_api.rb +11 -0
  101. data/sample_apis/reverse_api.rb +20 -0
  102. data/samples/README.md +11 -0
  103. data/samples/Rakefile +2 -0
  104. data/samples/configuration.rb +33 -0
  105. data/samples/consumer.rb +15 -0
  106. data/samples/contracts/README.md +1 -0
  107. data/samples/contracts/contract.js +93 -0
  108. data/samples/contracts/get_album_cover.json +48 -0
  109. data/samples/contracts/localhost/api/echo.json +37 -0
  110. data/samples/contracts/localhost/api/ping.json +38 -0
  111. data/samples/cops.rb +30 -0
  112. data/samples/forensics.rb +54 -0
  113. data/samples/generation.rb +48 -0
  114. data/samples/rake_tasks.sh +7 -0
  115. data/samples/rspec.rb +1 -0
  116. data/samples/samples.rb +92 -0
  117. data/samples/scripts/bootstrap +2 -0
  118. data/samples/scripts/wrapper +11 -0
  119. data/samples/server.rb +24 -0
  120. data/samples/server_cli.sh +12 -0
  121. data/samples/stenographer.rb +17 -0
  122. data/spec/coveralls_helper.rb +1 -0
  123. data/spec/fabricators/contract_fabricator.rb +94 -0
  124. data/spec/fabricators/http_fabricator.rb +48 -0
  125. data/spec/fabricators/webmock_fabricator.rb +24 -0
  126. data/spec/{unit/data → fixtures/contracts}/contract.json +2 -2
  127. data/spec/fixtures/contracts/contract_with_examples.json +58 -0
  128. data/spec/{unit/data → fixtures/contracts}/simple_contract.json +5 -3
  129. data/spec/{integration/data → fixtures/contracts}/strict_contract.json +5 -3
  130. data/spec/{integration/data → fixtures/contracts}/templating_contract.json +3 -2
  131. data/spec/{integration/data/simple_contract.json → fixtures/deprecated_contracts/deprecated_contract.json} +2 -1
  132. data/spec/fixtures/swagger/petstore.yaml +101 -0
  133. data/spec/integration/e2e_spec.rb +19 -20
  134. data/spec/integration/forensics/integration_matcher_spec.rb +90 -0
  135. data/spec/integration/rspec_spec.rb +22 -25
  136. data/spec/integration/templating_spec.rb +7 -6
  137. data/spec/pacto/dummy_server.rb +4 -0
  138. data/spec/pacto/{server → dummy_server}/dummy.rb +7 -6
  139. data/spec/pacto/dummy_server/jruby_workaround_helper.rb +23 -0
  140. data/spec/pacto/{server → dummy_server}/playback_servlet.rb +3 -2
  141. data/spec/spec_helper.rb +16 -7
  142. data/spec/unit/actors/from_examples_spec.rb +70 -0
  143. data/spec/unit/actors/json_generator_spec.rb +105 -0
  144. data/spec/unit/pacto/actor_spec.rb +23 -0
  145. data/spec/unit/pacto/configuration_spec.rb +7 -6
  146. data/spec/unit/pacto/consumer/faraday_driver_spec.rb +40 -0
  147. data/spec/unit/pacto/contract_builder_spec.rb +89 -0
  148. data/spec/unit/pacto/contract_factory_spec.rb +62 -11
  149. data/spec/unit/pacto/contract_files_spec.rb +1 -0
  150. data/spec/unit/pacto/contract_set_spec.rb +36 -0
  151. data/spec/unit/pacto/contract_spec.rb +51 -39
  152. data/spec/unit/pacto/cops/body_cop_spec.rb +107 -0
  153. data/spec/unit/pacto/{validators/response_header_validator_spec.rb → cops/response_header_cop_spec.rb} +30 -19
  154. data/spec/unit/pacto/cops/response_status_cop_spec.rb +26 -0
  155. data/spec/unit/pacto/cops_spec.rb +75 -0
  156. data/spec/unit/pacto/core/configuration_spec.rb +6 -5
  157. data/spec/unit/pacto/core/contract_registry_spec.rb +16 -83
  158. data/spec/unit/pacto/core/http_middleware_spec.rb +36 -0
  159. data/spec/unit/pacto/core/investigation_spec.rb +62 -0
  160. data/spec/unit/pacto/core/modes_spec.rb +5 -4
  161. data/spec/unit/pacto/erb_processor_spec.rb +3 -2
  162. data/spec/unit/pacto/extensions_spec.rb +10 -20
  163. data/spec/unit/pacto/generator/filters_spec.rb +11 -10
  164. data/spec/unit/pacto/generator/native_contract_generator_spec.rb +171 -0
  165. data/spec/unit/{hooks → pacto/hooks}/erb_hook_spec.rb +18 -11
  166. data/spec/unit/pacto/investigation_registry_spec.rb +77 -0
  167. data/spec/unit/pacto/logger_spec.rb +6 -5
  168. data/spec/unit/pacto/meta_schema_spec.rb +5 -4
  169. data/spec/unit/pacto/native_contract_factory_spec.rb +26 -0
  170. data/spec/unit/pacto/pacto_spec.rb +13 -28
  171. data/spec/unit/pacto/request_clause_spec.rb +16 -51
  172. data/spec/unit/pacto/request_pattern_spec.rb +6 -5
  173. data/spec/unit/pacto/response_clause_spec.rb +6 -19
  174. data/spec/unit/pacto/server/playback_servlet_spec.rb +21 -18
  175. data/spec/unit/pacto/stubs/observers/stenographer_spec.rb +33 -0
  176. data/spec/unit/pacto/stubs/uri_pattern_spec.rb +39 -11
  177. data/spec/unit/pacto/stubs/webmock_adapter_spec.rb +67 -117
  178. data/spec/unit/pacto/swagger_contract_factory_spec.rb +56 -0
  179. data/spec/unit/pacto/uri_spec.rb +1 -0
  180. data/tasks/release.rake +57 -0
  181. metadata +247 -76
  182. data/.rubocop-todo.yml +0 -24
  183. data/.ruby-gemset +0 -1
  184. data/.ruby-version +0 -1
  185. data/CHANGELOG +0 -12
  186. data/features/validate/body_only.feature +0 -85
  187. data/lib/pacto/contract_list.rb +0 -17
  188. data/lib/pacto/contract_validator.rb +0 -29
  189. data/lib/pacto/core/validation_registry.rb +0 -40
  190. data/lib/pacto/stubs/webmock_helper.rb +0 -69
  191. data/lib/pacto/validation.rb +0 -54
  192. data/lib/pacto/validators/body_validator.rb +0 -49
  193. data/lib/pacto/validators/request_body_validator.rb +0 -26
  194. data/lib/pacto/validators/response_body_validator.rb +0 -26
  195. data/lib/pacto/validators/response_status_validator.rb +0 -24
  196. data/spec/pacto/server.rb +0 -2
  197. data/spec/unit/pacto/contract_list_spec.rb +0 -35
  198. data/spec/unit/pacto/contract_validator_spec.rb +0 -85
  199. data/spec/unit/pacto/core/validation_registry_spec.rb +0 -76
  200. data/spec/unit/pacto/core/validation_spec.rb +0 -60
  201. data/spec/unit/pacto/generator_spec.rb +0 -132
  202. data/spec/unit/pacto/stubs/webmock_helper_spec.rb +0 -20
  203. data/spec/unit/pacto/validators/body_validator_spec.rb +0 -118
  204. 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
@@ -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 |t, args|
25
- if args.to_a.size < 2
26
- fail Pacto::UI.yellow('USAGE: rake pacto:validate[<host>, <contract_dir>]')
27
- end
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 |t, args|
45
+ task :generate, :input_dir, :output_dir, :host do |_t, args|
36
46
  if args.to_a.size < 3
37
- fail Pacto::UI.yellow('USAGE: rake pacto:generate[<request_contract_dir>, <output_dir>, <record_host>]')
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 |t, args|
49
- if args.to_a.size < 1
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.new
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.generate(contract_file, host)
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.red(e.message)
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.green('Successfully generated all contracts')
86
+ puts Pacto::UI.colorize('Successfully generated all contracts', :green)
117
87
  else
118
- fail Pacto::UI.red("The following contracts could not be generated: #{failed_contracts.join ','}")
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
@@ -1,43 +1,45 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Pacto
2
- class RequestClause
3
- attr_reader :host, :method, :schema
4
- attr_accessor :body
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(host, definition)
7
- @host = host
8
- @definition = definition
9
- @method = definition['method'].to_s.downcase.to_sym
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 uri
14
- @uri ||= Pacto::URI.for(host, path, params)
19
+ def http_method=(method)
20
+ normalize(method)
15
21
  end
16
22
 
17
- def body
18
- JSON::Generator.generate(@definition['body']) if @definition['body']
23
+ def pattern
24
+ @pattern ||= request_pattern_provider.for(self)
19
25
  end
20
26
 
21
- def path
22
- @definition['path']
23
- end
24
-
25
- def headers
26
- @definition['headers']
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
- def params
30
- @definition['params'] || {}
31
- end
39
+ private
32
40
 
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
+ 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
- uri_pattern = UriPattern.for(base_request)
5
- WebMock::RequestPattern.new(base_request.method, uri_pattern)
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
- 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
3
+ class ResponseClause < Pacto::Dash
4
+ property :status
5
+ property :headers, default: {}
6
+ property :schema, default: {}
14
7
  end
15
8
  end
@@ -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
- RSpec::Matchers.define :have_unmatched_requests do |method, uri|
11
- @unmatched_validations = Pacto::ValidationRegistry.instance.unmatched_validations
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
- !@unmatched_validations.empty?
16
+ @unmatched_investigations = Pacto::InvestigationRegistry.instance.unmatched_investigations
17
+ !@unmatched_investigations.empty?
14
18
  end
15
19
 
16
- failure_message_for_should do
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
- failure_message_for_should_not do
21
- unmatched_requests = @unmatched_validations.map(&:request).join("\n ")
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 :have_failed_validations do |method, uri|
27
- @failed_validations = Pacto::ValidationRegistry.instance.failed_validations
30
+ RSpec::Matchers.define :have_failed_investigations do |_method, _uri|
28
31
  match do
29
- !@failed_validations.empty?
32
+ @failed_investigations = Pacto::InvestigationRegistry.instance.failed_investigations
33
+ !@failed_investigations.empty?
30
34
  end
31
35
 
32
- failure_message_for_should do
33
- 'Expected Pacto to have found validation problems, but none were found.'
36
+ failure_message do
37
+ 'Expected Pacto to have found investigation problems, but none were found.'
34
38
  end
35
39
 
36
- failure_message_for_should_not do
37
- "Expected Pacto to have successfully validated all requests, but the following issues were found: #{@failed_validations}"
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
- @request_pattern.with options
57
+ @options = options
53
58
  end
54
59
 
55
- def validated?(request_pattern)
56
- @matching_validations = Pacto::ValidationRegistry.instance.validated? @request_pattern
57
- validated = !@matching_validations.nil?
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 validation_results
62
- @validation_results ||= @matching_validations.map(&:results).flatten.compact
66
+ def investigation_citations
67
+ @investigation_citations ||= @matching_investigations.map(&:citations).flatten.compact
63
68
  end
64
69
 
65
70
  def successfully?
66
- @matching_validations.map(&:successful?).uniq.eql? [true]
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 = @matching_validations.map(&:contract)
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.map(&:file).index { |file| @contract === file } # rubocop:disable CaseEquality
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
- failure_message_for_should do
86
+ failure_message do
80
87
  buffer = StringIO.new
81
88
  buffer.puts "expected Pacto to have validated #{@request_pattern}"
82
- if @matching_validations.nil? || @matching_validations.empty?
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 @matching_validations.map(&:contract).compact.empty?
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 validation errors were found:'
96
+ buffer.puts ' but investigation errors were found:'
90
97
  buffer.print ' '
91
- buffer.puts validation_results.join "\n "
92
- # validation_results.each do |validation_result|
93
- # buffer.puts " #{validation_result}"
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 = @matching_validations.map { |v| v.against_contract? @contract }.compact.join ','
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