rspec_api_blueprint_matchers 0.1.4 → 0.1.5

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
  SHA1:
3
- metadata.gz: 6dc5ebff2689d095904f17137d1554b9a3d05240
4
- data.tar.gz: bb68a6dcf5100596475dc17b36eb5e0e88f5ebe5
3
+ metadata.gz: c8bd0abe26c064df373492e78343f325abbd43c9
4
+ data.tar.gz: 7403ee224404cac77bc7706b11ed82e48285cb6b
5
5
  SHA512:
6
- metadata.gz: 6029d68ee28a95f469637a4385f2e5ff663d7452cf6e37e886117c9c0ea25208ebbcbe0dce3ebcfbc12bf08388c83d82b0f5d8b30a95bb078bd4268a85d571bb
7
- data.tar.gz: f596a4f15ecede8ac78cacd95ddae170364620fba52484f743912b90b3348e1ba7e4774753e7b8af2085049b88a3da24ffc7427262017ec6f6b5fb68ecb6919a
6
+ metadata.gz: f82510ff4c0051f2a614ba4df5dfbb77b2a28c97cc63b2d4e3574618d50aca81872f5de728289db9e31fcf0e304e62dba3227b6ad4073064d5242cde174484ed
7
+ data.tar.gz: 36f71401e0e26aae242359d060753a34c12f3afb46d9ca2eea935c5ce953db655c17339abbc0f0c9b770e8159c7757c659cfc54db9bcfe2dca28a6179d2ee2d6
@@ -21,6 +21,6 @@ AllCops:
21
21
  - "config/*"
22
22
  - "bin/*"
23
23
  - "config.ru"
24
- - "spec"
24
+ - "spec/**/*"
25
25
  - "vendor/gems/**/spec/**"
26
26
  TargetRubyVersion: 2.4
@@ -48,7 +48,7 @@ Metrics/CyclomaticComplexity:
48
48
  # Checks the length of lines in the source code.
49
49
 
50
50
  Metrics/LineLength:
51
- Max: 160
51
+ Max: 180
52
52
  AllowHereDoc: true
53
53
  AllowURI: true
54
54
  URISchemes:
@@ -80,7 +80,7 @@ Metrics/ModuleLength:
80
80
  # Checks for methods with too many parameters.
81
81
 
82
82
  Metrics/ParameterLists:
83
- Max: 5
83
+ Max: 6
84
84
  CountKeywordArgs: true
85
85
 
86
86
 
@@ -91,4 +91,3 @@ Metrics/ParameterLists:
91
91
 
92
92
  Metrics/PerceivedComplexity:
93
93
  Max: 7
94
-
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "rspec_apib/version"
3
3
  require "rspec_apib/parser"
4
- require "rspec_apib/extractors"
5
4
  require "rspec_apib/transaction_validator"
6
5
  require "rspec_apib/rspec"
7
6
  require "rspec_apib/config"
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  module RSpecApib
3
3
  module Element
4
+ # Represents an array in api-elements (http://api-elements.readthedocs.io/en/latest/)
4
5
  class Array < ::Array
6
+ # rubocop:disable Lint/UnusedMethodArgument
5
7
  def self.from_hash(hash, index:, parent:)
6
8
  new(hash["content"])
7
9
  end
@@ -19,10 +19,13 @@ module RSpecApib
19
19
  return node_or_nodes.map { |node| parse(node, index: index, parent: parent) } if node_or_nodes.is_a?(::Array)
20
20
  return transformed_basic_hash(node_or_nodes, index: index, parent: parent) if basic_hash?(node_or_nodes)
21
21
  return node_or_nodes unless !klass.nil? || base_element?(node_or_nodes)
22
- hash = node_or_nodes
22
+ parse_node(node_or_nodes, index: index, parent: parent, klass: klass)
23
+ end
24
+
25
+ def self.parse_node(hash, index:, parent:, klass: nil)
23
26
  klass_name = klass
24
27
  klass_name ||= hash["element"].slice(0, 1).capitalize + hash["element"].slice(1..-1).delete(" ")
25
- return node_or_nodes unless RSpecApib::Element.const_defined?(klass_name)
28
+ return hash unless RSpecApib::Element.const_defined?(klass_name)
26
29
  klass = RSpecApib::Element.const_get(klass_name)
27
30
  index[klass] ||= []
28
31
  element = klass.from_hash(hash, index: index, parent: parent)
@@ -23,6 +23,12 @@ module RSpecApib
23
23
  }
24
24
  return [failure_reason]
25
25
  end
26
+ validate_json_schema(schema, request_or_response)
27
+ end
28
+
29
+ private
30
+
31
+ def validate_json_schema(schema, request_or_response)
26
32
  schema = JSON.parse schema.content
27
33
  errors = JSON::Validator.fully_validate(schema, request_or_response.body)
28
34
  return [] if errors.length.zero?
@@ -35,8 +41,6 @@ module RSpecApib
35
41
  [failure_reason]
36
42
  end
37
43
 
38
- private
39
-
40
44
  def body_schema_asset
41
45
  content.find { |node| node.is_a?(Asset) && node.meta && node.meta["classes"] && node.meta["classes"].include?("messageBodySchema")}
42
46
  end
@@ -7,6 +7,7 @@ module RSpecApib
7
7
  # Indicates if the incoming request matches the method, path and path vars
8
8
  # @param [::RSpecApib::Request] The incoming request - normalized
9
9
  # @return [Boolean] true if matches else false
10
+ # rubocop:disable Lint/UnusedMethodArgument
10
11
  def matches?(response, options: {})
11
12
  matches_status?(response) &&
12
13
  matches_content_type?(response)
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module RSpecApib
3
3
  module Element
4
- # (byebug) parent.parent["attributes"]
5
- # {"meta"=>[#<struct RSpecApib::Element::Member element="member", meta={"classes"=>["user"]}, attributes={}, content={"FORMAT"=>"1A"}, parent=#<struct RSpecApib::Element::ParseResult element="parseResult", meta=nil, attributes=nil, content=nil, parent=nil>>, #<struct RSpecApib::Element::Member element="member", meta={"classes"=>["user"]}, attributes={}, content={"HOST"=>"http://api.shiftcommerce.com/inventory/v1"}, parent=#<struct RSpecApib::Element::ParseResult element="parseResult", meta=nil, attributes=nil, content=nil, parent=nil>>]}
6
-
4
+ # Represents a resource in api-elements (http://api-elements.readthedocs.io/en/latest/)
7
5
  class Resource < Base
8
6
  def transitions
9
7
  content.select { |item| item.is_a?(Transition) }
@@ -13,8 +11,6 @@ module RSpecApib
13
11
  content.select { |item| item.is_a?(Category) }
14
12
  end
15
13
 
16
- private
17
-
18
14
  def self.attributes_schema
19
15
  {
20
16
  href: "TemplatedHref"
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  module RSpecApib
3
3
  module Element
4
+ # Represents a string in api-elements (http://api-elements.readthedocs.io/en/latest/)
4
5
  class String < ::String
6
+ # rubocop:disable Lint/UnusedMethodArgument
5
7
  def self.from_hash(hash, index:, parent:)
6
8
  new(hash["content"] || "")
7
9
  end
@@ -2,8 +2,11 @@
2
2
  require "addressable"
3
3
  module RSpecApib
4
4
  module Element
5
+ # Represents a templated href in api-elements (http://api-elements.readthedocs.io/en/latest/)
5
6
  class TemplatedHref < Base
6
- # Note this is not really a hash !!
7
+ # Note this is not really a hash but the interface named it
8
+ # that early on as everything was a hash !!
9
+ # rubocop:disable Lint/UnusedMethodArgument
7
10
  def self.from_hash(hash, index:, parent:)
8
11
  new("templatedHref", nil, nil, hash, parent)
9
12
  end
@@ -23,10 +26,10 @@ module RSpecApib
23
26
  end
24
27
 
25
28
  def path
26
- a1 = Addressable::URI.parse(url)
27
- a1.path, a1.query, a1.fragment = nil
28
- a2 = Addressable::URI.parse(url)
29
- a2.to_s.gsub(a1.to_s, "")
29
+ url_1 = Addressable::URI.parse(url)
30
+ url_1.path, url_1.query, url_1.fragment = nil
31
+ url_2 = Addressable::URI.parse(url)
32
+ url_2.to_s.gsub(url_1.to_s, "")
30
33
  end
31
34
 
32
35
  def self.host_from_parent(node)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module RSpecApib
3
3
  module Element
4
+ # Represents a transition in api-elements (http://api-elements.readthedocs.io/en/latest/)
4
5
  class Transition < Base
5
6
  def http_transactions
6
7
  content.select { |item| item.is_a?(HttpTransaction) }
@@ -11,8 +12,6 @@ module RSpecApib
11
12
  [:href, :hrefVariables]
12
13
  end
13
14
 
14
- private
15
-
16
15
  def self.attributes_schema
17
16
  {
18
17
  href: "TemplatedHref"
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require "rspec_apib/transcluder"
3
- require "rspec_apib/extractors"
4
3
  require "rspec_apib/elements"
5
4
  require "open3"
6
5
  require "json"
@@ -73,7 +72,7 @@ module RSpecApib
73
72
  Open3.popen3("#{bin_path} -f json") do |stdin, stdout, _stderr, wait_thr|
74
73
  send_document(file: file, buffer: stdin)
75
74
  op = stdout.read
76
- exit_status = wait_thr.value
75
+ wait_thr.value
77
76
  end
78
77
  op
79
78
  end
@@ -17,7 +17,7 @@ module RSpecApib
17
17
  end
18
18
 
19
19
  def validate_body_with_json_schema?
20
- is_json?
20
+ json?
21
21
  end
22
22
 
23
23
  def content_type
@@ -32,7 +32,7 @@ module RSpecApib
32
32
 
33
33
  attr_accessor :raw_response
34
34
 
35
- def is_json?
35
+ def json?
36
36
  content_type =~ /json/
37
37
  end
38
38
  end
@@ -12,7 +12,8 @@ RSpec::Matchers.define :match_api_docs_for do |path:, request_method:, content_t
12
12
  error_messages << "Expected the transaction to match api docs for #{method}:#{path} but the provided parser was invalid"
13
13
  false
14
14
  else
15
- ::RSpecApib::TransactionValidator.new(path: path, request_method: request_method, content_type: content_type, parser: parser).validate(request: ::RSpecApib.normalize_request(actual.request), response: ::RSpecApib.normalize_response(actual.response), error_messages: error_messages)
15
+ validator = ::RSpecApib::TransactionValidator.new(path: path, request_method: request_method, content_type: content_type, parser: parser)
16
+ validator.validate(request: ::RSpecApib.normalize_request(actual.request), response: ::RSpecApib.normalize_response(actual.response), error_messages: error_messages)
16
17
  end
17
18
 
18
19
  end
@@ -12,7 +12,7 @@ module RSpecApib
12
12
  acc
13
13
  end
14
14
  transactions.each do |requested_tx|
15
- matching_transactions = documented_transaction_tracker.keys.each do |dtx|
15
+ documented_transaction_tracker.keys.each do |dtx|
16
16
  documented_transaction_tracker[dtx] = true if dtx.matches?(requested_tx.request, requested_tx.response)
17
17
  end
18
18
  end
@@ -30,8 +30,29 @@ module RSpecApib
30
30
  results
31
31
  end
32
32
 
33
+ def report_uncovered(errors:)
34
+ uncovered_transactions.each do |tx|
35
+ errors << self.class.uncovered_tx_message(tx)
36
+ end
37
+ end
38
+
39
+ def report_undocumented(errors:)
40
+ undocumented.each do |tx|
41
+ errors << self.class.undocumented_tx_message(tx)
42
+ end
43
+ end
44
+
33
45
  private
34
46
 
35
47
  attr_accessor :transactions, :parser
48
+
49
+ def self.uncovered_tx_message(tx)
50
+ "#{tx.request.request_method.to_s.upcase} #{tx.request.url} with response (#{tx.response.content_type}) status #{tx.response.status}- Not covered"
51
+ end
52
+
53
+ def self.undocumented_tx_message(tx)
54
+ "#{tx.request.request_method.upcase} #{tx.request.url} with response (#{tx.response.content_type}) status #{tx.response.status} - Not documented"
55
+ end
56
+
36
57
  end
37
58
  end
@@ -9,18 +9,14 @@ module RSpecApib
9
9
  def validate(transactions:, error_messages:)
10
10
  errors = []
11
11
  reporter = TransactionCoverageReport.new(transactions: transactions, parser: parser)
12
- uncovered = reporter.uncovered_transactions
13
- unless uncovered.empty?
14
- uncovered.each do |tx|
15
- errors << self.class.uncovered_tx_message(tx)
16
- end
17
- end
18
- undocumented = reporter.undocumented_transactions
19
- unless undocumented.empty?
20
- undocumented.each do |tx|
21
- errors << self.class.undocumented_tx_message(tx)
22
- end
23
- end
12
+ reporter.report_uncovered(errors: errors)
13
+ reporter.report_undocumented(errors: errors)
14
+ report_summary(errors: errors, error_messages: error_messages)
15
+ end
16
+
17
+ private
18
+
19
+ def report_summary(errors:, error_messages:)
24
20
  if !errors.empty?
25
21
  error_messages.concat errors
26
22
  error_messages << "Coverage Summary: #{uncovered.length} uncovered and #{undocumented.length} undocumented transactions"
@@ -30,21 +26,10 @@ module RSpecApib
30
26
  end
31
27
  end
32
28
 
33
- private
34
-
35
29
  attr_accessor :parser
36
30
 
37
31
  def matched_transaction(request:, response:)
38
32
  parser.http_transactions.find { |t| t.matches?(request, response, options: {validate_request_schema: :never, validate_response_schema: :never}) }
39
33
  end
40
-
41
- def self.uncovered_tx_message(tx)
42
- "#{tx.request.request_method.to_s.upcase} #{tx.request.url} with response (#{tx.response.content_type}) status #{tx.response.status}- Not covered"
43
- end
44
-
45
- def self.undocumented_tx_message(tx)
46
- "#{tx.request.request_method.upcase} #{tx.request.url} with response (#{tx.response.content_type}) status #{tx.response.status} - Not documented"
47
- end
48
-
49
34
  end
50
35
  end
@@ -12,25 +12,32 @@ module RSpecApib
12
12
  end
13
13
 
14
14
  def validate(request:, response:, error_messages:, options: {})
15
- candidates = transaction_candidates(request: request, response: response, options: options)
16
- results = candidates.map do |candidate|
17
- candidate.validate_schema(request, response, validate_request_schema: validate_request_schema, validate_response_schema: validate_response_schema)
18
- end
19
-
15
+ results = matched_transactions(request, response, options)
20
16
  if results.empty?
21
17
  error_messages << "No candidates for #{request.inspect} with response #{response.inspect}"
22
18
  return false
23
19
  end
24
20
 
25
21
  return true unless results.flatten.find {|r| !r[:request_errors].empty? || !r[:response_errors].empty?}
22
+ report_error_messages(results, error_messages)
23
+ false
24
+ end
25
+
26
+ private
27
+
28
+ def report_error_messages(results, error_messages)
26
29
  results.each do |result|
27
30
  error_messages << "The request validation failed - reasons #{result[:request_errors].join("\n")}" unless result[:request_errors].empty?
28
31
  error_messages << "The response validation failed - reasons #{result[:response_errors].join("\n")}" unless result[:response_errors].empty?
29
32
  end
30
- false
31
33
  end
32
34
 
33
- private
35
+ def matched_transactions(request, response, options)
36
+ candidates = transaction_candidates(request: request, response: response, options: options)
37
+ candidates.map do |candidate|
38
+ candidate.validate_schema(request, response, validate_request_schema: validate_request_schema, validate_response_schema: validate_response_schema)
39
+ end
40
+ end
34
41
 
35
42
  def transaction_candidates(request:, response:, options: {})
36
43
  transactions = parser.http_transactions.select do |t|
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module RSpecApib
3
- VERSION = "0.1.4".freeze
3
+ VERSION = "0.1.5".freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec_api_blueprint_matchers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shift Commerce Ltd
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-05-08 00:00:00.000000000 Z
11
+ date: 2017-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -163,9 +163,6 @@ files:
163
163
  - lib/rspec_apib/elements/string.rb
164
164
  - lib/rspec_apib/elements/templated_href.rb
165
165
  - lib/rspec_apib/elements/transition.rb
166
- - lib/rspec_apib/extractors.rb
167
- - lib/rspec_apib/extractors/http_transaction.rb
168
- - lib/rspec_apib/extractors/resource.rb
169
166
  - lib/rspec_apib/parser.rb
170
167
  - lib/rspec_apib/request.rb
171
168
  - lib/rspec_apib/response.rb
@@ -1,3 +0,0 @@
1
- # frozen_string_literal: true
2
- require "rspec_apib/extractors/resource"
3
- require "rspec_apib/extractors/http_transaction"
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
- module RSpecApib
3
- module Extractor
4
- class HttpTransaction
5
- def self.call(document)
6
- collector = []
7
- find_nodes_within(document, collector: collector)
8
- collector
9
- end
10
-
11
- private
12
-
13
- def self.find_nodes_within(node, collector:)
14
- return node.each { |node| find_nodes_within(node, collector: collector)} if node.is_a?(Array)
15
- return unless node.is_a?(Hash)
16
- if node["element"] == "httpTransaction"
17
- collector << node
18
- elsif node.key?("content")
19
- find_nodes_within(node["content"], collector: collector)
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
- module RSpecApib
3
- module Extractor
4
- class Resource
5
- def self.call(document)
6
- collector = []
7
- find_nodes_within(document, collector: collector)
8
- collector
9
- end
10
-
11
- private
12
-
13
- def self.find_nodes_within(node, collector:)
14
- return node.each { |node| find_nodes_within(node, collector: collector)} if node.is_a?(Array)
15
- return unless node.is_a?(Hash)
16
- if node["element"] == "resource"
17
- collector << node
18
- elsif node.key?("content")
19
- find_nodes_within(node["content"], collector: collector)
20
- end
21
- end
22
- end
23
- end
24
- end