require 'spec_helper'

describe 'parameter_order' do
  let(:msg) { 'optional parameter listed before required parameter' }

  ['define', 'class'].each do |type|
    context "#{type} with attrs in order" do
      let(:code) { "#{type} foo($bar, $baz='gronk') { }" }

      it 'does not detect any problems' do
        expect(problems).to be_empty
      end
    end

    context "#{type} with parameter that calls a function" do
      let(:code) { "#{type} foo($bar=extlookup($name)) {}" }

      it 'does not detect any problems' do
        expect(problems).to be_empty
      end
    end

    context "#{type} with attrs out of order" do
      let(:code) { "#{type} foo($bar='baz', $gronk) { }" }

      it 'only detects a single problem' do
        expect(problems.size).to eq(1)
      end

      col = ((type == 'class') ? 23 : 24)
      it 'creates a warning' do
        expect(problems).to contain_warning(msg).on_line(1).in_column(col)
      end
    end

    context "#{type} parameter set to another variable" do
      let(:code) do
        <<-END
          #{type} foo($bar, $baz = $name, $gronk=$::fqdn) {
          }
        END
      end

      it 'does not detect any problems' do
        expect(problems).to be_empty
      end
    end

    context "#{type} parameter set to another variable with incorrect order" do
      let(:code) do
        <<-END
          #{type} foo($baz = $name, $bar, $gronk=$::fqdn) {
          }
        END
      end

      it 'only detects a single problem' do
        expect(problems.size).to eq(1)
      end

      col = ((type == 'class') ? 35 : 36)
      it 'creates a warning' do
        expect(problems).to contain_warning(msg).on_line(1).in_column(col)
      end
    end

    context 'issue-101' do
      let(:code) do
        <<-END
          #{type} b (
            $foo,
            $bar='',
            $baz={}
          ) { }
        END
      end

      it 'does not detect any problems' do
        expect(problems).to be_empty
      end
    end

    context "#{type} parameter w/a hash containing a variable and no optional parameters" do
      let(:code) do
        <<-END
          $var1 = 'test'

          #{type} test (
            $entries = {
              '200 xxx' => {
                param1 => $var1,
                param2 => 'value2',
                param3 => 'value3',
              }
            },
            $mandatory => undef,
          ) { }
        END
      end

      it { expect(problems.size).to eq(0) }
    end

    context "#{type} parameter w/a hash containing a variable followed by an optional parameter" do
      let(:code) do
        <<-END
          $var1 = 'test'

          #{type} test (
            $entries = {
              '200 xxx' => {
                param1 => $var1,
                param2 => 'value2',
                param3 => 'value3',
              }
            },
            $optional,
            $mandatory => undef,
          ) { }
        END
      end

      it { expect(problems).to contain_warning(msg).on_line(11).in_column(13) }
    end

    context "#{type} parameter w/array containing a variable" do
      let(:code) do
        <<-END
          #{type} test (
            $var1 = [$::hostname, 'host'],
          ) { }
        END
      end

      it { expect(problems.size).to eq(0) }
    end

    context "#{type} parameter with array operation" do
      let(:code) do
        <<-END
          #{type} ntp (
            # XXX: remove self from list
            Array[String] $ntp_servers = [
              'foo',
              'bar',
              'baz',
            ] - $::fqdn,
            Array[String] $pools       = [],
          ) { }
        END
      end

      it { expect(problems).to be_empty }
    end

    context "#{type} parameters of Optional type are just regular parameters" do
      let(:code) do
        <<-END
          #{type} test (
            String $param1 = '',
            Optional[Boolean] $param2,
          ) { }
        END
      end

      it { expect(problems.size).to eq(1) }
    end

    [['Custom::Type1', 'Custom::Type2'], ['Custom::Type1', 'String'], ['String', 'Custom::Type2']].each_with_index do |testtypes, i| # rubocop:disable Performance/CollectionLiteralInLoop
      context "#{type} parameters of custom type - no values - #{i}" do
        let(:code) do
          <<-END
            #{type} test (
              #{testtypes[0]} $param1,
              #{testtypes[0]} $param2,
            ) { }
          END
        end

        it { expect(problems).to be_empty }
      end

      context "#{type} parameters of custom type - two values - #{i}" do
        let(:code) do
          <<-END
            #{type} test (
              #{testtypes[0]} $param1 = 'foo',
              #{testtypes[1]} $param2 = 'bar',
            ) { }
          END
        end

        it { expect(problems).to be_empty }
      end

      context "#{type} parameters of custom type - one value, wrong order - #{i}" do
        let(:code) do
          <<-END
            #{type} test (
              #{testtypes[0]} $param1 = 'foo',
              #{testtypes[1]} $param2,
            ) { }
          END
        end

        it { expect(problems.size).to eq(1) }
      end

      context "#{type} parameters of custom type - one value, right order - #{i}" do
        let(:code) do
          <<-END
            #{type} test (
              #{testtypes[0]} $param1,
              #{testtypes[1]} $param2 = 'bar',
            ) { }
          END
        end

        it { expect(problems).to be_empty }
      end
    end
  end
end
