#!/bin/bash
# See: https://github.com/djui/bashunit

skipAllTests=false

scriptDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
buildDir="$scriptDir/.."
tmpDir="/tmp/atmoz_sftp_test"

sudo="sudo"
cache="--no-cache"

build=${1:-"build"}
output=${2:-"quiet"}
cleanup=${3:-"cleanup"}
sftpImageName="atmoz/sftp_test"
sftpContainerName="atmoz_sftp_test"

if [ "$output" == "quiet" ]; then
    redirect="/dev/null"
else
    redirect="/dev/stdout"
fi

buildOptions="--tag $sftpImageName"

##############################################################################

function beforeTest() {
    if [ "$build" == "build" ]; then
        buildOptions="$buildOptions $cache --pull=true"
    fi

    $sudo docker build $buildOptions "$buildDir"
    if [ $? -gt 0 ]; then
        echo "Build failed"
        exit 1
    fi

    # Private key can not be read by others
    chmod go-rw "$scriptDir/id_rsa"

    rm -rf "$tmpDir" # clean state
    mkdir "$tmpDir"

    echo "test::$(id -u):$(id -g):dir1,dir2" >> "$tmpDir/users"
    echo "" >> "$tmpDir/users" # empty line
    echo "# comments are allowed" >> "$tmpDir/users"
    echo "  " >> "$tmpDir/users" # only whitespace
    echo "  # with whitespace in front" >> "$tmpDir/users"
    echo "user.with.dot::$(id -u):$(id -g)" >> "$tmpDir/users"
    $sudo docker run \
        -v "$tmpDir/users:/etc/sftp/users.conf:ro" \
        -v "$scriptDir/id_rsa.pub":/home/test/.ssh/keys/id_rsa.pub:ro \
        -v "$scriptDir/id_rsa.pub":/home/userFromEnv/.ssh/keys/id_rsa.pub:ro \
        -v "$scriptDir/id_rsa.pub":/home/user.with.dot/.ssh/keys/id_rsa.pub:ro \
        -v "$tmpDir":/home/test/share \
        --name "$sftpContainerName" \
        --expose 22 \
        -e "SFTP_USERS=userFromEnv::$(id -u):$(id -g) userFromEnv2::$(id -u):$(id -g)" \
        -d "$sftpImageName" \
        > "$redirect"

    waitForServer $sftpContainerName
}

function afterTest() {
    if [ "$output" != "quiet" ]; then
        echo "Docker logs:"
        $sudo docker logs "$sftpContainerName"
    fi

    if [ "$cleanup" == "cleanup" ]; then
        $sudo docker rm -fv "$sftpContainerName" > "$redirect"
        rm -rf "$tmpDir"
    fi
}

function getSftpIp() {
    $sudo docker inspect -f {{.NetworkSettings.IPAddress}} "$1"
}

function runSftpCommands() {
    ip="$(getSftpIp $1)"
    user="$2"
    shift 2

    commands=""
    for cmd in "$@"; do
        commands="$commands$cmd"$'\n'
    done

    echo "$commands" | sftp \
        -i "$scriptDir/id_rsa" \
        -oStrictHostKeyChecking=no \
        -oUserKnownHostsFile=/dev/null \
        -b - $user@$ip \
        > "$redirect" 2>&1

    status=$?
    sleep 1 # wait for commands to finish
    return $status
}

function waitForServer() {
    containerName="$1"
    echo -n "Waiting for $containerName to open port 22 ..."

    for i in {1..30}; do
        sleep 1
        ip="$(getSftpIp $containerName)"
        echo -n "."
        if [ -n "$ip" ] && nc -z $ip 22; then
            echo " OPEN"
            return 0;
        fi
    done

    echo " TIMEOUT"
    return 1
}

##############################################################################

function testContainerIsRunning() {
    $skipAllTests && skip && return 0

    ps="$($sudo docker ps -q -f name="$sftpContainerName")"
    assertNotEqual "$ps" ""

    if [ -z "$ps" ]; then
        skipAllTests=true
    fi
}

function testLoginUsingSshKey() {
    $skipAllTests && skip && return 0

    runSftpCommands "$sftpContainerName" "test" "exit"
    assertReturn $? 0
}

function testUserWithDotLogin() {
    $skipAllTests && skip && return 0

    runSftpCommands "$sftpContainerName" "user.with.dot" "exit"
    assertReturn $? 0
}

function testLoginUsingUserFromEnv() {
    $skipAllTests && skip && return 0

    runSftpCommands "$sftpContainerName" "userFromEnv" "exit"
    assertReturn $? 0
}

function testWritePermission() {
    $skipAllTests && skip && return 0

    runSftpCommands "$sftpContainerName" "test" \
        "cd share" \
        "mkdir test" \
        "exit"
    test -d "$tmpDir/test"
    assertReturn $? 0
}

function testDir() {
    $skipAllTests && skip && return 0

    runSftpCommands "$sftpContainerName" "test" \
        "cd dir1" \
        "mkdir test-dir1" \
        "get -rf test-dir1 $tmpDir/" \
        "cd ../dir2" \
        "mkdir test-dir2" \
        "get -rf test-dir2 $tmpDir/" \
        "exit"
    test -d "$tmpDir/test-dir1"
    assertReturn $? 0
    test -d "$tmpDir/test-dir2"
    assertReturn $? 0
}

# Smallest user config possible
function testMinimalContainerStart() {
    $skipAllTests && skip && return 0

    tmpContainerName="$sftpContainerName""_minimal"

    $sudo docker run \
        --name "$tmpContainerName" \
        -d "$sftpImageName" \
        m: \
        > "$redirect"

    waitForServer $tmpContainerName

    ps="$($sudo docker ps -q -f name="$tmpContainerName")"
    assertNotEqual "$ps" ""

    if [ -z "$ps" ]; then
        skipAllTests=true
    fi

    if [ "$output" != "quiet" ]; then
        $sudo docker logs "$tmpContainerName"
    fi

    if [ "$cleanup" == "cleanup" ]; then
        $sudo docker rm -fv "$tmpContainerName" > "$redirect"
    fi
}

function testLegacyConfigPath() {
    $skipAllTests && skip && return 0

    tmpContainerName="$sftpContainerName""_legacy"

    echo "test::$(id -u):$(id -g)" >> "$tmpDir/legacy_users"
    $sudo docker run \
        -v "$tmpDir/legacy_users:/etc/sftp-users.conf:ro" \
        --name "$tmpContainerName" \
        --expose 22 \
        -d "$sftpImageName" \
        > "$redirect"

    waitForServer $tmpContainerName

    ps="$($sudo docker ps -q -f name="$tmpContainerName")"
    assertNotEqual "$ps" ""

    if [ "$output" != "quiet" ]; then
        $sudo docker logs "$tmpContainerName"
    fi

    if [ "$cleanup" == "cleanup" ]; then
        $sudo docker rm -fv "$tmpContainerName" > "$redirect"
    fi
}


# Bind-mount folder using script in /etc/sftp.d/
function testCustomContainerStart() {
    $skipAllTests && skip && return 0

    tmpContainerName="$sftpContainerName""_custom"

    mkdir -p "$tmpDir/custom/bindmount"
    echo "mkdir -p /home/custom/bindmount && \
        chown custom /home/custom/bindmount && \
        mount --bind /custom /home/custom/bindmount" \
        > "$tmpDir/mount.sh"
    chmod +x "$tmpDir/mount.sh"

    $sudo docker run \
        --privileged=true \
        --name "$tmpContainerName" \
        -v "$scriptDir/id_rsa.pub":/home/custom/.ssh/keys/id_rsa.pub:ro \
        -v "$tmpDir/custom/bindmount":/custom \
        -v "$tmpDir/mount.sh":/etc/sftp.d/mount.sh \
        --expose 22 \
        -d "$sftpImageName" \
        custom:123 \
        > "$redirect"

    waitForServer $tmpContainerName

    ps="$($sudo docker ps -q -f name="$tmpContainerName")"
    assertNotEqual "$ps" ""

    runSftpCommands "$tmpContainerName" "custom" \
        "cd bindmount" \
        "mkdir test" \
        "exit"

    test -d "$tmpDir/custom/bindmount/test"
    assertReturn $? 0

    if [ "$output" != "quiet" ]; then
        $sudo docker logs "$tmpContainerName"
    fi

    if [ "$cleanup" == "cleanup" ]; then
        $sudo docker rm -fv "$tmpContainerName" > "$redirect"
    fi
}

##############################################################################

# Run tests
source "$scriptDir/bashunit.bash"
# Nothing happens after this