#!/bin/bash

err_report() {
    echo "$0: Error on line $1 - aborted"
    exit 1
}

trap 'err_report $LINENO' ERR

# Any failure causes exit
set -e
export DISABLE_SPRING=1

cleanup_db()
{
    echo Dropping database ...
    $rake db:drop
    echo Removing migrations ...
    rm -f db/migrate/* db/schema.rb
    case `ruby -e 'puts JRUBY_VERSION' 2> /dev/null` in
	[0-9]*)
        echo 'Jruby requires the database to be created before the server is started: running rake db:migrate'
        $rake db:migrate
	;;
    esac
}

case "$1" in
redo)
    . test/init_variables
    cd $railsapp
    cleanup_db
    actual_rails_version=`$rails -v`
    ;;
*)
    . test/setup_railsapp $1
    ;;
esac

run_test=$2

cd $railsapp
date > $custom_file
rm -f $catchall_file

testurl="$base_dir/test/testurl"
fake_smtp_server="$base_dir/test/fake_smtp_server"

server_pid=''
fake_smtp_pid=''

pick_a_port()
{
    while :
    do
        port=`expr 10000 + $RANDOM`
        # Check Tcp ports in Listen mode with No address resolution
        if (netstat -tln | egrep ":${port} .*:"); then
            echo "(Skipping used port)"
        else
            break
        fi
    done
}


start_server()
{
    # restart migration list
    rm -rf db/migrate db/schema.rb
    mkdir -p db/migrate

    # Increment port each time to make sure we have not trouble with address/port already allocated
    pick_a_port
    host=http://127.0.0.1:${port}
    bundle_prefix=''
    if [ -f Gemfile ]
    then
        bundle_prefix='bundle exec'
    fi
    server_arg=${RAILS_SERVER:-webrick}
    echo "start_server called using: `env | egrep '^RAILS|^RACK|^PATH='` $bundle_prefix $server_arg"
    case "$server_arg" in
    puma) 
        $bundle_prefix puma -b tcp://127.0.0.1:$port &
        ;;
    passenger) 
        $bundle_prefix passenger start -p $port &
        ;;
    thin) 
        $bundle_prefix thin start -p $port &
        ;;
    unicorn) 
        $bundle_prefix unicorn_rails -l 127.0.0.1:$port &
        ;;
    *)
        if [ -x script/server ]
        then
            echo Starting server on port $port using $bundle_prefix ./script/server ...
            $bundle_prefix ./script/server $server_arg -p $port &
        else
            echo Starting server on port $port using $rails s ...
            $bundle_prefix $rails server $server_arg -p $port &
        fi
        ;;
    esac
    server_pid=$!
    echo Server pid: $server_pid
    sleep 3
    echo
    echo 'Checking server is up ...'
    for i in 1 2 3 4 5 6
    do
      if $testurl ${host}/static.txt ; then
        break
      fi
      if kill -0 $server_pid ; then
        echo "waiting ${i} ..."
      else
        echo "ERROR: Server has died!!"
        exit 3
      fi
   done
}

stop_server()
{
    case "$server_pid" in
        [0-9]*)
            echo ========================================================
            echo "Killing rails server [pid: $server_pid]"
            kill -1 $server_pid  || true
            [ ! -x bin/spring ] || bin/spring stop || true
            sleep 2
            kill -9 $server_pid  || true
            ;;
    esac
    case "$fake_smtp_pid" in
        [0-9]*)
            echo ========================================================
            echo "Killing fake smtp server [pid: $fake_smtp_pid]"
            kill -1 $fake_smtp_pid || true
            sleep 2
            kill -9 $fake_smtp_pid || true
            ;;
    esac
    server_pid=''
    fake_smtp_pid=''
    wait
    sleep 2
}

finish()
{
    set +e
    echo ========================================================
    echo TEST ${1:-FAILED}
    echo ========================================================
    echo Result of: ls -lR $railsapp/log $railsapp/db
    ls -lR $railsapp/log $railsapp/db

    if [ -s $railsapp/log/test.log ]
    then
        echo ========================================================
        echo Last 50 lines of test log
        tail -50 $railsapp/log/test.log
    fi

    if [ -s $railsapp/log/production.log ]
    then
        echo ========================================================
        echo Last 50 lines of production log
        tail -50 $railsapp/log/production.log
    fi

    stop_server
    trap "" 0
    echo ========================================================
    echo TEST ${1:-FAILED}
    echo ========================================================
    exit ${2:-2}
}

trap "finish FAILED 1" 0

common_tests()
{

    test_no=$1

    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: CHECKING routes exist..."
        $rake routes | tee /tmp/t$$
        echo
        case `egrep health_check /tmp/t$$ || true` in
        '')
          echo WARNING - routes for health_check not listed!
          ;;
        esac
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING can get a static file ..."
        case "$RAILS_ENV=`egrep '^\s*config.serve_static_[asetfil]* *= *false' config/environments/${RAILS_ENV}.rb`" in
        production*static*false*)
            echo "  SKIPPED (disabled in production)"
            ;;
        *)
            grep serve_static_files config/environments/${RAILS_ENV}.rb config/[a-z]*.rb || echo no serve_static_files entry
            $testurl ${host}/static.txt 200 text/plain STATIC-FILE
            ;;
        esac
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING can get an example controller ..."
        $testurl ${host}/example 200 text/html 'example page'
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING direct call to catchall method on example controller ..."
        $testurl ${host}/example/catchall 200 text/html 'catch all route'
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/migration should pass with no database migrations ..."
        ls db/migrate
        $testurl ${host}/health_check/migration 200 text/plain $success
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/migration should fail without initial database migration ..."
        cp $base_dir/test/migrate/nine/* db/migrate
        ls db/migrate
        $testurl ${host}/health_check/migration 550 text/plain failed
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/database should pass without initial database migration (since it ignores the difference) ..."
        $testurl ${host}/health_check/database 200 text/plain $success
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/site should pass ..."
        $testurl ${host}/health_check/site 200 text/plain $success
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/migration should pass after initial database migration ..."
        $rake db:migrate
        $testurl ${host}/health_check/migration 200 text/plain $success
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/migration should fail without all migrations ..."
        cp $base_dir/test/migrate/twelve/* db/migrate

        case "$actual_rails_version" in
            *' '[12].*|*' '3.[01]*)
                echo removing db/migrate/*.*.rb "($actual_rails_version does not support extra dots in migration names)"
                rm -f db/migrate/*.*.rb
                ;;
        esac
        ls db/migrate
        $testurl ${host}/health_check/migration 550 text/plain failed
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/migration should pass after both database migrations ..."
        $rake db:migrate
        $testurl ${host}/health_check/migration 200 text/plain $success
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/migration should pass after both database migrations ..."
        $rake db:migrate
        $testurl ${host}/health_check/migration 200 text/plain $success
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/email should fail without smtp available ..."
        $testurl ${host}/health_check/email 550 text/plain failed
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/email should pass with smtp available ..."
        $fake_smtp_server &
        fake_smtp_pid=$!
        sleep 5
        $testurl ${host}/health_check/email 200 text/plain $success
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check (all) should fail without smtp available ..."
        $testurl ${host}/health_check 550 text/plain failed
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/all should fail without smtp available ..."
        $testurl ${host}/health_check 550 text/plain failed
        echo
    fi

    kill -9 $fake_smtp_pid || echo fake_smtp_server had finished as expected
    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check (all) should pass with smtp available ..."
        $fake_smtp_server &
        fake_smtp_pid=$!
        sleep 5
        $testurl ${host}/health_check 200 text/plain $success
        echo
    fi

    kill -9 $fake_smtp_pid || echo fake_smtp_server had finished as expected
    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/all should pass with smtp available ..."
        $fake_smtp_server &
        fake_smtp_pid=$!
        sleep 5
        $testurl ${host}/health_check/all 200 text/plain $success
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/custom should pass ..."
        $testurl ${host}/health_check/custom 200 text/plain $success
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/custom.html should pass (returning plain text) ..."
        $testurl ${host}/health_check/custom.html 200 text/plain $success
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/custom.json should pass ..."
        $testurl ${host}/health_check/custom.json 200 application/json '"healthy":true'
        $testurl ${host}/health_check/custom.json 200 application/json "\"message\":\"$success\""
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/custom.xml should pass ..."
        $testurl ${host}/health_check/custom.xml 200 application/xml '<healthy type="boolean">true</healthy>'
        $testurl ${host}/health_check/custom.xml 200 application/xml "<message>$success</message>"
        echo
    fi

    test_no=`expr 1 + $test_no`
    rm -f $custom_file
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check/custom should fail when custom returns string ..."
        $testurl ${host}/health_check/custom 550 text/plain failed
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check (all) should fail when custom check fails ..."
        $testurl ${host}/health_check 550 text/plain "$custom_file is missing!"
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check.json (all) should fail when custom check fails ..."
        $testurl ${host}/health_check.json 555 application/json '"healthy":false'
        $testurl ${host}/health_check.json 555 application/json "$custom_file is missing!"
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING health_check.xml (all) should fail when custom check fails ..."
        $testurl ${host}/health_check.xml 555 application/xml '<healthy type="boolean">false</healthy>'
        echo
    fi

    test_no=`expr 1 + $test_no`
    if [ -z "$run_test" ] || [ $test_no == "$run_test" ]; then
        echo "${test_no}: TESTING log files to check for deprecation warnings ..."
        if egrep ' is deprecated|DEPRECATION WARNING' $railsapp/log/[tp][er][so][td]*.log 
        then
            echo Found deprecation warnings - failed test
            exit 99
        fi
    fi

    date > $custom_file
}


# required for rails 4.1+ in production mode
export SECRET_KEY_BASE=cf2f49c38a3fe67416ddf680f4f3187c0fce7dd1b9b117b34d195df75b274e08a04877e23803b2fdf1aa9a655269d94bc4888aa325cf7e721cc47368cfe56a80

# required for rails 5 to server static files
export RAILS_SERVE_STATIC_FILES=on

case "$run_test" in
???)
    ;;
*)
    echo ========================================================
    echo TESTING WITHOUT CATCHALL in test env
    echo ========================================================
    export RAILS_ENV=test RACK_ENV=test

    start_server

    # get a static file

    echo
    echo 'TESTING no catchall route active ...'
    expected_status=404,500,502
    $testurl ${host}/another/url $expected_status

    common_tests 1
    ;;
esac


case "$run_test" in
''|???)
    echo ========================================================
    echo TESTING WITH CATCHALL in ${RAILS_ENV2:-production} env
    echo ========================================================
    export RAILS_ENV=${RAILS_ENV2:-production} RACK_ENV=${RAILS_ENV2:-production}

    case "$run_trest" in
     '')
        stop_server
        cleanup_db
        ;;
    esac

    date > $catchall_file

    start_server

    echo
    echo 'TESTING catchall route active ...'
    $testurl ${host}/another/url 200 text/html 'catch all route'
    echo

    common_tests 101
    ;;
esac

rm -f $catchall_file

finish PASSED 0
exit 0

# vi: sw=4 ai sm:
