# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
# or more contributor license agreements. Licensed under the Elastic License;
# you may not use this file except in compliance with the Elastic License.

require "spec_helper"
require "stud/temporary"
require "logstash/config/source_loader"
require "config_management/extension"
require "config_management/bootstrap_check"

describe LogStash::ConfigManagement::BootstrapCheck do
  let(:extension) { LogStash::ConfigManagement::Extension.new }

  let(:system_settings) { LogStash::Runner::SYSTEM_SETTINGS.clone }

  subject { described_class }

  before do
    # Make sure the new settings options provided by
    # the extension are now available to be set
    extension.additionals_settings(system_settings)
  end

  context "when `xpack.management.enabled` is `TRUE`" do
    let(:interval) { 6 }
    let(:settings) do
      apply_settings(
        {
          "xpack.management.enabled" => true,
          "xpack.management.logstash.poll_interval" => "#{interval}s"
        },
        system_settings
      )
    end

    it "sets `config.reload.automatic` to `TRUE`" do
      expect { subject.check(settings) }
        .to change { settings.get_value("config.reload.automatic") }.from(false).to(true)
    end

    it "sets the `config.reload.interval`" do
      expect { subject.check(settings) }
        .to change { settings.get_value("config.reload.interval").to_nanos }.to(interval * 1_000_000_000)
    end

    context "when `config.string` is given" do
      let(:settings) do
        apply_settings(
          {
            "xpack.management.enabled" => true,
            "config.string" => "input { generator {}} output { null {} }"
          },
          system_settings
        )
      end

      it "raises a `LogStash::BootstrapCheckError` error" do
        expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError
      end
    end

    context "when `config.test_and_exit` is given" do
      let(:settings) do
        apply_settings(
          {
            "xpack.management.enabled" => true,
            "config.test_and_exit" => true
          },
          system_settings
        )
      end

      it "raises a `LogStash::BootstrapCheckError` error" do
        expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError
      end
    end

    context "when `modules.cli` is given" do
      let(:settings) do
        apply_settings(
          {
            "xpack.management.enabled" => true,
            "modules.cli" => [{ "name" => "hello" }]
          },
          system_settings
        )
      end

      it "raises a `LogStash::BootstrapCheckError` error" do
        expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError
      end
    end

    context "when `modules` is given" do
      let(:settings) do
        apply_settings(
          {
            "xpack.management.enabled" => true,
            "modules" => [{ "name" => "hello" }]
          },
          system_settings
        )
      end

      it "raises a `LogStash::BootstrapCheckError` error" do
        expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError
      end
    end

    context "when `path.config` is given" do
      let(:settings) do
        apply_settings(
            {
                "xpack.management.enabled" => true,
                "path.config" => config_location
            },
            system_settings
        )
      end

      context 'when a configuration file exists in the specified location' do
        # gsub replacement is only for Windows platform, without `gsub` `Dir.glob` cannot discover files and test fails
        # note that in other `Dir.glob` places, we used `File.join/dirname` operations which doesn't create issue on Windows
        let(:config_location) { Stud::Temporary.file.path.gsub("\\", "/") }

        it "raises a `LogStash::BootstrapCheckError` error" do
          expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError
        end
      end

      context 'when a configuration file exists in the specified folder' do
        let(:config_location) { "#{File.dirname(Stud::Temporary.file.path)}/*" }

        it "raises a `LogStash::BootstrapCheckError` error" do
          expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError
        end
      end

      context 'when no configuration file exists in the location' do
        let(:config_location) { "/non_existent_directory/*.conf"}

        it "does not raise a `LogStash::BootstrapCheckError` error" do
          expect { subject.check(settings) }.to_not raise_error
        end
      end
    end

    context "when `xpack.management.pipeline.id` is missing" do
      context "when using an empty string" do
        let(:settings) do
          apply_settings(
            {
              "xpack.management.enabled" => true,
              "xpack.management.pipeline.id" => ""
            },
            system_settings
          )
        end

        it "raises a `LogStash::BootstrapCheckError`" do
          expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError
        end
      end

      context "when using an empty array" do
        let(:settings) do
          apply_settings(
            {
              "xpack.management.enabled" => true,
              "xpack.management.pipeline.id" => []
            },
            system_settings
          )
        end

        it "raises a `LogStash::BootstrapCheckError`" do
          expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError
        end
      end

      context "when defining invalid patterns" do
        let(:pipeline_ids) { ["pipeline1", "pipeline2", "@o@"] }
        let(:settings) do
          apply_settings(
            {
              "xpack.management.enabled" => true,
              "xpack.management.pipeline.id" => pipeline_ids
            },
            system_settings
          )
        end

        it "raises a `LogStash::BootstrapCheckError` with the invalid patterns" do
          expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError, /@o@/
        end
      end

      context "when defining wildcard patterns" do
        let(:pipeline_ids) { ["pipeline1", "pipeline2", "*pipeline*"] }
        let(:settings) do
          apply_settings(
              {
                  "xpack.management.enabled" => true,
                  "xpack.management.pipeline.id" => pipeline_ids
              },
              system_settings
          )
        end

        it "does not raise a `LogStash::BootstrapCheckError` error" do
          expect { subject.check(settings) }.to_not raise_error
        end
      end

      context "when defining duplicate ids" do
        let(:pipeline_ids) { ["pipeline1", "pipeline2", "pipeline1"] }
        let(:settings) do
          apply_settings(
            {
              "xpack.management.enabled" => true,
              "xpack.management.pipeline.id" => pipeline_ids
            },
            system_settings
          )
        end

        it "raises a `LogStash::BootstrapCheckError` with the duplicate ids" do
          expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError, /pipeline1/
        end
      end

      context "when defining duplicate ids with different capitalization" do
        let(:pipeline_ids) { ["pipeline1", "pipeline2", "PIPELINE1"] }
        let(:settings) do
          apply_settings(
            {
              "xpack.management.enabled" => true,
              "xpack.management.pipeline.id" => pipeline_ids
            },
            system_settings
          )
        end

        it "raises a `LogStash::BootstrapCheckError` with the duplicate ids" do
          expect { subject.check(settings) }.to raise_error LogStash::BootstrapCheckError, /pipeline1, PIPELINE1/
        end
      end
    end
  end
end
