Skip to content
Closed
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- Breaking: Trailing slashes are no longer ignored in dynamic paths. See [#403](https://github.com/ahx/openapi_first/issues/403).
Before this change `GET /things/24/` matched `/things/{id}:`, but it no longer does.
- Breaking: Failure type `:response_not_found` was split into two more specific types `:response_content_type_not_found` and `:response_status_not_found`. This should be mostly internal stuff. So if your custom error response used `response_not_found`, you will have to adapt.
- `OpenapiFirst::Test.app` now returns an instance of `OpenapiFirst::Test::App`, instead of `Rack::Builer` and delegates methods other than `#call` to the original app. This wrapper adds validated requests, responses to the rack env at `env[OpenapiFirst::Test::REQUEST]`, `env[OpenapiFirst::Test::RESPONSE]`

### Added
- The Coverage feature in `OpenapiFirst::Test` now supports parallel tests via a DRB client/sever. Thanks to Richard! See [#394](https://github.com/ahx/openapi_first/issues/394).
Expand Down
8 changes: 3 additions & 5 deletions lib/openapi_first/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module Test
autoload :Coverage, 'openapi_first/test/coverage'
autoload :Methods, 'openapi_first/test/methods'
autoload :Observe, 'openapi_first/test/observe'
autoload :App, 'openapi_first/test/app'
extend Registry

class CoverageError < Error; end
Expand Down Expand Up @@ -89,11 +90,8 @@ def self.report_coverage(formatter: Coverage::TerminalFormatter, **)
# the middlewares or manual request, response validation.
def self.app(app, spec: nil, api: :default)
spec ||= self[api]
Rack::Builder.app do
use OpenapiFirst::Middlewares::ResponseValidation, spec:, raise_error: false
use OpenapiFirst::Middlewares::RequestValidation, spec:, raise_error: false, error_response: false
run app
end

App.new(app, api: spec)
end

def self.install
Expand Down
30 changes: 30 additions & 0 deletions lib/openapi_first/test/app.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require_relative 'observe'

module OpenapiFirst
module Test
REQUEST = 'openapi.test.request'
RESPONSE = 'openapi.test.response'

# A wrapper of the original app
# with silent request/response validation to track requests/responses.
class App < SimpleDelegator
def initialize(app, api:)
super(app)
@app = app
@definition = Test[api]
end

def call(env)
request = Rack::Request.new(env)
env[Test::REQUEST] = @definition.validate_request(request, raise_error: false)
response = @app.call(env)
status, headers, body = response
env[Test::RESPONSE] =
@definition.validate_response(request, Rack::Response[status, headers, body], raise_error: false)
response
end
end
end
end
6 changes: 3 additions & 3 deletions spec/test/methods_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def last_response = Rack::Response.new

get('/')

expect(last_request.env[OpenapiFirst::REQUEST].operation_id).to eq('example#root')
expect(last_request.env[OpenapiFirst::Test::REQUEST].operation_id).to eq('example#root')
end
end
end
Expand Down Expand Up @@ -100,7 +100,7 @@ def last_response = Rack::Response.new
test_app = minitest_class.new(1).app
env = Rack::MockRequest.env_for('/')
expect(test_app.call(env)).to eq(Rack::Response.new('hello').finish)
expect(env[OpenapiFirst::REQUEST]).to be_valid
expect(env[OpenapiFirst::Test::REQUEST]).to be_valid
end

it 'adds an app method that wraps the app for a specific API' do
Expand All @@ -115,7 +115,7 @@ def last_response = Rack::Response.new
test_app = minitest_class.new(1).app
env = Rack::MockRequest.env_for('/')
expect(test_app.call(env)).to eq(Rack::Response.new('hello').finish)
expect(env[OpenapiFirst::REQUEST]).to be_valid
expect(env[OpenapiFirst::Test::REQUEST]).to be_valid
end

it 'adds an assert_api_conform method that targets the specified API' do
Expand Down
27 changes: 23 additions & 4 deletions spec/test_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -302,11 +302,18 @@ def call(_env)
let(:filename) { './spec/data/dice.yaml' }
let(:oad) { OpenapiFirst.load(filename) }

let(:original_app) do
Class.new do
def call(_env)
[200, { 'content-type' => 'application/json' }, ['1']]
end

def color = 'red'
end.new
end

let(:app) do
described_class.app(
->(_env) { [200, { 'content-type' => 'application/json' }, ['1']] },
spec: oad
)
described_class.app(original_app, api: oad)
end

include Rack::Test::Methods
Expand All @@ -326,6 +333,18 @@ def call(_env)
expect(called).to eq(%i[request response])
end

it 'keeps instance methods of the original app intact' do
expect(app.color).to eq('red')
end

it 'adds last validated request, reponse in the rack env' do
post '/roll'

last_env = last_request.env
expect(last_env[described_class::REQUEST]).to be_a(OpenapiFirst::ValidatedRequest)
expect(last_env[described_class::RESPONSE]).to be_a(OpenapiFirst::ValidatedResponse)
end

context 'when using registered OAD' do
let(:app) do
described_class.app(
Expand Down