# frozen_string_literal: true

module Gitlab::QA::Runtime
  RSpec.describe OmnibusConfiguration do
    subject(:omnibusConfiguration) { described_class.new }

    describe '#initialize' do
      it 'contains an initial comment denoting GitLab QA' do
        expect(omnibusConfiguration.instance_variable_get(:@config).size).to eq(1)
        expect(omnibusConfiguration.instance_variable_get(:@config).first).to include('GitLab QA')
      end

      context 'when prefix is specified' do
        let(:prefixed_configuration) { '# prefixed' }
        let(:generated_comment_configuration) do
          "# Generated by GitLab QA Omnibus Configurator at YYYY-MM-DD HH:MM:SS\n# prefixed"
        end

        it 'inserts configuration after the generation comment' do
          expect(described_class.new(prefixed_configuration).instance_variable_get(:@config).last)
            .to eq(prefixed_configuration)
        end

        it 'removes any duplicated generated by comments' do
          config = described_class.new(generated_comment_configuration).instance_variable_get(:@config)
          expect(config.last.split("\n").size).to eq(1)
        end
      end
    end

    describe '#expand_config_template' do
      let(:gitlab) do
        instance_double(
          Gitlab::QA::Component::Gitlab,
          address: "http://gitlab.test",
          class: Struct.new(:name).new("Gitlab::QA::Component::Gitlab")
        )
      end

      it 'expands the ERB template' do
        omnibusConfiguration.<< "gitlab_rails['a'] = external_url '<%= gitlab.address %>'"
        omnibusConfiguration.expand_config_template(gitlab)
        expect(omnibusConfiguration.instance_variable_get(:@config).last).to include('http://gitlab.test')
      end
    end

    describe '#sanitize!' do
      it 'removes duplicate omnibus entries' do
        omnibusConfiguration.<< "gitlab_rails['a'] = true"
        omnibusConfiguration.<< "gitlab_rails['a'] = true" # intentional duplicate
        omnibusConfiguration.<< "gitlab_rails['b'] = true"

        expect(omnibusConfiguration.sanitize!.size).to eq(3) # +1 from the configurator comment
      end

      it 'removes duplicate omnibus entries in multiline config' do
        omnibusConfiguration.<< <<~OMNIBUS
          a['setting_1'] = true
          a['setting_3'] = false
        OMNIBUS

        omnibusConfiguration.<< <<~OMNIBUS
          a['setting_1'] = true
          a['setting_4'] = false
        OMNIBUS

        expect(omnibusConfiguration.sanitize!.size).to eq(4) # +1 from the configurator comment
      end

      it 'splits each setting into an array item' do
        omnibusConfiguration.<< <<~OMNIBUS
          a['setting_1'] = true
          external_url 'http://gitlab.test
          a['setting_2'] = [
            {
              name: 'setting_2a_name',
              id: 'setting_2a_id'
            },
            {
              name: 'setting_2b_name',
              id: 'setting_2b_id'
            },
          ]
          a['setting_3'] = false
        OMNIBUS

        expect(omnibusConfiguration.sanitize!.size).to eq(5) # +1 from the configurator comment
      end

      it 'merges entries with an array' do
        omnibusConfiguration.<< <<~OMNIBUS
          gitlab_rails['omniauth_providers'] = [
            {
              name: 'github',
              app_id: '$QA_GITHUB_OAUTH_APP_ID',
              app_secret: '$QA_GITHUB_OAUTH_APP_SECRET',
              url: 'https://github.com/',
              verify_ssl: false,
              args: { scope: 'user:email' }
            }
          ]
          gitlab_rails['omniauth_allow_single_sign_on'] = ['github']
        OMNIBUS

        expected_output_1 = <<~OMNIBUS
          gitlab_rails['omniauth_providers'] = [
            {
              name: 'github',
              app_id: '$QA_GITHUB_OAUTH_APP_ID',
              app_secret: '$QA_GITHUB_OAUTH_APP_SECRET',
              url: 'https://github.com/',
              verify_ssl: false,
              args: { scope: 'user:email' }
            }]
        OMNIBUS

        expected_output_2 = "gitlab_rails['omniauth_allow_single_sign_on'] = ['github']"

        expect(omnibusConfiguration.sanitize![1]).to eq expected_output_1.chomp
        expect(omnibusConfiguration.sanitize![2]).to eq expected_output_2.chomp
      end

      it 'detects duplicate entries' do
        omnibusConfiguration.<< "gitlab_rails['a'] = true"
        omnibusConfiguration.<< "gitlab_rails['a'] = false"

        expect { omnibusConfiguration.sanitize! }.to raise_error(RuntimeError)
      end

      it 'removes duplicate omnibus entries in multiline config' do
        omnibusConfiguration.<< <<~OMNIBUS
          a['setting_1'] = false
          a['setting_3'] = false
        OMNIBUS

        omnibusConfiguration.<< <<~OMNIBUS
          a['setting_1'] = true
          a['setting_4'] = false
        OMNIBUS

        expect { omnibusConfiguration.sanitize! }.to raise_error(RuntimeError)
      end

      it 'sanitizes a malformed configuration' do
        omnibusConfiguration.<< "gitlab_rails['a']=true"

        # well formed
        # gitlab_rails['a'] = true
        # vs gitlab_rails['a']=true
        expect(omnibusConfiguration.sanitize!.last).to eq("gitlab_rails['a'] = true")
      end

      it 'allows comments' do
        omnibusConfiguration.<< "# test"
        omnibusConfiguration.<< "a = true"

        expect(omnibusConfiguration.sanitize!.size).to eq(3) # +1 from the configurator comment
      end

      it 'leaves multiple equal signs alone' do
        omnibusConfiguration.<< <<~RUBY
          a['test'] = { "key" => "value" }
        RUBY

        expect(omnibusConfiguration.sanitize!.last).to eq(%(a['test'] = { 'key' => 'value' }))
      end

      it 'converts double quotes into single quotes' do
        omnibusConfiguration.<< %(a['test'] = { "foo" => "bar" })

        expect(omnibusConfiguration.sanitize!.last).to eq("a['test'] = { 'foo' => 'bar' }")
      end

      it 'allows space-separated DSL-type entries' do
        omnibusConfiguration.<< %(mattermost_url 'abc123.test')
        expect(omnibusConfiguration.sanitize!.last).to eq("mattermost_url 'abc123.test'")
      end

      it 'method invocations' do
        omnibusConfiguration.<< <<~OMNIBUS
        gitaly['configuration'] = {
          storage: [
            {
              name: 'default',
              path: '/test/default/repositories',
            },
            {
              name: 'alternative',
              path: '/test/alternative/repositories',
            }
          ],
        }
        OMNIBUS

        expect(omnibusConfiguration.sanitize!.size).to eq(2)
      end
    end

    describe '#<<' do
      it 'adds the config to the base configuration' do
        omnibusConfiguration.<< "gitlab_rails['a'] = true"

        expect(omnibusConfiguration.instance_variable_get(:@config).size).to eq(2) # +1 from the configurator comment
      end

      it 'strips any newlines off' do
        omnibusConfiguration.<< "gitlab_rails['a'] = true\n"

        expect(omnibusConfiguration.instance_variable_get(:@config).last).to eq("gitlab_rails['a'] = true")
      end

      it 'ignores empty configurations' do
        omnibusConfiguration.<< "\n"

        expect(omnibusConfiguration.instance_variable_get(:@config).size).to eq(1)
      end
    end

    describe OmnibusConfigurations::Default do
      describe '#configuration' do
        it 'has a default configuration' do
          expect(omnibusConfiguration.configuration).not_to be_empty
        end

        it 'is a String type' do
          expect(omnibusConfiguration.configuration).to be_a String
        end

        it 'sets a theme' do
          expect(omnibusConfiguration.configuration)
            .to include("gitlab_rails['gitlab_default_theme']")
        end

        it 'disables animations' do
          expect(omnibusConfiguration.configuration)
            .to include("gitlab_rails['gitlab_disable_animations'] = true")
        end

        it 'sets cache expiry' do
          expect(omnibusConfiguration.configuration)
            .to include("gitlab_rails['application_settings_cache_seconds'] = 0")
        end
      end
    end

    describe OmnibusConfigurations::ObjectStorage do
      it 'has a preparation method' do
        expect(described_class.instance_methods(false)).to include(:prepare)
      end

      it 'has exec commands' do
        expect(described_class.instance_methods(false)).to include(:exec_commands)
      end

      describe '#prepare' do
        subject(:clazz) { described_class.new }

        it 'returns an instance of Component::Base' do
          expect(clazz.prepare).to be_a(Gitlab::QA::Component::Base)
        end
      end
    end
  end
end
