Files
brew/Library/Homebrew/test/cask/artifact/generated_completion_spec.rb
T
Mike McQuaid 204ea7e964 Make tests use public APIs and enable their cops
- Make every statically-poked method public and call it directly,
  keeping `public_send` only for dynamically-named public methods.
- Read and write state through public `attr_*` accessors instead of
  instance variable reflection.
- Enable `Homebrew/NoSendInTests` and
  `Homebrew/NoInstanceVariableAccessInTests` now the test suite is
  clean, so neither pattern creeps back in.
- Keep rare justified disables, e.g. `Module#remove_const` is
  private core Ruby and raw memoisation state has no accessor.
2026-07-27 08:11:46 +01:00

191 lines
6.6 KiB
Ruby

# typed: false
# frozen_string_literal: true
RSpec.describe Cask::Artifact::GeneratedCompletion, :cask do
let(:staged_path) { Pathname(Dir.mktmpdir) }
let(:cask) do
Cask::Cask.new("test-generated-completion") do
version "1.0"
sha256 :no_check
url "file:///dev/null"
generate_completions_from_executable "bin/foo", "completions"
end
end
let(:bash_dir) { cask.config.bash_completion }
let(:zsh_dir) { cask.config.zsh_completion }
let(:fish_dir) { cask.config.fish_completion }
before do
allow(cask).to receive(:staged_path).and_return(staged_path)
(staged_path/"bin").mkpath
(staged_path/"bin/foo").write("#!/bin/sh\necho \"$SHELL completion\"")
(staged_path/"bin/foo").chmod(0755)
end
after do
FileUtils.rm_rf(staged_path)
end
describe "#install_phase" do
it "generates completion scripts for default shells" do
artifact = cask.artifacts.grep(described_class).first
allow(Sandbox).to receive_messages(ensure_sandbox_installed!: nil, available?: true)
allow(Sandbox).to receive(:new) do
instance_double(Sandbox).tap do |sandbox|
allow(sandbox).to receive(:allow_read)
allow(sandbox).to receive(:allow_write_temp_and_cache)
allow(sandbox).to receive(:deny_read_home)
allow(sandbox).to receive(:deny_all_network)
allow(sandbox).to receive(:run) do |*args|
Pathname(args.fetch(7)).write("#{args.grep(/^SHELL=/).first.delete_prefix("SHELL=")} completion output")
end
end
end
artifact.install_phase
expect(bash_dir/"foo").to be_a_file
expect((bash_dir/"foo").read).to eq("bash completion output")
expect(zsh_dir/"_foo").to be_a_file
expect((zsh_dir/"_foo").read).to eq("zsh completion output")
expect(fish_dir/"foo.fish").to be_a_file
expect((fish_dir/"foo.fish").read).to eq("fish completion output")
end
it "sandboxes completion generation without network access" do
artifact = cask.artifacts.grep(described_class).first
sandboxes = []
calls = []
homes = []
allow(Sandbox).to receive_messages(ensure_sandbox_installed!: nil, available?: true)
allow(Sandbox).to receive(:new) do
instance_double(Sandbox).tap do |sandbox|
expect(sandbox).to receive(:allow_read).with(path: staged_path, type: :subpath)
expect(sandbox).to receive(:allow_write_temp_and_cache)
expect(sandbox).to receive(:deny_read_home)
expect(sandbox).to receive(:deny_all_network) { calls << :deny_all_network }
allow(sandbox).to receive(:run) do |*args|
calls << :run
homes << Pathname(args.grep(/^HOME=/).first.delete_prefix("HOME="))
Pathname(args.fetch(7)).write("completion")
end
sandboxes << sandbox
end
end
artifact.install_phase
expect(sandboxes.length).to eq(3)
expect(calls).to eq([:deny_all_network, :run, :deny_all_network, :run, :deny_all_network, :run])
expect(homes.uniq.length).to eq(3)
expect(homes).to all(satisfy { |home| !home.exist? })
end
context "when generation fails for one shell" do
it "warns and continues generating other shells" do
artifact = cask.artifacts.grep(described_class).first
allow(Sandbox).to receive_messages(ensure_sandbox_installed!: nil, available?: true)
allow(Sandbox).to receive(:new) do
instance_double(Sandbox).tap do |sandbox|
allow(sandbox).to receive(:allow_read)
allow(sandbox).to receive(:allow_write_temp_and_cache)
allow(sandbox).to receive(:deny_read_home)
allow(sandbox).to receive(:deny_all_network)
allow(sandbox).to receive(:run) do |*args|
raise "boom" if args.include?("SHELL=bash")
Pathname(args.fetch(7)).write("zsh completion")
end
end
end
expect { artifact.install_phase }
.to output(/Failed to generate bash completions/).to_stderr
expect(zsh_dir/"_foo").to be_a_file
end
end
end
describe "#uninstall_phase" do
it "removes generated completion scripts" do
artifact = cask.artifacts.grep(described_class).first
bash_dir.mkpath
zsh_dir.mkpath
fish_dir.mkpath
(bash_dir/"foo").write("bash")
(zsh_dir/"_foo").write("zsh")
(fish_dir/"foo.fish").write("fish")
artifact.uninstall_phase(command: NeverSudoSystemCommand)
expect(bash_dir/"foo").not_to exist
expect(zsh_dir/"_foo").not_to exist
expect(fish_dir/"foo.fish").not_to exist
end
end
context "with specific shells and format" do
let(:cask) do
Cask::Cask.new("test-generated-completion") do
version "1.0"
sha256 :no_check
url "file:///dev/null"
generate_completions_from_executable "bin/foo", "completions",
shells: [:zsh], shell_parameter_format: :arg, base_name: "bar"
end
end
it "generates only for the specified shell with the correct format" do
artifact = cask.artifacts.grep(described_class).first
captured_args = T.let([], T::Array[String])
allow(Sandbox).to receive_messages(ensure_sandbox_installed!: nil, available?: true)
allow(Sandbox).to receive(:new) do
instance_double(Sandbox).tap do |sandbox|
allow(sandbox).to receive(:allow_read)
allow(sandbox).to receive(:allow_write_temp_and_cache)
allow(sandbox).to receive(:deny_read_home)
allow(sandbox).to receive(:deny_all_network)
allow(sandbox).to receive(:run) do |*args|
captured_args = args.map(&:to_s)
Pathname(args.fetch(7)).write("zsh completion")
end
end
end
artifact.install_phase
expect(captured_args).to include("--shell=zsh")
expect(captured_args.fetch(5)).to end_with(" 2>/dev/null")
expect(zsh_dir/"_bar").to be_a_file
expect(bash_dir/"bar").not_to exist
expect(fish_dir/"bar.fish").not_to exist
end
end
context "with string shells" do
let(:cask) do
Cask::Cask.new("test-generated-completion") do
version "1.0"
sha256 :no_check
url "file:///dev/null"
generate_completions_from_executable "bin/foo", "completions",
shells: %w[bash zsh fish pwsh]
end
end
it "normalizes shells to symbols" do
artifact = cask.artifacts.grep(described_class).first
expect(artifact.shells).to eq([:bash, :zsh, :fish, :pwsh])
end
end
end