#!/bin/sh

# Copyright (C) 2014-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

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

export KEA_DHCP_DATA_DIR="@abs_top_builddir@/src/bin/admin/tests"
export KEA_LOG_FILE_DIR="@abs_top_builddir@/src/bin/admin/tests"


# Locations of memfile tools
kea_admin="@abs_top_builddir@/src/bin/admin/kea-admin"
kea_lfc="@abs_top_builddir@/src/bin/lfc/kea-lfc"

# Clean up any files used in testing.
clean_up() {
    remove_if_exists \
        "${config_file-}" \
        "${csv-}" \
        "${csv_2-}" \
        "@abs_top_builddir@/src/bin/admin/tests/kea.log" \
        "@abs_top_builddir@/src/bin/admin/tests/kea.log.lock"
}

# Print location of CSV file. Accepts 4 or 6 as parameter.
csv_file() {
    local v="${1}"
    printf '%s' "@abs_top_builddir@/src/bin/admin/tests/kea-dhcp${v}.csv"
}

# Print location of kea-dhcp[46] binaries. Accepts 4 or 6 as parameter.
kea_dhcp() {
    local v="${1}"
    printf '%s' "@abs_top_builddir@/src/bin/dhcp${v}/kea-dhcp${v}"
}

# Print the minimum allowed number of header columns for v4.
incomplete_memfile_header_v4() {
    printf 'address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname'
}

# Print the minimum allowed number of header columns for v6.
incomplete_memfile_header_v6() {
    printf 'address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname'
}

# Print the entire header for v4.
memfile_header_v4() {
    printf '%s,state,user_context,pool_id' "$(incomplete_memfile_header_v4)"
}

# Print the entire header for v6.
memfile_header_v6() {
    printf '%s,hwaddr,state,user_context,hwtype,hwaddr_source,pool_id' "$(incomplete_memfile_header_v6)"
}

# Print data copied from mysql_upgrade_12_to_13_test and pgsql_upgrade_7_0_to_8_0.
# @{
memfile_data_v4() {
    printf '0.0.0.10,32:30,33:30,40,1678900000,50,1,1,one&#x2cexample&#x2ccom,0,{"a":1&#x2c"b":2},0'
}
memfile_data_v6() {
    printf '::10,32:30:33,30,1678900000,40,50,1,60,70,1,1,one&#x2cexample&#x2ccom,38:30,0,{"a":1&#x2c"b":2},90,16,0'
}
# @}

# Print "server-id" configuration. Not available for v4.
server_id_v4() {
    :
}

# Print "server-id" configuration. Not available for v4.
server_id_v6() {
    printf ',
    "server-id": {
        "persist": false,
        "type": "EN"
    }
'
}

# Starts Kea and sets PID. It logs to stdout and stderr if DEBUG is enabled.
# Accepts 4 or 6 as parameter.
start_kea_dhcp() {
    local v="${1}"
    "$(kea_dhcp "${v}")" -c "${config_file}" >> "@abs_top_builddir@/src/bin/admin/tests/kea.log" 2>&1 &
    PID=${!}
    sleep 1
}

# Test that Kea creates a correctly populated CSV file if configured with
# persisting memfile.
memfile_init_test() {
    test_start 'memfile.init'

    for v in 4 6; do
      for i in no-file some-data; do
        if test -n "${DEBUG+x}"; then
            printf 'TRACE %s, %s\n' "${v}" "${i}"
        fi
        config=$(printf '%s%s%s' '
{
  "Dhcpx": {
    "lease-database": {
      "name": "@abs_top_builddir@/src/bin/admin/tests/kea-dhcpx.csv",
      "persist": true,
      "type": "memfile"
    },
    "loggers": [
      {
        "debuglevel": 99,
        "name": "kea-dhcpx",
        "output-options": [
          {
            "output": "@abs_top_builddir@/src/bin/admin/tests/kea.log"
          }
        ],
        "severity": "DEBUG"
      }
    ]' \
    "$(server_id_v${v})" \
    '
  }
}
')
        config_file="@abs_top_builddir@/src/bin/admin/tests/kea-dhcp${v}.conf"
        csv=$(csv_file "${v}")

        if test "${i}" = 'some-data'; then
            # Test that Kea accepts the output of lease-dump in memfile.
            {
              "memfile_header_v${v}"
              printf '\n'
              "memfile_data_v${v}"
              printf '\n'
            } > "${csv}"
        else
            # Make sure there is no CSV as the test requires.
            rm -f "${csv}"
        fi

        # Set DHCP version in config.
        printf '%s\n' "${config}" | \
            sed "s#Dhcpx#Dhcp${v}#g;
                 s#dhcpx#dhcp${v}#g" \
                > "${config_file}"

        start_kea_dhcp "${v}"
        # This assumes that the CSV creation + writing to CSV is atomic. Not
        # sure if it is, but if this ever fails on the comparison further below,
        # consider waiting here for line DHCPSRV_MEMFILE_LFC_SETUP in logs, even
        # though it doesn't clearly signal end of CSV writing.
        if ! wait_for_file "${csv}"; then
            clean_up
            clean_exit 1
        fi
        kill "${PID}" || true  # process may have exited early due to errors
        if ! wait_for_process_to_stop "${PID}"; then
            clean_up
            clean_exit 2
        fi
        content=$(head -n 1 "${csv}")
        expected=$(memfile_header_v${v})
        if test "${content}" != "${expected}"; then
            printf 'ERROR: %s does not contain expected header.\n< %s\n> %s\n' \
                "${csv}" "${content}" "${expected}" >&2
            clean_up
            clean_exit 3
        fi

        if grep -Fi ERROR "@abs_top_builddir@/src/bin/admin/tests/kea.log"; then
            printf 'ERROR: loading memfile failed. See error above.\n' >&2
            cat "${csv}"
            clean_up
            clean_exit 4
        fi

        clean_up
      done
    done

    test_finish 0
}

# Test that kea-lfc is able to upgrade a CSV file with incomplete header.
memfile_upgrade_test() {
    test_start 'memfile.upgrade'

    for v in 4 6; do
        csv=$(csv_file "${v}")
        "incomplete_memfile_header_v${v}" > "${csv}"
        printf '\n' >> "${csv}"
        csv_2="${csv}.2"

        "${kea_lfc}" "-${v}" \
            -c 'ignored-path' \
            -f "${csv}.completed" \
            -i "${csv}" \
            -o "${csv}.output" \
            -p "${csv}.pid" \
            -x "${csv_2}"

        content=$(cat "${csv_2}")
        expected=$(memfile_header_v${v})
        if test "${content}" != "${expected}"; then
            printf 'ERROR: %s does not contain expected header.\n< %s\n> %s\n' \
                "${csv}" "${content}" "${expected}" >&2
            clean_up
            clean_exit 1
        fi

        clean_up
    done

    test_finish 0
}

# shellcheck disable=SC2034
# SC2034: ... appears unused. Verify use (or export if used externally).
# reason: bin and bin_path are used in version_test
{
bin=$(basename "${kea_admin}")
bin_path=$(dirname "${kea_admin}")
version_test 'memfile.version' 'long_version_too_please'
}

memfile_init_test
memfile_upgrade_test
