# frozen_string_literal: true

require 'spec_helper'

module RailsBestPractices
  module Reviews
    describe LawOfDemeterReview do
      let(:runner) do
        Core::Runner.new(
          prepares: [Prepares::ModelPrepare.new, Prepares::SchemaPrepare.new], reviews: described_class.new
        )
      end

      describe 'belongs_to' do
        before do
          content = <<-EOF
          class Invoice < ActiveRecord::Base
            belongs_to :user
          end
          EOF
          runner.prepare('app/models/invoice.rb', content)

          content = <<-EOF
          ActiveRecord::Schema.define(version: 20110216150853) do
            create_table "users", force => true do |t|
              t.string :name
              t.string :address
              t.string :cellphone
            end
          end
          EOF
          runner.prepare('db/schema.rb', content)
        end

        it 'laws of demeter with erb' do
          content = <<-EOF
          <%= @invoice.user.name %>
          <%= @invoice.user.address %>
          <%= @invoice.user.cellphone %>
          EOF
          runner.review('app/views/invoices/show.html.erb', content)
          expect(runner.errors.size).to eq(3)
          expect(runner.errors[0].to_s).to eq('app/views/invoices/show.html.erb:1 - law of demeter')
        end

        it 'laws of demeter with haml' do
          content = <<~EOF
            = @invoice.user.name
            = @invoice.user.address
            = @invoice.user.cellphone
          EOF
          runner.review('app/views/invoices/show.html.haml', content)
          expect(runner.errors.size).to eq(3)
          expect(runner.errors[0].to_s).to eq('app/views/invoices/show.html.haml:1 - law of demeter')
        end

        it 'laws of demeter with slim' do
          content = <<~EOF
            = @invoice.user.name
            = @invoice.user.address
            = @invoice.user.cellphone
          EOF
          runner.review('app/views/invoices/show.html.slim', content)
          expect(runner.errors.size).to eq(3)
          expect(runner.errors[0].to_s).to eq('app/views/invoices/show.html.slim:1 - law of demeter')
        end

        it 'noes law of demeter' do
          content = <<-EOF
          <%= @invoice.user_name %>
          <%= @invoice.user_address %>
          <%= @invoice.user_cellphone %>
          EOF
          runner.review('app/views/invoices/show.html.erb', content)
          expect(runner.errors.size).to eq(0)
        end
      end

      describe 'has_one' do
        before do
          content = <<-EOF
          class Invoice < ActiveRecord::Base
            has_one :price
          end
          EOF
          runner.prepare('app/models/invoice.rb', content)

          content = <<-EOF
          ActiveRecord::Schema.define(version: 20110216150853) do
            create_table "prices", force => true do |t|
              t.string :currency
              t.integer :number
            end
          end
          EOF
          runner.prepare('db/schema.rb', content)
        end

        it 'laws of demeter' do
          content = <<-EOF
          <%= @invoice.price.currency %>
          <%= @invoice.price.number %>
          EOF
          runner.review('app/views/invoices/show.html.erb', content)
          expect(runner.errors.size).to eq(2)
          expect(runner.errors[0].to_s).to eq('app/views/invoices/show.html.erb:1 - law of demeter')
        end
      end

      context 'polymorphic association' do
        before do
          content = <<-EOF
          class Comment < ActiveRecord::Base
            belongs_to :commentable, polymorphic: true
          end
          EOF
          runner.prepare('app/models/comment.rb', content)

          content = <<-EOF
          class Post < ActiveRecord::Base
            has_many :comments
          end
          EOF
          runner.prepare('app/models/comment.rb', content)

          content = <<-EOF
          ActiveRecord::Schema.define(version: 20110216150853) do
            create_table "posts", force => true do |t|
              t.string :title
            end
          end
          EOF
          runner.prepare('db/schema.rb', content)
        end

        it 'laws of demeter' do
          content = <<-EOF
          <%= @comment.commentable.title %>
          EOF
          runner.review('app/views/comments/index.html.erb', content)
          expect(runner.errors.size).to eq(1)
          expect(runner.errors[0].to_s).to eq('app/views/comments/index.html.erb:1 - law of demeter')
        end
      end

      it 'noes law of demeter with method call' do
        content = <<-EOF
        class Question < ActiveRecord::Base
          has_many :answers, dependent: :destroy
        end
        EOF
        runner.prepare('app/models/question.rb', content)
        content = <<-EOF
        class Answer < ActiveRecord::Base
          belongs_to :question, counter_cache: true, touch: true
        end
        EOF
        runner.prepare('app/models/answer.rb', content)
        content = <<-EOF
        class CommentsController < ApplicationController
          def comment_url
            question_path(@answer.question)
          end
        end
        EOF
        runner.review('app/controllers/comments_controller.rb', content)
        expect(runner.errors.size).to eq(0)
      end

      it 'does not check ignored files' do
        runner =
          Core::Runner.new(
            prepares: [Prepares::ModelPrepare.new, Prepares::SchemaPrepare.new],
            reviews: described_class.new(ignored_files: %r{app/views/invoices})
          )
        content = <<-EOF
          <%= @invoice.user.name %>
          <%= @invoice.user.address %>
          <%= @invoice.user.cellphone %>
        EOF
        runner.review('app/views/invoices/show.html.erb', content)
        expect(runner.errors.size).to eq(0)
      end
    end
  end
end
