Skip to content

Commit 4d4f0ac

Browse files
authored
Merge pull request #393 from ahx/ignore_all_unknown_status
Add `Test::Configuration#ignore_all_unknown_status = true`
2 parents 51997b8 + a8970ec commit 4d4f0ac

9 files changed

Lines changed: 74 additions & 34 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
## Unreleased
44

5-
- Add `OpenapiFirst::Test::Configuration#ignore_unknown_responses=` (use only temporarily when working with an unmaintained OAD)
5+
- Add `OpenapiFirst::Test::Configuration#ignore_all_unknown_status=`. When this is set to true, it won't complain about undefined response statuses it sees during a test run. Use with caution.
6+
- 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.
67

78
## 2.11.1
89

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,15 @@ OpenapiFirst::Test.setup do |test|
211211
end
212212
```
213213
214+
Or you can ignore all unknown response status:
215+
216+
```ruby
217+
OpenapiFirst::Test.setup do |test|
218+
# …
219+
test.ignore_all_unknown_status = true
220+
end
221+
```
222+
214223
Exclude certain _responses_ from coverage with `skip_coverage`:
215224
216225
```ruby

lib/openapi_first/failure.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ class Failure
1313
invalid_header: [RequestInvalidError, 'Request header is invalid:'],
1414
invalid_path: [RequestInvalidError, 'Path segment is invalid:'],
1515
invalid_cookie: [RequestInvalidError, 'Cookie value is invalid:'],
16-
response_not_found: [ResponseNotFoundError],
16+
response_content_type_not_found: [ResponseNotFoundError],
17+
response_status_not_found: [ResponseNotFoundError],
1718
invalid_response_body: [ResponseInvalidError, 'Response body is invalid:'],
1819
invalid_response_header: [ResponseInvalidError, 'Response header is invalid:']
1920
}.freeze

lib/openapi_first/router/find_response.rb

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,30 +10,34 @@ module FindResponse
1010

1111
def self.call(responses, status, content_type, request_method:, path:)
1212
contents = find_status(responses, status)
13-
if contents.nil?
14-
message = "Status #{status} is not defined for #{request_method.upcase} #{path}. " \
15-
"Defined statuses are: #{responses.keys.join(', ')}."
16-
return Match.new(error: Failure.new(:response_not_found, message:), response: nil)
17-
end
13+
return response_status_not_found(status:, request_method:, path:, responses:) if contents.nil?
14+
1815
response = FindContent.call(contents, content_type)
19-
return response_not_found(content_type:, contents:, request_method:, path:) unless response
16+
return response_content_type_not_found(content_type:, contents:, request_method:, path:) unless response
2017

2118
Match.new(response:, error: nil)
2219
end
2320

24-
def self.response_not_found(content_type:, contents:, request_method:, path:)
21+
def self.response_content_type_not_found(content_type:, contents:, request_method:, path:)
2522
empty_content = content_type.nil? || content_type.empty?
2623
message =
2724
"Content-Type should be #{contents.keys.join(' or ')}, " \
2825
"but was #{empty_content ? 'empty' : content_type} for " \
2926
"#{request_method.upcase} #{path}"
3027

3128
Match.new(
32-
error: Failure.new(:response_not_found, message:),
29+
error: Failure.new(:response_content_type_not_found, message:),
3330
response: nil
3431
)
3532
end
36-
private_class_method :response_not_found
33+
private_class_method :response_content_type_not_found
34+
35+
def self.response_status_not_found(status:, request_method:, path:, responses:)
36+
message = "Status #{status} is not defined for #{request_method.upcase} #{path}. " \
37+
"Defined statuses are: #{responses.keys.join(', ')}."
38+
Match.new(error: Failure.new(:response_status_not_found, message:), response: nil)
39+
end
40+
private_class_method :response_status_not_found
3741

3842
def self.find_status(responses, status)
3943
# According to OAS status has to be a string,

lib/openapi_first/test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ def self.raise_request_error?(validated_request)
123123
!configuration.ignore_unknown_requests
124124
end
125125

126-
def self.raise_response_error?(validated_response)
127-
configuration.response_raise_error && !configuration.ignore_response?(validated_response)
126+
def self.raise_response_error?(invalid_response)
127+
configuration.response_raise_error && !configuration.ignore_response?(invalid_response)
128128
end
129129

130130
def self.uninstall

lib/openapi_first/test/configuration.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def initialize
1212
@skip_coverage = nil
1313
@response_raise_error = true
1414
@ignored_unknown_status = [404]
15-
@ignore_unknown_responses = false
15+
@ignore_all_unknown_response_status = false
1616
@report_coverage = true
1717
@ignore_unknown_requests = false
1818
@registry = {}
@@ -32,7 +32,7 @@ def observe(app, api: :default)
3232
end
3333

3434
attr_accessor :coverage_formatter_options, :coverage_formatter, :response_raise_error,
35-
:ignore_unknown_requests, :ignore_unknown_responses
35+
:ignore_unknown_requests, :ignore_all_unknown_response_status
3636
attr_reader :registry, :apps, :report_coverage, :ignored_unknown_status, :minimum_coverage
3737

3838
# Configure report coverage
@@ -65,10 +65,14 @@ def skip_coverage(&block)
6565
@skip_coverage = block
6666
end
6767

68+
alias ignore_all_unknown_response_status? ignore_all_unknown_response_status
69+
6870
def ignore_response?(validated_response)
69-
return true if @ignore_unknown_responses
71+
return false if validated_response.known?
72+
73+
return true if ignored_unknown_status.include?(validated_response.status)
7074

71-
ignored_unknown_status.include?(validated_response.status)
75+
ignore_all_unknown_response_status? && validated_response.error.type == :response_status_not_found
7276
end
7377
end
7478
end

spec/router/find_response_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def find(responses, status, content_type)
5757
]
5858
expect(find(responses, 409, 'application/json')).to have_attributes(
5959
response: nil, error: have_attributes(
60-
type: :response_not_found,
60+
type: :response_status_not_found,
6161
message: 'Status 409 is not defined for GET /stations. Defined statuses are: 200, 201.'
6262
)
6363
)
@@ -85,7 +85,7 @@ def find(responses, status, content_type)
8585
message = 'Content-Type should be application/text or application/xml, but was application/json for GET /stations'
8686
expect(find(responses, 200, 'application/json')).to have_attributes(
8787
response: nil,
88-
error: have_attributes(type: :response_not_found, message:)
88+
error: have_attributes(type: :response_content_type_not_found, message:)
8989
)
9090
end
9191
end

spec/test/configuration_spec.rb

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
describe '#ignore_response?' do
2424
let(:valid_response) { double(valid?: true, known?: true, status: 302) }
2525
let(:invalid_response) { double(valid?: false, known?: true, status: 302) }
26-
let(:unknown_response) { double(valid?: false, known?: false, status: 302) }
26+
let(:unknown_response_status) { double(valid?: false, known?: false, status: 302, error: OpenapiFirst::Failure.new(:response_status_not_found)) }
2727

2828
it 'returns false by default for a valid responses' do
2929
expect(configuration.ignore_response?(valid_response)).to eq(false)
@@ -34,22 +34,33 @@
3434
end
3535

3636
it 'returns false by default for an unkonwn responses' do
37-
expect(configuration.ignore_response?(unknown_response)).to eq(false)
37+
expect(configuration.ignore_response?(unknown_response_status)).to eq(false)
3838
end
3939

4040
context 'when status is ignored' do
41-
before { configuration.ignored_unknown_status << invalid_response.status }
41+
before { configuration.ignored_unknown_status << unknown_response_status.status }
4242

43-
it 'returns true' do
44-
expect(configuration.ignore_response?(invalid_response)).to eq(true)
43+
it 'returns true for an unknown response with that status' do
44+
expect(configuration.ignore_response?(unknown_response_status)).to eq(true)
45+
end
46+
47+
it 'returns false for an unknown response with another status' do
48+
unknown_response_status = double(valid?: false, known?: false, status: 409)
49+
expect(configuration.ignore_response?(unknown_response_status)).to eq(false)
4550
end
4651
end
4752

48-
context 'when all responses are ignored' do
49-
before { configuration.ignore_unknown_responses = true }
53+
context 'when all unknown response status are ignored' do
54+
before { configuration.ignore_all_unknown_response_status = true }
55+
56+
it 'returns true for any unknown response status' do
57+
expect(configuration.ignore_response?(unknown_response_status)).to eq(true)
58+
end
59+
60+
it 'returns false for an unknown response with a known status' do
61+
unknown_response_content_type = double(valid?: false, known?: false, status: 302, error: OpenapiFirst::Failure.new(:response_content_type_not_found))
5062

51-
it 'returns true' do
52-
expect(configuration.ignore_response?(invalid_response)).to eq(true)
63+
expect(configuration.ignore_response?(unknown_response_content_type)).to eq(false)
5364
end
5465
end
5566
end

spec/test_spec.rb

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -606,10 +606,20 @@ def call(_env)
606606
end
607607
end
608608

609-
it 'raises an error' do
610-
expect do
611-
app.call(Rack::MockRequest.env_for('/roll', method: 'POST'))
612-
end.to raise_error(OpenapiFirst::ResponseNotFoundError)
609+
context 'when response status is unknown' do
610+
it 'raises an error' do
611+
expect do
612+
app.call(Rack::MockRequest.env_for('/roll', method: 'POST'))
613+
end.to raise_error(OpenapiFirst::ResponseNotFoundError)
614+
end
615+
end
616+
617+
context 'when response content-type is unknown' do
618+
it 'raises an error' do
619+
expect do
620+
app.call(Rack::MockRequest.env_for('/roll', method: 'POST'))
621+
end.to raise_error(OpenapiFirst::ResponseNotFoundError)
622+
end
613623
end
614624

615625
context 'with ignored_unknown_status' do
@@ -624,9 +634,9 @@ def call(_env)
624634
end
625635
end
626636

627-
context 'with ignore_unknown_responses = true' do
637+
context 'with ignore_all_unknown_response_status = true' do
628638
before(:each) do
629-
described_class.configuration.ignore_unknown_responses = true
639+
described_class.configuration.ignore_all_unknown_response_status = true
630640
end
631641

632642
it 'does not raise an error' do

0 commit comments

Comments
 (0)