skooma 0.3.0 → 0.3.2

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: 9f8caa0911924b17126cad7544d4c54e4fe3f819c6b21c3489cd47180bb69996
4
- data.tar.gz: 052a2f72272a02b99cb44f62b6a1b1a319ca1c743742c66125d4da4ae9b402c1
3
+ metadata.gz: de042bc8ca38a43927cc9f6c1fdb829aecbbf214f771f9a092345f25d2d2f36a
4
+ data.tar.gz: a084b23b9eeb66af1a36d010b99bc86ae4e1b0c39e470096fc2ef788c9afb725
5
5
  SHA512:
6
- metadata.gz: 4392d35e7aade0d2683f38ef782ed6b2b980aee835ea489f173202ff75627a4cff4bc3ae522bb9e06ed6b69d7b073a08520902a3a6388cd22f3e8321bbedbb08
7
- data.tar.gz: 990bfcb689cd4ad8f76060c955abf9ce01fd3513a54499592136045d1f87f3000575356b1d0e7ddea78b16cc76e516d099dfeda18c35c36426a6426e933068ea
6
+ metadata.gz: 4286775a7c888f90b46fb35b827ce0f0f21c10ffd4737f6823326a14e00a21786f3e976877d71aee7b07a6578b09188fbe5334c8c67e4f24b78ccfe6ccc00494
7
+ data.tar.gz: 92e474fe454174ad5d4f1703cf8b1a9c3f9bd47bbe6eb382f60912926e28e3f9888acc612cc751c18ca8d7182f8c0922507302fbde8570ade7c0c7950c76925c
data/CHANGELOG.md CHANGED
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning].
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.2] - 2024-06-24
11
+
12
+ ### Fixed
13
+
14
+ - Fix deprecation `MiniTest::Unit.after_tests is now Minitest.after_run`. ([@barnaclebarnes])
15
+ - Exclude test helpers from eager loading. ([@skryukov])
16
+ - Update oas-3.1 base schema. ([@skryukov])
17
+
18
+ ## [0.3.1] - 2024-04-11
19
+
20
+ ### Added
21
+
22
+ - Add coverage for tested API operations. ([@skryukov])
23
+
24
+ ```ruby
25
+
26
+ # spec/rails_helper.rb
27
+
28
+ RSpec.configure do |config|
29
+ # To enable coverage, pass `coverage: :report` option,
30
+ # and to raise an error when an operation is not covered, pass `coverage: :strict` option:
31
+ config.include Skooma::RSpec[Rails.root.join("docs", "openapi.yml"), coverage: :report], type: :request
32
+ end
33
+ ```
34
+
35
+ ```shell
36
+ $ bundle exec rspec
37
+ # ...
38
+ OpenAPI schema /openapi.yml coverage report: 110 / 194 operations (56.7%) covered.
39
+ Uncovered paths:
40
+ GET /api/uncovered 200
41
+ GET /api/partially_covered 403
42
+ # ...
43
+ ```
44
+
10
45
  ## [0.3.0] - 2024-04-09
11
46
 
12
47
  ### Changed
@@ -39,16 +74,16 @@ and this project adheres to [Semantic Versioning].
39
74
 
40
75
  - Add support for APIs mounted under a path prefix. ([@skryukov])
41
76
 
42
- ```ruby
43
- # spec/rails_helper.rb
44
-
45
- RSpec.configure do |config|
46
- # ...
47
- path_to_openapi = Rails.root.join("docs", "openapi.yml")
48
- # pass path_prefix option if your API is mounted under a prefix:
49
- config.include Skooma::RSpec[path_to_openapi, path_prefix: "/internal/api"], type: :request
50
- end
51
- ```
77
+ ```ruby
78
+ # spec/rails_helper.rb
79
+
80
+ RSpec.configure do |config|
81
+ # ...
82
+ path_to_openapi = Rails.root.join("docs", "openapi.yml")
83
+ # pass path_prefix option if your API is mounted under a prefix:
84
+ config.include Skooma::RSpec[path_to_openapi, path_prefix: "/internal/api"], type: :request
85
+ end
86
+ ```
52
87
 
53
88
  ### Changed
54
89
 
@@ -83,10 +118,13 @@ end
83
118
 
84
119
  - Initial implementation. ([@skryukov])
85
120
 
121
+ [@barnaclebarnes]: https://github.com/barnaclebarnes
86
122
  [@skryukov]: https://github.com/skryukov
87
123
  [@ursm]: https://github.com/ursm
88
124
 
89
- [Unreleased]: https://github.com/skryukov/skooma/compare/v0.3.0...HEAD
125
+ [Unreleased]: https://github.com/skryukov/skooma/compare/v0.3.2...HEAD
126
+ [0.3.2]: https://github.com/skryukov/skooma/compare/v0.3.1...v0.3.2
127
+ [0.3.1]: https://github.com/skryukov/skooma/compare/v0.3.0...v0.3.1
90
128
  [0.3.0]: https://github.com/skryukov/skooma/compare/v0.2.3...v0.3.0
91
129
  [0.2.3]: https://github.com/skryukov/skooma/compare/v0.2.2...v0.2.3
92
130
  [0.2.2]: https://github.com/skryukov/skooma/compare/v0.2.1...v0.2.2
data/README.md CHANGED
@@ -16,7 +16,7 @@ Skooma is a Ruby library for validating API implementations against OpenAPI docu
16
16
 
17
17
  ### Learn more
18
18
 
19
- - [Let there be docs! A documentation-first approach to Rails API development](https://evilmartians.com/events/let-there-be-docs-a-documentation-first-approach-to-rails-api-development) – Talk and slides from Friendly.rb 2023
19
+ - [Let there be docs! A documentation-first approach to Rails API development](https://evilmartians.com/chronicles/let-there-be-docs-a-documentation-first-approach-to-rails-api-development)
20
20
 
21
21
  <a href="https://evilmartians.com/?utm_source=skooma&utm_campaign=project_page">
22
22
  <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
@@ -51,6 +51,10 @@ RSpec.configure do |config|
51
51
 
52
52
  # OR pass path_prefix option if your API is mounted under a prefix:
53
53
  config.include Skooma::RSpec[path_to_openapi, path_prefix: "/internal/api"], type: :request
54
+
55
+ # To enable coverage, pass `coverage: :report` option,
56
+ # and to raise an error when an operation is not covered, pass `coverage: :strict` option:
57
+ config.include Skooma::RSpec[path_to_openapi, coverage: :report], type: :request
54
58
  end
55
59
  ```
56
60
 
@@ -115,8 +119,15 @@ end
115
119
 
116
120
  ```ruby
117
121
  # test/test_helper.rb
122
+ path_to_openapi = Rails.root.join("docs", "openapi.yml")
123
+ ActionDispatch::IntegrationTest.include Skooma::Minitest[path_to_openapi]
124
+
125
+ # OR pass path_prefix option if your API is mounted under a prefix:
126
+ ActionDispatch::IntegrationTest.include Skooma::Minitest[path_to_openapi, path_prefix: "/internal/api"], type: :request
118
127
 
119
- ActionDispatch::IntegrationTest.include Skooma::Minitest[Rails.root.join("docs", "openapi.yml")]
128
+ # To enable coverage, pass `coverage: :report` option,
129
+ # and to raise an error when an operation is not covered, pass `coverage: :strict` option:
130
+ ActionDispatch::IntegrationTest.include Skooma::Minitest[path_to_openapi, coverage: :report], type: :request
120
131
  ```
121
132
 
122
133
  #### Validate OpenAPI document
@@ -18,15 +18,6 @@
18
18
  "externalDocs": { "$ref": "#/$defs/external-docs" },
19
19
  "xml": { "$ref": "#/$defs/xml" }
20
20
  },
21
- "dependentSchemas": {
22
- "discriminator": {
23
- "anyOf": [
24
- { "required": [ "oneOf" ] },
25
- { "required": [ "anyOf" ] },
26
- { "required": [ "allOf" ] }
27
- ]
28
- }
29
- },
30
21
 
31
22
  "$defs": {
32
23
  "extensible": {
@@ -167,8 +167,7 @@
167
167
  "type": "object",
168
168
  "properties": {
169
169
  "url": {
170
- "type": "string",
171
- "format": "uri-reference"
170
+ "type": "string"
172
171
  },
173
172
  "description": {
174
173
  "type": "string"
@@ -525,7 +524,7 @@
525
524
  "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie"
526
525
  },
527
526
  {
528
- "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-form"
527
+ "$ref": "#/$defs/styles-for-form"
529
528
  }
530
529
  ],
531
530
  "$defs": {
@@ -542,9 +541,6 @@
542
541
  },
543
542
  "then": {
544
543
  "properties": {
545
- "name": {
546
- "pattern": "[^/#?]+$"
547
- },
548
544
  "style": {
549
545
  "default": "simple",
550
546
  "enum": [
@@ -630,32 +626,6 @@
630
626
  }
631
627
  }
632
628
  }
633
- },
634
- "styles-for-form": {
635
- "if": {
636
- "properties": {
637
- "style": {
638
- "const": "form"
639
- }
640
- },
641
- "required": [
642
- "style"
643
- ]
644
- },
645
- "then": {
646
- "properties": {
647
- "explode": {
648
- "default": true
649
- }
650
- }
651
- },
652
- "else": {
653
- "properties": {
654
- "explode": {
655
- "default": false
656
- }
657
- }
658
- }
659
629
  }
660
630
  }
661
631
  }
@@ -782,38 +752,10 @@
782
752
  "$ref": "#/$defs/specification-extensions"
783
753
  },
784
754
  {
785
- "$ref": "#/$defs/encoding/$defs/explode-default"
755
+ "$ref": "#/$defs/styles-for-form"
786
756
  }
787
757
  ],
788
- "unevaluatedProperties": false,
789
- "$defs": {
790
- "explode-default": {
791
- "if": {
792
- "properties": {
793
- "style": {
794
- "const": "form"
795
- }
796
- },
797
- "required": [
798
- "style"
799
- ]
800
- },
801
- "then": {
802
- "properties": {
803
- "explode": {
804
- "default": true
805
- }
806
- }
807
- },
808
- "else": {
809
- "properties": {
810
- "explode": {
811
- "default": false
812
- }
813
- }
814
- }
815
- }
816
- }
758
+ "unevaluatedProperties": false
817
759
  },
818
760
  "responses": {
819
761
  "$comment": "https://spec.openapis.org/oas/v3.1.0#responses-object",
@@ -1100,8 +1042,7 @@
1100
1042
  "description": {
1101
1043
  "type": "string"
1102
1044
  }
1103
- },
1104
- "unevaluatedProperties": false
1045
+ }
1105
1046
  },
1106
1047
  "schema": {
1107
1048
  "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object",
@@ -1436,6 +1377,32 @@
1436
1377
  "additionalProperties": {
1437
1378
  "type": "string"
1438
1379
  }
1380
+ },
1381
+ "styles-for-form": {
1382
+ "if": {
1383
+ "properties": {
1384
+ "style": {
1385
+ "const": "form"
1386
+ }
1387
+ },
1388
+ "required": [
1389
+ "style"
1390
+ ]
1391
+ },
1392
+ "then": {
1393
+ "properties": {
1394
+ "explode": {
1395
+ "default": true
1396
+ }
1397
+ }
1398
+ },
1399
+ "else": {
1400
+ "properties": {
1401
+ "explode": {
1402
+ "default": false
1403
+ }
1404
+ }
1405
+ }
1439
1406
  }
1440
1407
  }
1441
1408
  }
@@ -0,0 +1,90 @@
1
+ module Skooma
2
+ class NoopCoverage
3
+ def track_request(*)
4
+ end
5
+
6
+ def report
7
+ end
8
+ end
9
+
10
+ class Coverage
11
+ class SimpleReport
12
+ def initialize(coverage)
13
+ @coverage = coverage
14
+ end
15
+
16
+ attr_reader :coverage
17
+
18
+ def report
19
+ puts <<~MSG
20
+ OpenAPI schema #{URI.parse(coverage.schema.uri.to_s).path} coverage report: #{coverage.covered_paths.count} / #{coverage.defined_paths.count} operations (#{coverage.covered_percent.round(2)}%) covered.
21
+ #{coverage.uncovered_paths.empty? ? "All paths are covered!" : "Uncovered paths:"}
22
+ #{coverage.uncovered_paths.map { |method, path, status| "#{method.upcase} #{path} #{status}" }.join("\n")}
23
+ MSG
24
+ end
25
+ end
26
+
27
+ def self.new(schema, mode: nil, format: nil)
28
+ case mode
29
+ when nil, false
30
+ NoopCoverage.new
31
+ when :report, :strict
32
+ super
33
+ else
34
+ raise ArgumentError, "Invalid coverage: #{mode}, expected :report, :strict, or false"
35
+ end
36
+ end
37
+
38
+ attr_reader :mode, :format, :defined_paths, :covered_paths, :schema
39
+
40
+ def initialize(schema, mode:, format:)
41
+ @schema = schema
42
+ @mode = mode
43
+ @format = format || SimpleReport
44
+ @defined_paths = find_defined_paths(schema)
45
+ @covered_paths = Set.new
46
+ end
47
+
48
+ def track_request(result)
49
+ operation = [nil, nil, nil]
50
+ result.collect_annotations(result.instance, keys: %w[paths responses]) do |node|
51
+ case node.key
52
+ when "paths"
53
+ operation[0] = node.annotation["method"]
54
+ operation[1] = node.annotation["current_path"]
55
+ when "responses"
56
+ operation[2] = node.annotation
57
+ end
58
+ end
59
+ covered_paths << operation
60
+ end
61
+
62
+ def uncovered_paths
63
+ defined_paths - covered_paths
64
+ end
65
+
66
+ def covered_percent
67
+ covered_paths.count * 100.0 / defined_paths.count
68
+ end
69
+
70
+ def report
71
+ format.new(self).report
72
+ exit 1 if mode == :strict && uncovered_paths.any?
73
+ end
74
+
75
+ private
76
+
77
+ def find_defined_paths(schema)
78
+ Set.new.tap do |paths|
79
+ schema["paths"].each do |path, path_item|
80
+ resolved_path_item = (path_item.key?("$ref") ? path_item.resolve_ref(path_item["$ref"]) : path_item)
81
+ resolved_path_item.slice("get", "post", "put", "patch", "delete", "options", "head", "trace").each do |method, operation|
82
+ operation["responses"]&.each do |code, _|
83
+ paths << [method, path, code]
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -5,13 +5,17 @@ require "pp"
5
5
  module Skooma
6
6
  module Matchers
7
7
  class ConformRequestSchema
8
- def initialize(schema, mapped_response)
9
- @schema = schema
8
+ def initialize(skooma, mapped_response)
9
+ @skooma = skooma
10
+ @schema = skooma.schema
10
11
  @mapped_response = mapped_response
11
12
  end
12
13
 
13
14
  def matches?(*)
14
15
  @result = @schema.evaluate(@mapped_response)
16
+
17
+ @skooma.coverage.track_request(@result) if @mapped_response["response"]
18
+
15
19
  @result.valid?
16
20
  end
17
21
 
@@ -3,8 +3,8 @@
3
3
  module Skooma
4
4
  module Matchers
5
5
  class ConformResponseSchema < ConformRequestSchema
6
- def initialize(schema, mapped_response, expected)
7
- super(schema, mapped_response)
6
+ def initialize(skooma, mapped_response, expected)
7
+ super(skooma, mapped_response)
8
8
  @expected = expected
9
9
  end
10
10
 
@@ -38,30 +38,39 @@ module Skooma
38
38
 
39
39
  raise "Response object not found"
40
40
  end
41
+
42
+ def skooma_openapi_schema
43
+ skooma.schema
44
+ end
41
45
  end
42
46
 
43
- def initialize(helper_methods_module, openapi_path, base_uri: "https://skoomarb.dev/", path_prefix: "")
47
+ def initialize(helper_methods_module, openapi_path, base_uri: "https://skoomarb.dev/", path_prefix: "", **params)
44
48
  super()
45
49
 
46
50
  registry = create_test_registry
47
51
  pathname = Pathname.new(openapi_path)
48
- source_uri = "#{base_uri}#{path_prefix.delete_suffix("/")}"
52
+ source_uri = "#{base_uri}#{path_prefix.delete_suffix("/").delete_prefix("/")}"
49
53
  source_uri += "/" unless source_uri.end_with?("/")
50
54
  registry.add_source(
51
55
  source_uri,
52
56
  JSONSkooma::Sources::Local.new(pathname.dirname.to_s)
53
57
  )
54
- schema = registry.schema(URI.parse("#{source_uri}#{pathname.basename}"), schema_class: Skooma::Objects::OpenAPI)
55
- schema.path_prefix = path_prefix
58
+ @schema = registry.schema(URI.parse("#{source_uri}#{pathname.basename}"), schema_class: Skooma::Objects::OpenAPI)
59
+ @schema.path_prefix = path_prefix
60
+
61
+ @coverage = Coverage.new(@schema, mode: params[:coverage], format: params[:coverage_format])
56
62
 
57
63
  include DefaultHelperMethods
58
64
  include helper_methods_module
59
65
 
60
- define_method :skooma_openapi_schema do
61
- schema
66
+ skooma_self = self
67
+ define_method :skooma do
68
+ skooma_self
62
69
  end
63
70
  end
64
71
 
72
+ attr_accessor :schema, :coverage
73
+
65
74
  private
66
75
 
67
76
  def create_test_registry
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "minitest/unit"
4
+
3
5
  module Skooma
4
6
  # Minitest helpers for OpenAPI schema validation
5
7
  # @example
@@ -10,19 +12,19 @@ module Skooma
10
12
  class Minitest < Matchers::Wrapper
11
13
  module HelperMethods
12
14
  def assert_conform_schema(expected_status)
13
- matcher = Matchers::ConformSchema.new(skooma_openapi_schema, mapped_response, expected_status)
15
+ matcher = Matchers::ConformSchema.new(skooma, mapped_response, expected_status)
14
16
 
15
17
  assert matcher.matches?, -> { matcher.failure_message }
16
18
  end
17
19
 
18
20
  def assert_conform_request_schema
19
- matcher = Matchers::ConformRequestSchema.new(skooma_openapi_schema, mapped_response(with_response: false))
21
+ matcher = Matchers::ConformRequestSchema.new(skooma, mapped_response(with_response: false))
20
22
 
21
23
  assert matcher.matches?, -> { matcher.failure_message }
22
24
  end
23
25
 
24
26
  def assert_conform_response_schema(expected_status)
25
- matcher = Matchers::ConformResponseSchema.new(skooma_openapi_schema, mapped_response(with_request: false), expected_status)
27
+ matcher = Matchers::ConformResponseSchema.new(skooma, mapped_response(with_request: false), expected_status)
26
28
 
27
29
  assert matcher.matches?, -> { matcher.failure_message }
28
30
  end
@@ -36,6 +38,8 @@ module Skooma
36
38
 
37
39
  def initialize(openapi_path, **params)
38
40
  super(HelperMethods, openapi_path, **params)
41
+
42
+ MiniTest.after_run { coverage.report }
39
43
  end
40
44
  end
41
45
  end
@@ -28,6 +28,8 @@ module Skooma
28
28
 
29
29
  return result.failure("Path #{instance["path"]} not found in schema") unless path
30
30
 
31
+ result.annotate({"current_path" => path})
32
+
31
33
  result.call(instance, path) do |subresult|
32
34
  subresult.annotate({"path_attributes" => attributes})
33
35
  path_schema.evaluate(instance, subresult)
@@ -13,12 +13,16 @@ module Skooma
13
13
  end
14
14
 
15
15
  json.evaluate(instance, result)
16
- return result.success if result.passed?
17
16
 
18
17
  path_item_result = result.parent
19
18
  path_item_result = path_item_result.parent until path_item_result.key.start_with?("/")
20
19
 
21
- path = path_item_result.annotation["path"]
20
+ paths_result = path_item_result.parent
21
+ paths_result.annotate(paths_result.annotation.merge("method" => key))
22
+
23
+ return result.success if result.passed?
24
+
25
+ path = paths_result.annotation["current_path"]
22
26
 
23
27
  result.failure("Path #{path}/#{key} is invalid")
24
28
  end
@@ -5,7 +5,7 @@ module Skooma
5
5
  class PathItem
6
6
  module Keywords
7
7
  class Delete < BaseOperation
8
- self.key = "options"
8
+ self.key = "delete"
9
9
  self.depends_on = %w[parameters]
10
10
  self.value_schema = :schema
11
11
  self.schema_value_class = Objects::Operation
data/lib/skooma/rspec.rb CHANGED
@@ -10,15 +10,15 @@ module Skooma
10
10
  class RSpec < Matchers::Wrapper
11
11
  module HelperMethods
12
12
  def conform_schema(expected_status)
13
- Matchers::ConformSchema.new(skooma_openapi_schema, mapped_response, expected_status)
13
+ Matchers::ConformSchema.new(skooma, mapped_response, expected_status)
14
14
  end
15
15
 
16
16
  def conform_response_schema(expected_status)
17
- Matchers::ConformResponseSchema.new(skooma_openapi_schema, mapped_response(with_request: false), expected_status)
17
+ Matchers::ConformResponseSchema.new(skooma, mapped_response(with_request: false), expected_status)
18
18
  end
19
19
 
20
20
  def conform_request_schema
21
- Matchers::ConformRequestSchema.new(skooma_openapi_schema, mapped_response(with_response: false))
21
+ Matchers::ConformRequestSchema.new(skooma, mapped_response(with_response: false))
22
22
  end
23
23
 
24
24
  def be_valid_document
@@ -28,6 +28,13 @@ module Skooma
28
28
 
29
29
  def initialize(openapi_path, **params)
30
30
  super(HelperMethods, openapi_path, **params)
31
+
32
+ skooma_self = self
33
+ ::RSpec.configure do |c|
34
+ c.after(:suite) do
35
+ at_exit { skooma_self.coverage.report }
36
+ end
37
+ end
31
38
  end
32
39
  end
33
40
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Skooma
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.2"
5
5
  end
data/lib/skooma.rb CHANGED
@@ -7,6 +7,11 @@ require_relative "skooma/inflector"
7
7
 
8
8
  loader = Zeitwerk::Loader.for_gem
9
9
  loader.inflector = Skooma::Inflector.new(__FILE__)
10
+
11
+ # Do not eager load the test helpers
12
+ loader.do_not_eager_load(File.join(__dir__, "skooma", "minitest.rb"))
13
+ loader.do_not_eager_load(File.join(__dir__, "skooma", "rspec.rb"))
14
+
10
15
  loader.setup
11
16
 
12
17
  module Skooma
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skooma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svyatoslav Kryukov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-09 00:00:00.000000000 Z
11
+ date: 2024-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -54,6 +54,7 @@ files:
54
54
  - data/oas-3.1/schema/2022-10-07.json
55
55
  - lib/skooma.rb
56
56
  - lib/skooma/body_parsers.rb
57
+ - lib/skooma/coverage.rb
57
58
  - lib/skooma/dialects/oas_3_1.rb
58
59
  - lib/skooma/env_mapper.rb
59
60
  - lib/skooma/inflector.rb
@@ -163,7 +164,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
164
  - !ruby/object:Gem::Version
164
165
  version: '0'
165
166
  requirements: []
166
- rubygems_version: 3.3.7
167
+ rubygems_version: 3.5.7
167
168
  signing_key:
168
169
  specification_version: 4
169
170
  summary: Validate API implementations against OpenAPI documents.