rspec_contracts 0.1.1 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6576fa2cf41a382ea4b30ec4214c2ead84d6c17e2906fbcfb956dd86c21437cf
4
- data.tar.gz: 5b5c0b1f48ea60ceaa207375152c19ca42d729780e939e2dff5245c201654424
3
+ metadata.gz: 416990094d07bd3e4a083be651518350f4a730fad14feb0e109d99710ea2b0f4
4
+ data.tar.gz: 734de59f3870c8afb9231bf55b8712c8fd61c244f8178f5dcf09ec1cbb3a7a39
5
5
  SHA512:
6
- metadata.gz: c734f0ab1fbfaecc7a51c67c8ec5c1eba147b9477908a8c9196a33ebc0ffcc030f357981379e569675b365700a0f6130427bf4cec9164282289730cd09bb38ca
7
- data.tar.gz: c8cc4756d8cc3a7475cb0346eb42bd897ddeea6e448d87961c8ff1529bd3234448354cf9b7eee4bbd01366a89e29ea2394d97b3d60f9de09663bb0d60d5c531d
6
+ metadata.gz: 1360e61a50dd701ca97deae3228f541e897712da1c6a9ce86c7a7e7f934cc4bf35e5e2f1434bee891f758402e3d8854534feaf4bac8686ae6faff71b7a780975
7
+ data.tar.gz: 33716e95b8cad067992ce6423a3c6b58ec16e22400f90ef2f77cb3812df74690c42f824fadf28240b4389724ab6913c9ddb45c8b4452e98152f1a47f4f949dcf
data/README.md CHANGED
@@ -117,7 +117,9 @@ The operation you're trying to test does not match either the HTTP method, or th
117
117
 
118
118
  The POST body (if one is present) does not match the schema for a valid request as defined by the given operation.
119
119
 
120
- ### `RspecContracts::Error::RequestValidation`
120
+ This error is never raised if there is no POST body.
121
+
122
+ ### `RspecContracts::Error::ResponseValidation`
121
123
 
122
124
  The response body does not match the schema for a valid response as defined by the given operation.
123
125
 
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/lazy_load_hooks'
3
+ require "active_support/lazy_load_hooks"
4
4
  require "rspec_contracts/engine"
5
5
  require "rspec_contracts/integration"
6
- require 'rspec_contracts/contract'
7
- require 'rspec_contracts/operation'
8
- require 'rspec_contracts/error'
9
- require 'rspec_contracts/error/operation_lookup'
10
- require 'rspec_contracts/error/request_validation'
11
- require 'rspec_contracts/error/response_validation'
12
- require 'rspec_contracts/error/path_validation'
13
- require 'rspec_contracts/error/schema'
14
- require 'rspec_contracts/path_validator'
15
- require 'rspec_contracts/request_validator'
16
- require 'rspec_contracts/response_validator'
17
- require 'rspec_contracts/railtie' if defined?(Rails::Railtie)
6
+ require "rspec_contracts/contract"
7
+ require "rspec_contracts/operation"
8
+ require "rspec_contracts/error"
9
+ require "rspec_contracts/error/operation_lookup"
10
+ require "rspec_contracts/error/request_validation"
11
+ require "rspec_contracts/error/response_validation"
12
+ require "rspec_contracts/error/path_validation"
13
+ require "rspec_contracts/error/schema"
14
+ require "rspec_contracts/path_validator"
15
+ require "rspec_contracts/request_validator"
16
+ require "rspec_contracts/response_validator"
17
+ require "rspec_contracts/railtie" if defined?(Rails::Railtie)
18
18
  require "openapi_parser"
19
19
  require "semverse"
20
20
 
@@ -37,9 +37,9 @@ module RspecContracts
37
37
  end
38
38
 
39
39
  def self.valid_json?(json)
40
- JSON.parse(json)
41
- return true
42
- rescue JSON::ParserError => e
43
- return false
40
+ JSON.parse(json.to_s)
41
+ true
42
+ rescue JSON::ParserError => _e
43
+ false
44
44
  end
45
45
  end
@@ -1,36 +1,40 @@
1
- class RspecContracts::Contract
2
- def initialize(schema)
3
- @schema = schema.with_indifferent_access
4
- @root = OpenAPIParser.parse(schema)
5
- end
1
+ # frozen_string_literal: true
6
2
 
7
- def [](key)
8
- return RspecContracts::Operation.new(nil, self) unless operations.key?(key)
3
+ module RspecContracts
4
+ class Contract
5
+ def initialize(schema)
6
+ @schema = schema.with_indifferent_access
7
+ @root = OpenAPIParser.parse(schema)
8
+ end
9
9
 
10
- operations[key]
11
- end
10
+ def [](key)
11
+ return RspecContracts::Operation.new(nil, self) unless operations.key?(key)
12
12
 
13
- def version
14
- @schema[:info][:version]
15
- rescue NoMethodError => _e
16
- raise RspecContracts::Error.new("Version not found in schema")
17
- end
13
+ operations[key]
14
+ end
18
15
 
19
- def operations
20
- @operations ||= paths.map do |p|
21
- p._openapi_all_child_objects.values.map do |op|
22
- next unless op.respond_to?(:operation_id)
16
+ def version
17
+ @schema[:info][:version]
18
+ rescue NoMethodError => _e
19
+ raise RspecContracts::Error.new("Version not found in schema")
20
+ end
23
21
 
24
- [op.operation_id, RspecContracts::Operation.new(op, self)]
25
- end.compact.to_h
26
- end.inject(:merge).with_indifferent_access
27
- end
22
+ def operations
23
+ @operations ||= paths.map do |p|
24
+ p._openapi_all_child_objects.values.map do |op|
25
+ next unless op.respond_to?(:operation_id)
28
26
 
29
- def paths
30
- @paths ||= @root.paths._openapi_all_child_objects.values
31
- end
27
+ [op.operation_id, RspecContracts::Operation.new(op, self)]
28
+ end.compact.to_h
29
+ end.inject(:merge).with_indifferent_access
30
+ end
31
+
32
+ def paths
33
+ @paths ||= @root.paths._openapi_all_child_objects.values
34
+ end
32
35
 
33
- def method_missing(m, *args, &block)
34
- @root.send(m, *args, &block)
36
+ def method_missing(m, *args, &block)
37
+ @root.send(m, *args, &block)
38
+ end
35
39
  end
36
40
  end
@@ -1,2 +1,6 @@
1
- class RspecContracts::Error < StandardError
1
+ # frozen_string_literal: true
2
+
3
+ module RspecContracts
4
+ class Error < StandardError
5
+ end
2
6
  end
@@ -1,2 +1,8 @@
1
- class RspecContracts::Error::OperationLookup < RspecContracts::Error
1
+ # frozen_string_literal: true
2
+
3
+ module RspecContracts
4
+ class Error
5
+ class OperationLookup < RspecContracts::Error
6
+ end
7
+ end
2
8
  end
@@ -1,2 +1,8 @@
1
- class RspecContracts::Error::PathValidation < RspecContracts::Error
1
+ # frozen_string_literal: true
2
+
3
+ module RspecContracts
4
+ class Error
5
+ class PathValidation < RspecContracts::Error
6
+ end
7
+ end
2
8
  end
@@ -1,2 +1,8 @@
1
- class RspecContracts::Error::RequestValidation < RspecContracts::Error
1
+ # frozen_string_literal: true
2
+
3
+ module RspecContracts
4
+ class Error
5
+ class RequestValidation < RspecContracts::Error
6
+ end
7
+ end
2
8
  end
@@ -1,2 +1,8 @@
1
- class RspecContracts::Error::ResponseValidation < RspecContracts::Error
1
+ # frozen_string_literal: true
2
+
3
+ module RspecContracts
4
+ class Error
5
+ class ResponseValidation < RspecContracts::Error
6
+ end
7
+ end
2
8
  end
@@ -1,2 +1,8 @@
1
- class RspecContracts::Error::Schema < RspecContracts::Error
1
+ # frozen_string_literal: true
2
+
3
+ module RspecContracts
4
+ class Error
5
+ class Schema < RspecContracts::Error
6
+ end
7
+ end
2
8
  end
@@ -1,24 +1,32 @@
1
- module RspecContracts::Integration
2
- http_verbs = %w(get post patch put delete)
1
+ # frozen_string_literal: true
3
2
 
4
- http_verbs.each do |method|
5
- define_method(method) do |*args, **kwargs|
6
- request_path = args.first
7
- api_operation = kwargs.delete(:api_operation)
8
- api_version = kwargs.delete(:api_version)
9
- super(*args, **kwargs).tap do |status_code|
10
- return unless api_operation # even a not found contract lookup will still be present
11
- return if api_version.present? && !Semverse::Constraint.new(api_operation.root.version).include?(api_version)
12
- raise RspecContracts::Error::OperationLookup.new("Operation not found") unless api_operation.valid?
3
+ module RspecContracts
4
+ module Integration
5
+ http_verbs = %w[get post patch put delete]
13
6
 
14
- RspecContracts.config.logger.tagged("rspec_contracts", api_operation.operation_id) do
7
+ http_verbs.each do |method|
8
+ define_method(method) do |*args, **kwargs|
9
+ request_path = args.first
10
+ api_operation = kwargs.delete(:api_operation)
11
+ api_version = kwargs.delete(:api_version)
12
+ super(*args, **kwargs).tap do |status_code|
13
+ next unless api_operation # even a not found contract lookup will still be present
14
+ next if api_version.present? && !Semverse::Constraint.new(api_operation.root.version).include?(api_version)
15
+ raise RspecContracts::Error::OperationLookup.new("Operation not found") unless api_operation.valid?
15
16
 
16
- RspecContracts::PathValidator.validate_path(api_operation, method, request_path) unless RspecContracts.config.path_validation_mode == :ignore
17
- RspecContracts::RequestValidator.validate_request(api_operation, request) unless RspecContracts.config.request_validation_mode == :ignore
17
+ RspecContracts.config.logger.tagged("rspec_contracts", api_operation.operation_id) do
18
+ unless RspecContracts.config.path_validation_mode == :ignore
19
+ RspecContracts::PathValidator.validate_path(api_operation, method, request_path)
20
+ end
21
+ unless RspecContracts.config.request_validation_mode == :ignore
22
+ RspecContracts::RequestValidator.validate_request(api_operation, request)
23
+ end
18
24
 
19
- unless RspecContracts.config.response_validation_mode == :ignore
20
- validatable_response = OpenAPIParser::RequestOperation::ValidatableResponseBody.new(status_code, JSON.parse(response.body), response.headers)
21
- RspecContracts::ResponseValidator.validate_response(api_operation, validatable_response)
25
+ unless RspecContracts.config.response_validation_mode == :ignore
26
+ parsed = RspecContracts.valid_json?(response.body) ? JSON.parse(response.body) : nil
27
+ vr = OpenAPIParser::RequestOperation::ValidatableResponseBody.new(status_code, parsed, response.headers)
28
+ RspecContracts::ResponseValidator.validate_response(api_operation, vr)
29
+ end
22
30
  end
23
31
  end
24
32
  end
@@ -1,19 +1,24 @@
1
- class RspecContracts::Operation
2
- attr_reader :root
3
- def initialize(op, root)
4
- @op = op
5
- @root = root
6
- end
1
+ # frozen_string_literal: true
7
2
 
8
- def valid?
9
- @op.present?
10
- end
3
+ module RspecContracts
4
+ class Operation
5
+ attr_reader :root
11
6
 
12
- def ==(val)
13
- val == @op
14
- end
7
+ def initialize(op, root)
8
+ @op = op
9
+ @root = root
10
+ end
11
+
12
+ def valid?
13
+ @op.present?
14
+ end
15
+
16
+ def ==(val)
17
+ val == @op
18
+ end
15
19
 
16
- def method_missing(m, *args, &block)
17
- @op.send(m, *args, &block)
20
+ def method_missing(m, *args, &block)
21
+ @op.send(m, *args, &block)
22
+ end
18
23
  end
19
24
  end
@@ -1,17 +1,21 @@
1
- class RspecContracts::PathValidator
2
- class << self
3
- def validate_path(op, method, path)
4
- lookup_path = path.remove(RspecContracts.config.base_path)
5
- return if operation_matches_request?(op, method, lookup_path)
1
+ # frozen_string_literal: true
6
2
 
7
- msg = "#{method.upcase} #{path} does not resolve to #{op.operation_id}"
8
- raise RspecContracts::Error::PathValidation.new(msg) if RspecContracts.config.path_validation_mode == :raise
9
-
10
- RspecContracts.config.logger.error "Contract validation warning: #{msg}"
11
- end
3
+ module RspecContracts
4
+ class PathValidator
5
+ class << self
6
+ def validate_path(op, method, path)
7
+ lookup_path = path.remove(RspecContracts.config.base_path)
8
+ return if operation_matches_request?(op, method, lookup_path)
9
+
10
+ msg = "#{method.upcase} #{path} does not resolve to #{op.operation_id}"
11
+ raise RspecContracts::Error::PathValidation.new(msg) if RspecContracts.config.path_validation_mode == :raise
12
+
13
+ RspecContracts.config.logger.error "Contract validation warning: #{msg}"
14
+ end
12
15
 
13
- def operation_matches_request?(op, method, path)
14
- op == op.root.request_operation(method.to_sym, path)&.operation_object
16
+ def operation_matches_request?(op, method, path)
17
+ op == op.root.request_operation(method.to_sym, path)&.operation_object
18
+ end
15
19
  end
16
20
  end
17
21
  end
@@ -1,5 +1,9 @@
1
- class RspecContracts::Railtie < Rails::Railtie
2
- initializer "rspec_contracts" do
3
- RspecContracts.install
1
+ # frozen_string_literal: true
2
+
3
+ module RspecContracts
4
+ class Railtie < Rails::Railtie
5
+ initializer "rspec_contracts" do
6
+ RspecContracts.install
7
+ end
4
8
  end
5
9
  end
@@ -1,22 +1,29 @@
1
- class RspecContracts::RequestValidator
2
- class << self
3
- def validate_request(op, request)
4
- body = request.body.read
5
- parsed_body = RspecContracts.valid_json?(body) ? JSON.parse(request.body.read) : nil
6
- op.validate_request_body(request.content_type, parsed_body, opts)
7
- rescue OpenAPIParser::OpenAPIError => e
8
- raise RspecContracts::Error::RequestValidation.new(e.message) if RspecContracts.config.request_validation_mode == :raise
1
+ # frozen_string_literal: true
9
2
 
10
- formatted_for_logging = {
11
- body: parsed_body,
12
- headers: request.headers.to_h.select { |k, _| k.starts_with?("HTTP_") }.transform_keys { |k| k.remove("HTTP_").downcase }
13
- }
14
- RspecContracts.config.logger.error "Contract validation warning: #{e.message}"
15
- RspecContracts.config.logger.error "Request was: #{formatted_for_logging}"
16
- end
3
+ module RspecContracts
4
+ class RequestValidator
5
+ class << self
6
+ def validate_request(op, request)
7
+ body = request.body.read
8
+ parsed_body = RspecContracts.valid_json?(body) ? JSON.parse(request.body.read) : nil
9
+ op.validate_request_body(request.content_type, parsed_body, opts)
10
+ rescue OpenAPIParser::OpenAPIError => e
11
+ if RspecContracts.config.request_validation_mode == :raise
12
+ raise RspecContracts::Error::RequestValidation.new(e.message)
13
+ end
14
+
15
+ formatted_for_logging = {
16
+ body: parsed_body,
17
+ headers: request.headers.to_h.select {|k, _| k.starts_with?("HTTP_") }
18
+ .transform_keys {|k| k.remove("HTTP_").downcase }
19
+ }
20
+ RspecContracts.config.logger.error "Contract validation warning: #{e.message}"
21
+ RspecContracts.config.logger.error "Request was: #{formatted_for_logging}"
22
+ end
17
23
 
18
- def opts
19
- OpenAPIParser::SchemaValidator::Options.new(coerce_value: true, datetime_coerce_class: DateTime)
24
+ def opts
25
+ OpenAPIParser::SchemaValidator::Options.new(coerce_value: true, datetime_coerce_class: DateTime)
26
+ end
20
27
  end
21
28
  end
22
29
  end
@@ -1,16 +1,23 @@
1
- class RspecContracts::ResponseValidator
2
- class << self
3
- def validate_response(op, resp)
4
- op.validate_response(resp, opts)
5
- rescue OpenAPIParser::OpenAPIError => e
6
- raise RspecContracts::Error::ResponseValidation.new(e.message) if RspecContracts.config.response_validation_mode == :raise
7
-
8
- RspecContracts.config.logger.error "Contract validation warning: #{e.message}"
9
- RspecContracts.config.logger.error "Response was: #{resp}"
10
- end
1
+ # frozen_string_literal: true
2
+
3
+ module RspecContracts
4
+ class ResponseValidator
5
+ class << self
6
+ def validate_response(op, resp)
7
+ op.validate_response(resp, opts(has_content: resp.content_type.present?))
8
+ rescue OpenAPIParser::OpenAPIError => e
9
+ if RspecContracts.config.response_validation_mode == :raise
10
+ raise RspecContracts::Error::ResponseValidation.new(e.message)
11
+ end
12
+
13
+ RspecContracts.config.logger.error "Contract validation warning: #{e.message}"
14
+ RspecContracts.config.logger.error "Response was: #{resp}"
15
+ end
11
16
 
12
- def opts
13
- OpenAPIParser::SchemaValidator::ResponseValidateOptions.new(strict: RspecContracts.config.strict_response_validation)
17
+ def opts(has_content: true)
18
+ OpenAPIParser::SchemaValidator::ResponseValidateOptions.new(strict: has_content &&
19
+ RspecContracts.config.strict_response_validation)
20
+ end
14
21
  end
15
22
  end
16
23
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RspecContracts
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.4"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec_contracts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Allen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-30 00:00:00.000000000 Z
11
+ date: 2020-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -45,7 +45,7 @@ dependencies:
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
47
  - !ruby/object:Gem::Dependency
48
- name: openapi_parser
48
+ name: semverse
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
@@ -59,13 +59,13 @@ dependencies:
59
59
  - !ruby/object:Gem::Version
60
60
  version: '0'
61
61
  - !ruby/object:Gem::Dependency
62
- name: semverse
62
+ name: active_model_serializers
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - ">="
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0'
68
- type: :runtime
68
+ type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
@@ -87,7 +87,7 @@ dependencies:
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
89
  - !ruby/object:Gem::Dependency
90
- name: active_model_serializers
90
+ name: bundler
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - ">="
@@ -101,7 +101,7 @@ dependencies:
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
103
  - !ruby/object:Gem::Dependency
104
- name: bundler
104
+ name: byebug
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - ">="
@@ -115,7 +115,7 @@ dependencies:
115
115
  - !ruby/object:Gem::Version
116
116
  version: '0'
117
117
  - !ruby/object:Gem::Dependency
118
- name: byebug
118
+ name: factory_bot_rails
119
119
  requirement: !ruby/object:Gem::Requirement
120
120
  requirements:
121
121
  - - ">="
@@ -129,7 +129,7 @@ dependencies:
129
129
  - !ruby/object:Gem::Version
130
130
  version: '0'
131
131
  - !ruby/object:Gem::Dependency
132
- name: factory_bot_rails
132
+ name: guard
133
133
  requirement: !ruby/object:Gem::Requirement
134
134
  requirements:
135
135
  - - ">="
@@ -143,7 +143,7 @@ dependencies:
143
143
  - !ruby/object:Gem::Version
144
144
  version: '0'
145
145
  - !ruby/object:Gem::Dependency
146
- name: guard
146
+ name: guard-bundler
147
147
  requirement: !ruby/object:Gem::Requirement
148
148
  requirements:
149
149
  - - ">="
@@ -157,7 +157,7 @@ dependencies:
157
157
  - !ruby/object:Gem::Version
158
158
  version: '0'
159
159
  - !ruby/object:Gem::Dependency
160
- name: guard-bundler
160
+ name: guard-rspec
161
161
  requirement: !ruby/object:Gem::Requirement
162
162
  requirements:
163
163
  - - ">="
@@ -171,7 +171,7 @@ dependencies:
171
171
  - !ruby/object:Gem::Version
172
172
  version: '0'
173
173
  - !ruby/object:Gem::Dependency
174
- name: guard-rspec
174
+ name: overcommit
175
175
  requirement: !ruby/object:Gem::Requirement
176
176
  requirements:
177
177
  - - ">="
@@ -185,7 +185,7 @@ dependencies:
185
185
  - !ruby/object:Gem::Version
186
186
  version: '0'
187
187
  - !ruby/object:Gem::Dependency
188
- name: overcommit
188
+ name: pry-rescue
189
189
  requirement: !ruby/object:Gem::Requirement
190
190
  requirements:
191
191
  - - ">="
@@ -199,7 +199,7 @@ dependencies:
199
199
  - !ruby/object:Gem::Version
200
200
  version: '0'
201
201
  - !ruby/object:Gem::Dependency
202
- name: pry-byebug
202
+ name: pry-stack_explorer
203
203
  requirement: !ruby/object:Gem::Requirement
204
204
  requirements:
205
205
  - - ">="
@@ -216,16 +216,16 @@ dependencies:
216
216
  name: rubocop
217
217
  requirement: !ruby/object:Gem::Requirement
218
218
  requirements:
219
- - - ">="
219
+ - - "~>"
220
220
  - !ruby/object:Gem::Version
221
- version: '0'
221
+ version: 0.83.0
222
222
  type: :development
223
223
  prerelease: false
224
224
  version_requirements: !ruby/object:Gem::Requirement
225
225
  requirements:
226
- - - ">="
226
+ - - "~>"
227
227
  - !ruby/object:Gem::Version
228
- version: '0'
228
+ version: 0.83.0
229
229
  - !ruby/object:Gem::Dependency
230
230
  name: rubocop-rspec
231
231
  requirement: !ruby/object:Gem::Requirement
@@ -244,16 +244,16 @@ dependencies:
244
244
  name: simplecov
245
245
  requirement: !ruby/object:Gem::Requirement
246
246
  requirements:
247
- - - ">="
247
+ - - "<"
248
248
  - !ruby/object:Gem::Version
249
- version: '0'
249
+ version: '0.18'
250
250
  type: :development
251
251
  prerelease: false
252
252
  version_requirements: !ruby/object:Gem::Requirement
253
253
  requirements:
254
- - - ">="
254
+ - - "<"
255
255
  - !ruby/object:Gem::Version
256
- version: '0'
256
+ version: '0.18'
257
257
  - !ruby/object:Gem::Dependency
258
258
  name: simplecov-console
259
259
  requirement: !ruby/object:Gem::Requirement