# frozen_string_literal: true

require 'spec_helper'

module RailsBestPractices
  module Reviews
    describe RemoveUnusedMethodsInControllersReview do
      let(:runner) do
        Core::Runner.new(
          prepares: [Prepares::ControllerPrepare.new, Prepares::RoutePrepare.new],
          reviews: described_class.new('except_methods' => ['ExceptableController#*'])
        )
      end

      context 'private/protected' do
        it 'removes unused methods' do
          content = <<-EOF
          RailsBestPracticesCom::Application.routes.draw do
            resources :posts do
              member do
                post 'link_to/:other_id' => 'posts#link_to_post'
                post 'extra_update' => 'posts#extra_update'
              end
            end
          end
          EOF
          runner.prepare('config/routes.rb', content)
          content = <<-EOF
          class PostsController < ActiveRecord::Base
            def show; end
            def extra_update; end
            def link_to_post; end
            protected
            def load_post; end
            private
            def load_user; end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(2)
          expect(runner.errors[0].to_s).to eq(
            'app/controllers/posts_controller.rb:6 - remove unused methods (PostsController#load_post)'
          )
          expect(runner.errors[1].to_s).to eq(
            'app/controllers/posts_controller.rb:8 - remove unused methods (PostsController#load_user)'
          )
        end

        it 'does not remove unused methods for before_filter' do
          content = <<-EOF
          RailsBestPracticesCom::Application.routes.draw do
            resources :posts
          end
          EOF
          runner.prepare('config/routes.rb', content)
          content = <<-EOF
          class PostsController < ActiveRecord::Base
            before_filter :load_post, :load_user
            def show; end
            protected
            def load_post; end
            def load_user; end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end

        it 'does not remove unused methods for around_filter' do
          content = <<-EOF
          RailsBestPracticesCom::Application.routes.draw do
            resources :posts
          end
          EOF
          runner.prepare('config/routes.rb', content)
          content = <<-EOF
          class PostsController < ActiveRecord::Base
            around_filter :set_timestamp
            protected
              def set_timestamp
                Time.zone = "Pacific Time (US & Canada)"
                yield
              ensure
                Time.zone = "UTC"
              end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end

        it 'does not remove unused methods for around_action (new syntax)' do
          content = <<-EOF
          class PostsController < ActiveRecord::Base
            around_action :use_time_zone

            protected

            def use_time_zone(&block)
              Time.use_zone(current_user.time_zone, &block)
            end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end

        it 'does not remove unused methods for layout' do
          content = <<-EOF
          RailsBestPracticesCom::Application.routes.draw do
            resources :posts
          end
          EOF
          runner.prepare('config/routes.rb', content)
          content = <<-EOF
          class PostsController < ActiveRecord::Base
            layout :choose_layout
            private
              def choose_layout
                "default"
              end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end

        it 'does not remove inherited_resources methods' do
          content = <<-EOF
          RailsBestPracticesCom::Application.routes.draw do
            resources :posts
          end
          EOF
          runner.prepare('config/routes.rb', content)
          content = <<-EOF
          class PostsController < InheritedResources::Base
            def show; end
            protected
            def resource; end
            def collection; end
            def begin_of_association_chain; end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end
      end

      context 'public' do
        it 'removes unused methods' do
          content = <<-EOF
          RailsBestPracticesCom::Application.routes.draw do
            resources :posts
          end
          EOF
          runner.prepare('config/routes.rb', content)
          content = <<-EOF
          class PostsController < ApplicationController
            def show; end
            def list; end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(1)
          expect(runner.errors[0].to_s).to eq(
            'app/controllers/posts_controller.rb:3 - remove unused methods (PostsController#list)'
          )
        end

        it 'does not remove inline routes' do
          content = <<-EOF
          RailsBestPracticesCom::Application.routes.draw do
            resources :posts, only: :none do
              get :display, :list, on: :member
            end
          end
          EOF
          runner.prepare('config/routes.rb', content)
          content = <<-EOF
          class PostsController < ApplicationController
            def display; end
            def list; end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end

        it 'does not remove unused methods if all actions are used in route' do
          content = <<-EOF
          ActionController::Routing::Routes.draw do |map|
            map.connect 'internal/:action/*whatever', controller: "internal"
          end
          EOF
          runner.prepare('config/routes.rb', content)
          content = <<-EOF
          class InternalController < ApplicationController
            def list; end
            def delete; end
            def whatever; end
          end
          EOF
          runner.prepare('app/controllers/internal_controller.rb', content)
          runner.review('app/controllers/internal_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end

        it 'does not remove unused methods if they are except_methods' do
          content = <<-EOF
          class ExceptableController < ApplicationController
            def list; end
          end
          EOF
          runner.prepare('app/controllers/exceptable_controller.rb', content)
          runner.review('app/controllers/exceptable_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end
      end

      context 'assignment' do
        it 'does not remove unused methods if call in base class' do
          content = <<-EOF
          RailsBestPracticesCom::Application.routes.draw do
            resources :user, only: :show do; end
          end
          EOF
          runner.prepare('config/routes.rb', content)
          application_content = <<-EOF
          class ApplicationController
            def current_user=(user); end
          end
          EOF
          runner.prepare('app/controllers/application_controller.rb', application_content)
          users_content = <<-EOF
          class UsersController < ApplicationController
            def show
              current_user = @user
            end
          end
          EOF
          runner.prepare('app/controllers/users_controller.rb', users_content)
          runner.review('app/controllers/application_controller.rb', application_content)
          runner.review('app/controllers/users_controller.rb', users_content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end
      end

      context 'helper_method' do
        it 'removes unused methods if helper method is not called' do
          content = <<-EOF
          class PostsController < ApplicationController
            helper_method :helper_post
            protected
              def helper_post; end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(1)
          expect(runner.errors[0].to_s).to eq(
            'app/controllers/posts_controller.rb:4 - remove unused methods (PostsController#helper_post)'
          )
        end

        it 'does not remove unused methods if call helper method in views' do
          content = <<-EOF
          class PostsController < ApplicationController
            helper_method :helper_post
            protected
              def helper_post; end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          content = <<-EOF
          <%= helper_post %>
          EOF
          runner.review('app/views/posts/show.html.erb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end

        it 'does not remove unused methods if call helper method in helpers' do
          content = <<-EOF
          class PostsController < ApplicationController
            helper_method :helper_post
            protected
              def helper_post; end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          content = <<-EOF
          module PostsHelper
            def new_post
              helper_post
            end
          end
          EOF
          runner.review('app/helpers/posts_helper.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end
      end

      context 'delegate to: :controller' do
        it 'removes unused methods if delegate method is not called' do
          content = <<-EOF
          class PostsController < ApplicationController
            protected
              def helper_post(type); end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          content = <<-EOF
          module PostsHelper
            delegate :helper_post, to: :controller
          end
          EOF
          runner.review('app/helpers/posts_helper.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(1)
          expect(runner.errors[0].to_s).to eq(
            'app/controllers/posts_controller.rb:3 - remove unused methods (PostsController#helper_post)'
          )
        end

        it 'removes unused methods if delegate method is called' do
          content = <<-EOF
          class PostsController < ApplicationController
            protected
              def helper_post(type); end
          end
          EOF
          runner.prepare('app/controllers/posts_controller.rb', content)
          runner.review('app/controllers/posts_controller.rb', content)
          content = <<-EOF
          module PostsHelper
            delegate :helper_post, to: :controller
          end
          EOF
          runner.review('app/helpers/posts_helper.rb', content)
          content = <<-EOF
          <%= helper_post("new") %>
          EOF
          runner.review('app/views/posts/show.html.erb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end
      end

      context 'cells' do
        it 'removes unused methods' do
          content = <<-EOF
          class PostsCell < Cell::Rails
            def list; end
          end
          EOF
          runner.prepare('app/cells/posts_cell.rb', content)
          runner.review('app/cells/posts_cell.rb', content)
          runner.after_review
          expect(runner.errors.size).to eq(1)
          expect(runner.errors[0].to_s).to eq('app/cells/posts_cell.rb:2 - remove unused methods (PostsCell#list)')
        end

        it 'does not remove unused methods if render_cell' do
          content = <<-EOF
          class PostsCell < Cell::Rails
            def list; end
            def display; end
          end
          EOF
          runner.prepare('app/cells/posts_cell.rb', content)
          runner.review('app/cells/posts_cell.rb', content)
          content = <<-EOF
          <%= render_cell :posts, :list %>
          <%= render_cell(:posts, :display) %>
          EOF
          runner.review('app/views/posts/index.html.erb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end

        it 'does not remove unused methods if render with state' do
          content = <<-EOF
          class PostsCell < Cell::Rails
            def list
              render state: :show
              render(state: :display)
            end

            def show; end
            def display; end
          end
          EOF
          runner.prepare('app/cells/posts_cell.rb', content)
          runner.review('app/cells/posts_cell.rb', content)
          content = <<-EOF
          <%= render_cell :posts, :list %>
          EOF
          runner.review('app/views/posts/index.html.erb', content)
          runner.after_review
          expect(runner.errors.size).to eq(0)
        end
      end

      it 'does not remove unused methods' do
        route_content = <<-EOF
        RailsBestPracticesCom::Application.routes.draw do
          namespace :admin do
            resources :users, only: :index
          end
        end
        EOF
        app_controller_content = <<-EOF
        module Admin
          class AppController < ApplicationController
            def index
              @collection = model.all
            end
          end
        end
        EOF
        users_controller_content = <<-EOF
        module Admin
          class UsersController < AppController
            private

            def model
              User
            end
          end
        end
        EOF
        runner.prepare('config/routes.rb', route_content)
        runner.prepare('app/controllers/admin/app_controller.rb', app_controller_content)
        runner.prepare('app/controllers/admin/users_controller.rb', users_controller_content)
        runner.review('app/controllers/admin/app_controller.rb', app_controller_content)
        runner.review('app/controllers/admin/users_controller.rb', users_controller_content)
        runner.after_review
        expect(runner.errors.size).to eq(0)
      end

      it 'does not check ignored files' do
        runner =
          Core::Runner.new(
            prepares: [Prepares::ControllerPrepare.new, Prepares::RoutePrepare.new],
            reviews: described_class.new(ignored_files: /posts_controller/, except_methods: [])
          )
        content = <<-EOF
          RailsBestPracticesCom::Application.routes.draw do
            resources :posts do
              member do
                post 'link_to/:other_id' => 'posts#link_to_post'
                post 'extra_update' => 'posts#extra_update'
              end
            end
          end
        EOF
        runner.prepare('config/routes.rb', content)
        content = <<-EOF
          class PostsController < ActiveRecord::Base
            def show; end
            def extra_update; end
            def link_to_post; end
            protected
            def load_post; end
            private
            def load_user; end
          end
        EOF
        runner.prepare('app/controllers/posts_controller.rb', content)
        runner.review('app/controllers/posts_controller.rb', content)
        runner.after_review
        expect(runner.errors.size).to eq(0)
      end
    end
  end
end
