#!/bin/bash
# This file is part of the rsyslog project, released under ASL 2.0

# This test tests omprog with the confirmMessages=on and useTransactions=on
# parameters, with the external program returning an error on certain
# transaction commits.

. ${srcdir:=.}/diag.sh init

uname
if [ $(uname) = "SunOS" ] ; then
    # On Solaris, this test causes rsyslog to hang. This is presumably due
    # to issue #2356 in the rsyslog core, which doesn't seem completely
    # corrected. TODO: re-enable this test when the issue is corrected.
    echo "Solaris: FIX ME"
    exit 77
fi

generate_conf
add_conf '
module(load="../plugins/omprog/.libs/omprog")

template(name="outfmt" type="string" string="%msg%\n")

:msg, contains, "msgnum:" {
    action(
        type="omprog"
        binary=`echo $srcdir/testsuites/omprog-transactions-bin.sh --failed_commits`
        template="outfmt"
        name="omprog_action"
        queue.type="Direct"  # the default; facilitates sync with the child process
        queue.dequeueBatchSize="6"
        confirmMessages="on"
        useTransactions="on"
        action.resumeRetryCount="10"
        action.resumeInterval="1"
    )
}
'
startup
injectmsg 0 10
shutdown_when_empty
wait_shutdown

# Since the transaction boundaries are not deterministic, we cannot check for
# an exact expected output. We must check the output programmatically.

transaction_state="NONE"
status_expected=true
messages_to_commit=()
messages_processed=()
line_num=1
error=

while IFS= read -r line; do
    if [[ $status_expected == true ]]; then
        case "$transaction_state" in
        "NONE")
            if [[ "$line" != "<= OK" ]]; then
                error="expecting an OK status from script"
                break
            fi
            ;;
        "STARTED")
            if [[ "$line" != "<= OK" ]]; then
                error="expecting an OK status from script"
                break
            fi
            transaction_state="ACTIVE"
            ;;
        "ACTIVE")
            if [[ "$line" != "<= DEFER_COMMIT" ]]; then
                error="expecting a DEFER_COMMIT status from script"
                break
            fi
            ;;
        "COMMITTED")
            if [[ "$line" == "<= Error: could not commit transaction" ]]; then
                messages_to_commit=()
                transaction_state="NONE"
            else
                if [[ "$line" != "<= OK" ]]; then
                    error="expecting an OK status from script"
                    break
                fi
                messages_processed+=("${messages_to_commit[@]}")
                messages_to_commit=()
                transaction_state="NONE"
            fi
            ;;
        esac
        status_expected=false;
    else
        if [[ "$line" == "=> BEGIN TRANSACTION" ]]; then
            if [[ "$transaction_state" != "NONE" ]]; then
                error="unexpected transaction start"
                break
            fi
            transaction_state="STARTED"
        elif [[ "$line" == "=> COMMIT TRANSACTION" ]]; then
            if [[ "$transaction_state" != "ACTIVE" ]]; then
                error="unexpected transaction commit"
                break
            fi
            transaction_state="COMMITTED"
        else
            if [[ "$transaction_state" != "ACTIVE" ]]; then
                error="unexpected message outside a transaction"
                break
            fi
            if [[ "$line" != "=> msgnum:"* ]]; then
                error="unexpected message contents"
                break
            fi
            prefix_to_remove="=> "
            messages_to_commit+=("${line#$prefix_to_remove}")
        fi
        status_expected=true;
    fi
    ((line_num++))
done < $RSYSLOG_OUT_LOG

if [[ -z "$error" && "$transaction_state" != "NONE" ]]; then
    error="unexpected end of file (transaction state: $transaction_state)"
fi

if [[ -n "$error" ]]; then
    echo "$RSYSLOG_OUT_LOG: line $line_num: $error"
    cat $RSYSLOG_OUT_LOG
    error_exit 1
fi

# Since the order in which failed messages are retried by rsyslog is not
# deterministic, we sort the processed messages before checking them.
IFS=$'\n' messages_sorted=($(sort <<<"${messages_processed[*]}"))
unset IFS

expected_messages=(
    "msgnum:00000000:"
    "msgnum:00000001:"
    "msgnum:00000002:"
    "msgnum:00000003:"
    "msgnum:00000004:"
    "msgnum:00000005:"
    "msgnum:00000006:"
    "msgnum:00000007:"
    "msgnum:00000008:"
    "msgnum:00000009:"
)
if [[ "${messages_sorted[*]}" != "${expected_messages[*]}" ]]; then
    echo "unexpected set of processed messages:"
    printf '%s\n' "${messages_processed[@]}"
    echo "contents of $RSYSLOG_OUT_LOG:"
    cat $RSYSLOG_OUT_LOG
    error_exit 1
fi

exit_test
