swagcov 0.5.0 → 0.6.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: 10f7019116f2fb27f9bcf01ed15450bb26e715778722c26448571ed006fb1675
4
- data.tar.gz: 35d038789bbdb47d93720fa0ab5e07d95eefc92810145e083fb3076e6f151e13
3
+ metadata.gz: ba29f88b400a4b44a37ee42725f70a5a97aa8ec9186de531ea13de33e8fec205
4
+ data.tar.gz: 6651caaa48d8fa61bd90f1598cfd3d78b8d6612ca6f14f76360ef8d85a256828
5
5
  SHA512:
6
- metadata.gz: 53e051e7b1c1b50b1cf23f0774fdd13aad99619463e7838ef8c5834d7e172fb5948e682813787ac22bfac8db64452dc4405ebc436d0266a99935915b012ebc90
7
- data.tar.gz: bc67b90db65336931ba6895ad5ee51f63ad3aea5855b2e44f53e05a6c7b7cc57746f483f5d2c24bb651841f437b4c6d5c09bfec77d419cb0cf5e15f7b119f53d
6
+ metadata.gz: 86b8e97506c580b7981090803aa41a609d1c1396613a57da1faf7c66864c40ac0337675fa009a5c48311ceff89bf8e6f1eb683d098a0153525a71d95cdb9c124
7
+ data.tar.gz: e62b2919cb26ff819d28abf200fce58d23b7f509b47200114ce6f69bc23d086fb2399b21be6e0363a6a15102f845d6ad5dc003e5fb36f175ab1c87947b21a68a
data/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
  ## main (unreleased)
3
3
  -
4
4
 
5
+ ## 0.6.0 (2025-04-09)
6
+ ### Fix
7
+ - Grammatical number for endpoint(s) count output ([#78](https://github.com/smridge/swagcov/pull/78))
8
+
9
+ ### Enhancement
10
+ - Add support for Rails 4.2 ([#72](https://github.com/smridge/swagcov/pull/72))
11
+
12
+ ### Refactor
13
+ - `BREAKING CHANGE`: `Swagcov::BadConfigurationError` is now `Swagcov::Errors::BadConfiguration` ([#74](https://github.com/smridge/swagcov/pull/74))
14
+ - `BREAKING CHANGE`: `Swagcov::VERSION` is now `Swagcov::Version::STRING` ([#77](https://github.com/smridge/swagcov/pull/77))
15
+ - Improve path matching processing for `ignore` and `only` routes ([#65](https://github.com/smridge/swagcov/pull/65))
16
+
17
+ ### Code Coverage
18
+ - Add test coverage reporting ([#68](https://github.com/smridge/swagcov/pull/68), [#69](https://github.com/smridge/swagcov/pull/69))
19
+
5
20
  ## 0.5.0 (2025-03-26)
6
21
  ### Enhancement
7
22
  - Add rake task for configuration installation ([#59](https://github.com/smridge/swagcov/pull/59))
@@ -23,7 +38,7 @@
23
38
  - Output width for better layout alignment ([#48](https://github.com/smridge/swagcov/pull/48))
24
39
 
25
40
  ### Code Coverage
26
- - Added official support for ruby 3.3 and 3.4 and rails 7.1, 7.2, 8.0. See [unit_tests.yml](/.github/workflows/unit_tests.yml) and [system_tests.yml](/.github/workflows/system_tests.yml) for detail
41
+ - Added official support for ruby 3.3 and 3.4 and rails 7.1, 7.2, 8.0. See [tests.yml](/.github/workflows/tests.yml) for detail
27
42
 
28
43
  ## 0.4.0 (2022-08-11)
29
44
  - Improve OpenAPI file processing ([#26](https://github.com/smridge/swagcov/pull/26))
data/README.md CHANGED
@@ -4,12 +4,30 @@
4
4
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop-hq/rubocop)
5
5
  [![GitHub License](https://img.shields.io/github/license/smridge/swagcov.svg)](https://github.com/smridge/swagcov/blob/main/LICENSE)
6
6
 
7
+ ![Tests Build](https://github.com/smridge/swagcov/actions/workflows/tests.yml/badge.svg)
8
+ ![Linting Build](https://github.com/smridge/swagcov/actions/workflows/linting.yml/badge.svg)
9
+ ![CodeQL Build](https://github.com/smridge/swagcov/actions/workflows/codeql-analysis.yml/badge.svg)
10
+ [![Coverage Status](https://coveralls.io/repos/github/smridge/swagcov/badge.svg?branch=main)](https://coveralls.io/github/smridge/swagcov?branch=main)
11
+
7
12
  See OpenAPI documentation coverage report for Rails Routes.
8
13
 
9
14
  ## Usages
10
15
  - See overview of different endpoints covered, missing and what you choose to ignore.
11
16
  - Add pass/fail to your build pipeline when missing Documentation Coverage.
12
17
 
18
+ ## Ruby and Rails Version Support
19
+ Versioning support from a test coverage perspective, see [tests.yml](/.github/workflows/tests.yml) for detail
20
+ | `ruby -v` | `rails 4.2` | `rails 5.0` | `rails 5.1` | `rails 5.2` | `rails 6.0` | `rails 6.1` | `rails 7.0` | `rails 7.1` | `rails 7.2` | `rails 8.0` |
21
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
22
+ | `2.5` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
23
+ | `2.6` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
24
+ | `2.7` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
25
+ | `3.0` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
26
+ | `3.1` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
27
+ | `3.2` | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
28
+ | `3.3` | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
29
+ | `3.4` | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
30
+
13
31
  ## Installation
14
32
  Add this line to your application's Gemfile:
15
33
  ```ruby
@@ -61,6 +79,8 @@ bundle exec rake swagcov:install
61
79
  - ^/v1
62
80
  ignore:
63
81
  - /v1/foobar/:token
82
+ - /v1/foobar:
83
+ - GET
64
84
  ```
65
85
 
66
86
  Execute:
@@ -108,9 +128,6 @@ bundle exec rake swagcov
108
128
  ```
109
129
  <img src="https://raw.githubusercontent.com/smridge/swagcov/main/images/ignore-and-only-endpoints.png">
110
130
 
111
- ## TODO
112
- - Add autogeneration of ignore paths
113
-
114
131
  ## Contributing
115
132
  See [CONTRIBUTING.md](CONTRIBUTING.md) for detail
116
133
 
@@ -2,28 +2,31 @@
2
2
 
3
3
  module Swagcov
4
4
  class Coverage
5
- attr_reader :total, :covered, :ignored, :routes_not_covered, :routes_covered, :routes_ignored
6
-
7
- def initialize dotfile: Swagcov::Dotfile.new, routes: ::Rails.application.routes.routes
8
- @total = 0
9
- @covered = 0
10
- @ignored = 0
11
- @routes_not_covered = []
12
- @routes_covered = []
13
- @routes_ignored = []
5
+ attr_reader :data
6
+
7
+ def initialize dotfile: ::Swagcov::Dotfile.new, routes: ::Rails.application.routes.routes
14
8
  @dotfile = dotfile
15
9
  @routes = routes
10
+ @data = {
11
+ covered: [],
12
+ ignored: [],
13
+ uncovered: [],
14
+ total_count: 0,
15
+ covered_count: 0,
16
+ ignored_count: 0,
17
+ uncovered_count: 0
18
+ }
16
19
  end
17
20
 
18
21
  def report
19
22
  collect_coverage
20
- routes_output(@routes_covered, "green")
21
- routes_output(@routes_ignored, "yellow")
22
- routes_output(@routes_not_covered, "red")
23
+ routes_output(@data[:covered], "green")
24
+ routes_output(@data[:ignored], "yellow")
25
+ routes_output(@data[:uncovered], "red")
23
26
 
24
27
  final_output
25
28
 
26
- @total - @covered
29
+ @data[:uncovered_count]
27
30
  end
28
31
 
29
32
  private
@@ -31,36 +34,36 @@ module Swagcov
31
34
  attr_reader :dotfile
32
35
 
33
36
  def collect_coverage
34
- openapi_files = ::Swagcov::OpenapiFiles.new(filepaths: dotfile.doc_paths)
37
+ openapi_files = ::Swagcov::OpenapiFiles.new(filepaths: dotfile.docs_config)
38
+ rails_version = ::Rails::VERSION::STRING
35
39
 
36
40
  @routes.each do |route|
37
41
  path = route.path.spec.to_s.chomp("(.:format)")
42
+ verb = rails_version > "5" ? route.verb : route.verb.inspect.gsub(%r{[$^/]}, "")
38
43
 
39
- next if third_party_route?(route, path)
44
+ next if third_party_route?(route, path, rails_version)
40
45
 
41
- if dotfile.ignore_path?(path, verb: route.verb)
42
- @ignored += 1
43
- @routes_ignored << { verb: route.verb, path: path, status: "ignored" }
46
+ if dotfile.ignore_path?(path, verb: verb)
47
+ update_data(:ignored, verb, path, "ignored")
44
48
  next
45
49
  end
46
50
 
47
51
  next if dotfile.only_path_mismatch?(path)
48
52
 
49
- @total += 1
53
+ @data[:total_count] += 1
50
54
 
51
- if (response_keys = openapi_files.find_response_keys(path: path, route_verb: route.verb))
52
- @covered += 1
53
- @routes_covered << { verb: route.verb, path: path, status: response_keys.join(" ") }
55
+ if (response_keys = openapi_files.find_response_keys(path: path, route_verb: verb))
56
+ update_data(:covered, verb, path, response_keys.join(" "))
54
57
  else
55
- @routes_not_covered << { verb: route.verb, path: path, status: "none" }
58
+ update_data(:uncovered, verb, path, "none")
56
59
  end
57
60
  end
58
61
  end
59
62
 
60
- def third_party_route? route, path
63
+ def third_party_route? route, path, rails_version
61
64
  # https://github.com/rails/rails/blob/48f3c3e201b57a4832314b2c957a3b303e89bfea/actionpack/lib/action_dispatch/routing/inspector.rb#L105-L107
62
65
  # Skips route paths like ["/rails/info/properties", "/rails/info", "/rails/mailers"]
63
- route.internal ||
66
+ internal_rails_route?(route, rails_version) ||
64
67
 
65
68
  # Skips routes like "/sidekiq"
66
69
  route.verb.blank? ||
@@ -71,6 +74,19 @@ module Swagcov
71
74
  path.include?("/active_storage/") || path.include?("/action_mailbox/")
72
75
  end
73
76
 
77
+ def internal_rails_route? route, rails_version
78
+ if rails_version > "5"
79
+ route.internal
80
+ else
81
+ ::ActionDispatch::Routing::RouteWrapper.new(route).internal?
82
+ end
83
+ end
84
+
85
+ def update_data key, verb, path, status
86
+ @data[:"#{key}_count"] += 1
87
+ @data[key] << { verb: verb, path: path, status: status }
88
+ end
89
+
74
90
  def routes_output routes, status_color
75
91
  routes.each do |route|
76
92
  $stdout.puts(
@@ -84,9 +100,9 @@ module Swagcov
84
100
 
85
101
  def min_width key
86
102
  strings =
87
- @routes_covered.map { |hash| hash[key] } +
88
- @routes_ignored.map { |hash| hash[key] } +
89
- @routes_not_covered.map { |hash| hash[key] }
103
+ @data[:covered].map { |hash| hash[key] } +
104
+ @data[:ignored].map { |hash| hash[key] } +
105
+ @data[:uncovered].map { |hash| hash[key] }
90
106
 
91
107
  strings.max_by(&:length).size
92
108
  end
@@ -96,37 +112,33 @@ module Swagcov
96
112
  $stdout.puts(
97
113
  format(
98
114
  "OpenAPI documentation coverage %<percentage>.2f%% (%<covered>d/%<total>d)",
99
- { percentage: 100.0 * @covered / @total, covered: @covered, total: @total }
100
- )
101
- )
102
-
103
- $stdout.puts(
104
- format(
105
- "%<total>s endpoints ignored",
106
- { total: @ignored.to_s.yellow }
115
+ {
116
+ percentage: 100.0 * @data[:covered_count] / @data[:total_count],
117
+ covered: @data[:covered_count],
118
+ total: @data[:total_count]
119
+ }
107
120
  )
108
121
  )
109
122
 
110
- $stdout.puts(
111
- format(
112
- "%<total>s endpoints checked",
113
- { total: @total.to_s.blue }
114
- )
115
- )
123
+ count_output
124
+ end
116
125
 
117
- $stdout.puts(
118
- format(
119
- "%<covered>s endpoints covered",
120
- { covered: @covered.to_s.green }
121
- )
122
- )
126
+ def count_output
127
+ {
128
+ ignored: "yellow",
129
+ total: "blue",
130
+ covered: "green",
131
+ uncovered: "red"
132
+ }.each do |key, color|
133
+ count = @data[:"#{key}_count"]
123
134
 
124
- $stdout.puts(
125
- format(
126
- "%<missing>s endpoints missing documentation",
127
- { missing: (@total - @covered).to_s.red }
135
+ $stdout.puts(
136
+ format(
137
+ "%<status>s #{key} #{count == 1 ? 'endpoint' : 'endpoints'}",
138
+ { status: count.to_s.send(color) }
139
+ )
128
140
  )
129
- )
141
+ end
130
142
  end
131
143
  end
132
144
  end
@@ -1,22 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Swagcov
4
- class BadConfigurationError < RuntimeError
5
- end
6
-
7
4
  class Dotfile
8
5
  DEFAULT_CONFIG_FILE_NAME = ".swagcov.yml"
9
6
 
10
7
  def initialize pathname: ::Rails.root.join(DEFAULT_CONFIG_FILE_NAME)
11
8
  @dotfile = load_yaml(pathname)
12
9
 
13
- raise BadConfigurationError, "Invalid config file (#{DEFAULT_CONFIG_FILE_NAME})" unless valid?
10
+ raise ::Swagcov::Errors::BadConfiguration, "Invalid config file (#{DEFAULT_CONFIG_FILE_NAME})" unless valid?
11
+
12
+ @ignored_regex = path_config_regex(ignored_config)
13
+ @only_regex = path_config_regex(only_config)
14
14
  end
15
15
 
16
16
  def ignore_path? path, verb:
17
- ignore_all_path_actions = ignored_regex&.match?(path)
17
+ return false unless @ignored_config
18
18
 
19
- ignored_verbs = ignored_config&.find { |config| config[path] }
19
+ ignore_all_path_actions = @ignored_regex.match?(path)
20
+
21
+ ignored_verbs = @ignored_config.find { |config| config[path] }
20
22
 
21
23
  return ignore_all_path_actions unless ignored_verbs.is_a?(::Hash)
22
24
 
@@ -24,11 +26,19 @@ module Swagcov
24
26
  end
25
27
 
26
28
  def only_path_mismatch? path
27
- only_regex && !only_regex.match?(path)
29
+ @only_config && !@only_regex.match?(path)
30
+ end
31
+
32
+ def docs_config
33
+ @docs_config ||= dotfile.dig("docs", "paths")
34
+ end
35
+
36
+ def ignored_config
37
+ @ignored_config ||= dotfile.dig("routes", "paths", "ignore")
28
38
  end
29
39
 
30
- def doc_paths
31
- dotfile.dig("docs", "paths")
40
+ def only_config
41
+ @only_config ||= dotfile.dig("routes", "paths", "only")
32
42
  end
33
43
 
34
44
  private
@@ -36,23 +46,13 @@ module Swagcov
36
46
  attr_reader :dotfile
37
47
 
38
48
  def load_yaml pathname
39
- raise BadConfigurationError, "Missing config file (#{DEFAULT_CONFIG_FILE_NAME})" unless pathname.exist?
49
+ unless pathname.exist?
50
+ raise ::Swagcov::Errors::BadConfiguration, "Missing config file (#{DEFAULT_CONFIG_FILE_NAME})"
51
+ end
40
52
 
41
- YAML.load_file(pathname)
53
+ ::YAML.load_file(pathname)
42
54
  rescue Psych::SyntaxError
43
- raise BadConfigurationError, "Malinformed config file (#{DEFAULT_CONFIG_FILE_NAME})"
44
- end
45
-
46
- def ignored_regex
47
- @ignored_regex ||= path_config_regex(ignored_config)
48
- end
49
-
50
- def ignored_config
51
- @ignored_config ||= dotfile.dig("routes", "paths", "ignore")
52
- end
53
-
54
- def only_regex
55
- @only_regex ||= path_config_regex(dotfile.dig("routes", "paths", "only"))
55
+ raise ::Swagcov::Errors::BadConfiguration, "Malinformed config file (#{DEFAULT_CONFIG_FILE_NAME})"
56
56
  end
57
57
 
58
58
  def path_config_regex path_config
@@ -71,7 +71,7 @@ module Swagcov
71
71
  end
72
72
 
73
73
  def valid?
74
- dotfile && doc_paths
74
+ dotfile && docs_config
75
75
  end
76
76
  end
77
77
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Swagcov
4
+ module Errors
5
+ class BadConfiguration < ::RuntimeError
6
+ end
7
+ end
8
+ end
@@ -9,7 +9,7 @@ module Swagcov
9
9
 
10
10
  def find_response_keys path:, route_verb:
11
11
  # replace :id with {id}
12
- regex = Regexp.new("^#{path.gsub(%r{:[^/]+}, '\\{[^/]+\\}')}?$")
12
+ regex = ::Regexp.new("^#{path.gsub(%r{:[^/]+}, '\\{[^/]+\\}')}?$")
13
13
 
14
14
  matching_paths_key = @openapi_paths.keys.grep(regex).first
15
15
  matching_request_method_key = @openapi_paths.dig(matching_paths_key, route_verb.downcase)
@@ -20,15 +20,15 @@ module Swagcov
20
20
  private
21
21
 
22
22
  def load_yamls
23
- Dir.glob(@filepaths).reduce({}) do |hash, filepath|
23
+ ::Dir.glob(@filepaths).reduce({}) do |hash, filepath|
24
24
  hash.merge(load_yaml(filepath))
25
25
  end
26
26
  end
27
27
 
28
28
  def load_yaml filepath
29
- YAML.load_file(filepath)["paths"]
30
- rescue Psych::SyntaxError
31
- raise BadConfigurationError, "Malinformed openapi file (#{filepath})"
29
+ ::YAML.load_file(filepath)["paths"]
30
+ rescue ::Psych::SyntaxError
31
+ raise ::Swagcov::Errors::BadConfiguration, "Malinformed openapi file (#{filepath})"
32
32
  end
33
33
  end
34
34
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Swagcov
4
- VERSION = "0.5.0"
4
+ module Version
5
+ STRING = "0.6.0"
6
+ end
5
7
  end
data/lib/swagcov.rb CHANGED
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "swagcov/version"
4
-
5
- if defined?(Rails)
6
- require "swagcov/railtie"
7
- require "swagcov/dotfile"
8
- require "swagcov/coverage"
9
- require "swagcov/openapi_files"
10
- require "swagcov/install"
11
- end
3
+ require "rails"
4
+ require "active_support/core_ext"
12
5
 
13
6
  require "swagcov/core_ext/string"
7
+ require "swagcov/coverage"
8
+ require "swagcov/dotfile"
9
+ require "swagcov/errors"
10
+ require "swagcov/install"
11
+ require "swagcov/openapi_files"
12
+ require "swagcov/railtie"
13
+ require "swagcov/version"
14
14
 
15
15
  module Swagcov
16
16
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swagcov
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sarah Ridge
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-26 00:00:00.000000000 Z
10
+ date: 2025-04-09 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '5'
18
+ version: '4.2'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '5'
25
+ version: '4.2'
26
26
  email:
27
27
  - sarahmarie@hey.com
28
28
  executables: []
@@ -37,12 +37,13 @@ files:
37
37
  - lib/swagcov/core_ext/string.rb
38
38
  - lib/swagcov/coverage.rb
39
39
  - lib/swagcov/dotfile.rb
40
+ - lib/swagcov/errors.rb
40
41
  - lib/swagcov/install.rb
41
42
  - lib/swagcov/openapi_files.rb
42
43
  - lib/swagcov/railtie.rb
43
44
  - lib/swagcov/version.rb
45
+ - lib/tasks/swagcov.rake
44
46
  - lib/tasks/swagcov/install.rake
45
- - lib/tasks/swagcove.rake
46
47
  homepage: https://github.com/smridge/swagcov
47
48
  licenses:
48
49
  - MIT
File without changes