skooma 0.1.0 → 0.2.1

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: 966ef8f92df0a6f38895c4aca4185b297256042173a3b42d32da01d5b826c24e
4
- data.tar.gz: c1a4661e7228815c3d1a031ffb646cefd067031e1b1138346f845af469a89135
3
+ metadata.gz: eef2c83a80d1526de1a465acbb29b70fc053b6b5e54ed1e6b6ed131f56a8fe0a
4
+ data.tar.gz: 0c4cdec06703bdeb6fd3cf6922401552afaa13aa20b9189e472e703288ba60aa
5
5
  SHA512:
6
- metadata.gz: 047e598c99b797d98b7758a015ced5323c0dfe2889f64cca8e33b7a139713fa7515683da2e054c1597a1a1b79e62b9f77ab10e72d0b7276908e15b862cb54367
7
- data.tar.gz: 1b0dbfa91b60430bd1397d4145cc4d339c2144bfdda88d9c9085800a51a9fa0077087e09c3eb998136cf35c95b49d9449153113976fe6c72fbf6fa5258334841
6
+ metadata.gz: b5089b0a710836b072b723014cfaaf88b4504853f09a42ddf6a6f9e36ed50271b082dfcc184e047f2bf4481e6a343b4bebbd3bced7dfcec2c5a4f85697fbf113
7
+ data.tar.gz: aacb7329ef950a1fc9ff87513a85803e5530cacaec97f0dad1330bf20fb45e9bc339fe9004af11e1b90cfa6cbb725a2cd339a69c6a23721b667e8d10959b37fb
data/CHANGELOG.md CHANGED
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning].
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.1] - 2023-10-23
11
+
12
+ ### Fixed
13
+
14
+ - Raise error when parameter attributes misses required keys. ([@skryukov])
15
+ - Fix output format. ([@skryukov])
16
+
17
+ ## [0.2.0] - 2023-10-23
18
+
19
+ ### Added
20
+
21
+ - Add `minitest` and `rake-test` support. ([@skryukov])
22
+ - Add `discriminator` keyword support. ([@skryukov])
23
+
24
+ ### Fixed
25
+
26
+ - Fix Zeitwerk eager loading. ([@skryukov])
27
+
10
28
  ## [0.1.0] - 2023-09-27
11
29
 
12
30
  ### Added
@@ -15,7 +33,9 @@ and this project adheres to [Semantic Versioning].
15
33
 
16
34
  [@skryukov]: https://github.com/skryukov
17
35
 
18
- [Unreleased]: https://github.com/skryukov/skooma/compare/v0.1.0...HEAD
36
+ [Unreleased]: https://github.com/skryukov/skooma/compare/v0.2.1...HEAD
37
+ [0.2.1]: https://github.com/skryukov/skooma/compare/v0.2.0...v0.2.1
38
+ [0.2.0]: https://github.com/skryukov/skooma/compare/v0.1.0...v0.2.0
19
39
  [0.1.0]: https://github.com/skryukov/skooma/commits/v0.1.0
20
40
 
21
41
  [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
data/README.md CHANGED
@@ -3,12 +3,16 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/skooma.svg)](https://rubygems.org/gems/skooma)
4
4
  [![Ruby](https://github.com/skryukov/skooma/actions/workflows/main.yml/badge.svg)](https://github.com/skryukov/skooma/actions/workflows/main.yml)
5
5
 
6
+ <img align="right" height="150" width="150" title="Skooma logo" src="./assets/logo.svg">
7
+
6
8
  Skooma is a Ruby library for validating API implementations against OpenAPI documents.
7
9
 
8
- Features:
10
+ ### Features
11
+
9
12
  - Supports OpenAPI 3.1.0
10
13
  - Supports OpenAPI document validation
11
14
  - Supports request/response validations against OpenAPI document
15
+ - Includes RSpec and Minitest helpers
12
16
 
13
17
  <a href="https://evilmartians.com/?utm_source=skooma&utm_campaign=project_page">
14
18
  <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
@@ -26,20 +30,24 @@ If bundler is not being used to manage dependencies, install the gem by executin
26
30
 
27
31
  ## Usage
28
32
 
29
- ### Configuration
33
+ Skooma provides `rspec` and `minitest` helpers for validating OpenAPI documents and requests/responses against them.
34
+ Skooma helpers are designed to be used with `rails` request specs or `rack-test`.
35
+
36
+ ### RSpec
37
+
38
+ #### Configuration
30
39
 
31
40
  ```ruby
32
41
  # spec/rails_helper.rb
33
42
 
34
43
  RSpec.configure do |config|
35
44
  # ...
36
- Skooma.create_registry
37
45
  path_to_openapi = Rails.root.join("docs", "openapi.yml")
38
46
  config.include Skooma::RSpec[path_to_openapi], type: :request
39
47
  end
40
48
  ```
41
49
 
42
- ### Validate OpenAPI document
50
+ #### Validate OpenAPI document
43
51
 
44
52
  ```ruby
45
53
  # spec/openapi_spec.rb
@@ -53,7 +61,7 @@ describe "OpenAPI document", type: :request do
53
61
  end
54
62
  ```
55
63
 
56
- ### Validate request
64
+ #### Validate request
57
65
 
58
66
  ```ruby
59
67
  # spec/requests/feed_spec.rb
@@ -94,6 +102,55 @@ end
94
102
  # " [\"animalId\", \"food\", \"amount\"]"}]}
95
103
  ```
96
104
 
105
+ ### Minitest
106
+
107
+ #### Configuration
108
+
109
+ ```ruby
110
+ # test/test_helper.rb
111
+
112
+ ActionDispatch::IntegrationTest.include Skooma::Minitest[Rails.root.join("docs", "openapi.yml")]
113
+ ```
114
+
115
+ #### Validate OpenAPI document
116
+
117
+ ```ruby
118
+ # test/openapi_test.rb
119
+
120
+ require "test_helper"
121
+
122
+ class OpenapiTest < ActionDispatch::IntegrationTest
123
+ test "is valid OpenAPI document" do
124
+ assert_is_valid_document(skooma_openapi_schema)
125
+ end
126
+ end
127
+ ```
128
+
129
+ #### Validate request
130
+
131
+ ```ruby
132
+ # test/integration/items_test.rb
133
+
134
+ require "test_helper"
135
+
136
+ class ItemsTest < ActionDispatch::IntegrationTest
137
+ test "GET /" do
138
+ get "/"
139
+ assert_conform_schema(200)
140
+ end
141
+
142
+ test "POST / conforms to schema with 201 response code" do
143
+ post "/", params: {foo: "bar"}, as: :json
144
+ assert_conform_schema(201)
145
+ end
146
+
147
+ test "POST / conforms to schema with 400 response code" do
148
+ post "/", params: {foo: "baz"}, as: :json
149
+ assert_conform_response_schema(400)
150
+ end
151
+ end
152
+ ```
153
+
97
154
  ## Alternatives
98
155
 
99
156
  - [openapi_first](https://github.com/ahx/openapi_first)
@@ -103,7 +160,6 @@ end
103
160
 
104
161
  - Full support for external `$ref`s
105
162
  - Full OpenAPI 3.1.0 support:
106
- - `discriminator` keyword
107
163
  - respect `style` and `explode` keywords
108
164
  - xml
109
165
  - Callbacks and webhooks validations
@@ -12,6 +12,8 @@ module Skooma
12
12
 
13
13
  registry.add_vocabulary(
14
14
  "https://spec.openapis.org/oas/3.1/vocab/base",
15
+ Skooma::Keywords::OAS31::Dialect::AnyOf,
16
+ Skooma::Keywords::OAS31::Dialect::OneOf,
15
17
  Skooma::Keywords::OAS31::Dialect::Discriminator,
16
18
  Skooma::Keywords::OAS31::Dialect::Xml,
17
19
  Skooma::Keywords::OAS31::Dialect::ExternalDocs,
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ module EnvMapper
5
+ class << self
6
+ PLAIN_HEADERS = %w[CONTENT_LENGTH CONTENT_TYPE].freeze
7
+ REGEXP_HTTP = /^HTTP_/.freeze
8
+
9
+ def call(env, response = nil, with_response: true, with_request: true)
10
+ result = {
11
+ "method" => env["REQUEST_METHOD"].downcase,
12
+ "path" => env["PATH_INFO"]
13
+ }
14
+ result["request"] = map_request(env) if with_request
15
+ result["response"] = map_response(response) if response && with_response
16
+
17
+ result
18
+ end
19
+
20
+ private
21
+
22
+ def map_request(env)
23
+ {
24
+ "query" => env["rack.request.query_string"] || env["QUERY_STRING"],
25
+ "headers" => env.select { |k, _| k.start_with?("HTTP_") || PLAIN_HEADERS.include?(k) }.transform_keys { |k| k.sub(REGEXP_HTTP, "").split("_").map(&:capitalize).join("-") },
26
+ "body" => env["RAW_POST_DATA"]
27
+ }
28
+ end
29
+
30
+ def map_response(response)
31
+ status, headers, body = response.to_a
32
+ full_body = +""
33
+ body.each { |chunk| full_body << chunk }
34
+ {
35
+ "status" => status,
36
+ "headers" => headers.to_h,
37
+ "body" => full_body
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Skooma
4
- class Inflector < Zeitwerk::Inflector
4
+ class Inflector < Zeitwerk::GemInflector
5
5
  STATIC_MAPPING = {
6
6
  "oas_3_1" => "OAS31",
7
7
  "openapi" => "OpenAPI",
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ module Keywords
5
+ module OAS31
6
+ module Dialect
7
+ class AnyOf < JSONSkooma::Keywords::Applicator::AnyOf
8
+ self.key = "anyOf"
9
+ self.value_schema = :array_of_schemas
10
+ self.depends_on = %w[discriminator]
11
+
12
+ def evaluate(instance, result)
13
+ discriminator_schema = result.sibling(instance, "discriminator")&.annotation
14
+ reorder_json(discriminator_schema)
15
+
16
+ super
17
+ end
18
+
19
+ private
20
+
21
+ def reorder_json(discriminator_schema)
22
+ return unless discriminator_schema
23
+
24
+ first = @json.delete_at(@json.index { |schema| resolve_uri(schema["$ref"]) == discriminator_schema })
25
+ @json.unshift first if first
26
+ end
27
+
28
+ def resolve_uri(uri)
29
+ uri = URI.parse(uri)
30
+ return uri if uri.absolute?
31
+
32
+ parent_schema.base_uri + uri if parent_schema.base_uri
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -4,8 +4,39 @@ module Skooma
4
4
  module Keywords
5
5
  module OAS31
6
6
  module Dialect
7
- class Discriminator < JSONSkooma::Keywords::BaseAnnotation
7
+ # Discriminator keyword is an annotation keyword,
8
+ # it does not affect validation of allOf/anyOf/oneOf schemas.
9
+ # See https://github.com/OAI/OpenAPI-Specification/pull/2618
10
+ class Discriminator < JSONSkooma::Keywords::Base
8
11
  self.key = "discriminator"
12
+
13
+ def evaluate(instance, result)
14
+ value = instance[json["propertyName"]]
15
+ uri = mapped_uri(value)
16
+ return result.failure("Could not resolve discriminator for value `#{value.inspect}`") if uri.nil?
17
+
18
+ parent_schema.registry.schema(
19
+ uri,
20
+ metaschema_uri: parent_schema.metaschema_uri,
21
+ cache_id: parent_schema.cache_id
22
+ )
23
+ result.annotate(uri)
24
+ rescue JSONSkooma::RegistryError => e
25
+ result.failure("Could not resolve discriminator mapping: #{e.message}")
26
+ end
27
+
28
+ private
29
+
30
+ def mapped_uri(value)
31
+ uri = json["mapping"]&.fetch(value, value)
32
+ return if uri.nil?
33
+
34
+ uri = "#/components/schemas/#{uri}" unless uri.start_with?("#") || uri.include?("/")
35
+ uri = URI.parse(uri)
36
+ return uri if uri.absolute?
37
+
38
+ parent_schema.base_uri + uri if parent_schema.base_uri
39
+ end
9
40
  end
10
41
  end
11
42
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ module Keywords
5
+ module OAS31
6
+ module Dialect
7
+ class OneOf < JSONSkooma::Keywords::Applicator::OneOf
8
+ self.key = "oneOf"
9
+ self.value_schema = :array_of_schemas
10
+ self.depends_on = %w[discriminator]
11
+
12
+ def evaluate(instance, result)
13
+ discriminator_schema = result.sibling(instance, "discriminator")&.annotation
14
+ reorder_json(discriminator_schema)
15
+
16
+ super
17
+ end
18
+
19
+ private
20
+
21
+ def reorder_json(discriminator_schema)
22
+ return unless discriminator_schema
23
+
24
+ first = @json.delete_at(@json.index { |schema| resolve_uri(schema["$ref"]) == discriminator_schema })
25
+ @json.unshift first if first
26
+ end
27
+
28
+ def resolve_uri(uri)
29
+ uri = URI.parse(uri)
30
+ return uri if uri.absolute?
31
+
32
+ parent_schema.base_uri + uri if parent_schema.base_uri
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ module Matchers
5
+ class BeValidDocument
6
+ def matches?(actual)
7
+ @actual = actual
8
+ return false unless comparable?
9
+
10
+ @result = @actual.validate
11
+ @result.valid?
12
+ end
13
+
14
+ def description
15
+ "be a valid OpenAPI document"
16
+ end
17
+
18
+ def failure_message
19
+ return "expected value to be an OpenAPI object" unless comparable?
20
+
21
+ <<~MSG
22
+ must valid against OpenAPI specification:
23
+ #{pretty(@result.output(:detailed))}
24
+ MSG
25
+ end
26
+
27
+ private
28
+
29
+ def pretty(result)
30
+ PP.pp(result, +"")
31
+ end
32
+
33
+ def comparable?
34
+ @actual.is_a?(Skooma::Objects::OpenAPI)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ module Matchers
5
+ class ConformRequestSchema
6
+ def initialize(schema, mapped_response)
7
+ @schema = schema
8
+ @mapped_response = mapped_response
9
+ end
10
+
11
+ def matches?(*)
12
+ @result = @schema.evaluate(@mapped_response)
13
+ @result.valid?
14
+ end
15
+
16
+ def description
17
+ "conform request schema"
18
+ end
19
+
20
+ def failure_message
21
+ <<~MSG
22
+ ENV:
23
+ #{pretty(@mapped_response)}
24
+
25
+ Validation Result:
26
+ #{pretty(@result.output(:skooma))}
27
+ MSG
28
+ end
29
+
30
+ private
31
+
32
+ def pretty(result)
33
+ PP.pp(result, +"")
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ module Matchers
5
+ class ConformResponseSchema < ConformRequestSchema
6
+ def initialize(schema, mapped_response, expected)
7
+ super(schema, mapped_response)
8
+ @expected = expected
9
+ end
10
+
11
+ def description
12
+ "conform response schema with #{@expected} response code"
13
+ end
14
+
15
+ def matches?(*)
16
+ return false unless status_matches?
17
+
18
+ super
19
+ end
20
+
21
+ def failure_message
22
+ return "Expected #{@expected} status code" unless status_matches?
23
+
24
+ super
25
+ end
26
+
27
+ private
28
+
29
+ def status_matches?
30
+ @mapped_response["response"]["status"] == @expected
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ module Matchers
5
+ class ConformSchema < ConformResponseSchema
6
+ def description
7
+ "conform schema with #{@expected} response code"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module Skooma
6
+ module Matchers
7
+ class Wrapper < Module
8
+ TEST_REGISTRY_NAME = "skooma_test_registry"
9
+
10
+ class << self
11
+ alias_method :[], :new
12
+ end
13
+
14
+ module DefaultHelperMethods
15
+ def mapped_response(with_response: true, with_request: true)
16
+ Skooma::EnvMapper.call(
17
+ request_object.env,
18
+ response_object,
19
+ with_response: with_response,
20
+ with_request: with_request
21
+ )
22
+ end
23
+
24
+ def request_object
25
+ # `rails` integration
26
+ return request if defined?(::ActionDispatch)
27
+ # `rack-test` integration
28
+ return last_request if defined?(::Rack::Test)
29
+
30
+ raise "Request object not found"
31
+ end
32
+
33
+ def response_object
34
+ # `rails` integration
35
+ return response if defined?(::ActionDispatch)
36
+ # `rack-test` integration
37
+ return last_response if defined?(::Rack::Test)
38
+
39
+ raise "Response object not found"
40
+ end
41
+ end
42
+
43
+ def initialize(helper_methods_module, openapi_path, base_uri: "https://skoomarb.dev/")
44
+ super()
45
+
46
+ registry = create_test_registry
47
+ pathname = Pathname.new(openapi_path)
48
+ registry.add_source(
49
+ base_uri,
50
+ JSONSkooma::Sources::Local.new(pathname.dirname.to_s)
51
+ )
52
+ schema = registry.schema(URI.parse("#{base_uri}#{pathname.basename}"), schema_class: Skooma::Objects::OpenAPI)
53
+
54
+ include DefaultHelperMethods
55
+ include helper_methods_module
56
+
57
+ define_method :skooma_openapi_schema do
58
+ schema
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def create_test_registry
65
+ JSONSkooma::Registry[TEST_REGISTRY_NAME]
66
+ rescue JSONSkooma::RegistryError
67
+ Skooma.create_registry(name: TEST_REGISTRY_NAME)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skooma
4
+ # Minitest helpers for OpenAPI schema validation
5
+ # @example
6
+ # describe TestApp do
7
+ # include Skooma::RSpec[Rails.root.join("docs", "openapi.yml")]
8
+ # # ...
9
+ # end
10
+ class Minitest < Matchers::Wrapper
11
+ module HelperMethods
12
+ def assert_conform_schema(expected_status)
13
+ matcher = Matchers::ConformSchema.new(skooma_openapi_schema, mapped_response, expected_status)
14
+
15
+ assert matcher.matches?, -> { matcher.failure_message }
16
+ end
17
+
18
+ def assert_conform_request_schema
19
+ matcher = Matchers::ConformRequestSchema.new(skooma_openapi_schema, mapped_response(with_response: false))
20
+
21
+ assert matcher.matches?, -> { matcher.failure_message }
22
+ end
23
+
24
+ def assert_conform_response_schema(expected_status)
25
+ matcher = Matchers::ConformResponseSchema.new(skooma_openapi_schema, mapped_response(with_request: false), expected_status)
26
+
27
+ assert matcher.matches?, -> { matcher.failure_message }
28
+ end
29
+
30
+ def assert_is_valid_document(document)
31
+ matcher = Matchers::BeValidDocument.new
32
+
33
+ assert matcher.matches?(document), -> { matcher.failure_message }
34
+ end
35
+ end
36
+
37
+ def initialize(openapi_path, **params)
38
+ super(HelperMethods, openapi_path, **params)
39
+ end
40
+ end
41
+ end
@@ -9,9 +9,11 @@ module Skooma
9
9
  module ValueParser
10
10
  class << self
11
11
  def call(instance, result)
12
- type = result.sibling(instance, "in").annotation
13
- key = result.sibling(instance, "name").annotation
14
- raise Error, "Missing key: #{key}" unless key
12
+ type = result.sibling(instance, "in")&.annotation
13
+ raise Error, "Missing `in` key #{result.path}" unless type
14
+
15
+ key = result.sibling(instance, "name")&.annotation
16
+ raise Error, "Missing `name` key #{instance.path}: #{key}" unless key
15
17
 
16
18
  case type
17
19
  when "query"
@@ -30,7 +32,7 @@ module Skooma
30
32
  when "cookie"
31
33
  # instance["headers"]["Cookie"]
32
34
  else
33
- raise Error, "Unknown location: #{result.sibling(instance, "in").annotation}"
35
+ raise Error, "Unknown location: #{type}"
34
36
  end
35
37
  end
36
38
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skooma
2
4
  module OutputFormat
3
5
  class << self
@@ -16,8 +18,9 @@ module Skooma
16
18
  "keywordLocation" => node.path.to_s
17
19
  }
18
20
 
19
- child_data = node.children.filter_map do |_, child|
20
- node_data(child) unless child.valid?
21
+ child_data = []
22
+ node.each_children do |child|
23
+ child_data << node_data(child) unless child.valid?
21
24
  end
22
25
 
23
26
  if first || child_data.length > 1
data/lib/skooma/rspec.rb CHANGED
@@ -1,166 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "yaml"
4
-
5
3
  module Skooma
6
4
  # RSpec matchers for OpenAPI schema validation
7
5
  # @example
8
- # require "skooma/rspec"
9
- # # ...
10
6
  # RSpec.configure do |config|
11
7
  # # ...
12
8
  # config.include Skooma::RSpec[Rails.root.join("docs", "openapi.yml")], type: :request
13
9
  # end
14
- class RSpec < Module
15
- class << self
16
- alias_method :[], :new
17
- end
18
-
19
- module Mapper
20
- PLAIN_HEADERS = %w[CONTENT_LENGTH CONTENT_TYPE].freeze
21
- REGEXP_HTTP = /^HTTP_/.freeze
22
-
23
- def mapped_response(response: true, request: true)
24
- result = {
25
- "method" => env["REQUEST_METHOD"].downcase,
26
- "path" => env["PATH_INFO"]
27
- }
28
-
29
- if response
30
- result["response"] = {
31
- "status" => response_data[0],
32
- "headers" => response_data[1],
33
- "body" => response_data[2]
34
- }
35
- end
36
-
37
- if request
38
- result["request"] = {
39
- "query" => env["rack.request.query_string"] || env["QUERY_STRING"],
40
- "headers" => env.select { |k, _| k.start_with?("HTTP_") || PLAIN_HEADERS.include?(k) }.transform_keys { |k| k.sub(REGEXP_HTTP, "").split("_").map(&:capitalize).join("-") },
41
- "body" => env["RAW_POST_DATA"]
42
- }
43
- end
44
-
45
- result
10
+ class RSpec < Matchers::Wrapper
11
+ module HelperMethods
12
+ def conform_schema(expected_status)
13
+ Matchers::ConformSchema.new(skooma_openapi_schema, mapped_response, expected_status)
46
14
  end
47
15
 
48
- def env
49
- request.env
16
+ def conform_response_schema(expected_status)
17
+ Matchers::ConformResponseSchema.new(skooma_openapi_schema, mapped_response(with_request: false), expected_status)
50
18
  end
51
19
 
52
- def response_data
53
- [response.status, response.headers.to_h, response.body]
20
+ def conform_request_schema
21
+ Matchers::ConformRequestSchema.new(skooma_openapi_schema, mapped_response(with_response: false))
54
22
  end
55
- end
56
-
57
- def initialize(openapi_path, base_uri: "https://skoomarb.dev/")
58
- super()
59
-
60
- pathname = Pathname.new(openapi_path)
61
-
62
- registry = Skooma.create_registry
63
- registry.add_source(
64
- base_uri,
65
- JSONSkooma::Sources::Local.new(pathname.dirname.to_s)
66
- )
67
- schema = registry.schema(URI.parse("#{base_uri}#{pathname.basename}"), schema_class: Skooma::Objects::OpenAPI)
68
23
 
69
- include Mapper
70
-
71
- define_method :skooma_openapi_schema do
72
- schema
24
+ def be_valid_document
25
+ Matchers::BeValidDocument.new
73
26
  end
74
27
  end
75
- end
76
-
77
- ::RSpec::Matchers.define(:conform_schema) do |expected_status|
78
- match do
79
- next false unless response.status == expected_status
80
-
81
- @result = skooma_openapi_schema.evaluate(mapped_response)
82
- @result.valid?
83
- end
84
-
85
- description do
86
- "conform schema with #{expected_status} response code"
87
- end
88
-
89
- failure_message do
90
- <<~MSG
91
- ENV:
92
- #{PP.pp(mapped_response, +"")}
93
-
94
- Validation Result:
95
- #{@result ? PP.pp(@result.output(:skooma), +"") : "Expected #{expected_status} status code"}
96
- MSG
97
- end
98
- end
99
-
100
- ::RSpec::Matchers.define(:conform_request_schema) do
101
- match do
102
- @result = skooma_openapi_schema.evaluate(mapped_response(response: false))
103
- @result.valid?
104
- end
105
-
106
- description do
107
- "conform request schema"
108
- end
109
-
110
- failure_message do
111
- <<~MSG
112
- ENV:
113
- #{PP.pp(mapped_response, +"")}
114
-
115
- Validation Result:
116
- #{@result ? PP.pp(@result.output(:skooma), +"") : "Expected #{expected_status} status code"}
117
- MSG
118
- end
119
- end
120
-
121
- ::RSpec::Matchers.define(:conform_response_schema) do |expected_status|
122
- match do
123
- next false unless response.status == expected_status
124
-
125
- @result = skooma_openapi_schema.evaluate(mapped_response(request: false))
126
- @result.valid?
127
- end
128
-
129
- description do
130
- "conform response schema with #{expected_status} response code"
131
- end
132
-
133
- failure_message do
134
- <<~MSG
135
- ENV:
136
- #{PP.pp(mapped_response, +"")}
137
-
138
- Validation Result:
139
- #{@result ? PP.pp(@result.output(:skooma), +"") : "Expected #{expected_status} status code"}
140
- MSG
141
- end
142
- end
143
-
144
- ::RSpec::Matchers.define(:be_valid_document) do
145
- match do |actual|
146
- @actual = actual
147
- next false unless comparable?
148
-
149
- @result = actual.validate
150
- @result.valid?
151
- end
152
-
153
- description { "be a valid OpenAPI document" }
154
-
155
- failure_message do
156
- next "expected value to be an OpenAPI object" unless comparable?
157
-
158
- pretty_output = PP.pp(@result.output(:detailed), +"")
159
- "must valid against OpenAPI specification:\n#{pretty_output}"
160
- end
161
28
 
162
- def comparable?
163
- actual.is_a?(Skooma::Objects::OpenAPI)
29
+ def initialize(openapi_path, **params)
30
+ super(HelperMethods, openapi_path, **params)
164
31
  end
165
32
  end
166
33
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skooma
2
4
  module Validators
3
5
  class Double < JSONSkooma::Validators::Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skooma
2
4
  module Validators
3
5
  class Float < JSONSkooma::Validators::Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skooma
2
4
  module Validators
3
5
  class Int32 < JSONSkooma::Validators::Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skooma
2
4
  module Validators
3
5
  class Int64 < JSONSkooma::Validators::Base
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Skooma
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/skooma.rb CHANGED
@@ -6,7 +6,7 @@ require "zeitwerk"
6
6
  require_relative "skooma/inflector"
7
7
 
8
8
  loader = Zeitwerk::Loader.for_gem
9
- loader.inflector = Skooma::Inflector.new
9
+ loader.inflector = Skooma::Inflector.new(__FILE__)
10
10
  loader.setup
11
11
 
12
12
  module Skooma
@@ -19,8 +19,8 @@ module Skooma
19
19
  JSONSkooma::Formatters.register :skooma, OutputFormat
20
20
 
21
21
  class << self
22
- def create_registry
23
- JSONSkooma.create_registry("2020-12", "oas-3.1", name: REGISTRY_NAME, assert_formats: true)
22
+ def create_registry(name: REGISTRY_NAME)
23
+ JSONSkooma.create_registry("2020-12", "oas-3.1", name: name, assert_formats: true)
24
24
  end
25
25
  end
26
26
  end
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.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svyatoslav Kryukov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-26 00:00:00.000000000 Z
11
+ date: 2023-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -30,17 +30,17 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.1'
33
+ version: '0.2'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.1'
40
+ version: '0.2'
41
41
  description: I bring some sugar for your APIs.
42
42
  email:
43
- - s.g.kryukov@yandex.ru
43
+ - me@skryukov.dev
44
44
  executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
@@ -55,14 +55,23 @@ files:
55
55
  - lib/skooma.rb
56
56
  - lib/skooma/body_parsers.rb
57
57
  - lib/skooma/dialects/oas_3_1.rb
58
+ - lib/skooma/env_mapper.rb
58
59
  - lib/skooma/inflector.rb
59
60
  - lib/skooma/instance.rb
60
61
  - lib/skooma/keywords/oas_3_1.rb
62
+ - lib/skooma/keywords/oas_3_1/dialect/any_of.rb
61
63
  - lib/skooma/keywords/oas_3_1/dialect/discriminator.rb
62
64
  - lib/skooma/keywords/oas_3_1/dialect/example.rb
63
65
  - lib/skooma/keywords/oas_3_1/dialect/external_docs.rb
66
+ - lib/skooma/keywords/oas_3_1/dialect/one_of.rb
64
67
  - lib/skooma/keywords/oas_3_1/dialect/xml.rb
65
68
  - lib/skooma/keywords/oas_3_1/schema.rb
69
+ - lib/skooma/matchers/be_valid_document.rb
70
+ - lib/skooma/matchers/conform_request_schema.rb
71
+ - lib/skooma/matchers/conform_response_schema.rb
72
+ - lib/skooma/matchers/conform_schema.rb
73
+ - lib/skooma/matchers/wrapper.rb
74
+ - lib/skooma/minitest.rb
66
75
  - lib/skooma/objects/base.rb
67
76
  - lib/skooma/objects/base/keywords/deprecated.rb
68
77
  - lib/skooma/objects/base/keywords/description.rb