Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Minitest support #90

Merged
merged 6 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,34 @@ Some examples' attributes can be overwritten via RSpec metadata options. Example

**NOTE**: `description` key will override also the one provided by `RSpec::OpenAPI.description_builder` method.

## Experimental minitest support

Even if you are not using `rspec` this gem might help you with its experimental support for `minitest`.

Example:

```rb
class TablesTest < ActionDispatch::IntegrationTest
openapi!

test "GET /index returns a list of tables" do
get '/tables', params: { page: '1', per: '10' }, headers: { authorization: 'k0kubun' }
assert_response :success
end

test "GET /index does not return tables if unauthorized" do
get '/tables'
assert_response :unauthorized
end

# ...
end
```

It should work with both classes inheriting from `ActionDispatch::IntegrationTest` and with classes using `Rack::Test` directly, as long as you call `openapi!` in your test class.

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.

## Links

Existing RSpec plugins which have OpenAPI integration:
Expand Down
18 changes: 16 additions & 2 deletions lib/rspec/openapi.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
require 'rspec/openapi/version'
require 'rspec/openapi/hooks' if ENV['OPENAPI']
require 'rspec/openapi/components_updater'
require 'rspec/openapi/default_schema'
require 'rspec/openapi/record_builder'
require 'rspec/openapi/result_recorder'
require 'rspec/openapi/schema_builder'
require 'rspec/openapi/schema_file'
require 'rspec/openapi/schema_merger'
require 'rspec/openapi/schema_cleaner'

if ENV['OPENAPI']
require 'rspec/openapi/minitest_hooks'
require 'rspec/openapi/rspec_hooks'
end

module RSpec::OpenAPI
@path = 'doc/openapi.yaml'
Expand All @@ -13,6 +25,7 @@ module RSpec::OpenAPI
@security_schemes = []
@example_types = %i[request]
@response_headers = []
@path_records = Hash.new { |h, k| h[k] = [] }

class << self
attr_accessor :path,
Expand All @@ -25,6 +38,7 @@ class << self
:servers,
:security_schemes,
:example_types,
:response_headers
:response_headers,
:path_records
end
end
51 changes: 0 additions & 51 deletions lib/rspec/openapi/hooks.rb

This file was deleted.

48 changes: 48 additions & 0 deletions lib/rspec/openapi/minitest_hooks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
require 'minitest'

module RSpec
module OpenAPI
module Minitest
class Example < Struct.new(:context, :description, :metadata, :file_path) ; end

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

def run(*args)
result = super
if ENV['OPENAPI'] && self.class.openapi?
file_path = self.method(name).source_location.first
human_name = name.sub(/^test_/, "").gsub(/_/, " ")
example = Example.new(self, human_name, {}, file_path)
path = RSpec::OpenAPI.path.yield_self { |p| p.is_a?(Proc) ? p.call(example) : p }
record = RSpec::OpenAPI::RecordBuilder.build(self, example: example)
RSpec::OpenAPI.path_records[path] << record if record
end
result
end

module ClassMethods
def openapi?
@openapi
end

def openapi!
@openapi = true
end
end
end
end
end
end

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

Minitest.after_run do
if ENV['OPENAPI']
result_recorder = RSpec::OpenAPI::ResultRecorder.new(RSpec::OpenAPI.path_records)
result_recorder.record_results!
puts result_record.error_message if result_recorder.errors?
end
end
2 changes: 1 addition & 1 deletion lib/rspec/openapi/record_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def build(context, example:)
private

def rails?
defined?(Rails) && Rails.application
defined?(Rails) && Rails.respond_to?(:application) && Rails.application
end

def rack_test?(context)
Expand Down
42 changes: 42 additions & 0 deletions lib/rspec/openapi/result_recorder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
class RSpec::OpenAPI::ResultRecorder
def initialize(path_records)
@path_records = path_records
@error_records = {}
end

def record_results!
title = File.basename(Dir.pwd)
@path_records.each do |path, records|
RSpec::OpenAPI::SchemaFile.new(path).edit do |spec|
schema = RSpec::OpenAPI::DefaultSchema.build(title)
schema[:info].merge!(RSpec::OpenAPI.info)
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)
RSpec::OpenAPI::SchemaMerger.merge!(new_from_zero, record_schema)
rescue StandardError, NotImplementedError => e # e.g. SchemaBuilder raises a NotImplementedError
@error_records[e] = record # Avoid failing the build
end
end
RSpec::OpenAPI::SchemaCleaner.cleanup!(spec, new_from_zero)
RSpec::OpenAPI::ComponentsUpdater.update!(spec, new_from_zero)
end
end
end

def errors?
@error_records.any?
end

def error_message
<<~EOS
RSpec::OpenAPI got errors building #{@error_records.size} requests

#{@error_records.map {|e, record| "#{e.inspect}: #{record.inspect}" }.join("\n")}
EOS
end
end
19 changes: 19 additions & 0 deletions lib/rspec/openapi/rspec_hooks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require 'rspec'

RSpec.configuration.after(:each) do |example|
if RSpec::OpenAPI.example_types.include?(example.metadata[:type]) && example.metadata[:openapi] != false
path = RSpec::OpenAPI.path.yield_self { |p| p.is_a?(Proc) ? p.call(example) : p }
record = RSpec::OpenAPI::RecordBuilder.build(self, example: example)
RSpec::OpenAPI.path_records[path] << record if record
end
end

RSpec.configuration.after(:suite) do
result_recorder = RSpec::OpenAPI::ResultRecorder.new(RSpec::OpenAPI.path_records)
result_recorder.record_results!
if result_recorder.errors?
error_message = result_recorder.error_message
colorizer = ::RSpec::Core::Formatters::ConsoleCodes
RSpec.configuration.reporter.message colorizer.wrap(error_message, :failure)
end
end
Loading