diff --git a/CHANGELOG.md b/CHANGELOG.md
index e0c0cb26..436f67d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ No stricter / less strict request validation. Mostly internal stuff. Plus a Sina
- Removed: `OpenapiFirst::Test::Coverage::TerminalReporter#format`, deprecated since 3.4.0. Use `#report` instead.
#### Added
+- API coverage now reports skipped requests and responses.
- Added: OpenAPI 3.2 documents are accepted, but not fully supported yet. They are handled using the OpenAPI 3.1 rules, so features introduced in 3.2 may be ignored. Loading such a document prints a warning. Operations defined under `additionalOperations` are routed. See #469.
- Added: `OpenapiFirst::Request#parameters` returns the parameters that are defined for a request as `OpenapiFirst::Parameter` objects, which expose `name`, `location`, `schema`, `required?`, `deprecated?`, `style`, `explode?` and `media_type`. It used to return an internal object with a different interface.
- Added: Show all covered endpoints in HTML coverage reporter and filter covered/uncovered endpoints
diff --git a/Gemfile.lock b/Gemfile.lock
index cd265ab5..539afe40 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -273,6 +273,7 @@ GEM
zeitwerk (2.8.2)
PLATFORMS
+ arm64-darwin-23
arm64-darwin-24
arm64-darwin-25
x86_64-linux
diff --git a/lib/openapi_first/test/coverage.rb b/lib/openapi_first/test/coverage.rb
index a7a62f72..40ccfe33 100644
--- a/lib/openapi_first/test/coverage.rb
+++ b/lib/openapi_first/test/coverage.rb
@@ -4,6 +4,7 @@
require_relative 'coverage/tracker'
require_relative 'coverage/covered_request'
require_relative 'coverage/covered_response'
+require_relative 'coverage/skipped_summary'
require 'drb'
module OpenapiFirst
@@ -15,7 +16,15 @@ module Coverage
autoload :TerminalReporter, 'openapi_first/test/coverage/terminal_reporter'
autoload :HtmlReporter, 'openapi_first/test/coverage/html_reporter'
- Result = Data.define(:plans, :coverage)
+ Result = Data.define(:plans, :coverage) do
+ def skipped_requests_count
+ plans.sum(&:skipped_requests_count)
+ end
+
+ def skipped_responses_count
+ plans.sum(&:skipped_responses_count)
+ end
+ end
class << self
def start(skip_response: nil, skip_route: nil)
diff --git a/lib/openapi_first/test/coverage/html_reporter.css b/lib/openapi_first/test/coverage/html_reporter.css
index f9e598f6..dd861653 100644
--- a/lib/openapi_first/test/coverage/html_reporter.css
+++ b/lib/openapi_first/test/coverage/html_reporter.css
@@ -28,6 +28,7 @@
--color-error: #f87171;
--color-info: #7dd3fc;
--color-warning: #fbbf24;
+ --color-skipped: #fb923c;
}
body {
@@ -112,17 +113,36 @@ body:has(#filter-covered:not(:checked)) details.route.is-covered {
body:has(#filter-uncovered:not(:checked)) details.route.is-uncovered {
display: none;
}
+body:has(#filter-skipped-requests:not(:checked)) details.route.is-skipped {
+ display: none;
+}
/* Hide a plan once none of its routes remain visible. */
body:has(#filter-covered:not(:checked))
- details.plan:not(:has(details.route.is-uncovered)) {
- display: none;
-}
+ details.plan:not(:has(details.route.is-uncovered)):not(
+ :has(details.route.is-skipped)
+ ),
body:has(#filter-uncovered:not(:checked))
- details.plan:not(:has(details.route.is-covered)) {
- display: none;
-}
+ details.plan:not(:has(details.route.is-covered)):not(
+ :has(details.route.is-skipped)
+ ),
+body:has(#filter-skipped-requests:not(:checked))
+ details.plan:not(:has(details.route.is-covered)):not(
+ :has(details.route.is-uncovered)
+ ),
body:has(#filter-covered:not(:checked)):has(#filter-uncovered:not(:checked))
+ details.plan:not(:has(details.route.is-skipped)),
+body:has(#filter-covered:not(:checked)):has(
+ #filter-skipped-requests:not(:checked)
+ )
+ details.plan:not(:has(details.route.is-uncovered)),
+body:has(#filter-uncovered:not(:checked)):has(
+ #filter-skipped-requests:not(:checked)
+ )
+ details.plan:not(:has(details.route.is-covered)),
+body:has(#filter-covered:not(:checked)):has(
+ #filter-uncovered:not(:checked)
+ ):has(#filter-skipped-requests:not(:checked))
details.plan {
display: none;
}
@@ -261,6 +281,21 @@ li {
color: var(--color-success);
}
+.skipped {
+ color: var(--color-skipped);
+}
+
+.skipped-note,
+.skipped-summary {
+ color: var(--color-skipped);
+ font-size: var(--font-size-xs);
+}
+
+.skipped-summary {
+ margin-inline-start: 0.75rem;
+ white-space: nowrap;
+}
+
.status {
color: var(--text-primary);
min-width: 3ch;
diff --git a/lib/openapi_first/test/coverage/html_reporter.html.erb b/lib/openapi_first/test/coverage/html_reporter.html.erb
index 674e6acc..2cb51943 100644
--- a/lib/openapi_first/test/coverage/html_reporter.html.erb
+++ b/lib/openapi_first/test/coverage/html_reporter.html.erb
@@ -10,9 +10,13 @@
API Coverage: <%= coverage.round(1) %>%
@@ -29,18 +33,23 @@
<% visible_routes(plan).each do |route| -%>
<% requests = request_items(route) -%>
<% responses = response_items(route) -%>
- >
+ >
<%= h route.request_method.upcase %>
<%= h route.path %>
<% status = route_status(route) -%>
- <% if status == :request_problem -%>
- ❌ <%= h explain_unfinished_request(route.requests.first) %>
+ <% if status == :skipped -%>
+ Skipped
+ <% elsif status == :request_problem -%>
+ ❌ <%= h explain_unfinished_request(route.tracked_requests.first) %>
<% elsif status == :responses_problem -%>
⚠️ <%= uncovered_responses_count(route) %> response(s) not covered
<% else -%>
✅
<% end -%>
+ <% if route.skipped_responses.any? && status != :skipped -%>
+ <%= route.skipped_responses.count %> response(s) skipped
+ <% end -%>
<% if (label = route.summary) -%>
<%= h label %>
<% end -%>
@@ -49,7 +58,12 @@
<% unless requests.empty? -%>
<% requests.each do |request| -%>
- <% if request.finished? -%>
+ <% if request.skipped? -%>
+ -
+ <% if request.content_type -%><%= h request.content_type %><% end -%>
+ Skipped, not covered by any test
+
+ <% elsif request.finished? -%>
-
✅
<% if request.content_type -%><%= h request.content_type %><% end -%>
@@ -67,7 +81,13 @@
<% unless responses.empty? -%>
<% responses.each do |response| -%>
- <% if response.finished? -%>
+ <% if response.skipped? -%>
+ -
+ <%= h response.status %>
+ <% if response.content_type -%><%= h response.content_type %><% end -%>
+ Skipped
+
+ <% elsif response.finished? -%>
-
✅
<%= h response.status %>
diff --git a/lib/openapi_first/test/coverage/html_reporter.rb b/lib/openapi_first/test/coverage/html_reporter.rb
index 56134a13..e18012ff 100644
--- a/lib/openapi_first/test/coverage/html_reporter.rb
+++ b/lib/openapi_first/test/coverage/html_reporter.rb
@@ -8,6 +8,8 @@ module Test
module Coverage
# Writes a self-contained HTML coverage report to a file.
class HtmlReporter
+ include SkippedSummary
+
def initialize(output: 'coverage/openapi_coverage.html', verbose: false, logger: Test.logger)
@output = output
@verbose = verbose
@@ -21,11 +23,14 @@ def report(coverage_result)
coverage_result.plans.each do |plan|
next if plan.coverage >= 100
- @logger.info "API validation coverage for #{plan.api_identifier}: #{plan.coverage.round(4)}%"
+ logger.info "API validation coverage for #{plan.api_identifier}: #{plan.coverage.round(4)}%"
end
- @logger.info "API coverage report written to #{@output}"
+ log_skipped_summary(coverage_result)
+ logger.info "API coverage report written to #{@output}"
end
+ private attr_reader :logger
+
TEMPLATE_PATH = File.join(__dir__, 'html_reporter.html.erb')
TEMPLATE = ERB.new(File.read(TEMPLATE_PATH), trim_mode: '-')
TEMPLATE.filename = TEMPLATE_PATH
diff --git a/lib/openapi_first/test/coverage/html_reporter/context.rb b/lib/openapi_first/test/coverage/html_reporter/context.rb
index d2009dab..280153e9 100644
--- a/lib/openapi_first/test/coverage/html_reporter/context.rb
+++ b/lib/openapi_first/test/coverage/html_reporter/context.rb
@@ -12,11 +12,14 @@ class Context
'API Coverage did not detect any API requests for the registered ' \
'API descriptions. Make sure to observe your application using OpenapiFirst::Test.'
- attr_reader :coverage, :plans, :verbose, :generated_at
+ attr_reader :coverage, :plans, :verbose, :generated_at,
+ :skipped_requests_count, :skipped_responses_count
def initialize(coverage_result, verbose, generated_at: Time.now)
@coverage = coverage_result.coverage
@plans = coverage_result.plans
+ @skipped_requests_count = coverage_result.skipped_requests_count
+ @skipped_responses_count = coverage_result.skipped_responses_count
@verbose = verbose
@generated_at = generated_at.strftime('%Y-%m-%d %H:%M:%S %z')
end
@@ -27,6 +30,10 @@ def get_binding # rubocop:disable Naming/AccessorMethodName
binding
end
+ def any_skipped?
+ skipped_requests_count.positive? || skipped_responses_count.positive?
+ end
+
def visible_routes(plan)
plan.routes
end
@@ -36,17 +43,25 @@ def any_request_made?(route)
end
def route_status(route)
- return :request_problem if route.requests.none?(&:finished?)
- return :responses_problem if any_request_made?(route) && route.responses.any? { |r| !r.finished? }
+ return :skipped if route.skipped?
+ return :request_problem if route.tracked_requests.none?(&:finished?)
+ return :responses_problem if any_request_made?(route) && route.tracked_responses.any? { |r| !r.finished? }
:ok
end
+ def route_class(route)
+ return 'is-skipped' if route.skipped?
+
+ route.finished? ? 'is-covered' : 'is-uncovered'
+ end
+
def uncovered_responses_count(route)
- route.responses.count { |r| !r.finished? }
+ route.tracked_responses.count { |r| !r.finished? }
end
def request_items(route)
+ return route.requests if route.skipped?
return [] unless any_request_made?(route) && route.requests.any?(&:content_type)
route.requests
diff --git a/lib/openapi_first/test/coverage/plan.rb b/lib/openapi_first/test/coverage/plan.rb
index 092f8690..e2b17098 100644
--- a/lib/openapi_first/test/coverage/plan.rb
+++ b/lib/openapi_first/test/coverage/plan.rb
@@ -14,14 +14,13 @@ class UnknownRequestError < StandardError; end
def self.for(oad, skip_response: nil, skip_route: nil)
plan = new(definition_key: oad.key, filepath: oad.filepath, title: oad.title)
- routes = oad.routes
- routes = routes.reject { |route| skip_route[route.path, route.request_method] } if skip_route
- routes.each do |route|
- responses = skip_response ? route.responses.reject(&skip_response) : route.responses
+ oad.routes.each do |route|
plan.add_route request_method: route.request_method,
path: route.path,
requests: route.requests,
- responses:
+ responses: route.responses,
+ skipped: skip_route ? skip_route[route.path, route.request_method] : false,
+ skip_response:
end
plan
end
@@ -63,15 +62,36 @@ def tasks
index.values
end
- def add_route(request_method:, path:, requests:, responses:)
- request_tasks = requests.to_a.map do |request|
- index[request.key] = RequestTask.new(request)
- end
+ def skipped_requests
+ routes.flat_map(&:skipped_requests)
+ end
+
+ def skipped_responses
+ routes.flat_map(&:skipped_responses)
+ end
+
+ def skipped_requests_count
+ skipped_requests.count
+ end
+
+ def skipped_responses_count
+ skipped_responses.count
+ end
+
+ def add_route(request_method:, path:, requests:, responses:, skipped: false, skip_response: nil)
+ request_tasks = requests.to_a.map { |request| add_task RequestTask.new(request, skipped:) }
response_tasks = responses.to_a.map do |response|
- index[response.key] = ResponseTask.new(response)
+ add_task ResponseTask.new(response, skipped: skipped || (skip_response ? skip_response[response] : false))
end
@routes << RouteTask.new(path:, request_method:, requests: request_tasks, responses: response_tasks)
end
+
+ private
+
+ def add_task(task)
+ index[task.key] = task unless task.skipped?
+ task
+ end
end
end
end
diff --git a/lib/openapi_first/test/coverage/request_task.rb b/lib/openapi_first/test/coverage/request_task.rb
index 4d9362ec..600531b0 100644
--- a/lib/openapi_first/test/coverage/request_task.rb
+++ b/lib/openapi_first/test/coverage/request_task.rb
@@ -9,10 +9,11 @@ module Coverage
class RequestTask
extend Forwardable
- def_delegators :@request, :path, :request_method, :content_type
+ def_delegators :@request, :key, :path, :request_method, :content_type
- def initialize(request_definition)
+ def initialize(request_definition, skipped: false)
@request = request_definition
+ @skipped = skipped
@requested = false
@last_error_message = nil
end
@@ -25,6 +26,10 @@ def track(validated_request)
@last_error_message = validated_request.error.exception_message unless validated_request.valid?
end
+ def skipped?
+ @skipped == true
+ end
+
def requested?
@requested == true
end
diff --git a/lib/openapi_first/test/coverage/response_task.rb b/lib/openapi_first/test/coverage/response_task.rb
index 5b48116b..f637604b 100644
--- a/lib/openapi_first/test/coverage/response_task.rb
+++ b/lib/openapi_first/test/coverage/response_task.rb
@@ -11,8 +11,9 @@ class ResponseTask
def_delegators :@response, :status, :content_type, :key
- def initialize(response_definition)
+ def initialize(response_definition, skipped: false)
@response = response_definition
+ @skipped = skipped
@responded = false
@last_error_message = nil
end
@@ -25,6 +26,10 @@ def track(validated_response)
@last_error_message = validated_response.error.exception_message unless validated_response.valid?
end
+ def skipped?
+ @skipped == true
+ end
+
def responded?
@responded == true
end
diff --git a/lib/openapi_first/test/coverage/route_task.rb b/lib/openapi_first/test/coverage/route_task.rb
index 7250243c..ff6275ff 100644
--- a/lib/openapi_first/test/coverage/route_task.rb
+++ b/lib/openapi_first/test/coverage/route_task.rb
@@ -4,8 +4,30 @@ module OpenapiFirst
module Test
module Coverage
RouteTask = Data.define(:path, :request_method, :requests, :responses) do
+ def skipped?
+ requests.all?(&:skipped?)
+ end
+
+ def tracked_requests
+ requests.reject(&:skipped?)
+ end
+
+ def tracked_responses
+ responses.reject(&:skipped?)
+ end
+
+ def skipped_requests
+ requests.select(&:skipped?)
+ end
+
+ def skipped_responses
+ responses.select(&:skipped?)
+ end
+
def finished?
- requests.all?(&:finished?) && responses.all?(&:finished?)
+ return false if skipped?
+
+ tracked_requests.all?(&:finished?) && tracked_responses.all?(&:finished?)
end
def summary
diff --git a/lib/openapi_first/test/coverage/skipped_summary.rb b/lib/openapi_first/test/coverage/skipped_summary.rb
new file mode 100644
index 00000000..ee02e3f7
--- /dev/null
+++ b/lib/openapi_first/test/coverage/skipped_summary.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module OpenapiFirst
+ module Test
+ module Coverage
+ # Shared reporting of skipped requests and responses for coverage reporters.
+ module SkippedSummary
+ def log_skipped_summary(coverage_result)
+ requests = coverage_result.skipped_requests_count
+ responses = coverage_result.skipped_responses_count
+ return if requests.zero? && responses.zero?
+
+ logger.info "API coverage skipped #{requests} request(s) and #{responses} response(s)."
+ return if requests.zero?
+
+ logger.warn "#{requests} request(s) were skipped entirely. " \
+ 'These requests are excluded from API coverage and are not contract-tested at all.'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/openapi_first/test/coverage/terminal_reporter.rb b/lib/openapi_first/test/coverage/terminal_reporter.rb
index c7491cc0..abbc7a87 100644
--- a/lib/openapi_first/test/coverage/terminal_reporter.rb
+++ b/lib/openapi_first/test/coverage/terminal_reporter.rb
@@ -5,6 +5,8 @@ module Test
module Coverage
# Reports coverage to a logger using ANSI-coloured lines.
class TerminalReporter
+ include SkippedSummary
+
def initialize(verbose: false, focused: true, logger: Test.logger)
@verbose = verbose
@focused = focused && !verbose
@@ -18,13 +20,14 @@ def report(coverage_result)
'Make sure to observe your application using OpenapiFirst::Test.'
end
coverage_result.plans.each { |plan| format_plan(plan) } if coverage.positive?
+ log_skipped_summary(coverage_result)
end
private attr_reader :out, :verbose, :focused, :logger
private
- def format_plan(plan) # rubocop:disable Metrics/PerceivedComplexity
+ def format_plan(plan)
logger.info "API validation coverage for #{plan.api_identifier}: #{plan.coverage}%"
return if plan.done? && !verbose
@@ -32,9 +35,7 @@ def format_plan(plan) # rubocop:disable Metrics/PerceivedComplexity
focused_route = requested_routes_count <= 1 && focused
plan.routes.each do |route|
- next if route.finished? && !verbose
-
- next if route.requests.none?(&:requested?) && focused_route
+ next unless report_route?(route, focused_route)
format_requests(route.requests)
@@ -42,9 +43,19 @@ def format_plan(plan) # rubocop:disable Metrics/PerceivedComplexity
end
end
+ def report_route?(route, focused_route)
+ return verbose if route.skipped?
+ return false if route.finished? && !verbose
+ return false if focused_route && route.requests.none?(&:requested?)
+
+ true
+ end
+
def format_requests(requests)
requests.each do |request|
- if request.finished?
+ if request.skipped?
+ log_skipped "⚠ #{request_label(request)} – Skipped!" if verbose
+ elsif request.finished?
log_success "✓ #{request_label(request)}"
else
log_error "❌ #{request_label(request)} – #{explain_unfinished_request(request)}"
@@ -54,7 +65,9 @@ def format_requests(requests)
def format_responses(responses)
responses.each do |response|
- if response.finished?
+ if response.skipped?
+ log_skipped " #{response_label(response)} – Skipped!" if verbose
+ elsif response.finished?
log_success " ✓ #{response_label(response)}" if verbose
else
log_error " ❌ #{response_label(response)} – #{explain_unfinished_response(response)}"
@@ -93,6 +106,10 @@ def log_success(msg)
logger.info "\e[32m#{msg}\e[0m"
end
+ def log_skipped(msg)
+ logger.info "\e[33m#{msg}\e[0m"
+ end
+
def log_error(msg)
logger.error "\e[31m#{msg}\e[0m"
end
diff --git a/spec/test/coverage/html_reporter_spec.rb b/spec/test/coverage/html_reporter_spec.rb
index de8f8ed9..fa40a674 100644
--- a/spec/test/coverage/html_reporter_spec.rb
+++ b/spec/test/coverage/html_reporter_spec.rb
@@ -20,6 +20,11 @@
}
}
}
+ },
+ '/internal' => {
+ 'get' => {
+ 'responses' => { '200' => { 'description' => 'ok' } }
+ }
}
}
}
@@ -116,6 +121,69 @@ def run_reporter(result, **opts)
end
end
+ context 'with skipped requests and responses' do
+ let(:plan) do
+ OpenapiFirst::Test::Coverage::Plan.for(
+ oad,
+ skip_response: ->(res) { res.status == '400' },
+ skip_route: ->(path, _method) { path == '/internal' }
+ )
+ end
+
+ it 'renders a toggle for skipped requests' do
+ html, = run_reporter(build_result(plan))
+ expect(html).to include('id="filter-skipped-requests"')
+ expect(html).to include('Skipped requests')
+ end
+
+ it 'shows the number of skipped requests and responses' do
+ html, = run_reporter(build_result(plan))
+ expect(html).to include('1 request(s) and 2 response(s) skipped')
+ end
+
+ it 'marks a skipped route as skipped instead of uncovered' do
+ html, = run_reporter(build_result(plan))
+ expect(html).to include('class="route is-skipped"')
+ expect(html).to include('/internal')
+ expect(html).to include('request-status skipped')
+ end
+
+ it 'lists the skipped request of a skipped route' do
+ html, = run_reporter(build_result(plan))
+ expect(html).to include('Skipped, not covered by any test')
+ end
+
+ it 'lists a skipped response of a route that is not skipped' do
+ html, = run_reporter(build_result(plan))
+ expect(html).to include('class="skipped"')
+ expect(html).to include('application/problem+json')
+ end
+
+ it 'does not count skipped responses as uncovered' do
+ plan.track_request(valid_request)
+ plan.track_response(valid_200_response)
+ html, = run_reporter(build_result(plan))
+ expect(html).to include('class="route is-covered"')
+ expect(html).not_to include('response(s) not covered')
+ expect(html).to include('1 response(s) skipped')
+ end
+
+ it 'warns that a request was skipped entirely' do
+ _, logger = run_reporter(build_result(plan))
+ expect(logger).to have_received(:info).with('API coverage skipped 1 request(s) and 2 response(s).')
+ expect(logger).to have_received(:warn).with(include('skipped entirely'))
+ end
+ end
+
+ context 'without any skipped requests or responses' do
+ it 'does not mention skipped requests or responses' do
+ html, logger = run_reporter(build_result(plan))
+ expect(html).not_to include('class="skipped-summary"')
+ expect(logger).not_to have_received(:info).with(include('skipped'))
+ expect(logger).not_to have_received(:warn).with(include('skipped'))
+ end
+ end
+
context 'with no plans registered' do
it 'writes a warning page' do
result = OpenapiFirst::Test::Coverage::Result.new(plans: [], coverage: 0)
diff --git a/spec/test/coverage/plan_spec.rb b/spec/test/coverage/plan_spec.rb
index 99c69ff6..3c72c7ee 100644
--- a/spec/test/coverage/plan_spec.rb
+++ b/spec/test/coverage/plan_spec.rb
@@ -168,6 +168,54 @@
expect(plan.tasks.count(&:finished?)).to eq(0)
end
+ describe 'skipped requests and responses' do
+ it 'keeps skipped responses in the routes and counts them' do
+ plan = described_class.for(oad, skip_response: ->(res) { res.status == '200' })
+ route = plan.routes.first
+
+ expect(route).not_to be_skipped
+ expect(route.responses.map(&:status)).to eq(%w[200 4XX])
+ expect(route.skipped_responses.map(&:status)).to eq(['200'])
+ expect(route.tracked_responses.map(&:status)).to eq(['4XX'])
+ expect(plan.skipped_requests_count).to eq(0)
+ expect(plan.skipped_responses_count).to eq(1)
+ end
+
+ it 'keeps a skipped route with all its requests and responses' do
+ plan = described_class.for(oad, skip_route: ->(_path, _request_method) { true })
+ route = plan.routes.first
+
+ expect(route).to be_skipped
+ expect(route.requests.map(&:skipped?)).to eq([true])
+ expect(route.responses.map(&:skipped?)).to eq([true, true])
+ expect(route.tracked_requests).to be_empty
+ expect(route.tracked_responses).to be_empty
+ expect(plan.skipped_requests_count).to eq(1)
+ expect(plan.skipped_responses_count).to eq(2)
+ end
+
+ it 'excludes skipped tasks from coverage' do
+ plan = described_class.for(oad, skip_route: ->(_path, _request_method) { true })
+
+ expect(plan.tasks).to be_empty
+ expect(plan).to be_done
+ end
+
+ it 'does not track a skipped request' do
+ plan = described_class.for(oad, skip_route: ->(_path, _request_method) { true })
+
+ plan.track_request(valid_request)
+
+ expect(plan.routes.first.requests.first).not_to be_requested
+ end
+
+ it 'is not finished when the route is skipped' do
+ plan = described_class.for(oad, skip_route: ->(_path, _request_method) { true })
+
+ expect(plan.routes.first).not_to be_finished
+ end
+ end
+
context 'with skip_response option' do
let(:plan) do
skip_response = ->(response) { response.status == '4XX' }
diff --git a/spec/test_spec.rb b/spec/test_spec.rb
index 55a54a5f..9d2846b6 100644
--- a/spec/test_spec.rb
+++ b/spec/test_spec.rb
@@ -143,7 +143,8 @@ def call(_env)
test.skip_coverage { |path, request_method| path == '/pets' && request_method == 'POST' }
end
route_tasks = described_class::Coverage.result.plans.first.routes
- expect(route_tasks.map { |route| [route.path, route.request_method] }).to eq([['/pets', 'GET'], ['/pets/{petId}', 'GET']])
+ expect(route_tasks.select(&:skipped?).map { |route| [route.path, route.request_method] }).to eq([['/pets', 'POST']])
+ expect(route_tasks.reject(&:skipped?).map { |route| [route.path, route.request_method] }).to eq([['/pets', 'GET'], ['/pets/{petId}', 'GET']])
end
it 'can skip_coverage for paths' do
@@ -152,7 +153,19 @@ def call(_env)
test.skip_coverage { |path| path == '/pets' }
end
route_tasks = described_class::Coverage.result.plans.first.routes
- expect(route_tasks.map { |route| [route.path, route.request_method] }).to eq([['/pets/{petId}', 'GET']])
+ expect(route_tasks.select(&:skipped?).map { |route| [route.path, route.request_method] }).to eq([['/pets', 'GET'], ['/pets', 'POST']])
+ expect(route_tasks.reject(&:skipped?).map { |route| [route.path, route.request_method] }).to eq([['/pets/{petId}', 'GET']])
+ end
+
+ it 'reports the number of skipped requests and responses' do
+ described_class.setup do |test|
+ test.register('./spec/data/petstore.yaml')
+ test.skip_coverage { |path, request_method| path == '/pets' && request_method == 'POST' }
+ test.skip_response_coverage { |response| response.status == 'default' }
+ end
+ result = described_class::Coverage.result
+ expect(result.skipped_requests_count).to eq(1)
+ expect(result.skipped_responses_count).to eq(3)
end
it 'is okay if no block is given if an OAD is registered ' do