Skip to content
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

## Unreleased

## 2.9.0

- OpenapiFirst::Test now raises an error for unknown requests. You can deactivate with:

```ruby
OpenapiFirst::Test.setup do |test|
# ...
test.ignore_unknown_request = true
end
```

- NotFoundError#message now includes the requested path

## 2.8.0

### OpenapiFirst::Test is now stricter and more configurable
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
openapi_first (2.8.0)
openapi_first (2.9.0)
hana (~> 1.3)
json_schemer (>= 2.1, < 3.0)
openapi_parameters (>= 0.5.1, < 2.0)
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ Here is how to set it up:

(✷1): It does not matter what method of openapi_first you use to validate requests/responses. Instead of using `OpenapiFirstTest.app` to wrap your application, you could also use the [middlewares](#rack-middlewares) or [test assertion method](#test-assertions), but you would have to do that for all requests/responses defined in your API description to make coverage work.

OpenapiFirst' request validation raises an error when a request is not defined. You can deactivate this during testing:

```ruby
OpenapiFirst::Test.setup do |test|
test.ignore_unknown_requests = true
end
```

## Rack Middlewares

### Request validation
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: ..
specs:
openapi_first (2.8.0)
openapi_first (2.9.0)
hana (~> 1.3)
json_schemer (>= 2.1, < 3.0)
openapi_parameters (>= 0.5.1, < 2.0)
Expand Down
2 changes: 1 addition & 1 deletion lib/openapi_first/failure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module OpenapiFirst
# This returned in ValidatedRequest#error and ValidatedResponse#error.
class Failure
TYPES = {
not_found: [NotFoundError, 'Request path is not defined.'],
not_found: [NotFoundError, 'Not found.'],
method_not_allowed: [RequestInvalidError, 'Request method is not defined.'],
unsupported_media_type: [RequestInvalidError, 'Request content type is not defined.'],
invalid_body: [RequestInvalidError, 'Request body invalid:'],
Expand Down
5 changes: 4 additions & 1 deletion lib/openapi_first/router.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ def add_response(response, request_method:, path:, status:, response_content_typ
# Return all request objects that match the given path and request method
def match(request_method, path, content_type: nil)
path_item, params = find_path_item(path)
return NOT_FOUND unless path_item
unless path_item
message = "Request path #{path} is not defined in API description."
return NOT_FOUND.with(error: Failure.new(:not_found, message:))
end

contents = path_item.dig(request_method, :requests)
return NOT_FOUND.with(error: Failure.new(:method_not_allowed)) unless contents
Expand Down
11 changes: 11 additions & 0 deletions lib/openapi_first/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ def self.install

OpenapiFirst.configure do |config|
@after_request_validation = config.after_request_validation do |validated_request, oad|
raise validated_request.error.exception if raise_request_error?(validated_request)

configuration.ignore_unknown_requests && validated_request.known?

Coverage.track_request(validated_request, oad)
end

Expand All @@ -114,6 +118,13 @@ def self.install
@installed = true
end

def self.raise_request_error?(validated_request)
return false if validated_request.valid?
return true if validated_request.known?

!configuration.ignore_unknown_requests
end

def self.raise_response_error?(validated_response)
configuration.response_raise_error && !configuration.ignored_unknown_status.include?(validated_response.status)
end
Expand Down
4 changes: 3 additions & 1 deletion lib/openapi_first/test/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def initialize
@response_raise_error = true
@ignored_unknown_status = [404]
@report_coverage = true
@ignore_unknown_requests = false
@registry = {}
@apps = {}
end
Expand All @@ -28,7 +29,8 @@ def observe(app, api: :default)
@apps[api] = app
end

attr_accessor :coverage_formatter_options, :coverage_formatter, :response_raise_error, :minimum_coverage
attr_accessor :coverage_formatter_options, :coverage_formatter, :response_raise_error, :minimum_coverage,
:ignore_unknown_requests
attr_reader :registry, :apps, :report_coverage, :ignored_unknown_status

# Configure report coverage
Expand Down
2 changes: 1 addition & 1 deletion lib/openapi_first/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module OpenapiFirst
VERSION = '2.8.0'
VERSION = '2.9.0'
end
39 changes: 39 additions & 0 deletions spec/test_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,45 @@ def call(_env)
end
end

describe 'handling unknown requests paths' do
let(:app) do
described_class.app(
->(_env) { [200, { 'content-type' => 'application/json' }, ['foo']] },
spec: definition
)
end

before(:each) do
described_class.setup do |test|
test.register(definition)
test.report_coverage = false
end
end

it 'raises an error' do
expect do
app.call(Rack::MockRequest.env_for('/unknown'))
end.to raise_error(OpenapiFirst::NotFoundError)
end

context 'with ignore_unknown_requests = true' do
before(:each) do
described_class.uninstall
described_class.setup do |test|
test.register(definition)
test.ignore_unknown_requests = true
test.report_coverage = false
end
end

it 'does not raise an error' do
expect do
app.call(Rack::MockRequest.env_for('/unknown'))
end.not_to raise_error
end
end
end

describe 'handling invalid responses' do
let(:definition) do
OpenapiFirst.parse(YAML.load(%(
Expand Down