Commit d09080e5 authored by Dmytro Zaporozhets's avatar Dmytro Zaporozhets

Merge branch 'pl-rubocop-put-under-scope' into 'master'

Check HTTP methods in Cop/Put{Project,Group}RoutesUnderScope

See merge request gitlab-org/gitlab!37345
parents 365bcd8d 718255ad
...@@ -481,3 +481,13 @@ Rails/SaveBang: ...@@ -481,3 +481,13 @@ Rails/SaveBang:
- 'ee/spec/**/*.rb' - 'ee/spec/**/*.rb'
- 'qa/spec/**/*.rb' - 'qa/spec/**/*.rb'
- 'qa/qa/specs/**/*.rb' - 'qa/qa/specs/**/*.rb'
Cop/PutProjectRoutesUnderScope:
Include:
- 'config/routes/project.rb'
- 'ee/config/routes/project.rb'
Cop/PutGroupRoutesUnderScope:
Include:
- 'config/routes/group.rb'
- 'ee/config/routes/group.rb'
...@@ -5,23 +5,27 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -5,23 +5,27 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
controller: :groups, controller: :groups,
constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom|ics)/ }) do constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom|ics)/ }) do
scope(path: '-') do scope(path: '-') do
get :edit, as: :edit_group # These routes are legit and the cop rule will be improved in
get :issues, as: :issues_group_calendar, action: :issues_calendar, constraints: lambda { |req| req.format == :ics } # https://gitlab.com/gitlab-org/gitlab/-/issues/230703
get :issues, as: :issues_group get :edit, as: :edit_group # rubocop:disable Cop/PutGroupRoutesUnderScope
get :merge_requests, as: :merge_requests_group get :issues, as: :issues_group_calendar, action: :issues_calendar, constraints: lambda { |req| req.format == :ics } # rubocop:disable Cop/PutGroupRoutesUnderScope
get :projects, as: :projects_group get :issues, as: :issues_group # rubocop:disable Cop/PutGroupRoutesUnderScope
get :details, as: :details_group get :merge_requests, as: :merge_requests_group # rubocop:disable Cop/PutGroupRoutesUnderScope
get :activity, as: :activity_group get :projects, as: :projects_group # rubocop:disable Cop/PutGroupRoutesUnderScope
put :transfer, as: :transfer_group get :details, as: :details_group # rubocop:disable Cop/PutGroupRoutesUnderScope
post :export, as: :export_group get :activity, as: :activity_group # rubocop:disable Cop/PutGroupRoutesUnderScope
get :download_export, as: :download_export_group put :transfer, as: :transfer_group # rubocop:disable Cop/PutGroupRoutesUnderScope
post :export, as: :export_group # rubocop:disable Cop/PutGroupRoutesUnderScope
get :download_export, as: :download_export_group # rubocop:disable Cop/PutGroupRoutesUnderScope
# TODO: Remove as part of refactor in https://gitlab.com/gitlab-org/gitlab-foss/issues/49693 # TODO: Remove as part of refactor in https://gitlab.com/gitlab-org/gitlab-foss/issues/49693
get 'shared', action: :show, as: :group_shared get 'shared', action: :show, as: :group_shared # rubocop:disable Cop/PutGroupRoutesUnderScope
get 'archived', action: :show, as: :group_archived get 'archived', action: :show, as: :group_archived # rubocop:disable Cop/PutGroupRoutesUnderScope
end end
get '/', action: :show, as: :group_canonical # These routes are legit and the cop rule will be improved in
# https://gitlab.com/gitlab-org/gitlab/-/issues/230703
get '/', action: :show, as: :group_canonical # rubocop:disable Cop/PutGroupRoutesUnderScope
end end
scope(path: 'groups/*group_id/-', scope(path: 'groups/*group_id/-',
...@@ -106,9 +110,11 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -106,9 +110,11 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
as: :group, as: :group,
constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom)/ }, constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom)/ },
controller: :groups) do controller: :groups) do
get '/', action: :show # These routes are legit and the cop rule will be improved in
patch '/', action: :update # https://gitlab.com/gitlab-org/gitlab/-/issues/230703
put '/', action: :update get '/', action: :show # rubocop:disable Cop/PutGroupRoutesUnderScope
delete '/', action: :destroy patch '/', action: :update # rubocop:disable Cop/PutGroupRoutesUnderScope
put '/', action: :update # rubocop:disable Cop/PutGroupRoutesUnderScope
delete '/', action: :destroy # rubocop:disable Cop/PutGroupRoutesUnderScope
end end
end end
...@@ -362,18 +362,18 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -362,18 +362,18 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
# #
# Service Desk # Service Desk
# #
get '/service_desk' => 'service_desk#show', as: :service_desk get '/service_desk' => 'service_desk#show', as: :service_desk # rubocop:todo Cop/PutProjectRoutesUnderScope
put '/service_desk' => 'service_desk#update', as: :service_desk_refresh put '/service_desk' => 'service_desk#update', as: :service_desk_refresh # rubocop:todo Cop/PutProjectRoutesUnderScope
# #
# Templates # Templates
# #
get '/templates/:template_type/:key' => 'templates#show', get '/templates/:template_type/:key' => 'templates#show', # rubocop:todo Cop/PutProjectRoutesUnderScope
as: :template, as: :template,
defaults: { format: 'json' }, defaults: { format: 'json' },
constraints: { key: %r{[^/]+}, template_type: %r{issue|merge_request}, format: 'json' } constraints: { key: %r{[^/]+}, template_type: %r{issue|merge_request}, format: 'json' }
get '/description_templates/names/:template_type', get '/description_templates/names/:template_type', # rubocop:todo Cop/PutProjectRoutesUnderScope
to: 'templates#names', to: 'templates#names',
as: :template_names, as: :template_names,
defaults: { format: 'json' }, defaults: { format: 'json' },
...@@ -382,39 +382,39 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -382,39 +382,39 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :pages, only: [:show, :update, :destroy] do # rubocop: disable Cop/PutProjectRoutesUnderScope resource :pages, only: [:show, :update, :destroy] do # rubocop: disable Cop/PutProjectRoutesUnderScope
resources :domains, except: :index, controller: 'pages_domains', constraints: { id: %r{[^/]+} } do # rubocop: disable Cop/PutProjectRoutesUnderScope resources :domains, except: :index, controller: 'pages_domains', constraints: { id: %r{[^/]+} } do # rubocop: disable Cop/PutProjectRoutesUnderScope
member do member do
post :verify post :verify # rubocop:todo Cop/PutProjectRoutesUnderScope
post :retry_auto_ssl post :retry_auto_ssl # rubocop:todo Cop/PutProjectRoutesUnderScope
delete :clean_certificate delete :clean_certificate # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
end end
namespace :prometheus do namespace :prometheus do
resources :alerts, constraints: { id: /\d+/ }, only: [:index, :create, :show, :update, :destroy] do # rubocop: disable Cop/PutProjectRoutesUnderScope resources :alerts, constraints: { id: /\d+/ }, only: [:index, :create, :show, :update, :destroy] do # rubocop: disable Cop/PutProjectRoutesUnderScope
post :notify, on: :collection post :notify, on: :collection # rubocop:todo Cop/PutProjectRoutesUnderScope
member do member do
get :metrics_dashboard get :metrics_dashboard # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
resources :metrics, constraints: { id: %r{[^\/]+} }, only: [:index, :new, :create, :edit, :update, :destroy] do # rubocop: disable Cop/PutProjectRoutesUnderScope resources :metrics, constraints: { id: %r{[^\/]+} }, only: [:index, :new, :create, :edit, :update, :destroy] do # rubocop: disable Cop/PutProjectRoutesUnderScope
get :active_common, on: :collection get :active_common, on: :collection # rubocop:todo Cop/PutProjectRoutesUnderScope
post :validate_query, on: :collection post :validate_query, on: :collection # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
post 'alerts/notify', to: 'alerting/notifications#create' post 'alerts/notify', to: 'alerting/notifications#create' # rubocop:todo Cop/PutProjectRoutesUnderScope
draw :legacy_builds draw :legacy_builds
resources :hooks, only: [:index, :create, :edit, :update, :destroy], constraints: { id: /\d+/ } do # rubocop: disable Cop/PutProjectRoutesUnderScope resources :hooks, only: [:index, :create, :edit, :update, :destroy], constraints: { id: /\d+/ } do # rubocop: disable Cop/PutProjectRoutesUnderScope
member do member do
post :test post :test # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
resources :hook_logs, only: [:show] do # rubocop: disable Cop/PutProjectRoutesUnderScope resources :hook_logs, only: [:show] do # rubocop: disable Cop/PutProjectRoutesUnderScope
member do member do
post :retry post :retry # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
end end
...@@ -431,7 +431,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -431,7 +431,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :tags, only: [:index, :destroy], # rubocop: disable Cop/PutProjectRoutesUnderScope resources :tags, only: [:index, :destroy], # rubocop: disable Cop/PutProjectRoutesUnderScope
constraints: { id: Gitlab::Regex.container_registry_tag_regex } do constraints: { id: Gitlab::Regex.container_registry_tag_regex } do
collection do collection do
delete :bulk_destroy delete :bulk_destroy # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
end end
...@@ -440,32 +440,32 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -440,32 +440,32 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :notes, only: [:create, :destroy, :update], concerns: :awardable, constraints: { id: /\d+/ } do # rubocop: disable Cop/PutProjectRoutesUnderScope resources :notes, only: [:create, :destroy, :update], concerns: :awardable, constraints: { id: /\d+/ } do # rubocop: disable Cop/PutProjectRoutesUnderScope
member do member do
delete :delete_attachment delete :delete_attachment # rubocop:todo Cop/PutProjectRoutesUnderScope
post :resolve post :resolve # rubocop:todo Cop/PutProjectRoutesUnderScope
delete :resolve, action: :unresolve delete :resolve, action: :unresolve # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
get 'noteable/:target_type/:target_id/notes' => 'notes#index', as: 'noteable_notes' get 'noteable/:target_type/:target_id/notes' => 'notes#index', as: 'noteable_notes' # rubocop:todo Cop/PutProjectRoutesUnderScope
resources :todos, only: [:create] # rubocop: disable Cop/PutProjectRoutesUnderScope resources :todos, only: [:create] # rubocop: disable Cop/PutProjectRoutesUnderScope
resources :uploads, only: [:create] do # rubocop: disable Cop/PutProjectRoutesUnderScope resources :uploads, only: [:create] do # rubocop: disable Cop/PutProjectRoutesUnderScope
collection do collection do
get ":secret/:filename", action: :show, as: :show, constraints: { filename: %r{[^/]+} }, format: false, defaults: { format: nil } get ":secret/:filename", action: :show, as: :show, constraints: { filename: %r{[^/]+} }, format: false, defaults: { format: nil } # rubocop:todo Cop/PutProjectRoutesUnderScope
post :authorize post :authorize # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
resources :runners, only: [:index, :edit, :update, :destroy, :show] do # rubocop: disable Cop/PutProjectRoutesUnderScope resources :runners, only: [:index, :edit, :update, :destroy, :show] do # rubocop: disable Cop/PutProjectRoutesUnderScope
member do member do
post :resume post :resume # rubocop:todo Cop/PutProjectRoutesUnderScope
post :pause post :pause # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
collection do collection do
post :toggle_shared_runners post :toggle_shared_runners # rubocop:todo Cop/PutProjectRoutesUnderScope
post :toggle_group_runners post :toggle_group_runners # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
...@@ -474,26 +474,26 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -474,26 +474,26 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
collection do collection do
scope '*ref', constraints: { ref: Gitlab::PathRegex.git_reference_regex } do scope '*ref', constraints: { ref: Gitlab::PathRegex.git_reference_regex } do
constraints format: /svg/ do constraints format: /svg/ do
get :pipeline get :pipeline # rubocop:todo Cop/PutProjectRoutesUnderScope
get :coverage get :coverage # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
end end
end end
scope :usage_ping, controller: :usage_ping do scope :usage_ping, controller: :usage_ping do
post :web_ide_clientside_preview post :web_ide_clientside_preview # rubocop:todo Cop/PutProjectRoutesUnderScope
post :web_ide_pipelines_count post :web_ide_pipelines_count # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
resources :web_ide_terminals, path: :ide_terminals, only: [:create, :show], constraints: { id: /\d+/, format: :json } do # rubocop: disable Cop/PutProjectRoutesUnderScope resources :web_ide_terminals, path: :ide_terminals, only: [:create, :show], constraints: { id: /\d+/, format: :json } do # rubocop: disable Cop/PutProjectRoutesUnderScope
member do member do
post :cancel post :cancel # rubocop:todo Cop/PutProjectRoutesUnderScope
post :retry post :retry # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
collection do collection do
post :check_config post :check_config # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
...@@ -506,8 +506,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -506,8 +506,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/29572 # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/29572
resources :snippets, concerns: :awardable, constraints: { id: /\d+/ } do # rubocop: disable Cop/PutProjectRoutesUnderScope resources :snippets, concerns: :awardable, constraints: { id: /\d+/ } do # rubocop: disable Cop/PutProjectRoutesUnderScope
member do member do
get :raw get :raw # rubocop:todo Cop/PutProjectRoutesUnderScope
post :mark_as_spam post :mark_as_spam # rubocop:todo Cop/PutProjectRoutesUnderScope
end end
end end
end end
......
...@@ -5,7 +5,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -5,7 +5,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
controller: :groups, controller: :groups,
constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom|ics)/ }) do constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom|ics)/ }) do
scope(path: '-') do scope(path: '-') do
get :subgroups, as: :subgroups_group get :subgroups, as: :subgroups_group # rubocop:todo Cop/PutGroupRoutesUnderScope
end end
end end
...@@ -184,10 +184,10 @@ end ...@@ -184,10 +184,10 @@ end
# Dependency proxy for containers # Dependency proxy for containers
# Because docker adds v2 prefix to URI this need to be outside of usual group routes # Because docker adds v2 prefix to URI this need to be outside of usual group routes
scope format: false do scope format: false do
get 'v2', to: proc { [200, {}, ['']] } get 'v2', to: proc { [200, {}, ['']] } # rubocop:disable Cop/PutGroupRoutesUnderScope
constraints image: Gitlab::PathRegex.container_image_regex, sha: Gitlab::PathRegex.container_image_blob_sha_regex do constraints image: Gitlab::PathRegex.container_image_regex, sha: Gitlab::PathRegex.container_image_blob_sha_regex do
get 'v2/*group_id/dependency_proxy/containers/*image/manifests/*tag' => 'groups/dependency_proxy_for_containers#manifest' get 'v2/*group_id/dependency_proxy/containers/*image/manifests/*tag' => 'groups/dependency_proxy_for_containers#manifest' # rubocop:todo Cop/PutGroupRoutesUnderScope
get 'v2/*group_id/dependency_proxy/containers/*image/blobs/:sha' => 'groups/dependency_proxy_for_containers#blob' get 'v2/*group_id/dependency_proxy/containers/*image/blobs/:sha' => 'groups/dependency_proxy_for_containers#blob' # rubocop:todo Cop/PutGroupRoutesUnderScope
end end
end end
...@@ -141,6 +141,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -141,6 +141,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
# It's under /-/jira scope but cop is only checking /-/
# rubocop: disable Cop/PutProjectRoutesUnderScope
scope path: '(/-/jira)', constraints: ::Constraints::JiraEncodedUrlConstrainer.new, as: :jira do scope path: '(/-/jira)', constraints: ::Constraints::JiraEncodedUrlConstrainer.new, as: :jira do
scope path: '*namespace_id/:project_id', scope path: '*namespace_id/:project_id',
namespace_id: Gitlab::Jira::Dvcs::ENCODED_ROUTE_REGEX, namespace_id: Gitlab::Jira::Dvcs::ENCODED_ROUTE_REGEX,
...@@ -171,3 +173,4 @@ scope path: '(/-/jira)', constraints: ::Constraints::JiraEncodedUrlConstrainer.n ...@@ -171,3 +173,4 @@ scope path: '(/-/jira)', constraints: ::Constraints::JiraEncodedUrlConstrainer.n
} }
end end
end end
# rubocop: enable Cop/PutProjectRoutesUnderScope
# frozen_string_literal: true # frozen_string_literal: true
require_relative '../routes_under_scope'
module RuboCop module RuboCop
module Cop module Cop
# Checks for a group routes outside '/-/' scope. # Checks for a group routes outside '/-/' scope.
# For more information see: https://gitlab.com/gitlab-org/gitlab/issues/29572 # For more information see: https://gitlab.com/gitlab-org/gitlab/issues/29572
class PutGroupRoutesUnderScope < RuboCop::Cop::Cop class PutGroupRoutesUnderScope < RuboCop::Cop::Cop
include RoutesUnderScope
MSG = 'Put new group routes under /-/ scope' MSG = 'Put new group routes under /-/ scope'
def_node_matcher :dash_scope?, <<~PATTERN def_node_matcher :dash_scope?, <<~PATTERN
(:send nil? :scope (hash <(pair (sym :path)(str "groups/*group_id/-")) ...>)) (:send nil? :scope (hash <(pair (sym :path)(str "groups/*group_id/-")) ...>))
PATTERN PATTERN
def on_send(node)
return unless in_group_routes?(node)
return unless resource?(node)
return unless outside_scope?(node)
add_offense(node)
end
def outside_scope?(node)
node.each_ancestor(:block).none? do |parent|
dash_scope?(parent.to_a.first)
end
end
def in_group_routes?(node)
path = node.location.expression.source_buffer.name
dirname = File.dirname(path)
filename = File.basename(path)
dirname.end_with?('config/routes') &&
filename.end_with?('group.rb')
end
def resource?(node)
node.method_name == :resource ||
node.method_name == :resources
end
end end
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
require_relative '../routes_under_scope'
module RuboCop module RuboCop
module Cop module Cop
# Checks for a project routes outside '/-/' scope. # Checks for a project routes outside '/-/' scope.
# For more information see: https://gitlab.com/gitlab-org/gitlab/issues/29572 # For more information see: https://gitlab.com/gitlab-org/gitlab/issues/29572
class PutProjectRoutesUnderScope < RuboCop::Cop::Cop class PutProjectRoutesUnderScope < RuboCop::Cop::Cop
include RoutesUnderScope
MSG = 'Put new project routes under /-/ scope' MSG = 'Put new project routes under /-/ scope'
def_node_matcher :dash_scope?, <<~PATTERN def_node_matcher :dash_scope?, <<~PATTERN
(:send nil? :scope (:str "-")) (:send nil? :scope (:str "-"))
PATTERN PATTERN
def on_send(node)
return unless in_project_routes?(node)
return unless resource?(node)
return unless outside_scope?(node)
add_offense(node)
end
def outside_scope?(node)
node.each_ancestor(:block).none? do |parent|
dash_scope?(parent.to_a.first)
end
end
def in_project_routes?(node)
path = node.location.expression.source_buffer.name
dirname = File.dirname(path)
filename = File.basename(path)
dirname.end_with?('config/routes') &&
filename.end_with?('project.rb')
end
def resource?(node)
node.method_name == :resource ||
node.method_name == :resources
end
end end
end end
end end
# frozen_string_literal: true
module RuboCop
# Common code used to implement cops checking routes outside of /-/ scope.
#
# Examples:
# * RuboCop::Cop::PutProjectRoutesUnderScope
# * RuboCop::Cop::PutGroupRoutesUnderScope
module RoutesUnderScope
ROUTE_METHODS = Set.new(%i[resource resources get post put patch delete]).freeze
def on_send(node)
return unless route_method?(node)
return unless outside_scope?(node)
add_offense(node)
end
def outside_scope?(node)
node.each_ancestor(:block).none? do |parent|
dash_scope?(parent.to_a.first)
end
end
def route_method?(node)
ROUTE_METHODS.include?(node.method_name)
end
end
end
...@@ -9,20 +9,21 @@ RSpec.describe RuboCop::Cop::PutGroupRoutesUnderScope, type: :rubocop do ...@@ -9,20 +9,21 @@ RSpec.describe RuboCop::Cop::PutGroupRoutesUnderScope, type: :rubocop do
subject(:cop) { described_class.new } subject(:cop) { described_class.new }
before do %w[resource resources get post put patch delete].each do |route_method|
allow(cop).to receive(:in_group_routes?).and_return(true) it "registers an offense when route is outside scope for `#{route_method}`" do
end offense = "#{route_method} :notes"
marker = '^' * offense.size
it 'registers an offense when route is outside scope' do
expect_offense(<<~PATTERN) expect_offense(<<~PATTERN)
scope(path: 'groups/*group_id/-', module: :groups) do scope(path: 'groups/*group_id/-', module: :groups) do
resource :issues resource :issues
end end
resource :notes #{offense}
^^^^^^^^^^^^^^^ Put new group routes under /-/ scope #{marker} Put new group routes under /-/ scope
PATTERN PATTERN
end end
end
it 'does not register an offense when resource inside the scope' do it 'does not register an offense when resource inside the scope' do
expect_no_offenses(<<~PATTERN) expect_no_offenses(<<~PATTERN)
......
...@@ -9,20 +9,21 @@ RSpec.describe RuboCop::Cop::PutProjectRoutesUnderScope, type: :rubocop do ...@@ -9,20 +9,21 @@ RSpec.describe RuboCop::Cop::PutProjectRoutesUnderScope, type: :rubocop do
subject(:cop) { described_class.new } subject(:cop) { described_class.new }
before do %w[resource resources get post put patch delete].each do |route_method|
allow(cop).to receive(:in_project_routes?).and_return(true) it "registers an offense when route is outside scope for `#{route_method}`" do
end offense = "#{route_method} :notes"
marker = '^' * offense.size
it 'registers an offense when route is outside scope' do
expect_offense(<<~PATTERN) expect_offense(<<~PATTERN)
scope '-' do scope '-' do
resource :issues resource :issues
end end
resource :notes #{offense}
^^^^^^^^^^^^^^^ Put new project routes under /-/ scope #{marker} Put new project routes under /-/ scope
PATTERN PATTERN
end end
end
it 'does not register an offense when resource inside the scope' do it 'does not register an offense when resource inside the scope' do
expect_no_offenses(<<~PATTERN) expect_no_offenses(<<~PATTERN)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment