Skip to content

Commit

Permalink
Merge pull request #339 from Ravinou/develop
Browse files Browse the repository at this point in the history
Integrate unit tests with Bats for shell scripts
  • Loading branch information
Ravinou authored Nov 1, 2024
2 parents 6a6bf3f + 7f61e46 commit 0298e71
Show file tree
Hide file tree
Showing 15 changed files with 536 additions and 30 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/bats.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Bats

on:
push:
branches:
- main
- develop
pull_request:
branches:
- main
- develop
jobs:
bats-test:
name: Run bats tests against shells
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build container & run bats tests
run: |
docker compose -f tests/bats/docker-compose.yml up --abort-on-container-exit --build
34 changes: 17 additions & 17 deletions .github/workflows/docker-image-test.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
name: Test Docker Container Build on Pull Request
name: Test to build docker container on Pull Request

on:
pull_request:
branches:
- main
- develop
pull_request:
branches:
- main
- develop

jobs:
build-container:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker Container
run: |
docker buildx build --platform linux/amd64,linux/arm64 -t borgwarehouse:pr-${{ github.event.pull_request.number }} .
build-container:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build BorgWarehouse Container
run: |
docker buildx build --platform linux/amd64,linux/arm64 -t borgwarehouse:pr-${{ github.event.pull_request.number }} .
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,7 @@ config/repo.json
config/users.json

# docker files
docker-compose.yml
docker-compose.yml

# Commit tests docker-compose
!tests/bats/docker-compose.yml
2 changes: 1 addition & 1 deletion helpers/shells/createRepo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pool="${home}/repos"
authorized_keys="${home}/.ssh/authorized_keys"

# Check args
if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" != "true" ] && [ "$3" != "false" ];then
if [ "$1" == "" ] || [ "$2" == "" ] || ! [[ "$2" =~ ^[0-9]+$ ]] || [ "$3" != "true" ] && [ "$3" != "false" ]; then
echo -n "This shell takes 3 arguments : SSH Public Key, Quota in Go [e.g. : 10], Append only mode [true|false]"
exit 1
fi
Expand Down
10 changes: 5 additions & 5 deletions helpers/shells/deleteRepo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ if [[ $# -ne 1 || $1 = "" ]]; then
exit 1
fi

# Check if the repositoryName length is 8 char. With createRepo.sh our randoms have a length of 8 characters.
# If we receive another length there is necessarily a problem.
# Check if the repositoryName pattern is an hexa 8 char. With createRepo.sh our randoms are hexa of 8 characters.
# If we receive another pattern there is necessarily a problem.
repositoryName=$1
if [ ${#repositoryName} != 8 ]; then
echo -n "Error with the length of the repositoryName."
exit 2
if ! [[ "$repositoryName" =~ ^[a-f0-9]{8}$ ]]; then
echo "Invalid repository name. Must be an 8-character hex string."
exit 2
fi

# Delete the repository and the line associated in the authorized_keys file
Expand Down
10 changes: 8 additions & 2 deletions helpers/shells/getStorageUsed.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ fi
# Default value if .env not exists
: "${home:=/home/borgwarehouse}"

# Use jc to output a JSON format with du command
# Get the size of each repository and format as JSON
cd "${home}"/repos
du -s -- * | jc --du
output=$(du -s -- * 2>/dev/null | awk '{print "{\"size\":" $1 ",\"name\":\"" $2 "\"}"}' | jq -s '.')
if [ -z "$output" ]; then
output="[]"
fi

# Print the JSON output
echo "$output"
8 changes: 4 additions & 4 deletions helpers/shells/updateRepo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ then
exit 2
fi

# Check if repositoryName length is 8 char. With createRepo.sh our randoms have a length of 8 characters.
# If we receive another length, there is necessarily a problem.
# Check if the repositoryName pattern is an hexa 8 char. With createRepo.sh our randoms are hexa of 8 characters.
# If we receive another pattern there is necessarily a problem.
repositoryName=$1
if [ ${#repositoryName} != 8 ]; then
echo -n "Error with the length of the repositoryName."
if ! [[ "$repositoryName" =~ ^[a-f0-9]{8}$ ]]; then
echo "Invalid repository name. Must be an 8-character hex string."
exit 3
fi

Expand Down
3 changes: 3 additions & 0 deletions tests/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## BATS tests against bash scripts

From `tests/bats`, launch `docker compose up --build`
19 changes: 19 additions & 0 deletions tests/bats/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM bash:latest

RUN apk add --no-cache \
bats \
openssl \
borgbackup \
jq \
coreutils

COPY helpers/shells/ /test/scripts/
COPY tests/bats/createRepo.bats /test/tests/createRepo.bats
COPY tests/bats/deleteRepo.bats /test/tests/deleteRepo.bats
COPY tests/bats/updateRepo.bats /test/tests/updateRepo.bats
COPY tests/bats/getLastSave.bats /test/tests/getLastSave.bats
COPY tests/bats/getStorageUsed.bats /test/tests/getStorageUsed.bats

RUN chmod +x /test/scripts/*.sh

CMD ["bats", "/test/tests/"]
104 changes: 104 additions & 0 deletions tests/bats/createRepo.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env bats

setup() {
# Set up the environment for each test
export home="/tmp/borgwarehouse"
mkdir -p /tmp/borgwarehouse
mkdir -p /tmp/borgwarehouse/repos
mkdir -p /tmp/borgwarehouse/.ssh
touch /tmp/borgwarehouse/.ssh/authorized_keys

# SSH keys samples for testing
export SSH_KEY_ED25519="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICtujwncNGgdNxWOCQedMCnrhRZT4B7eUyyFJNryvQj9 publickey"
export SSH_KEY_ED25519_SK="sk-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICtujwncNGgdNxWOCQedMCnrhRZT4B7eUyyFJNryvQj9 publickey"
export SSH_KEY_RSA="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDf8SFSuWqPtPYKjoXL8aowdKYfeKFKbE6w4CvqXPSRtgwKGWJva/UVF8Q/jGClwsVpTJfZnA76fnih76cE4ZiucPtDM2dyDILHNSZo/8rwUVkNB4P3aaCxV6lVMurmIgF4ibWQFBdyWKCJM7nQjO71TlMw/HfqpeYXXdjL1MBlMzqOZYLDrPoiJEiAfKheVeCONMlo8HMfEPxiu7bwfF7vQqYstcbZ55RN1t7RYaxlCTZaj0GOxIGKLmmHTDGzQQIaOGSr3+8Gk1I/MFle2/dYKbBEi97NrJowRO4a4pVbVso0YKyESL3U40uZly1bzoNx4DvMBbFwYSE1IJbs/AQIfB6KH4yLtQTmfb4qPRLCS1CBWBZKeKJ304p6rxKuv+CjagsFwdG5cS7cCosfdEU43QuWngnYQGUwMKskxX/7rPm+WZItN7XiNoMRmzaC+T0cIRXH7Cl7VFE3cbTzgmqJPVeUpccjTP/BdDahHKFqyVhAFvyI7JM4ct1/tU8o015TM1EXzMBeJOxalj6RsuDIFjjEaMN5pZmlHGBFBmcRgY7TqYAwr02maKb9BtcPOGIPgpI3AMzqNX+LjFssI0AuqBGTYN8v6OBr2NmTHfZlnClucjoAw71QPeQySABqrX0p9xieX15Ly1z9oMH9lapW6X9e0JnQBMnz1N2eaq1qAQ== publickey"
}

teardown() {
# Clean up the environment after each test
rm -rf /tmp/borgwarehouse
}

@test "Test createRepo.sh with missing arguments" {
run bash /test/scripts/createRepo.sh
[ "$status" -eq 1 ]
[ "$output" == "This shell takes 3 arguments : SSH Public Key, Quota in Go [e.g. : 10], Append only mode [true|false]" ]
}

@test "Test createRepo.sh with missing Quota and append-only mode arguments" {
run bash /test/scripts/createRepo.sh "$SSH_KEY_ED25519"
[ "$status" -eq 1 ]
[ "$output" == "This shell takes 3 arguments : SSH Public Key, Quota in Go [e.g. : 10], Append only mode [true|false]" ]
}

@test "Test createRepo.sh with missing Append-only mode argument" {
run bash /test/scripts/createRepo.sh "$SSH_KEY_ED25519" 10
echo $output
cat ${home}/.ssh/authorized_keys
[ "$status" -eq 1 ]
[ "$output" == "This shell takes 3 arguments : SSH Public Key, Quota in Go [e.g. : 10], Append only mode [true|false]" ]
}

@test "Test createRepo.sh with invalid SSH key format" {
run bash /test/scripts/createRepo.sh "invalid-key" 10 true
[ "$status" -eq 2 ]
[ "$output" == "Invalid public SSH KEY format. Provide a key in OpenSSH format (rsa, ed25519, ed25519-sk)" ]
}

@test "Test createRepo.sh with invalid Quota format" {
run bash /test/scripts/createRepo.sh "$SSH_KEY_ED25519" "AA" true
[ "$status" -eq 1 ]
[ "$output" == "This shell takes 3 arguments : SSH Public Key, Quota in Go [e.g. : 10], Append only mode [true|false]" ]
}

@test "Test createRepo.sh with invalid Append-only mode format" {
run bash /test/scripts/createRepo.sh "$SSH_KEY_ED25519" 10 blabla
[ "$status" -eq 1 ]
[ "$output" == "This shell takes 3 arguments : SSH Public Key, Quota in Go [e.g. : 10], Append only mode [true|false]" ]
}


@test "Test createRepo.sh if authorized_keys is missing" {
rm /tmp/borgwarehouse/.ssh/authorized_keys
run bash /test/scripts/createRepo.sh "$SSH_KEY_ED25519" 10 true
[ "$status" -eq 5 ]
[ "$output" == "${home}/.ssh/authorized_keys must be present" ]
}

@test "Test createRepo.sh if SSH key is already present in authorized_keys" {
# Add a key
echo "$SSH_KEY_ED25519" > /tmp/borgwarehouse/.ssh/authorized_keys
# Try to re-add the same key
run bash /test/scripts/createRepo.sh "$SSH_KEY_ED25519" 10 true
[ "$status" -eq 3 ]
[ "$output" == "SSH pub key already present in authorized_keys" ]
}

@test "Test createRepo.sh repository name generation" {
run bash /test/scripts/createRepo.sh "$SSH_KEY_ED25519" 10 false
[[ "$output" =~ ^[0-9a-f]{8}$ ]] # Must return a 8 characters hexa string
}

@test "Test createRepo.sh key ED25519 insertion in authorized_keys" {
run bash /test/scripts/createRepo.sh "$SSH_KEY_ED25519" 10 false
expected_line="command=\"cd ${home}/repos;borg serve --restrict-to-path ${home}/repos/${output} --storage-quota 10G\",restrict $SSH_KEY_ED25519"
grep -qF "$expected_line" /tmp/borgwarehouse/.ssh/authorized_keys
}

@test "Test createRepo.sh key ED25519-SK insertion in authorized_keys" {
run bash /test/scripts/createRepo.sh "$SSH_KEY_ED25519_SK" 10 false
expected_line="command=\"cd ${home}/repos;borg serve --restrict-to-path ${home}/repos/${output} --storage-quota 10G\",restrict $SSH_KEY_ED25519_SK"
grep -qF "$expected_line" /tmp/borgwarehouse/.ssh/authorized_keys
}

@test "Test createRepo.sh key RSA insertion in authorized_keys" {
run bash /test/scripts/createRepo.sh "$SSH_KEY_RSA" 10 false
expected_line="command=\"cd ${home}/repos;borg serve --restrict-to-path ${home}/repos/${output} --storage-quota 10G\",restrict $SSH_KEY_RSA"
grep -qF "$expected_line" /tmp/borgwarehouse/.ssh/authorized_keys
}

@test "Test createRepo.sh key ED25519 insertion in authorized_keys with append only mode" {
run bash /test/scripts/createRepo.sh "$SSH_KEY_ED25519" 10 true
expected_line="command=\"cd ${home}/repos;borg serve --append-only --restrict-to-path ${home}/repos/${output} --storage-quota 10G\",restrict $SSH_KEY_ED25519"
grep -qF "$expected_line" /tmp/borgwarehouse/.ssh/authorized_keys
}
90 changes: 90 additions & 0 deletions tests/bats/deleteRepo.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env bats

setup() {
# Setup the environment for each test
export home="/tmp/borgwarehouse"
mkdir -p "${home}/repos"
mkdir -p "${home}/.ssh"
touch "${home}/.ssh/authorized_keys"

# SSH keys samples for testing
export SSH_KEY_ED25519="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICtujwncNGgdNxWOCQedMCnrhRZT4B7eUyyFJNryvQj9 publickey"
}

teardown() {
# Cleanup after each test
rm -rf /tmp/borgwarehouse
}

@test "Test deleteRepo.sh with missing arguments" {
run bash /test/scripts/deleteRepo.sh
[ "$status" -eq 1 ]
[ "$output" == "You must provide a repositoryName in argument." ]
}

@test "Test deleteRepo.sh with repositoryName shorter than 8 characters" {
run bash /test/scripts/deleteRepo.sh "1234567"
[ "$status" -eq 2 ]
[ "$output" == "Invalid repository name. Must be an 8-character hex string." ]
}

@test "Test deleteRepo.sh with repositoryName longer than 8 characters" {
run bash /test/scripts/deleteRepo.sh "123456789"
[ "$status" -eq 2 ]
[ "$output" == "Invalid repository name. Must be an 8-character hex string." ]
}

@test "Test deleteRepo.sh with unexpected character in repositoryName" {
run bash /test/scripts/deleteRepo.sh "ffff/123"
[ "$status" -eq 2 ]
[ "$output" == "Invalid repository name. Must be an 8-character hex string." ]
}

@test "Test deleteRepo.sh for non-existing repository and associated key" {
# Add an SSH key to authorized_keys without creating the repository
echo "command=\"cd ${home}/repos;borg serve --restrict-to-path ${home}/repos/abcdef12 --storage-quota 10G\",restrict $SSH_KEY_ED25519" >> "${home}/.ssh/authorized_keys"

run bash /test/scripts/deleteRepo.sh "abcdef12"

[ "$status" -eq 0 ]
[ "$output" == "The folder ${home}/repos/abcdef12 did not exist (repository never initialized or used). The line associated in the authorized_keys file has been deleted." ]

# Check that the line was removed from authorized_keys
! grep -q "abcdef12" "${home}/.ssh/authorized_keys"
}

@test "Test deleteRepo.sh for existing repository but no associated key in authorized_keys" {
# Create a repository folder without adding the corresponding entry in authorized_keys
mkdir -p "${home}/repos/abcdef13"

run bash /test/scripts/deleteRepo.sh "abcdef13"

[ "$status" -eq 0 ]
[ "$output" == "The folder ${home}/repos/abcdef13 and all its data have been deleted. The line associated in the authorized_keys file has been deleted." ]

# Check that the repository folder is deleted
[ ! -d "${home}/repos/abcdef13" ]

# Check that no line was present in authorized_keys to begin with (and nothing was affected)
! grep -q "abcdef13" "${home}/.ssh/authorized_keys"
}

@test "Test deleteRepo.sh for existing repository and associated key" {
# Create a repository folder and add a corresponding entry in authorized_keys
mkdir -p "${home}/repos/abcdef12"
echo "command=\"cd ${home}/repos;borg serve --restrict-to-path ${home}/repos/abcdef12 --storage-quota 10G\",restrict $SSH_KEY_ED25519" >> "${home}/.ssh/authorized_keys"

run bash /test/scripts/deleteRepo.sh "abcdef12"

[ "$status" -eq 0 ]
[ "$output" == "The folder ${home}/repos/abcdef12 and all its data have been deleted. The line associated in the authorized_keys file has been deleted." ]

# Check that the repository folder is deleted
[ ! -d "${home}/repos/abcdef12" ]

# Check that the line was removed from authorized_keys
! grep -q "abcdef12" "${home}/.ssh/authorized_keys"
}



8 changes: 8 additions & 0 deletions tests/bats/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
services:
bats-test:
build:
context: ../..
dockerfile: tests/bats/Dockerfile
volumes:
- ../../helpers/shells:/test/scripts:ro
container_name: bats-test-container
Loading

0 comments on commit 0298e71

Please sign in to comment.