#!/bin/sh

# Copyright (C) 2020-2025 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Exit with error if commands exit with non-zero and if undefined variables are
# used.
set -eu

# Test suite for Dhcp4
test_suite="dhcp4"

# Path to the temporary configuration file.
# shellcheck disable=SC2034
# SC2034: ... appears unused. Verify use (or export if used externally).
# reason: used in dhcp_test_lib.sh.in
CFG_FILE="@abs_top_builddir@/src/bin/shell/tests/test_config.json"

# Path to the Kea lease file.
export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/shell/tests"
LEASE_FILE="@abs_top_builddir@/src/bin/shell/tests/test_leases.csv"

# Path to the Dhcp4 Server log file.
LOG_FILE="@abs_top_builddir@/src/bin/shell/tests/test.log"

# Set env KEA_LOG_FILE_DIR to override default log path.
export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/shell/tests"

# Dhcp4 Server configuration to be stored in the configuration file.
# todo: use actual configuration once we support it.
CONFIG="{
    \"Dhcp4\":
    {
        \"control-sockets\": [
        {
            \"socket-type\": \"http\",
            \"socket-address\": \"127.0.0.1\",
            \"socket-port\": 8081,
            \"authentication\":
            {
                \"type\": \"basic\",
                \"realm\": \"ISC.ORG\",
                \"clients\": [
                {
                    \"user\": \"pet\",
                    \"password\": \"meow\"
                }
                ]
            }
        }
        ],
        \"interfaces-config\": {
           \"interfaces\": [ ]
        },
        \"valid-lifetime\": 4000,
        \"renew-timer\": 1000,
        \"rebind-timer\": 2000,
        \"lease-database\":
        {
            \"type\": \"memfile\",
            \"name\": \"$LEASE_FILE\",
            \"persist\": true,
            \"lfc-interval\": 0
        },
        \"subnet4\": [
        {
            \"id\": 1,
            \"subnet\": \"10.0.0.0/8\",
            \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
        } ],
        \"ddns-qualifying-suffix\": \"\",
        \"dhcp-ddns\": {
            \"enable-updates\": true
        },
        \"loggers\": [
        {
            \"name\": \"kea-dhcp4\",
            \"output-options\": [
                {
                    \"output\": \"$LOG_FILE\"
                }
            ],
            \"severity\": \"DEBUG\"
        }
        ]
    }
}"

# In these tests we need to use two binaries: Dhcp4 Server and Kea shell.
# Using bin and bin_path would be confusing, so we omit defining bin
# and bin_path on purpose.
dhcp4_bin="kea-dhcp4"
dhcp4_bin_path="@abs_top_builddir@/src/bin/dhcp4"

shell_bin="kea-shell"
shell_bin_path="@abs_top_builddir@/src/bin/shell"

tmpfile_path="@abs_top_builddir@/src/bin/shell/tests"

# Import common test library.
# shellcheck source=src/lib/testutils/dhcp_test_lib.sh.in
. "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh"

run_kea_shell() {
    # shellcheck disable=SC2086
    # SC2086: Double quote to prevent globbing and word splitting.
    # reason: ${auth} can be empty here, if we quote, it will be interpreted as
    # an empty string which is not desired as it will result in unrecognized
    # argument from kea-shell.
    echo | "${shell_bin_path}/${shell_bin}" --host 127.0.0.1 --port 8081 \
        ${auth} "${cmd}" > "${tmpfile_path}/shell-stdout.txt"
}

# This test verifies that Dhcp4 Server is shut down gracefully when it
# receives a SIGINT or SIGTERM signal.
shell_command_test() {
    test_name=${1}  # Test name
    auth=${2}       # Authentication
    cmd=${3}        # Command to be sent
    exp_result=${4} # Expected result
    exp_rsp=${5}    # Expected response

    # Setup phase: start Dhcp4.

    # Log the start of the test and print test name.
    test_start "${test_suite}-${test_name}"

    # Create new configuration file.
    create_config "${CONFIG}"

    # Instruct Dhcp4 Server to log to the specific file.
    set_logger
    # Start Dhcp4 Server.
    start_kea ${dhcp4_bin_path}/${dhcp4_bin}
    # Wait up to 20s for Dhcp4 Server to start.
    wait_for_kea 20
    if [ "${_WAIT_FOR_KEA}" -eq 0 ]; then
        printf "ERROR: timeout waiting for Dhcp4 Server to start.\n"
        clean_exit 1
    fi

    # Check if it is still running. It could have terminated (e.g. as a result
    # of configuration failure).
    get_pid ${dhcp4_bin}
    if [ "${_GET_PIDS_NUM}" -ne 1 ]; then
        printf "ERROR: expected one Dhcp4 Server process to be started.\
 Found %d processes started.\n" "${_GET_PIDS_NUM}"
        clean_exit 1
    fi

    # Check in the log file, how many times server has been configured.
    # It should be just once on startup.
    get_reconfigs
    if [ "${_GET_RECONFIGS}" -ne 1 ]; then
        printf "ERROR: server been configured %s time(s),\
 but exactly 1 was expected.\n" "${_GET_RECONFIGS}"
        clean_exit 1
    else
        printf "Server successfully configured.\n"
    fi

    # Main test phase: send command, check response.
    tmp="echo | ${shell_bin_path}/${shell_bin} --host \
 127.0.0.1 --port 8081 ${auth} ${cmd} > ${tmpfile_path}/shell-stdout.txt"
    echo "Executing kea-shell ($tmp)"

    run_command \
        run_kea_shell

    # Check the exit code
    if [ "${exp_result}" = "fail" ]; then
        if [ "${EXIT_CODE}" -eq 0 ]; then
            echo "ERROR:" \
            "kea-shell returned ${EXIT_CODE} exit code, expected 1."
        else
            echo "kea-shell returned ${EXIT_CODE} exit code as expected."
        fi
    elif [ "${EXIT_CODE}" -ne 0 ]; then
        echo "ERROR:" \
        "kea-shell returned ${EXIT_CODE} exit code, expected 0."
    else
        echo "kea-shell returned ${EXIT_CODE} exit code as expected."
    fi

    # Now check the response
    rm -f ${tmpfile_path}/shell-expected.txt
    printf '%s\n' "${exp_rsp}" > ${tmpfile_path}/shell-expected.txt
    if diff "${tmpfile_path}/shell-stdout.txt" "${tmpfile_path}/shell-expected.txt"; then
        echo "Content returned by kea-shell meets expectation."
        rm ${tmpfile_path}/shell-*.txt
    else
        echo "ERROR:" \
        "content returned is different than expected." \
        "See ${tmpfile_path}/shell-*.txt"
        echo "EXPECTED:"
        cat ${tmpfile_path}/shell-expected.txt
        echo "ACTUAL RESULT:"
        cat ${tmpfile_path}/shell-stdout.txt
        clean_exit 1
    fi
    # Main test phase ends.

    # Cleanup phase: shutdown Dhcp4
    # Send SIGTERM signal to Dhcp4 Server
    send_signal 15 ${dhcp4_bin}

    # Now wait for process to log that it is exiting.
    wait_for_message 10 "DHCP4_SHUTDOWN" 1
    if [ "${_WAIT_FOR_MESSAGE}" -eq 0 ]; then
        printf "ERROR: Dhcp4 Server did not log shutdown.\n"
        clean_exit 1
    fi

    # Make sure the server is down.
    wait_for_server_down 5 ${dhcp4_bin}
    assert_eq 1 "${_WAIT_FOR_SERVER_DOWN}" \
        "Expected wait_for_server_down return %d, returned %d"

    test_finish 0
}

shell_command_test "shell.no-auth" "" "list-commands" "fail" \
    "Failed to run: HTTP Error 401: Unauthorized"
shell_command_test "shell.bad-auth" \
    "--auth-user foo --auth-password bar" "list-commands" "fail" \
    "Failed to run: HTTP Error 401: Unauthorized"
shell_command_test "shell.authorized" \
    "--auth-user pet --auth-password meow" "list-commands" "" \
    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
shell_command_test "shell.bad-auth-password-file" \
    "--auth-user foo --auth-password-file foobar" "list-commands" "fail" \
    "Failed to run: [Errno 2] No such file or directory: 'foobar'"
shell_command_test "shell.bad-auth-password-file-content" \
    "--auth-user foo --auth-password-file ${tmpfile_path}/auth_bad_password_file" "list-commands" "fail" \
    "Failed to run: HTTP Error 401: Unauthorized"
shell_command_test "shell.good-auth-password-file-content" \
    "--auth-user pet --auth-password-file ${tmpfile_path}/auth_password_file" "list-commands" "" \
    "[ { \"arguments\": [ \"build-report\", \"config-backend-pull\", \"config-get\", \"config-hash-get\", \"config-reload\", \"config-set\", \"config-test\", \"config-write\", \"dhcp-disable\", \"dhcp-enable\", \"leases-reclaim\", \"list-commands\", \"server-tag-get\", \"shutdown\", \"statistic-get\", \"statistic-get-all\", \"statistic-remove\", \"statistic-remove-all\", \"statistic-reset\", \"statistic-reset-all\", \"statistic-sample-age-set\", \"statistic-sample-age-set-all\", \"statistic-sample-count-set\", \"statistic-sample-count-set-all\", \"status-get\", \"subnet4-select-test\", \"subnet4o6-select-test\", \"version-get\" ], \"result\": 0 } ]"
shell_command_test "shell.flag-precedence" \
    "--auth-user pet --auth-password meow --auth-password-file ${tmpfile_path}/auth_bad_password_file" "list-commands" "fail" \
    "Failed to run: HTTP Error 401: Unauthorized"
