# typed: true
# frozen_string_literal: true

require "cmd/shared_examples/args_parse"
require "dev-cmd/tests"

RSpec.describe Homebrew::DevCmd::Tests do
  it_behaves_like "parseable arguments"

  describe "#check_test_environment!", :needs_linux do
    subject(:tests) { described_class.new([]) }

    before do
      require "extend/os/linux/dev-cmd/tests"
      require "sandbox"

      allow(Homebrew::EnvConfig).to receive(:sandbox_linux?).and_return(true)
      allow(GitHub::Actions).to receive(:env_set?).and_return(false)
    end

    it "does not require the Linux sandbox when Linux sandboxing is disabled" do
      allow(Homebrew::EnvConfig).to receive(:sandbox_linux?).and_return(false)
      allow(Sandbox).to receive_messages(available?: false, failure_reason: "sandbox unavailable")
      expect(Sandbox).not_to receive(:ensure_sandbox_available!)

      expect { tests.check_test_environment! }.not_to raise_error
    end

    it "does not fail on GitHub Actions when the Linux sandbox is unavailable" do
      allow(Sandbox).to receive(:available?).and_return(false)
      allow(GitHub::Actions).to receive(:env_set?).and_return(true)
      expect(Sandbox).not_to receive(:ensure_sandbox_available!)

      expect { tests.check_test_environment! }.not_to raise_error
    end

    it "fails outside GitHub Actions when the Linux sandbox is unavailable" do
      allow(Sandbox).to receive_messages(available?: false, failure_reason: "Landlock is not available.")

      expect { tests.check_test_environment! }
        .to raise_error(RuntimeError, "Landlock is not available.")
    end

    it "passes when the Linux sandbox is available" do
      allow(Sandbox).to receive(:available?).and_return(true)

      expect { tests.check_test_environment! }.not_to raise_error
    end
  end

  describe "#setup_environment!" do
    subject(:tests) { described_class.new([]) }

    before do
      require "api"

      allow(Homebrew::API).to receive(:fetch_api_files!)
    end

    it "keeps generic cache files out of the sandboxed test home" do
      tests.setup_environment!

      expect(ENV.fetch("XDG_CACHE_HOME")).to eq("#{HOMEBREW_CACHE}/tests")
      expect(ENV.fetch("XDG_CACHE_HOME")).not_to start_with("#{Dir.home}/")
    end

    it "can disable Sorbet runtime" do
      ENV["HOMEBREW_TESTS_NO_SORBET_RUNTIME"] = "1"
      tests.setup_environment!

      expect([
        ENV.fetch("HOMEBREW_TESTS_NO_SORBET_RUNTIME", nil),
        ENV.fetch("HOMEBREW_SORBET_RUNTIME", nil),
        ENV.fetch("HOMEBREW_SORBET_RECURSIVE", nil),
      ]).to eq(["1", nil, nil])
    end
  end

  describe "#changed_test_files" do
    subject(:changed_test_files) { tests.changed_test_files }

    let(:tests) { described_class.new([]) }

    context "when a spec file changed" do
      let(:changed_file) { "Library/Homebrew/test/cmd/help_spec.rb\n" }

      before do
        allow(Utils::Git).to receive(:changed_files).and_return(changed_file.split("\n"))
      end

      it "includes the changed spec file" do
        expect(changed_test_files).to include("test/cmd/help_spec.rb")
      end
    end

    context "when a non-test Ruby file changed" do
      let(:changed_file) { "Library/Homebrew/cmd/help.rb\n" }

      before do
        allow(Utils::Git).to receive(:changed_files).and_return(changed_file.split("\n"))
      end

      it "maps the file to its corresponding spec" do
        expect(changed_test_files).to include("test/cmd/help_spec.rb")
      end
    end

    context "when integration shared context changed" do
      let(:changed_file) do
        "Library/Homebrew/test/support/helper/spec/shared_context/integration_test.rb\n"
      end

      before do
        allow(Utils::Git).to receive(:changed_files).and_return(changed_file.split("\n"))
      end

      it "includes integration tests and excludes unrelated tests", :aggregate_failures do
        expect(changed_test_files).to include("test/cmd/help_spec.rb")
        expect(changed_test_files).not_to include("test/dev-cmd/tests_spec.rb")
      end
    end

    context "when cask shared context changed" do
      let(:changed_file) do
        "Library/Homebrew/test/support/helper/spec/shared_context/homebrew_cask.rb\n"
      end

      before do
        allow(Utils::Git).to receive(:changed_files).and_return(changed_file.split("\n"))
      end

      it "includes cask tests and excludes non-cask tests", :aggregate_failures do
        expect(changed_test_files).to include("test/cmd/outdated_spec.rb")
        expect(changed_test_files).not_to include("test/cmd/help_spec.rb")
        expect(changed_test_files).not_to include("test/dev-cmd/pr-pull_spec.rb")
        expect(changed_test_files).not_to include("test/cmd/bundle/remove_subcommand_spec.rb")
      end
    end
  end
end
