rspec-openapi 0.8.1 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86927b142ea56ae6b4c2c4f70c1cf8d21159b85f614369d4af15c91489e56647
4
- data.tar.gz: 3711fffd6052537911f4bfaafdb2d22fba78a1649f71c97f16325d79ccc3d427
3
+ metadata.gz: d55c7178770a58a980d06669adfab735ba84b51a283302dd8e14e91af4b7b4e9
4
+ data.tar.gz: 7b0030924262f8a1cd7027df612315b8d6e3fb89e91f9b4e57c3ec83f7e9bfd6
5
5
  SHA512:
6
- metadata.gz: 7a54a6a78f142fc39b7a4ad5a5afd92e2486ba320e414e0782f0eb2c386535588e6126c851d868b265807a29844706547a6757fb1d45a2eb1cda820923fa7c15
7
- data.tar.gz: b53892f024ecb7733fb99e83ab1c299d3ae0b36465a2ca806d1234896a90fd45bf47df335bc14977004aa8c113c272daadf029b4c0c7ad46773be1ad5e2dec0e
6
+ metadata.gz: 4cf846ca12d2062ae17c0b5d1829a7887c206425330c015f5fdde310c59678cb802b4cadb60f63c1ca6d55063c3d4f1cc2ac7813db10c6d8611a0fa3af975192
7
+ data.tar.gz: 7501fac5fbfd8b0c4cec6cb196feb9ff09a640c4aff7165a93a6880eebea194d5c30674eefadb53987538ebed913d8d43ab625f1a7c8a3a12ad76ba15b459687
@@ -26,11 +26,19 @@ jobs:
26
26
  rails: 6.1.6
27
27
  - ruby: ruby:3.1
28
28
  rails: 7.0.3
29
+ coverage: coverage
29
30
  env:
30
31
  RAILS_VERSION: ${{ matrix.rails == '' && '6.1.6' || matrix.rails }}
32
+ COVERAGE: ${{ matrix.coverage || '' }}
31
33
  steps:
32
34
  - uses: actions/checkout@v2
33
35
  - name: bundle install
34
36
  run: bundle install -j$(nproc) --retry 3
35
37
  - run: bundle exec rspec
36
38
  timeout-minutes: 1
39
+ - name: Upload coverage reports
40
+ uses: codecov/codecov-action@v3
41
+ if: matrix.coverage == 'coverage'
42
+ with:
43
+ fail_ci_if_error: true
44
+ files: ./coverage/coverage.xml
data/.rubocop.yml CHANGED
@@ -17,6 +17,8 @@ Style/ClassAndModuleChildren:
17
17
  EnforcedStyle: compact
18
18
  Exclude:
19
19
  - 'lib/rspec/openapi/version.rb'
20
+ Layout/FirstArrayElementIndentation:
21
+ EnforcedStyle: consistent
20
22
  Metrics/BlockLength:
21
23
  Exclude:
22
24
  - 'spec/**/*'
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ unless ENV['COVERAGE'] && ENV['COVERAGE'].empty?
4
+ require 'simplecov'
5
+ require 'simplecov-cobertura'
6
+
7
+ SimpleCov.at_fork.call(Process.pid)
8
+ SimpleCov.formatter SimpleCov::Formatter::MultiFormatter.new([
9
+ SimpleCov::Formatter::CoberturaFormatter,
10
+ ])
11
+ SimpleCov.start do
12
+ add_filter '/spec/'
13
+ add_filter '/scripts/'
14
+ end
15
+ end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## v0.9.0
2
+ - bugfix: Fix engine path resolution
3
+ [#113](https://github.com/exoego/rspec-openapi/pull/113)
4
+ - bugfix: fix multiple uploaded files
5
+ [#117](https://github.com/exoego/rspec-openapi/pull/117), [#126](https://github.com/exoego/rspec-openapi/pull/126)
6
+ - feat: Add required_request_params to metadata
7
+ [#114](https://github.com/exoego/rspec-openapi/pull/114)
8
+ - bugfix(minitest):
9
+ [#128](https://github.com/exoego/rspec-openapi/pull/128)
10
+ - doc(minitest): Add instructions for minitest triggered yaml generation
11
+ [#116](https://github.com/exoego/rspec-openapi/pull/116)
12
+ - chore: Don't dump records into temporary file
13
+ [#127](https://github.com/exoego/rspec-openapi/pull/127)
14
+
1
15
  ## v0.8.1
2
16
  - bugfix: Empty `required` array should not be present.
3
17
  [#111](https://github.com/exoego/rspec-openapi/pull/111)
data/Gemfile CHANGED
@@ -10,6 +10,8 @@ gem 'roda'
10
10
  gem 'rspec-rails'
11
11
 
12
12
  group :test do
13
+ gem 'simplecov'
14
+ gem 'simplecov-cobertura'
13
15
  gem 'super_diff'
14
16
  end
15
17
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # rspec-openapi ![test](https://github.com/k0kubun/rspec-openapi/workflows/test/badge.svg)
1
+ # rspec-openapi ![test](https://github.com/exoego/rspec-openapi/workflows/test/badge.svg) [![codecov](https://codecov.io/gh/exoego/rspec-openapi/branch/master/graph/badge.svg?token=egYm6AlxkD)](https://codecov.io/gh/exoego/rspec-openapi)
2
2
 
3
3
  Generate OpenAPI schema from RSpec request specs.
4
4
 
@@ -308,6 +308,7 @@ Some examples' attributes can be overwritten via RSpec metadata options. Example
308
308
  summary: 'list all posts',
309
309
  description: 'list all posts ordered by pub_date',
310
310
  tags: %w[v1 posts],
311
+ required_request_params: %w[limit],
311
312
  security: [{"MyToken" => []}],
312
313
  } do
313
314
  # ...
@@ -344,6 +345,12 @@ It should work with both classes inheriting from `ActionDispatch::IntegrationTes
344
345
 
345
346
  Please note that not all features present in the rspec integration work with minitest (yet). For example, custom per test case metadata is not supported. A custom `description_builder` will not work either.
346
347
 
348
+ Run minitest with OPENAPI=1 to generate `doc/openapi.yaml` for your request specs.
349
+
350
+ ```bash
351
+ $ OPENAPI=1 bundle exec rails t
352
+ ```
353
+
347
354
  ## Links
348
355
 
349
356
  Existing RSpec plugins which have OpenAPI integration:
@@ -5,11 +5,7 @@ require 'minitest'
5
5
  module RSpec::OpenAPI::Minitest
6
6
  Example = Struct.new(:context, :description, :metadata, :file_path)
7
7
 
8
- module TestPatch
9
- def self.prepended(base)
10
- base.extend(ClassMethods)
11
- end
12
-
8
+ module RunPatch
13
9
  def run(*args)
14
10
  result = super
15
11
  if ENV['OPENAPI'] && self.class.openapi?
@@ -22,6 +18,12 @@ module RSpec::OpenAPI::Minitest
22
18
  end
23
19
  result
24
20
  end
21
+ end
22
+
23
+ module ActivateOpenApiClassMethods
24
+ def self.prepended(base)
25
+ base.extend(ClassMethods)
26
+ end
25
27
 
26
28
  module ClassMethods
27
29
  def openapi?
@@ -35,10 +37,12 @@ module RSpec::OpenAPI::Minitest
35
37
  end
36
38
  end
37
39
 
38
- Minitest::Test.prepend RSpec::OpenAPI::Minitest::TestPatch
40
+ Minitest::Test.prepend RSpec::OpenAPI::Minitest::ActivateOpenApiClassMethods
41
+
42
+ if ENV['OPENAPI']
43
+ Minitest::Test.prepend RSpec::OpenAPI::Minitest::RunPatch
39
44
 
40
- Minitest.after_run do
41
- if ENV['OPENAPI']
45
+ Minitest.after_run do
42
46
  result_recorder = RSpec::OpenAPI::ResultRecorder.new(RSpec::OpenAPI.path_records)
43
47
  result_recorder.record_results!
44
48
  puts result_record.error_message if result_recorder.errors?
@@ -6,10 +6,12 @@ RSpec::OpenAPI::Record = Struct.new(
6
6
  :path_params, # @param [Hash] - {:controller=>"v1/statuses", :action=>"create", :id=>"1"}
7
7
  :query_params, # @param [Hash] - {:query=>"string"}
8
8
  :request_params, # @param [Hash] - {:request=>"body"}
9
+ :required_request_params, # @param [Array] - ["param1", "param2"]
9
10
  :request_content_type, # @param [String] - "application/json"
10
11
  :request_headers, # @param [Array] - [["header_key1", "header_value1"], ["header_key2", "header_value2"]]
11
12
  :summary, # @param [String] - "v1/statuses #show"
12
13
  :tags, # @param [Array] - ["Status"]
14
+ :operation_id, # @param [String] - "request-1234"
13
15
  :description, # @param [String] - "returns a status"
14
16
  :security, # @param [Array] - [{securityScheme1: []}]
15
17
  :status, # @param [Integer] - 200
@@ -11,7 +11,8 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
11
11
  request, response = extract_request_response(context)
12
12
  return if request.nil?
13
13
 
14
- path, summary, tags, raw_path_params, description, security = extract_request_attributes(request, example)
14
+ path, summary, tags, operation_id, required_request_params, raw_path_params, description, security =
15
+ extract_request_attributes(request, example)
15
16
 
16
17
  request_headers, response_headers = extract_headers(request, response)
17
18
 
@@ -21,10 +22,12 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
21
22
  path_params: raw_path_params,
22
23
  query_params: request.query_parameters,
23
24
  request_params: raw_request_params(request),
25
+ required_request_params: required_request_params,
24
26
  request_content_type: request.media_type,
25
27
  request_headers: request_headers,
26
28
  summary: summary,
27
29
  tags: tags,
30
+ operation_id: operation_id,
28
31
  description: description,
29
32
  security: security,
30
33
  status: response.status,
@@ -61,13 +64,21 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
61
64
  metadata = example.metadata[:openapi] || {}
62
65
  summary = metadata[:summary]
63
66
  tags = metadata[:tags]
67
+ operation_id = metadata[:operation_id]
68
+ required_request_params = metadata[:required_request_params] || []
64
69
  security = metadata[:security]
65
70
  description = metadata[:description] || RSpec::OpenAPI.description_builder.call(example)
66
71
  raw_path_params = request.path_parameters
67
72
  path = request.path
68
73
  if rails?
69
- route = find_rails_route(request)
70
- path = route.path.spec.to_s.delete_suffix('(.:format)')
74
+ # Reverse the destructive modification by Rails https://github.com/rails/rails/blob/v6.0.3.4/actionpack/lib/action_dispatch/journey/router.rb#L33-L41
75
+ fixed_request = request.dup
76
+ fixed_request.path_info = File.join(request.script_name, request.path_info) if request.script_name.present?
77
+
78
+ route, path = find_rails_route(fixed_request)
79
+ raise "No route matched for #{fixed_request.request_method} #{fixed_request.path_info}" if route.nil?
80
+
81
+ path = path.delete_suffix('(.:format)')
71
82
  summary ||= route.requirements[:action]
72
83
  tags ||= [route.requirements[:controller]&.classify].compact
73
84
  # :controller and :action always exist. :format is added when routes is configured as such.
@@ -75,7 +86,7 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
75
86
  raw_path_params = raw_path_params.slice(*(raw_path_params.keys - %i[controller action format]))
76
87
  end
77
88
  summary ||= "#{request.method} #{path}"
78
- [path, summary, tags, raw_path_params, description, security]
89
+ [path, summary, tags, operation_id, required_request_params, raw_path_params, description, security]
79
90
  end
80
91
 
81
92
  def extract_request_response(context)
@@ -99,21 +110,18 @@ class << RSpec::OpenAPI::RecordBuilder = Object.new
99
110
  end
100
111
 
101
112
  # @param [ActionDispatch::Request] request
102
- def find_rails_route(request, app: Rails.application, fix_path: true)
103
- # Reverse the destructive modification by Rails https://github.com/rails/rails/blob/v6.0.3.4/actionpack/lib/action_dispatch/journey/router.rb#L33-L41
104
- if fix_path && !request.script_name.empty?
105
- request = request.dup
106
- request.path_info = File.join(request.script_name, request.path_info)
107
- end
108
-
113
+ def find_rails_route(request, app: Rails.application, path_prefix: '')
109
114
  app.routes.router.recognize(request) do |route|
115
+ path = route.path.spec.to_s
110
116
  if route.app.matches?(request)
111
- return find_rails_route(request, app: route.app.app, fix_path: false) if route.app.engine?
112
-
113
- return route
117
+ if route.app.engine?
118
+ route, path = find_rails_route(request, app: route.app.app, path_prefix: path)
119
+ next if route.nil?
120
+ end
121
+ return [route, path_prefix + path]
114
122
  end
115
123
  end
116
- raise "No route matched for #{request.request_method} #{request.path_info}"
124
+ nil
117
125
  end
118
126
 
119
127
  # workaround to get real request parameters
@@ -15,7 +15,6 @@ class RSpec::OpenAPI::ResultRecorder
15
15
  RSpec::OpenAPI::SchemaMerger.merge!(spec, schema)
16
16
  new_from_zero = {}
17
17
  records.each do |record|
18
- File.open('/tmp/records', 'a') { |f| f.puts record.to_yaml }
19
18
  begin
20
19
  record_schema = RSpec::OpenAPI::SchemaBuilder.build(record)
21
20
  RSpec::OpenAPI::SchemaMerger.merge!(spec, record_schema)
@@ -27,6 +27,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
27
27
  record.http_method.downcase => {
28
28
  summary: record.summary,
29
29
  tags: record.tags,
30
+ operationId: record.operation_id,
30
31
  security: record.security,
31
32
  parameters: build_parameters(record),
32
33
  requestBody: build_request_body(record),
@@ -73,6 +74,7 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
73
74
  parameters << {
74
75
  name: build_parameter_name(key, value),
75
76
  in: 'query',
77
+ required: record.required_request_params.include?(key),
76
78
  schema: build_property(try_cast(value)),
77
79
  example: (try_cast(value) if example_enabled?),
78
80
  }.compact
@@ -191,10 +193,23 @@ class << RSpec::OpenAPI::SchemaBuilder = Object.new
191
193
 
192
194
  def adjust_params(value)
193
195
  value.each do |key, v|
194
- if v.is_a?(ActionDispatch::Http::UploadedFile)
196
+ case v
197
+ when ActionDispatch::Http::UploadedFile
195
198
  value[key] = v.original_filename
196
- elsif v.is_a?(Hash)
199
+ when Hash
197
200
  adjust_params(v)
201
+ when Array
202
+ result = v.map do |item|
203
+ case item
204
+ when ActionDispatch::Http::UploadedFile
205
+ item.original_filename
206
+ when Hash
207
+ adjust_params(item)
208
+ else
209
+ item
210
+ end
211
+ end
212
+ value[key] = result
198
213
  end
199
214
  end
200
215
  end
@@ -47,7 +47,7 @@ class << RSpec::OpenAPI::SchemaCleaner = Object.new
47
47
  paths_to_objects.each do |path|
48
48
  parent = base.dig(*path.take(path.length - 1))
49
49
  # "required" array must not be present if empty
50
- parent.delete('required') if parent['required'].empty?
50
+ parent.delete('required') if parent['required'] && parent['required'].empty?
51
51
  end
52
52
  end
53
53
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module OpenAPI
5
- VERSION = '0.8.1'
5
+ VERSION = '0.9.0'
6
6
  end
7
7
  end
data/lib/rspec/openapi.rb CHANGED
@@ -10,10 +10,8 @@ require 'rspec/openapi/schema_file'
10
10
  require 'rspec/openapi/schema_merger'
11
11
  require 'rspec/openapi/schema_cleaner'
12
12
 
13
- if ENV['OPENAPI']
14
- require 'rspec/openapi/minitest_hooks'
15
- require 'rspec/openapi/rspec_hooks'
16
- end
13
+ require 'rspec/openapi/minitest_hooks' if Object.const_defined?('Minitest')
14
+ require 'rspec/openapi/rspec_hooks' if ENV['OPENAPI'] && Object.const_defined?('RSpec')
17
15
 
18
16
  module RSpec::OpenAPI
19
17
  @path = 'doc/openapi.yaml'
data/scripts/rspec ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # (The MIT License)
5
+ # Copyright (c) 2012 Chad Humphries, David Chelimsky, Myron Marston
6
+ # Copyright (c) 2009 Chad Humphries, David Chelimsky
7
+ # Copyright (c) 2006 David Chelimsky, The RSpec Development Team
8
+ # Copyright (c) 2005 Steven Baker
9
+
10
+ require 'rspec/core'
11
+ RSpec::Core::Runner.invoke
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # (The MIT License)
5
+ # Copyright (c) 2012 Chad Humphries, David Chelimsky, Myron Marston
6
+ # Copyright (c) 2009 Chad Humphries, David Chelimsky
7
+ # Copyright (c) 2006 David Chelimsky, The RSpec Development Team
8
+ # Copyright (c) 2005 Steven Baker
9
+
10
+ # Turn on verbose to make sure we not generating any ruby warning
11
+ $VERBOSE = true
12
+
13
+ # So our "did they run the rspec command?" detection logic thinks
14
+ # that we run `rspec`.
15
+ $0 = 'rspec'
16
+
17
+ # This is necessary for when `--standalone` is being used.
18
+ $LOAD_PATH.unshift File.expand_path '../bundle', __dir__
19
+
20
+ # For the travis build we put the bundle directory up a directory
21
+ # so it can be shared among the repos for faster bundle installs.
22
+ $LOAD_PATH.unshift File.expand_path '../../bundle', __dir__
23
+
24
+ require 'bundler/setup'
25
+
26
+ # To use simplecov while running rspec-core's test suite, we must
27
+ # load simplecov _before_ loading any of rspec-core's files.
28
+ # So, this executable exists purely as a wrapper script that
29
+ # first loads simplecov, and then loads rspec.
30
+ begin
31
+ # Simplecov emits some ruby warnings when loaded, so silence them.
32
+ old_verbose = $VERBOSE
33
+ $VERBOSE = false
34
+
35
+ unless (ENV.fetch('COVERAGE', nil) && ENV['COVERAGE'].empty?) || RUBY_VERSION < '1.9.3'
36
+ require 'simplecov'
37
+
38
+ SimpleCov.start do
39
+ # Flaky :(
40
+ # enable_coverage :branch
41
+ end
42
+ end
43
+ rescue LoadError
44
+ # simplecov is not available
45
+ ensure
46
+ $VERBOSE = old_verbose
47
+ end
48
+
49
+ load File.expand_path('rspec', __dir__)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-openapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takashi Kokubun
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-04-29 00:00:00.000000000 Z
12
+ date: 2023-10-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -54,6 +54,7 @@ files:
54
54
  - ".rspec"
55
55
  - ".rubocop.yml"
56
56
  - ".rubocop_todo.yml"
57
+ - ".simplecov_spawn.rb"
57
58
  - CHANGELOG.md
58
59
  - Gemfile
59
60
  - LICENSE.txt
@@ -76,6 +77,8 @@ files:
76
77
  - lib/rspec/openapi/schema_merger.rb
77
78
  - lib/rspec/openapi/version.rb
78
79
  - rspec-openapi.gemspec
80
+ - scripts/rspec
81
+ - scripts/rspec_with_simplecov
79
82
  - test.png
80
83
  homepage: https://github.com/exoego/rspec-openapi
81
84
  licenses: