Context
As part of a Bash script that is tested using the BATS, I noticed that my tests are not terminated when I run a function that activates an ssh-account.
Code
The following function assumes a private and public ssh key pair exists in /home/<username>/.ssh/
. If I run it manually using source src/the_bash_script.sh && activate_ssh_account <my_git_username>
, it works and says Identity added: /home/name/.ssh/<my_git_email>
:
#!/bin/bash
# Activates/enables the ssh for
activate_ssh_account() {
git_username=$1
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/"$git_username"
}
However, when it is ran from the test with:
#!./test/libs/bats/bin/bats
load 'libs/bats-support/load'
load 'libs/bats-assert/load'
# https://github.com/bats-core/bats-file#Index-of-all-functions
load 'libs/bats-file/load'
# https://github.com/bats-core/bats-assert#usage
load 'assert_utils'
source src/the_bash_script.sh
@test "Check if ssh-account is activated after activating it." {
activate_ssh_account "some_git_username"
assert_equal "Something" "Something_else"
}
It hangs indefinitely.
Question
How can I activate an ssh-account without causing the BATS tests to hang indefinitely?
CodePudding user response:
The test hangs indefinitely because BATS waits for ssh-agent
to terminate (which runs in the background once line eval "$(ssh-agent -s)"
is executed). To be more specific, BATS waits for file descriptor 3 to be closed (which is being held open by ssh-agent
).
Thus, this can be solved by either implementing the workaround mentioned in the documentation or by killing ssh-agent
.
Use workaround from documentation:
#!/bin/bash
# Activates/enables the ssh for
activate_ssh_account() {
git_username=$1
eval "$(ssh-agent -s 3>&-)"
ssh-add ~/.ssh/"$git_username"
}
This closes fd3 for ssh-agent
and BATS will no longer hang. Note that this leaves ssh-agent
running in the background, even after BATS exits. It is unclear from your question if that is desired or not. If it's not, use the alternative below.
Kill ssh-agent
:
Add a cleanup trap to activate_ssh_account
:
#!/bin/bash
# Activates/enables the ssh for
activate_ssh_account() {
trap "trap - RETURN; kill \$SSH_AGENT_PID" RETURN
git_username=$1
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/"$git_username"
}
The trap is executed when the function exits and kills ssh-agent
using the pid exported by eval "$(ssh-agent -s)"
(i.e. variable SSH_AGENT_PID
).
If you don't want to use a trap for some reason, this will also work:
#!/bin/bash
# Activates/enables the ssh for
activate_ssh_account() {
git_username=$1
eval "$(ssh-agent -s)"
result=0
ssh-add ~/.ssh/"$git_username" || result=$?
kill $SSH_AGENT_PID
return $result
}
Note that the ||
construct is necessary because BATS will stop executing the function's code once a command fails (i.e. without ||
, kill
will not be executed if ssh-add
fails).
As a side note, to actually test if activate_ssh_account
succeeds or fails, you should use assert_success
instead of assert_equal
(unless there is more code that you omitted in your question).
CodePudding user response:
I never used BATS but by reading the doc I can say that there is a specific command for sharing common code. You may need to specify the full path though:
load
: Share common codeYou may want to share common code across multiple test files. Bats includes a convenient load command for sourcing a Bash source file relative to the location of the current test file. For example, if you have a Bats test in
test/foo.bats
, the command
load test_helper
will source the script test/test_helper.bash in your test file. This can be useful for sharing functions to set up your environment or load fixtures.
Option1:
Try replacing
source src/the_bash_script.sh
with
load '/full/path/to/src/the_bash_script.sh'
Option 2:
Try adding the sourcing in @test
@test "Check if ssh-account is activated after activating it." {
source /full/path/to/src/the_bash_script.sh
activate_ssh_account "some_git_username"
assert_equal "Something" "Something_else"
}