Add UT for new interactive behaviour

also adding helpers to simulate STDIN being tty or file
This commit is contained in:
Peter Rullmann
2025-05-08 20:24:50 +02:00
parent 092ca425d7
commit d62c35e63e
4 changed files with 67 additions and 16 deletions

View File

@@ -361,6 +361,7 @@ class CliAppTest < CliTestCase
SSHKit::Backend::Abstract.any_instance.expects(:exec) SSHKit::Backend::Abstract.any_instance.expects(:exec)
.with("ssh -t root@1.1.1.1 -p 22 'docker run -it --rm --network kamal --env-file .kamal/apps/app/env/roles/web.env --log-opt max-size=\"10m\" dhh/app:latest ruby -v'") .with("ssh -t root@1.1.1.1 -p 22 'docker run -it --rm --network kamal --env-file .kamal/apps/app/env/roles/web.env --log-opt max-size=\"10m\" dhh/app:latest ruby -v'")
stub_stdin_tty do
run_command("exec", "-i", "ruby -v").tap do |output| run_command("exec", "-i", "ruby -v").tap do |output|
assert_hook_ran "pre-connect", output assert_hook_ran "pre-connect", output
assert_match "docker login -u [REDACTED] -p [REDACTED]", output assert_match "docker login -u [REDACTED] -p [REDACTED]", output
@@ -368,12 +369,14 @@ class CliAppTest < CliTestCase
assert_match "Launching interactive command with version latest via SSH from new container on 1.1.1.1...", output assert_match "Launching interactive command with version latest via SSH from new container on 1.1.1.1...", output
end end
end end
end
test "exec interactive with reuse" do test "exec interactive with reuse" do
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true) Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
SSHKit::Backend::Abstract.any_instance.expects(:exec) SSHKit::Backend::Abstract.any_instance.expects(:exec)
.with("ssh -t root@1.1.1.1 -p 22 'docker exec -it app-web-999 ruby -v'") .with("ssh -t root@1.1.1.1 -p 22 'docker exec -it app-web-999 ruby -v'")
stub_stdin_tty do
run_command("exec", "-i", "--reuse", "ruby -v").tap do |output| run_command("exec", "-i", "--reuse", "ruby -v").tap do |output|
assert_hook_ran "pre-connect", output assert_hook_ran "pre-connect", output
assert_match "Get current version of running container...", output assert_match "Get current version of running container...", output
@@ -381,6 +384,20 @@ class CliAppTest < CliTestCase
assert_match "Launching interactive command with version 999 via SSH from existing container on 1.1.1.1...", output assert_match "Launching interactive command with version 999 via SSH from existing container on 1.1.1.1...", output
end end
end end
end
test "exec interactive with pipe on STDIN" do
Kamal::Commands::Hook.any_instance.stubs(:hook_exists?).returns(true)
SSHKit::Backend::Abstract.any_instance.expects(:exec)
.with("ssh -t root@1.1.1.1 -p 22 'docker exec -i app-web-999 ruby -v'")
stub_stdin_file do
run_command("exec", "-i", "--reuse", "ruby -v").tap do |output|
assert_hook_ran "pre-connect", output
assert_match "Launching interactive command with version 999 via SSH from existing container on 1.1.1.1...", output
end
end
end
test "containers" do test "containers" do
run_command("containers").tap do |output| run_command("containers").tap do |output|

View File

@@ -118,14 +118,21 @@ class CommandsAccessoryTest < ActiveSupport::TestCase
test "execute in new container over ssh" do test "execute in new container over ssh" do
new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do
assert_match %r{docker run -it --rm --network kamal --env MYSQL_ROOT_HOST=\"%\" --env-file .kamal/apps/app/env/accessories/mysql.env private.registry/mysql:8.0 mysql -u root}, assert_match %r{docker run -it --rm --network kamal --env MYSQL_ROOT_HOST=\"%\" --env-file .kamal/apps/app/env/accessories/mysql.env private.registry/mysql:8.0 mysql -u root},
new_command(:mysql).execute_in_new_container_over_ssh("mysql", "-u", "root") stub_stdin_tty { new_command(:mysql).execute_in_new_container_over_ssh("mysql", "-u", "root") }
end end
end end
test "execute in existing container over ssh" do test "execute in existing container over ssh" do
new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do
assert_match %r{docker exec -it app-mysql mysql -u root}, assert_match %r{docker exec -it app-mysql mysql -u root},
new_command(:mysql).execute_in_existing_container_over_ssh("mysql", "-u", "root") stub_stdin_tty { new_command(:mysql).execute_in_existing_container_over_ssh("mysql", "-u", "root") }
end
end
test "execute in existing container with piped input over ssh" do
new_command(:mysql).stub(:run_over_ssh, ->(cmd) { cmd.join(" ") }) do
assert_match %r{docker exec -i app-mysql mysql -u root},
stub_stdin_file { new_command(:mysql).execute_in_existing_container_over_ssh("mysql", "-u", "root") }
end end
end end

View File

@@ -288,7 +288,7 @@ class CommandsAppTest < ActiveSupport::TestCase
test "execute in new container over ssh" do test "execute in new container over ssh" do
assert_match %r{docker run -it --rm --network kamal --env-file .kamal/apps/app/env/roles/web.env --log-opt max-size="10m" dhh/app:999 bin/rails c}, assert_match %r{docker run -it --rm --network kamal --env-file .kamal/apps/app/env/roles/web.env --log-opt max-size="10m" dhh/app:999 bin/rails c},
new_command.execute_in_new_container_over_ssh("bin/rails", "c", env: {}) stub_stdin_tty { new_command.execute_in_new_container_over_ssh("bin/rails", "c", env: {}) }
end end
test "execute in new container over ssh with tags" do test "execute in new container over ssh with tags" do
@@ -296,18 +296,23 @@ class CommandsAppTest < ActiveSupport::TestCase
@config[:env]["tags"] = { "tag1" => { "ENV1" => "value1" } } @config[:env]["tags"] = { "tag1" => { "ENV1" => "value1" } }
assert_equal "ssh -t root@1.1.1.1 -p 22 'docker run -it --rm --network kamal --env ENV1=\"value1\" --env-file .kamal/apps/app/env/roles/web.env --log-opt max-size=\"10m\" dhh/app:999 bin/rails c'", assert_equal "ssh -t root@1.1.1.1 -p 22 'docker run -it --rm --network kamal --env ENV1=\"value1\" --env-file .kamal/apps/app/env/roles/web.env --log-opt max-size=\"10m\" dhh/app:999 bin/rails c'",
new_command.execute_in_new_container_over_ssh("bin/rails", "c", env: {}) stub_stdin_tty { new_command.execute_in_new_container_over_ssh("bin/rails", "c", env: {}) }
end end
test "execute in new container with custom options over ssh" do test "execute in new container with custom options over ssh" do
@config[:servers] = { "web" => { "hosts" => [ "1.1.1.1" ], "options" => { "mount" => "somewhere", "cap-add" => true } } } @config[:servers] = { "web" => { "hosts" => [ "1.1.1.1" ], "options" => { "mount" => "somewhere", "cap-add" => true } } }
assert_match %r{docker run -it --rm --network kamal --env-file .kamal/apps/app/env/roles/web.env --log-opt max-size=\"10m\" --mount \"somewhere\" --cap-add dhh/app:999 bin/rails c}, assert_match %r{docker run -it --rm --network kamal --env-file .kamal/apps/app/env/roles/web.env --log-opt max-size=\"10m\" --mount \"somewhere\" --cap-add dhh/app:999 bin/rails c},
new_command.execute_in_new_container_over_ssh("bin/rails", "c", env: {}) stub_stdin_tty { new_command.execute_in_new_container_over_ssh("bin/rails", "c", env: {}) }
end end
test "execute in existing container over ssh" do test "execute in existing container over ssh" do
assert_match %r{docker exec -it app-web-999 bin/rails c}, assert_match %r{docker exec -it app-web-999 bin/rails c},
new_command.execute_in_existing_container_over_ssh("bin/rails", "c", env: {}) stub_stdin_tty { new_command.execute_in_existing_container_over_ssh("bin/rails", "c", env: {}) }
end
test "execute in existing container with piped input over ssh" do
assert_match %r{docker exec -i app-web-999 bin/rails c},
stub_stdin_file { new_command.execute_in_existing_container_over_ssh("bin/rails", "c", env: {}) }
end end
test "run over ssh" do test "run over ssh" do

View File

@@ -3,6 +3,7 @@ require "active_support/test_case"
require "active_support/testing/autorun" require "active_support/testing/autorun"
require "active_support/testing/stream" require "active_support/testing/stream"
require "rails/test_unit/line_filtering" require "rails/test_unit/line_filtering"
require "pty"
require "debug" require "debug"
require "mocha/minitest" # using #stubs that can alter returns require "mocha/minitest" # using #stubs that can alter returns
require "minitest/autorun" # using #stub that take args require "minitest/autorun" # using #stub that take args
@@ -48,6 +49,27 @@ class ActiveSupport::TestCase
capture(:stderr) { yield }.strip capture(:stderr) { yield }.strip
end end
def stub_stdin_tty
PTY.open do |master, slave|
stub_stdin(master) { yield }
end
end
def stub_stdin_file
File.open("/dev/null", "r") do |file|
stub_stdin(file) { yield }
end
end
def stub_stdin(io)
original_stdin = STDIN.dup
STDIN.reopen(io)
yield
ensure
STDIN.reopen(original_stdin)
original_stdin.close
end
def with_test_secrets(**files) def with_test_secrets(**files)
setup_test_secrets(**files) setup_test_secrets(**files)
yield yield