openapi_first 2.3.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 851414a9f2a8d64df210a610ed9577ad6608c85173a06221b96c3d390dcca1e5
4
- data.tar.gz: 815e970727a8e683740b622768ccd031a138e4ef4ca38c8aebcd73bf2a7f4230
3
+ metadata.gz: 131c3511b96fc2a420b9c0a1b2e4deeb5b24d8602ad6210e5a60ed1a46fe6257
4
+ data.tar.gz: f6b7a44491e9aedf68498b2200a46e6048b9036360447a34c077c7476da8fc70
5
5
  SHA512:
6
- metadata.gz: 8696eaabb5455c0e009233a3f8099198aa222cb1a442ef53314e5ed756373f41eff3747f69c0036cb43c708a746b21289866b9d7cfeace5649a68a686ad302ef
7
- data.tar.gz: 9bb2db9c0443eb75299c755a79a0e9c6d696bc27a5683bc8fe4994e3e7e965e336afe855bbe41666346697668ae312697f5b0e307ec89a13c1c9ecf6c4087134
6
+ metadata.gz: 498b7341aa473504c5fa13dd95e92d7d1e2317d690ddbd88fac36974c05b10cccd8ae8b9dccc025036151aab484304e14f053003668b576c547ac1c7c0716d4b
7
+ data.tar.gz: 5cb776022d6e0fb684b3c30e7dd507a6591e5bc0b34bc384347a28e2b5030f20c976fa6f92b9b3e81c8d8ec6912d46e6b0ee053189267de320ccf418e7d88f4f
data/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2.4.0
6
+
7
+ - Support less verbose test setup without the need to call `OpenapiFirst::Test.report_coverage`, which will be called `at_exit`:
8
+ ```ruby
9
+ OpenapiFirst::Test.setup do |test|
10
+ test.register('openapi/openapi.yaml')
11
+ test.minimum_coverage = 100 # Setting this will lead to an `exit 2` if coverage is below minimum
12
+ end
13
+ ```
14
+ - Add `OpenapiFirst::Test::Setup#minimum_coverage=` to control exit behaviour (exit 2 if coverage is below minimum)
15
+ - Add `verbose` option to `OpenapiFirst::Test.report_coverage(verbose: true)`
16
+ to see all passing requests/responses
17
+
5
18
  ## 2.3.0
6
19
 
7
20
  ### New feature
data/README.md CHANGED
@@ -129,9 +129,11 @@ This can be useful in a test or staging environment, especially if you are adopt
129
129
  use OpenapiFirst::Middlewares::ResponseValidation, spec: 'openapi.yaml' if ENV['RACK_ENV'] == 'test'
130
130
 
131
131
  # Pass `raise_error: false` to not raise an error:
132
- use OpenapiFirst::Middlewares::ResponseValidation, raise_error: true, spec: 'openapi.yaml'
132
+ use OpenapiFirst::Middlewares::ResponseValidation, raise_error: false, spec: 'openapi.yaml'
133
133
  ```
134
134
 
135
+ If you are adopting OpenAPI you can use these options together with [hooks](#hooks) to get notified about requests/responses that do match your API description.
136
+
135
137
  ## Contract Testing
136
138
 
137
139
  ### Coverage
@@ -149,21 +151,17 @@ Here is how to set it up for RSpec in your `spec/spec_helper.rb`:
149
151
  require 'openapi_first'
150
152
  OpenapiFirst::Test.setup do |test|
151
153
  test.register('openapi/openapi.yaml')
154
+ test.minimum_coverage = 100 # Setting this will lead to an `exit 2` if coverage is below minimum
152
155
  end
153
156
  ```
154
157
  2. Wrap your app with silent request / response validation. This validates all requets/responses you do during your test run. (✷1)
155
158
  ```ruby
156
159
  config.before type: :request do
157
160
  def app
158
- OpenapiFirst::Test::(App)
161
+ OpenapiFirst::Test.app(App)
159
162
  end
160
163
  end
161
164
  ```
162
- 3. Check coverage after your test suite has finished
163
- ```ruby
164
- # Prints a coverage report to the terminal
165
- config.after(:suite) { OpenapiFirst::Test.report_coverage }
166
- ```
167
165
 
168
166
  (✷1): Instead of using `OpenapiFirstTest.app` to wrap your application, you can use the 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.
169
167
 
@@ -26,8 +26,12 @@ module OpenapiFirst
26
26
  attr_reader :status, :content_type, :content_schema, :headers, :headers_schema, :key
27
27
 
28
28
  def validate(response)
29
- parsed_values = @parser.parse(response)
30
- error = @validator.call(parsed_values)
29
+ parsed_values = nil
30
+ error = catch FAILURE do
31
+ parsed_values = @parser.parse(response)
32
+ nil
33
+ end
34
+ error ||= @validator.call(parsed_values)
31
35
  ValidatedResponse.new(response, parsed_values:, error:, response_definition: self)
32
36
  end
33
37
 
@@ -5,6 +5,10 @@ module OpenapiFirst
5
5
  module Coverage
6
6
  # This is the default formatter
7
7
  class TerminalFormatter
8
+ def initialize(verbose: false)
9
+ @verbose = verbose
10
+ end
11
+
8
12
  # This takes a list of Coverage::Plan instances and outputs a String
9
13
  def format(coverage_result)
10
14
  @out = StringIO.new
@@ -12,7 +16,7 @@ module OpenapiFirst
12
16
  @out.string
13
17
  end
14
18
 
15
- private attr_reader :out
19
+ private attr_reader :out, :verbose
16
20
 
17
21
  private
18
22
 
@@ -27,10 +31,10 @@ module OpenapiFirst
27
31
  def format_plan(plan)
28
32
  filepath = plan.filepath
29
33
  puts ['', "API validation coverage for #{filepath}: #{plan.coverage}%"]
30
- return if plan.done?
34
+ return if plan.done? && !verbose
31
35
 
32
36
  plan.routes.each do |route|
33
- next if route.finished?
37
+ next if route.finished? && !verbose
34
38
 
35
39
  format_requests(route.requests)
36
40
  next if route.requests.none?(&:requested?)
@@ -52,7 +56,7 @@ module OpenapiFirst
52
56
  def format_responses(responses)
53
57
  responses.each do |response|
54
58
  if response.finished?
55
- puts green " ✓ #{response_label(response)}"
59
+ puts green " ✓ #{response_label(response)}" if verbose
56
60
  else
57
61
  puts red " ❌ #{response_label(response)} – #{explain_unfinished_response(response)}"
58
62
  end
@@ -14,33 +14,58 @@ module OpenapiFirst
14
14
 
15
15
  # Helper class to setup tests
16
16
  class Setup
17
+ def initialize
18
+ @minimum_coverage = 0
19
+ yield self
20
+ end
21
+
17
22
  def register(*)
18
23
  Test.register(*)
19
24
  end
25
+
26
+ attr_accessor :minimum_coverage
27
+
28
+ # This called at_exit
29
+ def handle_exit
30
+ coverage = Coverage.result.coverage
31
+ # :nocov:
32
+ puts 'API Coverage did not detect any API requests for the registered API descriptions' if coverage.zero?
33
+ Test.report_coverage if coverage.positive?
34
+ return unless minimum_coverage > coverage
35
+
36
+ puts "API Coverage fails with exit 2, because API coverage of #{coverage}%" \
37
+ "is below minimum of #{minimum_coverage}%!"
38
+ exit 2
39
+ # :nocov:
40
+ end
20
41
  end
21
42
 
22
- def self.setup
43
+ def self.setup(&)
23
44
  unless block_given?
24
45
  raise ArgumentError, "Please provide a block to #{self.class}.setup to register you API descriptions"
25
46
  end
26
47
 
27
48
  Coverage.start
28
- setup = Setup.new
29
- yield setup
30
- return unless definitions.empty?
31
-
32
- raise NotRegisteredError,
33
- 'No API descriptions have been registered. ' \
34
- 'Please register your API description via ' \
35
- "OpenapiFirst::Test.setup { |test| test.register('myopenapi.yaml') }"
49
+ setup = Setup.new(&)
50
+
51
+ if definitions.empty?
52
+ raise NotRegisteredError,
53
+ 'No API descriptions have been registered. ' \
54
+ 'Please register your API description via ' \
55
+ "OpenapiFirst::Test.setup { |test| test.register('myopenapi.yaml') }"
56
+ end
57
+
58
+ @setup ||= at_exit do
59
+ setup.handle_exit
60
+ end
36
61
  end
37
62
 
38
63
  # Print the coverage report
39
64
  # @param formatter A formatter to define the report.
40
65
  # @output [IO] An output where to puts the report.
41
- def self.report_coverage(formatter: Coverage::TerminalFormatter)
66
+ def self.report_coverage(formatter: Coverage::TerminalFormatter, **)
42
67
  coverage_result = Coverage.result
43
- puts formatter.new.format(coverage_result)
68
+ puts formatter.new(**).format(coverage_result)
44
69
  puts "The overal API validation coverage of this run is: #{coverage_result.coverage}%"
45
70
  end
46
71
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
- VERSION = '2.3.0'
4
+ VERSION = '2.4.0'
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_first
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Haller
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-14 00:00:00.000000000 Z
10
+ date: 2025-02-26 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: hana
@@ -160,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
160
160
  - !ruby/object:Gem::Version
161
161
  version: '0'
162
162
  requirements: []
163
- rubygems_version: 3.6.2
163
+ rubygems_version: 3.6.5
164
164
  specification_version: 4
165
165
  summary: Implement HTTP APIs based on OpenApi 3.x
166
166
  test_files: []