skooma 0.2.1 → 0.2.3

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: eef2c83a80d1526de1a465acbb29b70fc053b6b5e54ed1e6b6ed131f56a8fe0a
4
- data.tar.gz: 0c4cdec06703bdeb6fd3cf6922401552afaa13aa20b9189e472e703288ba60aa
3
+ metadata.gz: 6263d8960b9a3b49287103608c6770c967b6fadfcf58d826df2a492c4dedbd35
4
+ data.tar.gz: 5dd2c141d4baa7f0b95c1b3f3fbfd1afdafaae0097a1139956ba1c7e45f876a1
5
5
  SHA512:
6
- metadata.gz: b5089b0a710836b072b723014cfaaf88b4504853f09a42ddf6a6f9e36ed50271b082dfcc184e047f2bf4481e6a343b4bebbd3bced7dfcec2c5a4f85697fbf113
7
- data.tar.gz: aacb7329ef950a1fc9ff87513a85803e5530cacaec97f0dad1330bf20fb45e9bc339fe9004af11e1b90cfa6cbb725a2cd339a69c6a23721b667e8d10959b37fb
6
+ metadata.gz: e5ddc4010685045a5b2a9908597096af07fb5ac21cce3e1c456568111fed4fc2fb708603be0a47dbd6e61bec0d64e0d85e27841ab36583f0fb61b73759d3f05b
7
+ data.tar.gz: 1210a2ed7f56206369242baa147795acd3a6bc77aa6b719cb3ccc26ab33367a775921d0d94fd8741b651274afdc9183c20af194e4a289190d5968de239a9ae56
data/CHANGELOG.md CHANGED
@@ -7,6 +7,42 @@ and this project adheres to [Semantic Versioning].
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.3] - 2024-01-18
11
+
12
+ ### Added
13
+
14
+ - Add support for multiple OpenAPI documents. ([@skryukov])
15
+
16
+ ### Fixed
17
+
18
+ - Fix `Skooma::Error: Missing name key /request` by setting `content` and `required` keyword dependencies. ([@skryukov])
19
+
20
+ ## [0.2.2] - 2024-01-04
21
+
22
+ ### Added
23
+
24
+ - Add support for APIs mounted under a path prefix. ([@skryukov])
25
+
26
+ ```ruby
27
+ # spec/rails_helper.rb
28
+
29
+ RSpec.configure do |config|
30
+ # ...
31
+ path_to_openapi = Rails.root.join("docs", "openapi.yml")
32
+ # pass path_prefix option if your API is mounted under a prefix:
33
+ config.include Skooma::RSpec[path_to_openapi, path_prefix: "/internal/api"], type: :request
34
+ end
35
+ ```
36
+
37
+ ### Changed
38
+
39
+ - Bump `json_skooma` version to `~> 0.2.0`. ([@skryukov])
40
+
41
+ ### Fixed
42
+
43
+ - Better checks to automatic request/response detection to prevent methods overrides via RSpec helpers (i.e. `subject(:response)`). ([@skryukov])
44
+ - Fail response validation when expected response code or `responses` keyword aren't listed. ([@skryukov])
45
+
10
46
  ## [0.2.1] - 2023-10-23
11
47
 
12
48
  ### Fixed
@@ -33,7 +69,9 @@ and this project adheres to [Semantic Versioning].
33
69
 
34
70
  [@skryukov]: https://github.com/skryukov
35
71
 
36
- [Unreleased]: https://github.com/skryukov/skooma/compare/v0.2.1...HEAD
72
+ [Unreleased]: https://github.com/skryukov/skooma/compare/v0.2.3...HEAD
73
+ [0.2.3]: https://github.com/skryukov/skooma/compare/v0.2.2...v0.2.3
74
+ [0.2.2]: https://github.com/skryukov/skooma/compare/v0.2.1...v0.2.2
37
75
  [0.2.1]: https://github.com/skryukov/skooma/compare/v0.2.0...v0.2.1
38
76
  [0.2.0]: https://github.com/skryukov/skooma/compare/v0.1.0...v0.2.0
39
77
  [0.1.0]: https://github.com/skryukov/skooma/commits/v0.1.0
data/README.md CHANGED
@@ -14,6 +14,10 @@ Skooma is a Ruby library for validating API implementations against OpenAPI docu
14
14
  - Supports request/response validations against OpenAPI document
15
15
  - Includes RSpec and Minitest helpers
16
16
 
17
+ ### Learn more
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
20
+
17
21
  <a href="https://evilmartians.com/?utm_source=skooma&utm_campaign=project_page">
18
22
  <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
19
23
  </a>
@@ -44,6 +48,9 @@ RSpec.configure do |config|
44
48
  # ...
45
49
  path_to_openapi = Rails.root.join("docs", "openapi.yml")
46
50
  config.include Skooma::RSpec[path_to_openapi], type: :request
51
+
52
+ # OR pass path_prefix option if your API is mounted under a prefix:
53
+ config.include Skooma::RSpec[path_to_openapi, path_prefix: "/internal/api"], type: :request
47
54
  end
48
55
  ```
49
56
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pp"
4
+
3
5
  module Skooma
4
6
  module Matchers
5
7
  class BeValidDocument
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pp"
4
+
3
5
  module Skooma
4
6
  module Matchers
5
7
  class ConformRequestSchema
@@ -19,7 +19,7 @@ module Skooma
19
19
  end
20
20
 
21
21
  def failure_message
22
- return "Expected #{@expected} status code" unless status_matches?
22
+ return "Expected #{@expected} status code, but got #{@mapped_response["response"]["status"]}" unless status_matches?
23
23
 
24
24
  super
25
25
  end
@@ -23,33 +23,36 @@ module Skooma
23
23
 
24
24
  def request_object
25
25
  # `rails` integration
26
- return request if defined?(::ActionDispatch)
26
+ return @request if defined?(::ActionDispatch) && @request.is_a?(::ActionDispatch::Request)
27
27
  # `rack-test` integration
28
- return last_request if defined?(::Rack::Test)
28
+ return last_request if defined?(::Rack::Test) && defined?(:last_request)
29
29
 
30
30
  raise "Request object not found"
31
31
  end
32
32
 
33
33
  def response_object
34
34
  # `rails` integration
35
- return response if defined?(::ActionDispatch)
35
+ return @response if defined?(::ActionDispatch) && @response.is_a?(::ActionDispatch::Response)
36
36
  # `rack-test` integration
37
- return last_response if defined?(::Rack::Test)
37
+ return last_response if defined?(::Rack::Test) && defined?(:last_response)
38
38
 
39
39
  raise "Response object not found"
40
40
  end
41
41
  end
42
42
 
43
- def initialize(helper_methods_module, openapi_path, base_uri: "https://skoomarb.dev/")
43
+ def initialize(helper_methods_module, openapi_path, base_uri: "https://skoomarb.dev/", path_prefix: "")
44
44
  super()
45
45
 
46
46
  registry = create_test_registry
47
47
  pathname = Pathname.new(openapi_path)
48
+ source_uri = "#{base_uri}#{path_prefix.delete_suffix("/")}"
49
+ source_uri += "/" unless source_uri.end_with?("/")
48
50
  registry.add_source(
49
- base_uri,
51
+ source_uri,
50
52
  JSONSkooma::Sources::Local.new(pathname.dirname.to_s)
51
53
  )
52
- schema = registry.schema(URI.parse("#{base_uri}#{pathname.basename}"), schema_class: Skooma::Objects::OpenAPI)
54
+ schema = registry.schema(URI.parse("#{source_uri}#{pathname.basename}"), schema_class: Skooma::Objects::OpenAPI)
55
+ schema.path_prefix = path_prefix
53
56
 
54
57
  include DefaultHelperMethods
55
58
  include helper_methods_module
@@ -32,7 +32,7 @@ module Skooma
32
32
  subresult.annotate({"path_attributes" => attributes})
33
33
  path_schema.evaluate(instance, subresult)
34
34
 
35
- if subresult.passed?
35
+ if subresult.passed? && subresult.children.any?
36
36
  result.success
37
37
  else
38
38
  result.failure("Path #{instance["path"]} is invalid")
@@ -43,6 +43,7 @@ module Skooma
43
43
  private
44
44
 
45
45
  def find_route(instance_path)
46
+ instance_path = instance_path.delete_prefix(json.root.path_prefix)
46
47
  return [instance_path, {}, json[instance_path]] if json.key?(instance_path)
47
48
 
48
49
  @regexp_map.reduce(nil) do |result, (path, path_regex, subschema)|
@@ -31,6 +31,18 @@ module Skooma
31
31
  super(Instance.new(instance), result)
32
32
  end
33
33
 
34
+ def path_prefix=(value)
35
+ raise ArgumentError, "Path prefix must be a string" unless value.is_a?(String)
36
+
37
+ @path_prefix = value
38
+ @path_prefix = "/#{@path_prefix}" unless @path_prefix.start_with?("/")
39
+ @path_prefix = @path_prefix.delete_suffix("/") if @path_prefix.end_with?("/")
40
+ end
41
+
42
+ def path_prefix
43
+ @path_prefix || ""
44
+ end
45
+
34
46
  def json_schema_dialect_uri
35
47
  @json_schema_dialect_uri || parent_schema&.json_schema_dialect_uri
36
48
  end
@@ -12,7 +12,7 @@ module Skooma
12
12
  def initialize(parent_schema, value)
13
13
  super
14
14
  keys = json.filter_map { |v| v["in"] && [v["in"].value, v["name"].value] }
15
- parent_params = (parent_schema.parent["parameters"] || [])
15
+ parent_params = parent_schema.parent["parameters"] || []
16
16
  parent_params.reject! do |v|
17
17
  v["in"] && keys.include?([v["in"].value, v["name"].value])
18
18
  end
@@ -8,6 +8,7 @@ module Skooma
8
8
  self.key = "content"
9
9
  self.value_schema = :object_of_schemas
10
10
  self.schema_value_class = Objects::MediaType
11
+ self.depends_on = %w[in name style explode allowReserved allowEmptyValue]
11
12
 
12
13
  def evaluate(instance, result)
13
14
  return if instance.value.nil?
@@ -6,6 +6,7 @@ module Skooma
6
6
  module Keywords
7
7
  class Required < JSONSkooma::Keywords::Base
8
8
  self.key = "required"
9
+ self.depends_on = %w[in name style explode allowReserved allowEmptyValue]
9
10
 
10
11
  def evaluate(instance, result)
11
12
  if json.value && ValueParser.call(instance, result)&.value.nil?
@@ -13,7 +13,7 @@ module Skooma
13
13
  raise Error, "Missing `in` key #{result.path}" unless type
14
14
 
15
15
  key = result.sibling(instance, "name")&.annotation
16
- raise Error, "Missing `name` key #{instance.path}: #{key}" unless key
16
+ raise Error, "Missing `name` key #{result.path}" unless key
17
17
 
18
18
  case type
19
19
  when "query"
@@ -8,6 +8,10 @@ module Skooma
8
8
  def evaluate(instance, result)
9
9
  return result.discard unless instance["method"] == key
10
10
 
11
+ if json["responses"].nil? && instance["response"]
12
+ return result.failure("Responses are not listed for #{key.upcase} #{instance["path"]}")
13
+ end
14
+
11
15
  json.evaluate(instance, result)
12
16
  return result.success if result.passed?
13
17
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Skooma
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.3"
5
5
  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.2.1
4
+ version: 0.2.3
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-10-23 00:00:00.000000000 Z
11
+ date: 2024-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -30,15 +30,15 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.2'
33
+ version: 0.2.0
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.2'
41
- description: I bring some sugar for your APIs.
40
+ version: 0.2.0
41
+ description: Apply a documentation-first approach to API development.
42
42
  email:
43
43
  - me@skryukov.dev
44
44
  executables: []
@@ -166,5 +166,5 @@ requirements: []
166
166
  rubygems_version: 3.3.7
167
167
  signing_key:
168
168
  specification_version: 4
169
- summary: I bring some sugar for your APIs.
169
+ summary: Validate API implementations against OpenAPI documents.
170
170
  test_files: []