#!/bin/bash
#
# Test the PostgreSQL image.
#
# IMAGE_NAME specifies the name of the candidate image used for testing.
# The image has to be available before this script is executed.
#

set -exo nounset
shopt -s nullglob

# library from container-common-scripts
. test/test-lib.sh

# local library
. test/pg-test-lib.sh

TEST_LIST="\
run_container_creation_tests
run_general_tests
run_change_password_test
run_replication_test
run_master_restart_test
run_doc_test
run_s2i_test
run_test_cfg_hook
run_s2i_bake_data_test
run_s2i_enable_ssl_test
run_upgrade_test
run_migration_test
run_pgaudit_test
run_latest_imagestreams_test
"

test $# -eq 1 -a "${1-}" == --list && echo "$TEST_LIST" && exit 0
test -n "${IMAGE_NAME-}" || false 'make sure $IMAGE_NAME is defined'
test -n "${VERSION-}" || false 'make sure $VERSION is defined'
test -n "${OS-}" || false 'make sure $OS is defined'

CIDFILE_DIR=$(mktemp --suffix=postgresql_test_cidfiles -d)

volumes_to_clean=
images_to_clean=()
files_to_clean=
test_dir="$(readlink -f "$(dirname "$0")")"
test_short_summary=''
TESTSUITE_RESULT=1

_cleanup_commands_space=
_cleanup_commands=

add_cleanup_command ()
{
    local cmd= space=
    for arg; do
        cmd+="$space$(printf "%q" "$arg")"
        space=' '
    done
    _cleanup_commands+="$_cleanup_commands_space$cmd"
    _cleanup_commands_space='
'
}
function cleanup() {
  # Print a big fat separator to find the error easier
  echo "=================================== Cleanup begins here ============================="

  for cidfile in $CIDFILE_DIR/* ; do
    CONTAINER=$(cat $cidfile)

    echo "Stopping and removing container $CONTAINER..."
    docker stop $CONTAINER
    exit_status=$(docker inspect -f '{{.State.ExitCode}}' $CONTAINER)
    if [ "$exit_status" != "0" ]; then
      echo "Dumping logs for $CONTAINER"
      docker logs $CONTAINER
    fi
    docker rm $CONTAINER
    rm $cidfile
    echo "Done."
  done
  rmdir $CIDFILE_DIR

  ct_path_foreach "$volumes_to_clean" cleanup_volume_dir

  if test -n "${images_to_clean-}"; then
    # Workaround for RHEL 7 bash bug:
    # https://bugzilla.redhat.com/show_bug.cgi?id=1636393
    for image in "${images_to_clean[@]}"; do
      docker rmi -f "$image"
    done
  fi

  ct_path_foreach "$files_to_clean" rm

  echo "$_cleanup_commands" | while read -r line; do
    eval "$line"
  done

  echo "$test_short_summary"

  if [ $TESTSUITE_RESULT -eq 0 ] ; then
    echo "Tests for ${IMAGE_NAME} succeeded."
  else
    echo "Tests for ${IMAGE_NAME} failed."
  fi
}
trap cleanup EXIT

cleanup_volume_dir ()
{
  test ! -d "$1" && : "WARN: cleaned $1 for some reason" && return 0
  # When we run this test script as non-root (we should?), the PostgreSQL server
  # within container is still run under 'postgres' user.  It means that, taking
  # into account 0077 umask of PostgreSQL server, we are unable to remove files
  # created by server.  That's why we need to let docker escalate the privileges
  # again.
  local datadir=/var/lib/pgsql/data
  docker run -v "$1:$datadir:Z" --rm "$IMAGE_NAME" /bin/sh -c "/bin/rm -rf $datadir/userdata"
  rmdir "$1"
}

function get_cid() {
  local id="$1" ; shift || return 1
  echo $(cat "$CIDFILE_DIR/$id")
}

function get_container_ip() {
  local id="$1" ; shift
  docker inspect --format='{{.NetworkSettings.IPAddress}}' $(get_cid "$id")
}

function get_ip_from_cid() {
  local cid="$1"; shift
  docker inspect --format='{{.NetworkSettings.IPAddress}}' $cid
}

function postgresql_cmd() {
  docker run --rm -e PGPASSWORD="$PASS" "$IMAGE_NAME" psql "postgresql://$PGUSER@$CONTAINER_IP:5432/${DB-db}" "$@"
}

function test_connection() {
  local name=$1 ; shift
  ip=$(get_container_ip $name)
  echo "  Testing PostgreSQL connection to $ip..."
  local max_attempts=20
  local sleep_time=2
  for i in $(seq $max_attempts); do
    echo "    Trying to connect..."
    set +e
    # Don't let the code come here if neither user nor admin is able to
    # connect.
    if [ -v PGUSER ] && [ -v PASS ]; then
      CONTAINER_IP=$ip postgresql_cmd <<< "SELECT 1;"
    else
      PGUSER=postgres PASS=$ADMIN_PASS CONTAINER_IP=$ip DB=postgres postgresql_cmd <<< "SELECT 1;"
    fi
    status=$?
    set -e
    if [ $status -eq 0 ]; then
      echo "  Success!"
      return 0
    fi
    sleep $sleep_time
  done
  return 1
}

function test_postgresql() {
  echo "  Testing PostgreSQL"
  postgresql_cmd <<< "CREATE EXTENSION 'uuid-ossp';" # to test contrib package
  postgresql_cmd <<< "CREATE TABLE tbl (col1 VARCHAR(20), col2 VARCHAR(20));"
  postgresql_cmd <<< "INSERT INTO tbl VALUES ('foo1', 'bar1');"
  postgresql_cmd <<< "INSERT INTO tbl VALUES ('foo2', 'bar2');"
  postgresql_cmd <<< "INSERT INTO tbl VALUES ('foo3', 'bar3');"
  postgresql_cmd <<< "SELECT * FROM tbl;"
  #postgresql_cmd <<< "DROP TABLE tbl;"
  echo "  Success!"
}

function create_container() {
  local name=$1 ; shift
  local cargs=${DOCKER_ARGS:-}
  # TODO: fix all create_container() invocations so that we don't need this,
  # e.g. multiline DOCKER_ARGS var should end by trailing backslashes
  cargs=$(echo "$cargs" | tr  '\n' ' ')
  cidfile="$CIDFILE_DIR/$name"
  # create container with a cidfile in a directory for cleanup
  eval "docker run $cargs --cidfile \$cidfile -d \$IMAGE_NAME \"\$@\""
  echo "Created container $(cat $cidfile)"
}


create_volume_dir ()
{
  volume_dir=`mktemp -d --tmpdir pg-testdata.XXXXX`
  setfacl -m u:26:-wx "$volume_dir"
  ct_path_append volumes_to_clean "$volume_dir"
}


create_temp_file ()
{
  temp_file=`mktemp --tmpdir pg-testfile.XXXXX`
  setfacl -m u:26:rw- "$temp_file"
  ct_path_append files_to_clean "$temp_file"
}


function assert_login_access() {
  local PGUSER=$1 ; shift
  local PASS=$1 ; shift
  local success=$1 ; shift

  echo "testing login as $PGUSER:$PASS; should_success=$success"

  if postgresql_cmd <<<'SELECT 1;' ; then
    if $success ; then
      echo "    $PGUSER($PASS) access granted as expected"
      return
    fi
  else
    if ! $success ; then
      echo "    $PGUSER($PASS) access denied as expected"
      return
    fi
  fi
  echo "    $PGUSER($PASS) login assertion failed"
  exit 1
}

function assert_local_access() {
  local id="$1" ; shift
  docker exec -i $(get_cid "$id") bash -c psql <<< "SELECT 1;"
}


# Make sure the invocation of docker run fails.
function assert_container_creation_fails() {

  # Time the docker run command. It should fail. If it doesn't fail,
  # postgresql will keep running so we kill it with SIGKILL to make sure
  # timeout returns a non-zero value.
  set +e
  timeout -s 9 --preserve-status 60s docker run --rm "$@" $IMAGE_NAME
  ret=$?
  set -e

  # Timeout will exit with a high number.
  if [ $ret -gt 30 ]; then
    return 1
  fi
}


# assert_container_creation_succeeds NAME [ARGS]
# ----------------------------------------------
# Chcek that 'docker run' with IMAGE_NAME succeeds with docker arguments
# specified as ARGS.
assert_container_creation_succeeds ()
{
  local check_env=false
  local name=pg-success-"$(ct_random_string)"
  local PGUSER='' PGPASS=''  DB=''  ADMIN_PASS=
  local docker_args=

  for arg; do
    docker_args+=" $(printf "%q" "$arg")"
    if $check_env; then
      local env=${arg//=*/}
      local val=${arg//$env=/}
      case $env in
        POSTGRESQL_ADMIN_PASSWORD)  ADMIN_PASS=$val ;;
        POSTGRESQL_USER)            PGUSER=$val ;;
        POSTGRESQL_PASSWORD)        PGPASS=$val ;;
        POSTGRESQL_DATABASE)        DB=$val ;;
      esac
      check_env=false
    elif test "$arg" = -e; then
      check_env=:
    fi
  done

  DOCKER_ARGS=$docker_args create_container "$name"

  if test -n "$PGUSER" && test -n "$PGPASS"; then
    PGUSER=$PGUSER PASS=$PGPASS DB=$DB test_connection "$name"
  fi

  if test -n "$ADMIN_PASS"; then
    PGUSER=postgres PASS=$ADMIN_PASS DB=$DB test_connection "$name"
  fi

  docker stop "$(get_cid "$name")"
}


function try_image_invalid_combinations() {
  assert_container_creation_fails -e POSTGRESQL_USER=user -e POSTGRESQL_PASSWORD=pass "$@"
  assert_container_creation_fails -e POSTGRESQL_USER=user -e POSTGRESQL_DATABASE=db "$@"
  assert_container_creation_fails -e POSTGRESQL_PASSWORD=pass -e POSTGRESQL_DATABASE=db "$@"
}

function run_container_creation_tests() {
  echo "  Testing image entrypoint usage"
  try_image_invalid_combinations
  try_image_invalid_combinations  -e POSTGRESQL_ADMIN_PASSWORD=admin_pass

  VERY_LONG_IDENTIFIER="very_long_identifier_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  assert_container_creation_fails -e POSTGRESQL_USER= -e POSTGRESQL_PASSWORD=pass -e POSTGRESQL_DATABASE=db -e POSTGRESQL_ADMIN_PASSWORD=admin_pass
  assert_container_creation_fails -e POSTGRESQL_USER=$VERY_LONG_IDENTIFIER -e POSTGRESQL_PASSWORD=pass -e POSTGRESQL_DATABASE=db -e POSTGRESQL_ADMIN_PASSWORD=admin_pass
  assert_container_creation_succeeds -e POSTGRESQL_USER=user -e POSTGRESQL_PASSWORD="\"" -e POSTGRESQL_DATABASE=db -e POSTGRESQL_ADMIN_PASSWORD=admin_pass
  assert_container_creation_succeeds -e POSTGRESQL_USER=user -e POSTGRESQL_PASSWORD=pass -e POSTGRESQL_DATABASE=9invalid -e POSTGRESQL_ADMIN_PASSWORD=admin_pass
  assert_container_creation_fails -e POSTGRESQL_USER=user -e POSTGRESQL_PASSWORD=pass -e POSTGRESQL_DATABASE=$VERY_LONG_IDENTIFIER -e POSTGRESQL_ADMIN_PASSWORD=admin_pass
  assert_container_creation_succeeds -e POSTGRESQL_USER=user -e POSTGRESQL_PASSWORD=pass -e POSTGRESQL_DATABASE=db -e POSTGRESQL_ADMIN_PASSWORD="\""
  echo "  Success!"

  assert_container_creation_succeeds -e POSTGRESQL_ADMIN_PASSWORD="the @password"
  assert_container_creation_succeeds -e POSTGRESQL_PASSWORD="the pass" -e POSTGRESQL_USER="the user" -e POSTGRESQL_DATABASE="the db"
}

function test_config_option() {
  local name=$1 ; shift
  local setting=$1 ; shift
  local value=$1 ; shift

  docker exec $(get_cid ${name}) grep -q "${setting} = ${value}" /var/lib/pgsql/openshift-custom-postgresql.conf
}


# wait_ready
# ----------
# Wait until the PG container becomes ready
wait_ready ()
{
  while ! docker exec "$(get_cid "$1")" /usr/libexec/check-container ; do
    sleep 1
  done
}


# assert_runtime_option option value
# ----------------------------------
assert_runtime_option ()
{
  local name=$1 option=$2 value=$3
  wait_ready "$name"
  set -- $(docker exec "$(get_cid "$name")" bash -c "psql -tA -c 'SHOW $option;'")
  test "$value" = "$1"
}


function run_configuration_tests() {
  local name=$1 ; shift
  echo "  Testing image configuration settings"
  test_config_option ${name} max_connections ${POSTGRESQL_MAX_CONNECTIONS}
  test_config_option ${name} max_prepared_transactions ${POSTGRESQL_MAX_PREPARED_TRANSACTIONS}
  test_config_option ${name} shared_buffers ${POSTGRESQL_SHARED_BUFFERS}
  echo "  Success!"
}

test_scl_usage() {
  local name="$1"
  local run_cmd="$2"
  local expected="$3"

  echo "  Testing the image SCL enable"
  out=$(docker run --rm ${IMAGE_NAME} /bin/bash -c "${run_cmd}")
  if ! echo "${out}" | grep -q "${expected}"; then
    echo "ERROR[/bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'"
    return 1
  fi
  out=$(docker exec $(get_cid $name) /bin/bash -c "${run_cmd}" 2>&1)
  if ! echo "${out}" | grep -q "${expected}"; then
    echo "ERROR[exec /bin/bash -c "${run_cmd}"] Expected '${expected}', got '${out}'"
    return 1
  fi
  out=$(docker exec $(get_cid $name) /bin/sh -ic "${run_cmd}" 2>&1)
  if ! echo "${out}" | grep -q "${expected}"; then
    echo "ERROR[exec /bin/sh -ic "${run_cmd}"] Expected '${expected}', got '${out}'"
    return 1
  fi
}


function run_tests() {
  echo "  Testing general usage (run_tests) with '$1' as argument"
  local name=$1 ; shift

  user_login=false
  admin_login=false
  envs=
  # NOTE: We work wrongly with variables so please don't try to pass spaces
  # within PGUSER/PASS/ADMIN_PASS variables.
  [ -v PGUSER ] && envs+=" -e POSTGRESQL_USER=$PGUSER"
  [ -v PASS ] && envs+=" -e POSTGRESQL_PASSWORD=$PASS"
  if [ -v PGUSER ] && [ -v PASS ]; then
    envs+=" -e POSTGRESQL_DATABASE=db"
    user_login=:
  fi

  if [ -v ADMIN_PASS ]; then
    envs="$envs -e POSTGRESQL_ADMIN_PASSWORD=$ADMIN_PASS"
    admin_login=:
  fi
  if [ -v POSTGRESQL_MAX_CONNECTIONS ]; then
    envs="$envs -e POSTGRESQL_MAX_CONNECTIONS=$POSTGRESQL_MAX_CONNECTIONS"
  fi
  if [ -v POSTGRESQL_MAX_PREPARED_TRANSACTIONS ]; then
    envs="$envs -e POSTGRESQL_MAX_PREPARED_TRANSACTIONS=$POSTGRESQL_MAX_PREPARED_TRANSACTIONS"
  fi
  if [ -v POSTGRESQL_SHARED_BUFFERS ]; then
    envs="$envs -e POSTGRESQL_SHARED_BUFFERS=$POSTGRESQL_SHARED_BUFFERS"
  fi
  DOCKER_ARGS="${DOCKER_ARGS:-} $envs" create_container $name
  CONTAINER_IP=$(get_container_ip $name)
  test_connection $name
  echo "  Testing scl usage"
  test_scl_usage $name 'psql --version' "$VERSION"

  echo "  Testing login accesses"
  assert_login_access "${PGUSER:-}"     "${PASS-}"            "$user_login"
  assert_login_access "${PGUSER:-}"     "${PASS-}_foo"        false

  assert_login_access postgres          "${ADMIN_PASS-}"      "$admin_login"
  assert_login_access postgres          "${ADMIN_PASS-}_foo"  false

  assert_local_access $name
  run_configuration_tests $name
  echo "  Success!"

  if $user_login; then
    test_postgresql $name
  fi

  if $admin_login; then
    DB=postgres PGUSER=postgres PASS=$ADMIN_PASS test_postgresql $name
  fi
}

function run_slave() {
  local suffix="$1"; shift
  docker run $cluster_args -e POSTGRESQL_MASTER_IP=${master_hostname} \
    -d --cidfile ${CIDFILE_DIR}/slave-${suffix}.cid $IMAGE_NAME run-postgresql-slave
}

function run_master() {
  local suffix="$1"; shift
  master_args=${master_args-}
  docker run $cluster_args $master_args \
    -d --cidfile ${CIDFILE_DIR}/master-${suffix}.cid $IMAGE_NAME run-postgresql-master >/dev/null
}

function test_slave_visibility() {
  local max_attempts=30

  for slave in $slave_cids; do
    slave_ip=$(get_ip_from_cid $slave)
    if [ -z "$slave_ip" ]; then
        echo "Failed to get IP for slave $slave."
        echo "Dumping logs for $slave"
        docker logs "$slave"
        return 1
    fi
    for i in $(seq $max_attempts); do
      result="$(postgresql_cmd -c "select client_addr from pg_stat_replication;" | grep "$slave_ip" || true)"
      if [[ -n "${result}" ]]; then
        echo "${slave_ip} successfully registered as SLAVE for ${master_ip}"
        break
      fi
      if [[ "${i}" == "${max_attempts}" ]]; then
        echo "The ${slave_ip} failed to register in MASTER"
        echo "Dumping logs for $slave"
        docker logs $slave
        return 1
      fi
      sleep 1
    done
  done
}

function test_value_replication() {
  local max_attempts=30

  # Setup the replication data
  local value
  value=24
  postgresql_cmd -c "CREATE TABLE $table_name (a integer); INSERT INTO $table_name VALUES ($value);"

  # Read value from slaves and check whether it is expected
  for slave in $slave_cids; do
    slave_ip=$(get_ip_from_cid $slave)
    CONTAINER_IP=$slave_ip
    for i in $(seq $max_attempts); do
      result="$(postgresql_cmd -At -c "select * from $table_name" || :)"
      if [[ "$result" == "$value" ]]; then
        echo "${slave_ip} successfully got value from MASTER ${master_ip}"
        break
      fi
      if [[ "${i}" == "${max_attempts}" ]]; then
        echo "The ${slave_ip} failed to see value added on MASTER"
        echo "Dumping logs for $slave"
        docker logs $slave
        return 1
      fi
      sleep 1
    done
  done
}

function setup_replication_cluster() {
  # Run the PostgreSQL master
  run_master "$cid_suffix"

  # Run the PostgreSQL slaves
  local i
  master_ip=$(get_container_ip "master-$cid_suffix.cid")
  local cluster_args="$cluster_args --add-host postgresql-master:$master_ip"
  local master_hostname="postgresql-master"
  for i in $(seq ${slave_num:-1}); do
    slave_cids="$slave_cids $(run_slave $cid_suffix-$i)"
  done

}

function run_master_restart_test() {
  local DB=postgres
  local PGUSER=master
  local PASS=master

  echo "Testing failed master restart"
  local cluster_args="-e POSTGRESQL_ADMIN_PASSWORD=pass -e POSTGRESQL_MASTER_USER=$PGUSER -e POSTGRESQL_MASTER_PASSWORD=$PASS"
  local cid_suffix="mrestart"
  local table_name="t1"
  local master_ip=
  local slave_cids=

  create_volume_dir
  local master_args="-v ${volume_dir}:/var/lib/pgsql/data:Z"

  # Setup the cluster
  slave_num=2 setup_replication_cluster

  # Check if the master knows about the slaves
  CONTAINER_IP=$master_ip
  test_slave_visibility

  echo "Kill the master and create a new one"
  local cidfile=$CIDFILE_DIR/master-$cid_suffix.cid
  docker kill $(cat $cidfile)
  # Don't forget to remove its .cid file
  rm $cidfile

  run_master $cid_suffix
  CONTAINER_IP=$(get_container_ip master-$cid_suffix.cid)

  # Update master_ip in slaves
  for slave in $slave_cids; do
    docker exec -u 0 $slave bash -c "sed \"s/$master_ip/$CONTAINER_IP/g\" /etc/hosts >/tmp/hosts && cp /tmp/hosts /etc/hosts"
  done
  master_ip=$CONTAINER_IP
  # Check if the new master sees existing slaves
  test_slave_visibility

  # Check if the replication works
  table_name="t1" test_value_replication
}

function run_replication_test() {
  local DB=postgres
  local PGUSER=master
  local PASS=master

  echo "Testing master-slave replication"
  local cluster_args="-e POSTGRESQL_ADMIN_PASSWORD=pass -e POSTGRESQL_MASTER_USER=$PGUSER -e POSTGRESQL_MASTER_PASSWORD=$PASS"
  local cid_suffix="basic"
  local master_ip=
  local slave_cids=

  # Setup the cluster
  setup_replication_cluster

  # Check if the master knows about the slaves
  CONTAINER_IP=$master_ip
  test_slave_visibility

  # Do some real work to test replication in practice
  table_name="t1" test_value_replication
}

function run_change_password_test() {
  echo "  Testing password change"
  local name="change_password"

  local database='db'
  local user='user'
  local password='password'
  local admin_password='adminPassword'

  create_volume_dir
  local volume_options="-v ${volume_dir}:/var/lib/pgsql/data:Z"

  DOCKER_ARGS="
-e POSTGRESQL_DATABASE=${database}
-e POSTGRESQL_USER=${user}
-e POSTGRESQL_PASSWORD=${password}
-e POSTGRESQL_ADMIN_PASSWORD=${admin_password}
$volume_options
" create_container ${name}

  # need to set these because `postgresql_cmd` relies on global variables
  PGUSER=${user}
  PASS=${password}

  # need this to wait for the container to start up
  CONTAINER_IP=$(get_container_ip ${name})
  test_connection ${name}

  echo "  Testing login"

  assert_login_access ${user} ${password} true
  assert_login_access 'postgres' ${admin_password} true

  echo "  Changing passwords"

  docker stop $(get_cid ${name})
  DOCKER_ARGS="
-e POSTGRESQL_DATABASE=${database}
-e POSTGRESQL_USER=${user}
-e POSTGRESQL_PASSWORD=NEW_${password}
-e POSTGRESQL_ADMIN_PASSWORD=NEW_${admin_password}
$volume_options
" create_container "${name}_NEW"

  # need to set this because `postgresql_cmd` relies on global variables
  PASS="NEW_${password}"

  # need this to wait for the container to start up
  CONTAINER_IP=$(get_container_ip "${name}_NEW")
  test_connection "${name}_NEW"

  echo "  Testing login with new passwords"

  assert_login_access ${user} "NEW_${password}" true
  assert_login_access ${user} ${password} false

  assert_login_access 'postgres' "NEW_${admin_password}" true
  assert_login_access 'postgres' ${admin_password} false

  echo "  Success!"
}

run_upgrade_test ()
{
    # Do not run on Fedora or RHEL8 until the upgrade script
    # is fixed for non-SCL use cases
    { [ "${OS}" == "fedora" ] || [ "${OS}" == "rhel8" ]; } && return 0

    local upgrade_path="none 9.2 9.4 9.5 9.6 10 12 none" prev= act=
    for act in $upgrade_path; do
        if test "$act" = $VERSION; then
            break
        fi
        prev=$act
    done
    test "$prev" != none
    # Check if the previous image is available in the registry
    docker pull "$(get_image_id "$prev:remote")" || return 0

    # TODO: We run this script from $VERSION directory, through test/run symlink.
    test/run_upgrade_test "$prev:remote" "$VERSION:local"
}

run_migration_test ()
{
    [ "${OS}" == "fedora" ] && return 0

    local from_version
    # Only test a subset of the migration path on non-intel hosts
    if [ "$(uname -i)" == "x86_64" ]; then
        local upgrade_path="9.2 9.4 9.5 9.6 10 12"
    else
        local upgrade_path="10 12"
    fi

    for from_version in $upgrade_path; do
        # Do not test migration from $VERSION:remote to $VERSION:local
        test $(version2number $from_version) -lt $(version2number "$VERSION") \
            || break
        # Skip if the previous image is not available in the registry
        docker pull "$(get_image_id "$from_version:remote")" || continue
        test/run_migration_test $from_version:remote $VERSION:local
    done
}

run_doc_test() {
  local tmpdir=$(mktemp -d)
  local f
  echo "  Testing documentation in the container image"
  # Extract the help files from the container
  for f in help.1 ; do
    docker run --rm ${IMAGE_NAME} /bin/bash -c "cat /${f}" >${tmpdir}/$(basename ${f})
    # Check whether the files include some important information
    for term in "POSTGRESQL\_ADMIN\_PASSWORD" volume 5432 ; do
      if ! cat ${tmpdir}/$(basename ${f}) | grep -F -q -e "${term}" ; then
        echo "ERROR: File /${f} does not include '${term}'."
        return 1
      fi
    done
  done
  # Check whether the files use the correct format
  if ! file ${tmpdir}/help.1 | grep -q roff ; then
    echo "ERROR: /help.1 is not in troff or groff format"
    return 1
  fi
  echo "  Success!"
  echo
}

test_the_app_image () {
  local container_name=$1
  local mount_opts=$2
  echo "    Testing s2i app image with invalid configuration"
  assert_container_creation_fails -e POSTGRESQL_PASSWORD=pass -e POSTGRESQL_DATABASE=db
  echo "    Testing s2i app image with correct configuration"

    DOCKER_ARGS="
-e POSTGRESQL_DATABASE=db
-e POSTGRESQL_USER=user
-e POSTGRESQL_PASSWORD=password
-e POSTGRESQL_ADMIN_PASSWORD=password
-e POSTGRESQL_BACKUP_USER=backuser
-e POSTGRESQL_BACKUP_PASSWORD=pass
${mount_opts}
" create_container "$container_name"

  # need this to wait for the container to start up
  PGUSER=user     PASS=password           test_connection "$container_name"
  PGUSER=backuser PASS=pass     DB=backup test_connection "$container_name"

  docker stop "$(get_cid $container_name)" >/dev/null
}

run_s2i_test() {
  local temp_file
  echo "  Testing s2i usage"
  ct_s2i_usage "${IMAGE_NAME}" --pull-policy=never 1>/dev/null

  echo "  Testing s2i build"

  local s2i_image_name=$IMAGE_NAME-testapp_$(ct_random_string)
  images_to_clean+=( "$s2i_image_name" )

  ct_s2i_build_as_df "file://${test_dir}/test-app" "${IMAGE_NAME}" "$s2i_image_name" 1>/dev/null
  IMAGE_NAME=$s2i_image_name test_the_app_image s2i_config_build ""

  echo "  Testing s2i mount"
  create_temp_file
  cat "$test_dir"/test-app/postgresql-init/backup_user.sh >> "$temp_file"

  # Test against original image, not the s2i one.  But even if so, we expect
  # user mouns the directory under "s2i" direcetory $APP_DATA/src.
  local mount_point=/opt/app-root/src/postgresql-init/add_backup_user.sh
  test_the_app_image _s2i_test_mount "-v ${temp_file}:$mount_point:z,ro"
  echo "  Success!"
}

function run_general_tests() {
  PGUSER=user PASS=pass POSTGRESQL_MAX_CONNECTIONS=42 POSTGRESQL_MAX_PREPARED_TRANSACTIONS=42 POSTGRESQL_SHARED_BUFFERS=64MB run_tests no_admin
  PGUSER=user1 PASS=pass1 ADMIN_PASS=r00t run_tests admin
  DB=postgres ADMIN_PASS=r00t run_tests only_admin
  # Test with arbitrary uid for the container
  DOCKER_ARGS="-u 12345" PGUSER=user2 PASS=pass run_tests no_admin_altuid
  DOCKER_ARGS="-u 12345" PGUSER=user3 PASS=pass1 ADMIN_PASS=r00t run_tests admin_altuid
  DB=postgres DOCKER_ARGS="-u 12345" ADMIN_PASS=rOOt run_tests only_admin_altuid
}

run_test_cfg_hook()
{
  local volume_dir name=pg-test-cfg-dir
  volume_dir=$(mktemp -d --tmpdir pg-hook-volume.XXXXX)
  add_cleanup_command /bin/rm -rf "$volume_dir"
  setfacl -R -m u:26:rwx "$volume_dir"
  cp -r "$test_dir"/examples/custom-config/* "$volume_dir"
  setfacl -R -m u:26:rwx "$volume_dir"

  DOCKER_ARGS="
-e POSTGRESQL_ADMIN_PASSWORD=password
-v $volume_dir:/opt/app-root/src:Z
  " create_container "$name"
  assert_runtime_option "$name" shared_buffers 111MB

  # Check that POSTGRESQL_SHARED_BUFFERS has effect.
  DOCKER_ARGS="
-e POSTGRESQL_ADMIN_PASSWORD=password
-e POSTGRESQL_SHARED_BUFFERS=113MB
  " create_container "$name-2"
  assert_runtime_option "$name-2" shared_buffers 113MB

  # Check that volume has priority over POSTGRESQL_SHARED_BUFFERS.
  DOCKER_ARGS="
-e POSTGRESQL_ADMIN_PASSWORD=password
-e POSTGRESQL_SHARED_BUFFERS=113MB
-v $volume_dir:/opt/app-root/src:Z
  " create_container "$name-3"
  assert_runtime_option "$name-3" shared_buffers 111MB
}

run_s2i_enable_ssl_test()
{
  local s2i_image_name="$IMAGE_NAME-ssl_$(ct_random_string)"
  ct_s2i_build_as_df "file://$test_dir/examples/enable-ssl" "${IMAGE_NAME}" "$s2i_image_name" 1>/dev/null
  images_to_clean+=( "$s2i_image_name" )

  local container_name=enable-ssl-test

  DOCKER_ARGS="-e POSTGRESQL_ADMIN_PASSWORD=password" \
    IMAGE_NAME="$s2i_image_name" create_container "$container_name"

  wait_ready "$container_name"
  CONTAINER_IP=$(get_container_ip $container_name)

  DB=postgres assert_login_access postgres password true

  docker run --rm -e PGPASSWORD="password" "$IMAGE_NAME" psql "postgresql://postgres@$CONTAINER_IP:5432/postgres?sslmode=require" || \
    false "FAIL: Did not manage to connect using SSL only."

  docker stop "$(get_cid "$container_name")"
}

run_s2i_bake_data_test ()
{
  local s2i_image_name="$IMAGE_NAME-bake_$(ct_random_string)"
  ct_s2i_build_as_df "file://$test_dir/examples/s2i-dump-data" "${IMAGE_NAME}" "$s2i_image_name" 1>/dev/null
  images_to_clean+=( "$s2i_image_name" )

  local container_name=bake-data-test

  DOCKER_ARGS="-e POSTGRESQL_ADMIN_PASSWORD=password" \
    IMAGE_NAME="$s2i_image_name" create_container "$container_name"

  wait_ready "$container_name"

  test "hello world" == "$(docker exec "$(get_cid "$container_name")" \
                           bash -c "psql -tA -c 'SELECT * FROM test;'")"

  docker stop "$(get_cid "$container_name")"
}

run_pgaudit_test()
{
  # extension pgaudit is not available for older versions
  case ${VERSION} in
    9.6|10|11) echo "pgaudit not expected, test skipped."; return ;;
    *) ;;
  esac

  local config_dir data_dir name=pg-test-pgaudit

  # create a dir for config
  config_dir=$(mktemp -d --tmpdir pg-hook-volume.XXXXX)
  add_cleanup_command /bin/rm -rf "$config_dir"
  cp -r "$test_dir"/examples/pgaudit/* "$config_dir"
  setfacl -R -m u:26:rwx "$config_dir"

  # create a dir for data
  create_volume_dir
  data_dir="${volume_dir}"

  DOCKER_ARGS="
-e POSTGRESQL_ADMIN_PASSWORD=password
-v ${config_dir}:/opt/app-root/src:Z
-v ${data_dir}:/var/lib/pgsql/data:Z
  " create_container "$name"

  assert_runtime_option "$name" shared_preload_libraries pgaudit
  wait_ready "$name"

  # enable the pgaudit extension
  # Deliberately moving heredoc into the container, otherwise it does not work
  # in podman 1.6.x due to https://bugzilla.redhat.com/show_bug.cgi?id=1827324
  docker exec -i $(get_cid "$name") bash -c "psql <<EOSQL
CREATE EXTENSION pgaudit;
SET pgaudit.log = 'read, ddl';
CREATE DATABASE pgaudittest;
EOSQL"

  # simulate some trafic that should be audited
  docker exec -i $(get_cid "$name") bash -c "psql pgaudittest <<EOSQL
SET pgaudit.log = 'read, ddl';
CREATE TABLE account (id int, name text, password text, description text);
INSERT INTO account (id, name, password, description) VALUES (1, 'user1', 'HASH1', 'blah, blah');
SELECT * FROM account;
EOSQL"

  # give server some time for write all audit messages
  sleep 1

  grep -E 'AUDIT: SESSION,.*,.*,DDL,CREATE DATABASE,,,CREATE DATABASE pgaudittest' "${data_dir}"/userdata/log/postgresql-*.log
  grep -E 'AUDIT: SESSION,.*,.*,READ,SELECT,,,SELECT' "${data_dir}"/userdata/log/postgresql-*.log
}

function run_latest_imagestreams_test() {
  local result=1
  # Switch to root directory of a container
  echo "Testing the latest version in imagestreams"
  pushd "${test_dir}/.." >/dev/null || return 1
  ct_check_latest_imagestreams
  result=$?
  popd >/dev/null || return 1
  return $result
}

function run_all_tests() {
  for test_case in $TEST_LIST; do
    : "Running test $test_case"
    $test_case
  done;
}

# configuration defaults
POSTGRESQL_MAX_CONNECTIONS=100
POSTGRESQL_MAX_PREPARED_TRANSACTIONS=0
POSTGRESQL_SHARED_BUFFERS=32MB

# Run the chosen tests
TEST_LIST=${TESTS:-$TEST_LIST} run_all_tests

TESTSUITE_RESULT=0
echo "All tests passed."
