This allows the server to take appropriate action based on client version. It we add new features that require an updated client version, we can check the client's version, and give them a give the clients a nice error message instructing them to upgrade. This required a minor change to Rodish to execute code after option parsing and before subcommand processing, even if there are no valid subcommands provided. I made that change and released a new version, so this bumps the rodish version. The cli version is stored in cli/version.txt. bin/ubi reads this file when run. In the rake ubi/ubi-cross tasks, the Rakefile reads this file and sets the version in the compiled go file.
211 lines
7.0 KiB
Ruby
211 lines
7.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative "spec_helper"
|
|
|
|
require "puma/cli"
|
|
require "nio"
|
|
require "open3"
|
|
|
|
Gem.ruby # force early loading to work in frozen specs
|
|
|
|
# rubocop:disable RSpec/DescribeClass
|
|
# There is no class in this case.
|
|
RSpec.describe "bin/ubi" do
|
|
# rubocop:enable RSpec/DescribeClass
|
|
|
|
# rubocop:disable RSpec/BeforeAfterAll
|
|
# We only want one server for all tests. Spinning up a separate
|
|
# thread/server for each test would be very slow. Doing this is
|
|
# safe, as we are not leaking state between tests (the server is
|
|
# stateless and the web app it serves is a frozen Roda app).
|
|
before(:all) do
|
|
port = 8484
|
|
queue = Queue.new
|
|
@server = Puma::CLI.new(["-s", "-e", "test", "-b", "tcp://localhost:#{port}", "-t", "1:1", "spec/cli_config.ru"])
|
|
@server.launcher.events.on_booted { queue.push(nil) }
|
|
Thread.new do
|
|
@server.launcher.run
|
|
end
|
|
queue.pop
|
|
@prog = ENV["UBI_CMD"] || "bin/ubi"
|
|
@env = {
|
|
"UBI_URL" => "http://localhost:#{port}/cli",
|
|
"UBI_TOKEN" => "a",
|
|
"UBI_SSH" => "/bin/echo",
|
|
"UBI_PG_DUMPALL" => "/bin/echo"
|
|
}.freeze
|
|
@debug_env = @env.merge("UBI_DEBUG" => "1")
|
|
end
|
|
|
|
after(:all) do
|
|
@server.launcher.send(:stop)
|
|
end
|
|
# rubocop:enable RSpec/BeforeAfterAll
|
|
|
|
it "returns error if there is no UBI_TOKEN provided" do
|
|
o, e, s = Open3.capture3(@prog, "foo")
|
|
expect(o).to eq ""
|
|
expect(e).to eq "! Personal access token must be provided in UBI_TOKEN env variable for use\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "shows error if invalid token is used" do
|
|
o, e, s = Open3.capture3(@env.merge("UBI_TOKEN" => "b"), @prog, "foo")
|
|
expect(o).to eq ""
|
|
expect(e).to eq "invalid token\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "prints response body to stdout on success" do
|
|
o, e, s = Open3.capture3(@env, @prog, "foo")
|
|
expect(o).to eq "foo"
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 0
|
|
end
|
|
|
|
it "includes sent argv when using UBI_DEBUG" do
|
|
o, e, s = Open3.capture3(@debug_env, @prog, "foo")
|
|
expect(o).to match(/\A(\[:)?sending(, "|: \[)foo"?\]\nfoo\z/)
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 0
|
|
end
|
|
|
|
it "sends expected headers" do
|
|
o, e, s = Open3.capture3(@env, @prog, "headers")
|
|
expect(o).to eq "close application/json text/plain Bearer: a"
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 0
|
|
end
|
|
|
|
it "sends version header" do
|
|
o, e, s = Open3.capture3(@env, @prog, "--version")
|
|
expect(o).to match(UbiCli::UBI_VERSION_REGEXP)
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 0
|
|
end
|
|
|
|
it "prints response body to stderr on failure" do
|
|
o, e, s = Open3.capture3(@env, @prog, "error", "foo")
|
|
expect(o).to eq ""
|
|
expect(e).to eq "error foo\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "handles valid confirmations" do
|
|
o, e, s = Open3.capture3(@env, @prog, "confirm", "foo", stdin_data: "valid")
|
|
expect(o).to eq "Pre-Confirm\nTest-Confirm-Prompt: valid-confirm: foo"
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 0
|
|
end
|
|
|
|
it "includes both argvs when using UBI_DEBUG for confirmations" do
|
|
o, e, s = Open3.capture3(@debug_env, @prog, "confirm", "foo", stdin_data: "valid")
|
|
expect(o).to match(/
|
|
sending.*confirm.*foo"?\]\n
|
|
Pre-Confirm\n
|
|
Test-Confirm-Prompt:\ .*sending.*--confirm.*valid.*confirm.*foo"?\]\n
|
|
valid-confirm:\ foo\z
|
|
/x)
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 0
|
|
end
|
|
|
|
it "handles invalid confirmations" do
|
|
o, e, s = Open3.capture3(@env, @prog, "confirm", "foo", stdin_data: "invalid")
|
|
expect(o).to eq "Pre-Confirm\nTest-Confirm-Prompt: "
|
|
expect(e).to eq "invalid-confirm: foo\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "does not recurse confirmation even if requested" do
|
|
o, e, s = Open3.capture3(@env, @prog, "confirm", "foo", stdin_data: "recurse")
|
|
expect(o).to eq "Pre-Confirm\nTest-Confirm-Prompt: "
|
|
expect(e).to eq "! Invalid server response, repeated confirmation attempt\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "executes supported program" do
|
|
o, e, s = Open3.capture3(@env, @prog, "exec", "ssh", "dash2", "foo")
|
|
expect(o).to eq "foo --\n"
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 0
|
|
end
|
|
|
|
it "uses exit status of executed program" do
|
|
o, e, s = Open3.capture3(@env.merge("UBI_SSH" => "false"), @prog, "exec", "ssh", "dash2", "foo")
|
|
expect(o).to eq ""
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "executes supported program with new argument after --" do
|
|
o, e, s = Open3.capture3(@env, @prog, "exec", "ssh", "new-after", "foo")
|
|
expect(o).to eq "foo -- new\n"
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 0
|
|
end
|
|
|
|
it "does not execute supported program with new argument before --" do
|
|
o, e, s = Open3.capture3(@env, @prog, "exec", "ssh", "new-before", "foo")
|
|
expect(o).to eq ""
|
|
expect(e).to eq "! Invalid server response, argument before '--' not in submitted argv\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "shows executed commands when using UBI_DEBUG" do
|
|
o, e, s = Open3.capture3(@debug_env, @prog, "exec", "ssh", "dash2", "foo")
|
|
expect(o).to match(/
|
|
sending.*exec.*ssh.*dash2.*foo"?\]\n
|
|
.*exec.*\/bin\/echo.*foo.*--"?\]\n
|
|
foo\ --\n\z
|
|
/x)
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 0
|
|
end
|
|
|
|
it "shows failing argv for invalid execution when using UBI_DEBUG" do
|
|
o, e, s = Open3.capture3(@debug_env, @prog, "exec", "ssh", "new-before", "foo")
|
|
expect(o).to match(/
|
|
sending.*exec.*ssh.*new-before.*foo"?\]\n
|
|
.*failure.*\/bin\/echo.*foo.*new.*--"?\]\n?\z
|
|
/x)
|
|
expect(e).to eq "! Invalid server response, argument before '--' not in submitted argv\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "does not execute invalid program" do
|
|
o, e, s = Open3.capture3(@env, @prog, "exec", "invalid", "dash2", "foo")
|
|
expect(o).to eq ""
|
|
expect(e).to eq "! Invalid server response, unsupported program requested\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "does not execute program not in origin argv" do
|
|
o, e, s = Open3.capture3(@env, @prog, "exec", "ssh", "prog-switch", "foo")
|
|
expect(o).to eq ""
|
|
expect(e).to eq "! Invalid server response, not executing program not in original argv\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "does not execute program without --" do
|
|
o, e, s = Open3.capture3(@env, @prog, "exec", "ssh", "as-is", "foo")
|
|
expect(o).to eq ""
|
|
expect(e).to eq "! Invalid server response, no '--' in returned argv\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
|
|
it "allows pg_dumpall program without -- with -d" do
|
|
o, e, s = Open3.capture3(@env, @prog, "exec", "pg_dumpall", "newd", "foo")
|
|
expect(o).to eq "foo -dnew\n"
|
|
expect(e).to eq ""
|
|
expect(s.exitstatus).to eq 0
|
|
end
|
|
|
|
it "does not execute program with multiple new arguments" do
|
|
o, e, s = Open3.capture3(@env, @prog, "exec", "ssh", "new2", "foo")
|
|
expect(o).to eq ""
|
|
expect(e).to eq "! Invalid server response, multiple arguments not in submitted argv\n"
|
|
expect(s.exitstatus).to eq 1
|
|
end
|
|
end
|