Skip to content

Instantly share code, notes, and snippets.

@kgilpin
Created November 29, 2022 16:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kgilpin/0f86f7ca8bb815e105ce77d94adc61e0 to your computer and use it in GitHub Desktop.
Save kgilpin/0f86f7ca8bb815e105ce77d94adc61e0 to your computer and use it in GitHub Desktop.
Rake task to detect untested routes
# Sort by path, then by method
compare_routes = lambda do |a,b|
compare = a[1] <=> b[1]
compare = a[0] <=> b[0] if compare == 0
compare
end
normalize_method = lambda do |method|
method = method.upcase
# For Rails, HEAD request is served by 'show' (GET)
# Similarly, PUT request is served by 'update' (PATCH)
method = 'GET' if method == 'HEAD'
method = 'PATCH' if method == 'PUT'
method = 'GET' unless %w[GET CONNECT DELETE HEAD PUT POST PATCH OPTIONS TRACE].member?(method)
method
end
rails_routes = lambda do
`rails routes`.split("\n")
.map do |line|
tokens = line.split(/\s+/)
path_index = tokens.index { |token| token[0] == '/' }
next unless path_index
method = normalize_method.(tokens[path_index-1])
path = tokens[path_index]
path.gsub!('(.:format)', '')
path.gsub!(/:[^\/]+/) { |match| [ '{', match[1..-1], '}' ].join('') }
[ method, path ]
end
.compact
.sort(&compare_routes)
.map { |route| route.join(' ') }
.uniq
end
openapi_routes = lambda do
# Assumes AppMap-generated OpenAPI in the file swagger/openapi.yaml
paths = YAML.load(File.read('swagger/openapi.yaml'))['paths']
path_names = paths.keys
paths.map do |entry|
path_name, data = entry
methods = data.keys
methods.map do |method|
[ normalize_method.(method), path_name ]
end
end
.flatten(1)
.sort(&compare_routes)
.map { |route| route.join(' ') }
.uniq
end
namespace :appmap do
desc "Use information from swagger/openapi.yaml (generated by AppMap openapi command), and config/untested_routes.txt (a whitelist of routes to ignore) to determine which API routes are untested"
task :untested_routes do
# Create this files as empty, if it doesn't exist.
# To allow untested routes that won't print in the final report, add those routes to this file.
untested_routes_whitelist = File.read('config/untested_routes.txt')
.split("\n")
.select { |line| line.length > 0 && line[0] != '#' }
.map(&:strip)
untested_routes = rails_routes.() - openapi_routes.() - untested_routes_whitelist
undeclared_routes = openapi_routes.() - rails_routes.()
unless untested_routes.empty?
puts 'Untested routes'
puts '---------------'
puts untested_routes.join("\n")
end
unless undeclared_routes.empty?
puts unless untested_routes.empty?
puts 'Undeclared routes'
puts '-----------------'
puts undeclared_routes.join("\n")
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment