open_api-rspec 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f73e7bbf3bdd35a76c36adcad852ea9e96b005b2d49be0492c9df8e0c24183ee
4
+ data.tar.gz: 42451d1a200f757dfd77e961f4c54592a739646ba572ed6153bf940d7b68d81d
5
+ SHA512:
6
+ metadata.gz: 39b8db5cde406f84d045e8b9b2ef1ab63003541033ea208e410bad3da09486b025be6a9047defb269a61101e2330784b751c0bbd8f62b8b7f9eb5817de176d19
7
+ data.tar.gz: d03479e35b8ad4e01f4a298b83067bcf060391ab1cae527b7ff39bc005f3863896d0b84d63741c35d61294de02d089f1e39ed481c089167b11728669a674bf06
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ coverage
data/.reek ADDED
@@ -0,0 +1,59 @@
1
+ ---
2
+ DuplicateMethodCall:
3
+ exclude:
4
+ - OpenApi::RSpec::SchemaParser#deep_body_keys
5
+ - OpenApi::RSpec::SchemaParser#get_deep_keys
6
+ - OpenApi::RSpec::SchemaParser#initialize
7
+ - OpenApi::RSpec::SchemaParser#openapi_body_params
8
+ - OpenApi::RSpec::SchemaParser#schema_for_url_and_request_method_and_response_status
9
+ FeatureEnvy:
10
+ exclude:
11
+ - OpenApi::RSpec::SchemaParser#deep_body_keys
12
+ - OpenApi::RSpec::SchemaParser#get_deep_keys
13
+ - OpenApi::RSpec::SchemaParser#openapi_required_form_data_params
14
+ - OpenApi::RSpec::SchemaParser#openapi_required_path_params
15
+ - OpenApi::RSpec::SchemaParser#openapi_required_query_string_params
16
+ NestedIterators:
17
+ exclude:
18
+ - OpenApi::RSpec::SchemaParser#deep_body_keys
19
+ - OpenApi::RSpec::SchemaParser#get_deep_keys
20
+ - OpenApi::RSpec::SchemaParser#initialize
21
+ - OpenApi::RSpec::SchemaParser#openapi_body_params
22
+ TooManyInstanceVariables:
23
+ exclude:
24
+ - OpenApi::RSpec::SchemaParser
25
+ TooManyMethods:
26
+ exclude:
27
+ - OpenApi::RSpec::SchemaParser
28
+ TooManyStatements:
29
+ exclude:
30
+ - initialize
31
+ - OpenApi::RSpec::SchemaParser#deep_body_keys
32
+ - OpenApi::RSpec::SchemaParser#get_deep_keys
33
+ - OpenApi::RSpec::SchemaParser#openapi_body_params
34
+ UncommunicativeVariableName:
35
+ exclude:
36
+ - OpenApi::RSpec::SchemaParser#get_deep_keys
37
+ - OpenApi::RSpec::SchemaParser#initialize
38
+ - OpenApi::RSpec::SchemaParser#openapi_body_params
39
+ - OpenApi::RSpec::SchemaParser#openapi_form_data_params
40
+ - OpenApi::RSpec::SchemaParser#openapi_path_params
41
+ - OpenApi::RSpec::SchemaParser#openapi_query_string_params
42
+ - OpenApi::RSpec::SchemaParser#openapi_required_form_data_params
43
+ - OpenApi::RSpec::SchemaParser#openapi_required_path_params
44
+ - OpenApi::RSpec::SchemaParser#openapi_required_query_string_params
45
+ - OpenApi::RSpec::SchemaParser#request_path_params
46
+ - OpenApi::RSpec::SchemaParser#schema_for_url_and_request_method_body_parameters
47
+ - OpenApi::RSpec::SchemaParser#schema_for_url_and_request_method_form_data_parameters
48
+ - OpenApi::RSpec::SchemaParser#schema_for_url_and_request_method_path_parameters
49
+ - OpenApi::RSpec::SchemaParser#schema_for_url_and_request_method_query_string_parameters
50
+ UtilityFunction:
51
+ exclude:
52
+ - OpenApi::RSpec::SchemaParser#has_documented_param_in_openapi_url_fragments
53
+ - OpenApi::RSpec::SchemaParser#openapi_url_fragment_name
54
+
55
+ IrresponsibleModule:
56
+ enabled: false
57
+
58
+ exclude_paths:
59
+ - vendor/bundle
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,17 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ require: rubocop-rspec
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 2.5
7
+ Exclude:
8
+ - '**/bin/*'
9
+ - '*.gemspec'
10
+ - 'vendor/bundle/**/*'
11
+ DisplayCopNames: true
12
+
13
+ Style/Documentation:
14
+ Enabled: false
15
+
16
+ Metrics/LineLength:
17
+ Max: 100
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,59 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2018-02-25 12:32:27 -0700 using RuboCop version 0.52.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ Metrics/AbcSize:
11
+ Max: 55
12
+
13
+ # Offense count: 3
14
+ # Configuration parameters: CountComments, ExcludedMethods.
15
+ Metrics/BlockLength:
16
+ Max: 60
17
+
18
+ # Offense count: 2
19
+ # Configuration parameters: CountBlocks.
20
+ Metrics/BlockNesting:
21
+ Max: 4
22
+
23
+ # Offense count: 1
24
+ # Configuration parameters: CountComments.
25
+ Metrics/ClassLength:
26
+ Max: 190
27
+
28
+ # Offense count: 1
29
+ Metrics/CyclomaticComplexity:
30
+ Max: 14
31
+
32
+ # Offense count: 4
33
+ # Configuration parameters: CountComments.
34
+ Metrics/MethodLength:
35
+ Max: 67
36
+
37
+ # Offense count: 1
38
+ Metrics/PerceivedComplexity:
39
+ Max: 16
40
+
41
+ # Offense count: 1
42
+ # Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist, MethodDefinitionMacros.
43
+ # NamePrefix: is_, has_, have_
44
+ # NamePrefixBlacklist: is_, has_, have_
45
+ # NameWhitelist: is_a?
46
+ # MethodDefinitionMacros: define_method, define_singleton_method
47
+ Naming/PredicateName:
48
+ Exclude:
49
+ - 'spec/**/*'
50
+ - 'lib/open_api/rspec/schema_parser.rb'
51
+
52
+ # Offense count: 1
53
+ # Cop supports --auto-correct.
54
+ # Configuration parameters: AutoCorrect, EnforcedStyle.
55
+ # SupportedStyles: predicate, comparison
56
+ Style/NumericPredicate:
57
+ Exclude:
58
+ - 'spec/**/*'
59
+ - 'lib/open_api/rspec/schema_parser.rb'
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.16.1
6
+ before_script:
7
+ - bin/lois bundler-audit -c travis -g $GITHUB_CREDENTIALS
8
+ - bin/lois rubocop -c travis -g $GITHUB_CREDENTIALS
9
+ - bin/lois reek -c travis -g $GITHUB_CREDENTIALS
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in open_api-rspec.gemspec
8
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,133 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ open_api-rspec (0.1.0)
5
+ open_api-schema_validator
6
+ rspec
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (5.1.5)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (~> 0.7)
14
+ minitest (~> 5.1)
15
+ tzinfo (~> 1.1)
16
+ addressable (2.5.2)
17
+ public_suffix (>= 2.0.2, < 4.0)
18
+ ast (2.4.0)
19
+ axiom-types (0.1.1)
20
+ descendants_tracker (~> 0.0.4)
21
+ ice_nine (~> 0.11.0)
22
+ thread_safe (~> 0.3, >= 0.3.1)
23
+ brakeman (4.2.0)
24
+ bundler-audit (0.6.0)
25
+ bundler (~> 1.2)
26
+ thor (~> 0.18)
27
+ byebug (10.0.0)
28
+ codeclimate-engine-rb (0.4.1)
29
+ virtus (~> 1.0)
30
+ coderay (1.1.2)
31
+ coercible (1.0.0)
32
+ descendants_tracker (~> 0.0.1)
33
+ concurrent-ruby (1.0.5)
34
+ descendants_tracker (0.0.4)
35
+ thread_safe (~> 0.3, >= 0.3.1)
36
+ diff-lcs (1.3)
37
+ docile (1.1.5)
38
+ equalizer (0.0.11)
39
+ httparty (0.16.0)
40
+ multi_xml (>= 0.5.2)
41
+ i18n (0.9.5)
42
+ concurrent-ruby (~> 1.0)
43
+ ice_nine (0.11.2)
44
+ json (2.1.0)
45
+ json-schema (2.8.0)
46
+ addressable (>= 2.4)
47
+ lois (0.1.6)
48
+ activesupport
49
+ brakeman
50
+ bundler-audit
51
+ httparty
52
+ reek
53
+ rubocop
54
+ simplecov
55
+ thor
56
+ method_source (0.9.0)
57
+ minitest (5.11.3)
58
+ multi_xml (0.6.0)
59
+ open_api-schema_validator (0.1.0)
60
+ json-schema
61
+ parallel (1.12.1)
62
+ parser (2.4.0.2)
63
+ ast (~> 2.3)
64
+ powerpack (0.1.1)
65
+ pry (0.11.3)
66
+ coderay (~> 1.1.0)
67
+ method_source (~> 0.9.0)
68
+ pry-byebug (3.6.0)
69
+ byebug (~> 10.0)
70
+ pry (~> 0.10)
71
+ public_suffix (3.0.2)
72
+ rainbow (2.2.2)
73
+ rake
74
+ rake (12.3.0)
75
+ reek (4.7.3)
76
+ codeclimate-engine-rb (~> 0.4.0)
77
+ parser (>= 2.4.0.0, < 2.5)
78
+ rainbow (~> 2.0)
79
+ rspec (3.7.0)
80
+ rspec-core (~> 3.7.0)
81
+ rspec-expectations (~> 3.7.0)
82
+ rspec-mocks (~> 3.7.0)
83
+ rspec-core (3.7.1)
84
+ rspec-support (~> 3.7.0)
85
+ rspec-expectations (3.7.0)
86
+ diff-lcs (>= 1.2.0, < 2.0)
87
+ rspec-support (~> 3.7.0)
88
+ rspec-mocks (3.7.0)
89
+ diff-lcs (>= 1.2.0, < 2.0)
90
+ rspec-support (~> 3.7.0)
91
+ rspec-support (3.7.1)
92
+ rubocop (0.52.1)
93
+ parallel (~> 1.10)
94
+ parser (>= 2.4.0.2, < 3.0)
95
+ powerpack (~> 0.1)
96
+ rainbow (>= 2.2.2, < 4.0)
97
+ ruby-progressbar (~> 1.7)
98
+ unicode-display_width (~> 1.0, >= 1.0.1)
99
+ rubocop-rspec (1.23.0)
100
+ rubocop (>= 0.52.1)
101
+ ruby-progressbar (1.9.0)
102
+ simplecov (0.15.1)
103
+ docile (~> 1.1.0)
104
+ json (>= 1.8, < 3)
105
+ simplecov-html (~> 0.10.0)
106
+ simplecov-html (0.10.2)
107
+ thor (0.20.0)
108
+ thread_safe (0.3.6)
109
+ tzinfo (1.2.5)
110
+ thread_safe (~> 0.1)
111
+ unicode-display_width (1.3.0)
112
+ virtus (1.0.5)
113
+ axiom-types (~> 0.1)
114
+ coercible (~> 1.0)
115
+ descendants_tracker (~> 0.0, >= 0.0.3)
116
+ equalizer (~> 0.0, >= 0.0.9)
117
+
118
+ PLATFORMS
119
+ ruby
120
+
121
+ DEPENDENCIES
122
+ bundler
123
+ lois
124
+ open_api-rspec!
125
+ pry-byebug
126
+ rake
127
+ reek
128
+ rubocop
129
+ rubocop-rspec
130
+ simplecov
131
+
132
+ BUNDLED WITH
133
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # OpenApi::Rspec [![Build Status](https://travis-ci.org/ketiko/open_api-rspec.svg?branch=master)](https://travis-ci.org/ketiko/open_api-rspec)
2
+
3
+ RSpec matchers and shared examples for OpenApi
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'open_api-rspec'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install open_api-rspec
20
+
21
+ ## Usage
22
+
23
+ Test if your swagger.json endpoint is a valid schema.
24
+
25
+ ```ruby
26
+ require 'rails_helper'
27
+
28
+ RSpec.describe SwaggerController, type: :request do
29
+ describe '#swagger' do
30
+ before do
31
+ get '/swagger.json'
32
+ end
33
+
34
+ it { expect(response).to have_http_status(:ok) }
35
+ it { expect(response.body).to be_valid_openapi_schema }
36
+ end
37
+ end
38
+ ```
39
+
40
+ Test that a response body matches a specific OpenApi Schema.
41
+ ```ruby
42
+ require 'rails_helper'
43
+
44
+ RSpec.describe UsersController, type: :request do
45
+ describe '#show' do
46
+ before do
47
+ get '/users/1'
48
+ end
49
+
50
+ it { expect(response).to have_http_status(:ok) }
51
+ it { expect(response).to match_openapi_response_schema :User }
52
+ end
53
+ end
54
+ ```
55
+ Test that a request spec matches an OpenApi Schema.
56
+ This will check body, path, query, form data params.
57
+ It will also fail if any unknown params are passed.
58
+ It checks that there is a documented response code and verifies its response schema
59
+ ```ruby
60
+ require 'rails_helper'
61
+
62
+ RSpec.describe UsersController, type: :request do
63
+ describe '#show' do
64
+ before do
65
+ get '/users/1'
66
+ end
67
+
68
+ it { expect(response).to have_http_status(:ok) }
69
+ it { expect(response).to match_openapi_response_schema :User }
70
+ it_behaves_like :an_openapi_endpoint
71
+ end
72
+ end
73
+ ```
74
+
75
+ Note for the endpoint matcher to work you must declare something like the following:
76
+
77
+ ```ruby
78
+ #file: spec/support/openapi_schema.rb
79
+
80
+ RSpec.shared_context "Shared OpenAPI JSON" do
81
+ let(:open_api_json) { '{my open api json schema'} }
82
+ end
83
+
84
+ RSpec.configure do |rspec|
85
+ rspec.include_context "Shared OpenAPI JSON", type: :request
86
+ end
87
+ ```
88
+
89
+ ## Development
90
+
91
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
92
+
93
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
94
+
95
+ ## Contributing
96
+
97
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/open_api-rspec.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'code_climate_reek' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path('../bundle', __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require 'rubygems'
27
+ require 'bundler/setup'
28
+
29
+ load Gem.bin_path('reek', 'code_climate_reek')
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'open_api/rspec'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
data/bin/lois ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'lois' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path('../bundle', __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require 'rubygems'
27
+ require 'bundler/setup'
28
+
29
+ load Gem.bin_path('lois', 'lois')
data/bin/reek ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'reek' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path('../bundle', __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require 'rubygems'
27
+ require 'bundler/setup'
28
+
29
+ load Gem.bin_path('reek', 'reek')
data/bin/rspec ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rspec' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "pathname"
12
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require "rubygems"
27
+ require "bundler/setup"
28
+
29
+ load Gem.bin_path("rspec-core", "rspec")
data/bin/rubocop ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'rubocop' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require 'pathname'
12
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile',
13
+ Pathname.new(__FILE__).realpath)
14
+
15
+ bundle_binstub = File.expand_path('../bundle', __FILE__)
16
+
17
+ if File.file?(bundle_binstub)
18
+ if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
19
+ load(bundle_binstub)
20
+ else
21
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
22
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
23
+ end
24
+ end
25
+
26
+ require 'rubygems'
27
+ require 'bundler/setup'
28
+
29
+ load Gem.bin_path('rubocop', 'rubocop')
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec'
4
+ require 'open_api/rspec/version'
5
+ require 'open_api/rspec/schema_parser'
6
+ require 'open_api/rspec/matchers/open_api'
7
+ require 'open_api/rspec/shared_examples/open_api'
8
+
9
+ module OpenApi
10
+ module Rspec
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenApi
4
+ module RSpec
5
+ module Matchers
6
+ module OpenApi
7
+ ::RSpec::Matchers.define :match_openapi_response_schema do |schema|
8
+ match do |response|
9
+ ::OpenApi::SchemaValidator.validate_schema!(
10
+ SwaggerSchema.to_h,
11
+ response.is_a?(Hash) ? response : ::JSON.parse(response.body),
12
+ fragment: "#/definitions/#{schema}"
13
+ )
14
+ end
15
+ end
16
+
17
+ ::RSpec::Matchers.define :be_valid_openapi_schema do
18
+ match do |string|
19
+ json = ::JSON.parse(string)
20
+ ::OpenApi::SchemaValidator.validate!(json)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,230 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenApi
4
+ module RSpec
5
+ class SchemaParser
6
+ attr_reader :openapi_schema, :request, :response, :schema_for_url
7
+ UUID_REGEX = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
8
+
9
+ def initialize(openapi_schema, request, response)
10
+ @openapi_schema = openapi_schema
11
+ @request = request
12
+ @response = response
13
+
14
+ url = request.path.gsub(@openapi_schema['basePath'], '')
15
+ url_fragments = url.split('/')
16
+
17
+ @openapi_schema['paths'].each do |path, meta|
18
+ path_fragments = path.to_s.split('/')
19
+ next if path_fragments.count != url_fragments.count
20
+
21
+ mismatch = false
22
+ @request_path_params = {}
23
+ path_fragments.each_with_index do |a, i|
24
+ if has_documented_param_in_openapi_url_fragments(a)
25
+ openapi_for_http_method = meta[request.method.to_s.downcase.to_s]
26
+ unless openapi_for_http_method
27
+ mismatch = true
28
+ break
29
+ end
30
+ openapi_params = openapi_for_http_method['parameters']
31
+ unless openapi_params
32
+ mismatch = true
33
+ break
34
+ end
35
+ openapi_path_params = openapi_params.select do |path_param|
36
+ path_param['in'].to_s == 'path'
37
+ end
38
+ unless openapi_path_params
39
+ mismatch = true
40
+ break
41
+ end
42
+ meta_param = openapi_path_params.find do |path_param|
43
+ path_param['name'].to_s == openapi_url_fragment_name(a).to_s
44
+ end
45
+ if meta_param
46
+ case meta_param['type'].to_s
47
+ when 'integer'
48
+ if (url_fragments[i] =~ /\d/) != 0
49
+ mismatch = true
50
+ break
51
+ else
52
+ @request_path_params[a.delete('{').delete('}')] = url_fragments[i]
53
+ next
54
+ end
55
+ when 'string'
56
+ if (url_fragments[i] =~ /#{UUID_REGEX}/) != 0 &&
57
+ (url_fragments[i] =~ /[a-zA-Z]+/) != 0
58
+
59
+ mismatch = true
60
+ break
61
+ else
62
+ @request_path_params[a.delete('{').delete('}')] = url_fragments[i]
63
+ next
64
+ end
65
+ else
66
+ mismatch = true
67
+ break
68
+ end
69
+ else
70
+ mismatch = true
71
+ break
72
+ end
73
+ end
74
+
75
+ mismatch = a != url_fragments[i]
76
+ break if mismatch
77
+ end
78
+
79
+ next if mismatch
80
+
81
+ @schema_for_url = meta
82
+
83
+ break
84
+ end
85
+ end
86
+
87
+ def has_documented_param_in_openapi_url_fragments(fragment)
88
+ (fragment =~ /^{.+}$/) == 0
89
+ end
90
+
91
+ def openapi_url_fragment_name(fragment)
92
+ fragment.match(/^{(.+)}$/).captures.first
93
+ end
94
+
95
+ def request_path_params
96
+ @request_path_params.map { |k, _| k.to_s }
97
+ end
98
+
99
+ def schema_for_url_and_request_method
100
+ if schema_for_url
101
+ schema_for_url[request.method.to_s.downcase.to_s]
102
+ else
103
+ {}
104
+ end
105
+ end
106
+
107
+ def schema_for_url_and_request_method_parameters
108
+ schema_for_url_and_request_method['parameters'] || [{}]
109
+ end
110
+
111
+ def schema_for_url_and_request_method_query_string_parameters
112
+ schema_for_url_and_request_method_parameters
113
+ .select { |p| p['in'].to_s == 'query' }
114
+ end
115
+
116
+ def schema_for_url_and_request_method_path_parameters
117
+ schema_for_url_and_request_method_parameters
118
+ .select { |p| p['in'].to_s == 'path' }
119
+ end
120
+
121
+ def schema_for_url_and_request_method_form_data_parameters
122
+ schema_for_url_and_request_method_parameters
123
+ .select { |p| p['in'].to_s == 'formData' }
124
+ end
125
+
126
+ def schema_for_url_and_request_method_body_parameters
127
+ schema_for_url_and_request_method_parameters
128
+ .select { |p| p['in'].to_s == 'body' }
129
+ end
130
+
131
+ def schema_for_url_and_request_method_and_response_status
132
+ if schema_for_url_and_request_method['responses']
133
+ schema = schema_for_url_and_request_method['responses'][response.status.to_s]
134
+ schema
135
+ else
136
+ {}
137
+ end
138
+ end
139
+
140
+ def openapi_query_string_params
141
+ schema_for_url_and_request_method_query_string_parameters.map { |p| p['name'].to_s }
142
+ end
143
+
144
+ def openapi_path_params
145
+ schema_for_url_and_request_method_path_parameters.map { |p| p['name'].to_s }
146
+ end
147
+
148
+ def openapi_form_data_params
149
+ schema_for_url_and_request_method_form_data_parameters.map { |p| p['name'].to_s }
150
+ end
151
+
152
+ def openapi_body_params
153
+ schema_for_url_and_request_method_body_parameters.flat_map do |p|
154
+ if p['schema'].present?
155
+ scheme = p['schema']['$ref'].tr('/', ' ')[2..-1].strip.split.map(&:to_s)
156
+ body = @openapi_schema.dig(*scheme)
157
+ body['properties'].flat_map do |k, v|
158
+ deep_body_keys(k, v)
159
+ end
160
+ else
161
+ p['name'].to_s
162
+ end
163
+ end
164
+ end
165
+
166
+ def deep_body_keys(name, hash)
167
+ if hash['type'] == 'object'
168
+ hash['properties'].flat_map do |p_name, p_hash|
169
+ parent = name.to_s.camelize(:lower).to_s
170
+ sub_keys = deep_body_keys(p_name, p_hash)
171
+ sub_keys.flat_map do |sub|
172
+ "#{parent}##{sub}".to_s
173
+ end
174
+ end
175
+ else
176
+ [name.to_s.camelize(:lower).to_s]
177
+ end
178
+ end
179
+
180
+ def openapi_request_params
181
+ [
182
+ openapi_query_string_params,
183
+ openapi_path_params,
184
+ openapi_form_data_params,
185
+ openapi_body_params
186
+ ].flatten.compact
187
+ end
188
+
189
+ def openapi_required_form_data_params
190
+ schema_for_url_and_request_method_form_data_parameters
191
+ .select { |p| p['required'] == true }
192
+ .map { |p| p['name'].to_s }
193
+ end
194
+
195
+ def openapi_required_path_params
196
+ schema_for_url_and_request_method_path_parameters
197
+ .select { |p| p['required'] == true }
198
+ .map { |p| p['name'].to_s }
199
+ end
200
+
201
+ def openapi_required_query_string_params
202
+ schema_for_url_and_request_method_query_string_parameters
203
+ .select { |p| p['required'] == true }
204
+ .map { |p| p['name'].to_s }
205
+ end
206
+
207
+ def request_params
208
+ valid_params = get_deep_keys(request.params.except('format', 'action', 'controller'))
209
+
210
+ valid_params - request_path_params
211
+ end
212
+
213
+ def get_deep_keys(hash)
214
+ return [] if hash.empty?
215
+
216
+ hash.flat_map do |k, v|
217
+ if v.is_a? Hash
218
+ parent = k.to_s.camelize(:lower).to_s
219
+ sub_keys = get_deep_keys(v)
220
+ sub_keys.map do |sub|
221
+ "#{parent}##{sub}".to_s
222
+ end
223
+ else
224
+ k.to_s.camelize(:lower).to_s
225
+ end
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenApi
4
+ module RSpec
5
+ module SharedExamples
6
+ ::RSpec.shared_examples_for :an_openapi_endpoint do
7
+ let(:schema_hash) { ::JSON.parse(open_api_json) }
8
+ let(:schema_parser) { SchemaParser.new(schema_hash, request, response) }
9
+
10
+ it 'has openapi documentation for url' do
11
+ expect(schema_parser.schema_for_url).not_to be_nil
12
+ end
13
+
14
+ it 'matches an allowed http request method' do
15
+ expect(schema_parser.schema_for_url_and_request_method).not_to be_nil
16
+ end
17
+
18
+ it 'has all required request query parameters' do
19
+ schema_parser.openapi_required_query_string_params.each do |openapi_param|
20
+ expect(schema_parser.request_params).to include(openapi_param)
21
+ end
22
+ end
23
+
24
+ it 'has all required request path parameters' do
25
+ schema_parser.openapi_required_path_params.each do |openapi_param|
26
+ expect(schema_parser.request_path_params).to include(openapi_param)
27
+ end
28
+ end
29
+
30
+ it 'has all required request form data parameters' do
31
+ schema_parser.openapi_required_form_data_params.each do |openapi_param|
32
+ expect(schema_parser.request_params).to include(openapi_param)
33
+ end
34
+ end
35
+
36
+ it 'does not allow undocumented request path parameters' do
37
+ schema_parser.request_path_params.each do |request_param|
38
+ expect(schema_parser.openapi_path_params).to include(request_param)
39
+ end
40
+ end
41
+
42
+ it 'does not allow undocumented request parameters' do
43
+ schema_parser.request_params.each do |request_param|
44
+ expect(schema_parser.openapi_request_params).to include(request_param)
45
+ end
46
+ end
47
+
48
+ it 'matches an allowed http response status' do
49
+ expect(schema_parser.schema_for_url_and_request_method_and_response_status).not_to be_nil
50
+ end
51
+ it 'matches the response schema' do
52
+ response_schema = schema_parser.schema_for_url_and_request_method_and_response_status
53
+ results = if response_schema[:schema]
54
+ ::OpenApi::SchemaValidator.validate_schema!(
55
+ schema_hash,
56
+ ::JSON.parse(response.body),
57
+ fragment: response_schema[:schema][:$ref]
58
+ )
59
+ else
60
+ response_schema
61
+ end
62
+
63
+ expect(results).to be_truthy
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenApi
4
+ module Rspec
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,34 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "open_api/rspec/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "open_api-rspec"
8
+ spec.version = OpenApi::Rspec::VERSION
9
+ spec.authors = ["Ryan Hansen"]
10
+ spec.email = ["ketiko@gmail.com"]
11
+
12
+ spec.summary = %q{RSpec matchers and shared examples for OpenApi}
13
+ spec.description = %q{RSpec matchers and shared examples for OpenApi}
14
+ spec.homepage = "https://github.com/ketiko/open_api-rspec"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency 'bundler'
24
+ spec.add_development_dependency 'lois'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'reek'
27
+ spec.add_development_dependency 'rubocop'
28
+ spec.add_development_dependency 'rubocop-rspec'
29
+ spec.add_development_dependency 'simplecov'
30
+ spec.add_development_dependency 'pry-byebug'
31
+
32
+ spec.add_dependency 'rspec'
33
+ spec.add_dependency 'open_api-schema_validator'
34
+ end
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: open_api-rspec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Hansen
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-02-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: lois
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: reek
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: open_api-schema_validator
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: RSpec matchers and shared examples for OpenApi
154
+ email:
155
+ - ketiko@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".gitignore"
161
+ - ".reek"
162
+ - ".rspec"
163
+ - ".rubocop.yml"
164
+ - ".rubocop_todo.yml"
165
+ - ".travis.yml"
166
+ - Gemfile
167
+ - Gemfile.lock
168
+ - README.md
169
+ - Rakefile
170
+ - bin/code_climate_reek
171
+ - bin/console
172
+ - bin/lois
173
+ - bin/reek
174
+ - bin/rspec
175
+ - bin/rubocop
176
+ - bin/setup
177
+ - lib/open_api/rspec.rb
178
+ - lib/open_api/rspec/matchers/open_api.rb
179
+ - lib/open_api/rspec/schema_parser.rb
180
+ - lib/open_api/rspec/shared_examples/open_api.rb
181
+ - lib/open_api/rspec/version.rb
182
+ - open_api-rspec.gemspec
183
+ homepage: https://github.com/ketiko/open_api-rspec
184
+ licenses: []
185
+ metadata: {}
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ requirements: []
201
+ rubyforge_project:
202
+ rubygems_version: 2.7.6
203
+ signing_key:
204
+ specification_version: 4
205
+ summary: RSpec matchers and shared examples for OpenApi
206
+ test_files: []