# frozen_string_literal: true

require_relative '../../spec_helper'
require_lib 'reek/smell_detectors/irresponsible_module'

RSpec.describe Reek::SmellDetectors::IrresponsibleModule do
  it 'reports the right values' do
    src = <<-RUBY
      class Alfa
      end
    RUBY

    expect(src).to reek_of(:IrresponsibleModule,
                           lines:   [1],
                           context: 'Alfa',
                           message: 'has no descriptive comment',
                           source:  'string')
  end

  it 'does count all occurrences' do
    src = <<-RUBY
      class Alfa
        # Method is necessary because we don't count namespace classes.
        def bravo; end
        class Charlie
        end
      end
    RUBY

    expect(src).
      to reek_of(:IrresponsibleModule, lines: [1], context: 'Alfa').
      and reek_of(:IrresponsibleModule, lines: [4], context: 'Alfa::Charlie')
  end

  %w(class module).each do |scope|
    it "reports a #{scope} without a comment" do
      src = <<-RUBY
        #{scope} Alfa
        end
      RUBY

      expect(src).to reek_of(:IrresponsibleModule)
    end

    it "does not report a #{scope} having a comment" do
      src = <<-RUBY
        # Do not report me, I'm responsible!
        #{scope} Alfa; end
      RUBY

      expect(src).not_to reek_of(:IrresponsibleModule)
    end

    it "reports a #{scope} with an empty comment" do
      src = <<-RUBY
        #
        #
        #
        #{scope} Alfa; end
      RUBY

      expect(src).to reek_of(:IrresponsibleModule)
    end

    it "reports a #{scope} with a preceding comment with intermittent material" do
      src = <<-RUBY
        # This is a comment that should not be related to Bravo

        require 'alfa'

        #{scope} Bravo
        end
      RUBY

      expect(src).to reek_of(:IrresponsibleModule)
    end

    it "reports a #{scope} with only a trailing comment" do
      src = <<-RUBY
        #{scope} Alfa
        end # This belongs to Alfa but doesn't count
      RUBY

      expect(src).to reek_of(:IrresponsibleModule)
    end

    it "does not report #{scope} used only as a namespace" do
      src = <<-RUBY
        #{scope} Alfa
          # Describes Bravo
          #{scope} Bravo
            def charlie
            end
          end
        end
      RUBY

      expect(src).not_to reek_of(:IrresponsibleModule, context: 'Alfa')
    end

    it "does not report #{scope} used only as a namespace for several nested moduless" do
      src = <<-RUBY
        #{scope} Alfa
          # Describes Bravo
          class Bravo
            def charlie
            end
          end

          # Describes Delta
          module Delta
          end
        end
      RUBY

      expect(src).not_to reek_of(:IrresponsibleModule, context: 'Alfa')
    end

    it "reports #{scope} that is used as a namespace but also has methods" do
      src = <<-RUBY
        #{scope} Alfa
          def bravo
          end

          # Describes Charlie
          #{scope} Charlie
          end
        end
      RUBY

      expect(src).to reek_of(:IrresponsibleModule, context: 'Alfa')
    end

    it "reports #{scope} that is used as a namespace but also has singleton methods" do
      src = <<-RUBY
        #{scope} Alfa
          def self.bravo
          end

          # Describes Charlie
          #{scope} Charlie
          end
        end
      RUBY

      expect(src).to reek_of(:IrresponsibleModule, context: 'Alfa')
    end

    it "does not report a namespace #{scope} that has a nested class through assignment" do
      src = <<-RUBY
        #{scope} Alfa
          # Bravo is responsible
          Bravo = Class.new Charlie do
            def delta; end
          end
        end
      RUBY

      expect(src).not_to reek_of(:IrresponsibleModule, context: 'Alfa')
    end

    it "does not report #{scope} only containing constants" do
      src = <<-RUBY
        #{scope} Alfa
          Bravo = 23
        end
      RUBY

      expect(src).not_to reek_of(:IrresponsibleModule, context: 'Alfa')
    end

    it "reports #{scope} that contains method calls" do
      src = <<-RUBY
        #{scope} Alfa
          bravo :charlie
        end
      RUBY

      expect(src).to reek_of(:IrresponsibleModule, context: 'Alfa')
    end

    it "reports #{scope} that contains non-constant assignments" do
      src = <<-RUBY
        #{scope} Alfa
          bravo = charlie
        end
      RUBY

      expect(src).to reek_of(:IrresponsibleModule, context: 'Alfa')
    end

    it "reports an irresponsible #{scope} defined through assignment" do
      src = <<-RUBY
        # Alfa is responsible, but Bravo is not
        #{scope} Alfa
          Bravo = Class.new Charlie # Only "class" is supposed to reek here.
        end
      RUBY

      expect(src).to reek_of(:IrresponsibleModule, context: 'Alfa::Bravo')
    end

    it 'reports structs defined through assignment' do
      src = <<-RUBY
        # Alfa is responsible, but Bravo is not
        #{scope} Alfa
          Bravo = Struct.new(:charlie)
        end
      RUBY

      expect(src).to reek_of(:IrresponsibleModule, context: 'Alfa::Bravo')
    end

    it 'does not report constants that are not classes' do
      src = <<-RUBY
        # Alfa is responsible
        #{scope} Alfa
          Bravo = 23
          Charlie = Hash.new
          Delta = ''.freeze
          Echo = Class.new.new
        end
      RUBY

      expect(src).not_to reek_of(:IrresponsibleModule)
    end
  end
end
