Skip to content

Commit

Permalink
Merge commit '6a1fbf413927d6397458e207b91865d49b57b1df' into fix-engi…
Browse files Browse the repository at this point in the history
…ne-path-resolution

# Conflicts:
#	spec/rails/doc/openapi.yaml
  • Loading branch information
pcoliveira committed Aug 16, 2023
2 parents 288b7c3 + 6a1fbf4 commit 1b722ed
Show file tree
Hide file tree
Showing 27 changed files with 674 additions and 84 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,19 @@ jobs:
rails: 6.1.6
- ruby: ruby:3.1
rails: 7.0.3
coverage: coverage
env:
RAILS_VERSION: ${{ matrix.rails == '' && '6.1.6' || matrix.rails }}
COVERAGE: ${{ matrix.coverage || '' }}
steps:
- uses: actions/checkout@v2
- name: bundle install
run: bundle install -j$(nproc) --retry 3
- run: bundle exec rspec
timeout-minutes: 1
- name: Upload coverage reports
uses: codecov/codecov-action@v3
if: matrix.coverage == 'coverage'
with:
fail_ci_if_error: true
files: ./coverage/coverage.xml
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Style/ClassAndModuleChildren:
EnforcedStyle: compact
Exclude:
- 'lib/rspec/openapi/version.rb'
Layout/FirstArrayElementIndentation:
EnforcedStyle: consistent
Metrics/BlockLength:
Exclude:
- 'spec/**/*'
15 changes: 15 additions & 0 deletions .simplecov_spawn.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

unless ENV['COVERAGE'] && ENV['COVERAGE'].empty?
require 'simplecov'
require 'simplecov-cobertura'

SimpleCov.at_fork.call(Process.pid)
SimpleCov.formatter SimpleCov::Formatter::MultiFormatter.new([
SimpleCov::Formatter::CoberturaFormatter,
])
SimpleCov.start do
add_filter '/spec/'
add_filter '/scripts/'
end
end
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ gem 'roda'
gem 'rspec-rails'

group :test do
gem 'simplecov'
gem 'simplecov-cobertura'
gem 'super_diff'
end

Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# rspec-openapi ![test](https://github.com/k0kubun/rspec-openapi/workflows/test/badge.svg)
# 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)

Generate OpenAPI schema from RSpec request specs.

Expand Down Expand Up @@ -308,6 +308,7 @@ Some examples' attributes can be overwritten via RSpec metadata options. Example
summary: 'list all posts',
description: 'list all posts ordered by pub_date',
tags: %w[v1 posts],
required_request_params: %w[limit],
security: [{"MyToken" => []}],
} do
# ...
Expand Down Expand Up @@ -344,6 +345,12 @@ It should work with both classes inheriting from `ActionDispatch::IntegrationTes

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.

Run minitest with OPENAPI=1 to generate `doc/openapi.yaml` for your request specs.

```bash
$ OPENAPI=1 bundle exec rails t
```

## Links

Existing RSpec plugins which have OpenAPI integration:
Expand Down
6 changes: 2 additions & 4 deletions lib/rspec/openapi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@
require 'rspec/openapi/schema_merger'
require 'rspec/openapi/schema_cleaner'

if ENV['OPENAPI']
require 'rspec/openapi/minitest_hooks'
require 'rspec/openapi/rspec_hooks'
end
require 'rspec/openapi/minitest_hooks' if Object.const_defined?('Minitest')
require 'rspec/openapi/rspec_hooks' if ENV['OPENAPI'] && Object.const_defined?('RSpec')

module RSpec::OpenAPI
@path = 'doc/openapi.yaml'
Expand Down
20 changes: 12 additions & 8 deletions lib/rspec/openapi/minitest_hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@
module RSpec::OpenAPI::Minitest
Example = Struct.new(:context, :description, :metadata, :file_path)

module TestPatch
def self.prepended(base)
base.extend(ClassMethods)
end

module RunPatch
def run(*args)
result = super
if ENV['OPENAPI'] && self.class.openapi?
Expand All @@ -22,6 +18,12 @@ def run(*args)
end
result
end
end

module ActivateOpenApiClassMethods
def self.prepended(base)
base.extend(ClassMethods)
end

module ClassMethods
def openapi?
Expand All @@ -35,10 +37,12 @@ def openapi!
end
end

Minitest::Test.prepend RSpec::OpenAPI::Minitest::TestPatch
Minitest::Test.prepend RSpec::OpenAPI::Minitest::ActivateOpenApiClassMethods

if ENV['OPENAPI']
Minitest::Test.prepend RSpec::OpenAPI::Minitest::RunPatch

Minitest.after_run do
if ENV['OPENAPI']
Minitest.after_run do
result_recorder = RSpec::OpenAPI::ResultRecorder.new(RSpec::OpenAPI.path_records)
result_recorder.record_results!
puts result_record.error_message if result_recorder.errors?
Expand Down
1 change: 1 addition & 0 deletions lib/rspec/openapi/record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
:path_params, # @param [Hash] - {:controller=>"v1/statuses", :action=>"create", :id=>"1"}
:query_params, # @param [Hash] - {:query=>"string"}
:request_params, # @param [Hash] - {:request=>"body"}
:required_request_params, # @param [Array] - ["param1", "param2"]
:request_content_type, # @param [String] - "application/json"
:request_headers, # @param [Array] - [["header_key1", "header_value1"], ["header_key2", "header_value2"]]
:summary, # @param [String] - "v1/statuses #show"
Expand Down
7 changes: 5 additions & 2 deletions lib/rspec/openapi/record_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ def build(context, example:)
request, response = extract_request_response(context)
return if request.nil?

path, summary, tags, raw_path_params, description, security = extract_request_attributes(request, example)
path, summary, tags, required_request_params, raw_path_params, description, security =
extract_request_attributes(request, example)

request_headers, response_headers = extract_headers(request, response)

Expand All @@ -21,6 +22,7 @@ def build(context, example:)
path_params: raw_path_params,
query_params: request.query_parameters,
request_params: raw_request_params(request),
required_request_params: required_request_params,
request_content_type: request.media_type,
request_headers: request_headers,
summary: summary,
Expand Down Expand Up @@ -61,6 +63,7 @@ def extract_request_attributes(request, example)
metadata = example.metadata[:openapi] || {}
summary = metadata[:summary]
tags = metadata[:tags]
required_request_params = metadata[:required_request_params] || []
security = metadata[:security]
description = metadata[:description] || RSpec::OpenAPI.description_builder.call(example)
raw_path_params = request.path_parameters
Expand All @@ -83,7 +86,7 @@ def extract_request_attributes(request, example)
raw_path_params = raw_path_params.slice(*(raw_path_params.keys - %i[controller action format]))
end
summary ||= "#{request.method} #{path}"
[path, summary, tags, raw_path_params, description, security]
[path, summary, tags, required_request_params, raw_path_params, description, security]
end

def extract_request_response(context)
Expand Down
1 change: 0 additions & 1 deletion lib/rspec/openapi/result_recorder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ def record_results!
RSpec::OpenAPI::SchemaMerger.merge!(spec, schema)
new_from_zero = {}
records.each do |record|
File.open('/tmp/records', 'a') { |f| f.puts record.to_yaml }
begin
record_schema = RSpec::OpenAPI::SchemaBuilder.build(record)
RSpec::OpenAPI::SchemaMerger.merge!(spec, record_schema)
Expand Down
18 changes: 16 additions & 2 deletions lib/rspec/openapi/schema_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def build_parameters(record)
parameters << {
name: build_parameter_name(key, value),
in: 'query',
required: record.required_request_params.include?(key),
schema: build_property(try_cast(value)),
example: (try_cast(value) if example_enabled?),
}.compact
Expand Down Expand Up @@ -191,10 +192,23 @@ def build_example(value)

def adjust_params(value)
value.each do |key, v|
if v.is_a?(ActionDispatch::Http::UploadedFile)
case v
when ActionDispatch::Http::UploadedFile
value[key] = v.original_filename
elsif v.is_a?(Hash)
when Hash
adjust_params(v)
when Array
result = v.map do |item|
case item
when ActionDispatch::Http::UploadedFile
item.original_filename
when Hash
adjust_params(item)
else
item
end
end
value[key] = result
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rspec/openapi/schema_cleaner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def cleanup_empty_required_array!(base)
paths_to_objects.each do |path|
parent = base.dig(*path.take(path.length - 1))
# "required" array must not be present if empty
parent.delete('required') if parent['required'].empty?
parent.delete('required') if parent['required'] && parent['required'].empty?
end
end

Expand Down
11 changes: 11 additions & 0 deletions scripts/rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

# (The MIT License)
# Copyright (c) 2012 Chad Humphries, David Chelimsky, Myron Marston
# Copyright (c) 2009 Chad Humphries, David Chelimsky
# Copyright (c) 2006 David Chelimsky, The RSpec Development Team
# Copyright (c) 2005 Steven Baker

require 'rspec/core'
RSpec::Core::Runner.invoke
49 changes: 49 additions & 0 deletions scripts/rspec_with_simplecov
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

# (The MIT License)
# Copyright (c) 2012 Chad Humphries, David Chelimsky, Myron Marston
# Copyright (c) 2009 Chad Humphries, David Chelimsky
# Copyright (c) 2006 David Chelimsky, The RSpec Development Team
# Copyright (c) 2005 Steven Baker

# Turn on verbose to make sure we not generating any ruby warning
$VERBOSE = true

# So our "did they run the rspec command?" detection logic thinks
# that we run `rspec`.
$0 = 'rspec'

# This is necessary for when `--standalone` is being used.
$LOAD_PATH.unshift File.expand_path '../bundle', __dir__

# For the travis build we put the bundle directory up a directory
# so it can be shared among the repos for faster bundle installs.
$LOAD_PATH.unshift File.expand_path '../../bundle', __dir__

require 'bundler/setup'

# To use simplecov while running rspec-core's test suite, we must
# load simplecov _before_ loading any of rspec-core's files.
# So, this executable exists purely as a wrapper script that
# first loads simplecov, and then loads rspec.
begin
# Simplecov emits some ruby warnings when loaded, so silence them.
old_verbose = $VERBOSE
$VERBOSE = false

unless (ENV.fetch('COVERAGE', nil) && ENV['COVERAGE'].empty?) || RUBY_VERSION < '1.9.3'
require 'simplecov'

SimpleCov.start do
# Flaky :(
# enable_coverage :branch
end
end
rescue LoadError
# simplecov is not available
ensure
$VERBOSE = old_verbose
end

load File.expand_path('rspec', __dir__)
47 changes: 38 additions & 9 deletions spec/integration_tests/rails_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
ENV['RAILS_ENV'] ||= 'test'
ENV['OPENAPI_OUTPUT'] ||= 'yaml'

require File.expand_path('../rails/config/environment', __dir__)

require 'minitest/autorun'
require File.expand_path('../rails/config/environment', __dir__)

RSpec::OpenAPI.request_headers = %w[X-Authorization-Token]
RSpec::OpenAPI.response_headers = %w[X-Cursor]
Expand Down Expand Up @@ -104,13 +103,7 @@ class TablesUpdateTest < ActionDispatch::IntegrationTest
openapi!

test 'returns a table' do
png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP
QBoAAAAASUVORK5CYII='.unpack1('m')
File.binwrite('test.png', png)
image = Rack::Test::UploadedFile.new('test.png', 'image/png')
patch '/tables/1', headers: { authorization: 'k0kubun' }, params: {
nested: { image: image, caption: 'Some caption' },
}
patch '/tables/1', headers: { authorization: 'k0kubun' }, params: { name: 'test' }
assert_response 200
end
end
Expand Down Expand Up @@ -141,6 +134,42 @@ class ImageTest < ActionDispatch::IntegrationTest
get '/images'
assert_response 200
end

test 'returns a image payload with upload' do
png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP
QBoAAAAASUVORK5CYII='.unpack1('m')
File.binwrite('test.png', png)
image = Rack::Test::UploadedFile.new('test.png', 'image/png')
post '/images/upload', params: { image: image }
assert_response 200
end

test 'returns a image payload with upload nested' do
png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP
QBoAAAAASUVORK5CYII='.unpack1('m')
File.binwrite('test.png', png)
image = Rack::Test::UploadedFile.new('test.png', 'image/png')
post '/images/upload_nested', params: { nested_image: { image: image, caption: 'Some caption' } }
assert_response 200
end

test 'returns a image payload with upload multiple' do
png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP
QBoAAAAASUVORK5CYII='.unpack1('m')
File.binwrite('test.png', png)
image = Rack::Test::UploadedFile.new('test.png', 'image/png')
post '/images/upload_multiple', params: { images: [image, image] }
assert_response 200
end

test 'returns a image payload with upload multiple nested' do
png = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAAAAADhZOFXAAAADklEQVQIW2P4DwUMlDEA98A/wTjP
QBoAAAAASUVORK5CYII='.unpack1('m')
File.binwrite('test.png', png)
image = Rack::Test::UploadedFile.new('test.png', 'image/png')
post '/images/upload_multiple_nested', params: { images: [{ image: image }, { image: image }] }
assert_response 200
end
end

class ExtraRoutesTest < ActionDispatch::IntegrationTest
Expand Down
2 changes: 1 addition & 1 deletion spec/integration_tests/roda_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
require_relative '../roda/roda_app'
require 'json'
require 'rack/test'
require 'rspec/openapi'
require 'minitest/autorun'
require 'rspec/openapi'

ENV['OPENAPI_OUTPUT'] ||= 'yaml'

Expand Down
6 changes: 6 additions & 0 deletions spec/minitest/rack_test_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,10 @@
expect(new_yaml).to eq org_yaml
end
end

describe 'with disabled OpenAPI generation' do
it 'can run tests' do
minitest 'spec/integration_tests/roda_test.rb'
end
end
end
6 changes: 6 additions & 0 deletions spec/minitest/rails_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@
expect(new_yaml).to eq org_yaml
end
end

describe 'with disabled OpenAPI generation' do
it 'can run tests' do
minitest 'spec/integration_tests/rails_test.rb'
end
end
end
Loading

0 comments on commit 1b722ed

Please sign in to comment.