frappe_docker/tests/easy-docker/test_helper.bash

445 lines
14 KiB
Bash
Executable file

#!/usr/bin/env bash
easy_docker_test_repo_root() {
local helper_dir=""
helper_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
(cd "${helper_dir}/../.." && pwd)
}
easy_docker_test_begin() {
EASY_DOCKER_TEST_TMPDIR="$(mktemp -d)"
export EASY_DOCKER_TEST_TMPDIR
unset EASY_DOCKER_REPO_ROOT_OVERRIDE
}
easy_docker_test_end() {
if [ -n "${EASY_DOCKER_TEST_TMPDIR:-}" ] && [ -d "${EASY_DOCKER_TEST_TMPDIR}" ]; then
rm -rf "${EASY_DOCKER_TEST_TMPDIR}"
fi
}
easy_docker_test_bin_dir() {
printf '%s/bin\n' "${EASY_DOCKER_TEST_TMPDIR}"
}
easy_docker_test_write_executable() {
local target_path="${1}"
local system_bash=""
shift
system_bash="$(command -v bash)"
mkdir -p "$(dirname "${target_path}")"
{
printf '#!%s\n' "${system_bash}"
printf '%s\n' "$@"
} >"${target_path}"
chmod +x "${target_path}"
}
easy_docker_test_write_bin_command() {
local command_name="${1}"
local target_path=""
shift
target_path="$(easy_docker_test_bin_dir)/${command_name}"
easy_docker_test_write_executable "${target_path}" "$@"
}
easy_docker_test_prepend_bin_dir() {
PATH="$(easy_docker_test_bin_dir):${PATH}"
export PATH
}
easy_docker_test_source_common_modules() {
local repo_root=""
repo_root="$(easy_docker_test_repo_root)"
# shellcheck source=scripts/easy-docker/lib/core/commands.sh
source "${repo_root}/scripts/easy-docker/lib/core/commands.sh"
# shellcheck source=scripts/easy-docker/lib/core/messages.sh
source "${repo_root}/scripts/easy-docker/lib/core/messages.sh"
# shellcheck source=scripts/easy-docker/lib/core/json.sh
source "${repo_root}/scripts/easy-docker/lib/core/json.sh"
}
easy_docker_test_source_core_render_modules() {
local repo_root=""
repo_root="$(easy_docker_test_repo_root)"
easy_docker_test_source_common_modules
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/core.sh
source "${repo_root}/scripts/easy-docker/lib/app/wizard/common/core.sh"
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/compose/render.sh
source "${repo_root}/scripts/easy-docker/lib/app/wizard/common/compose/render.sh"
}
easy_docker_test_source_apps_modules() {
local repo_root=""
repo_root="$(easy_docker_test_repo_root)"
easy_docker_test_source_common_modules
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/core.sh
source "${repo_root}/scripts/easy-docker/lib/app/wizard/common/core.sh"
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/helpers.sh
source "${repo_root}/scripts/easy-docker/lib/app/wizard/common/helpers.sh"
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/apps/metadata.sh
source "${repo_root}/scripts/easy-docker/lib/app/wizard/common/apps/metadata.sh"
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/apps/persistence.sh
source "${repo_root}/scripts/easy-docker/lib/app/wizard/common/apps/persistence.sh"
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/apps/catalog.sh
source "${repo_root}/scripts/easy-docker/lib/app/wizard/common/apps/catalog.sh"
# shellcheck source=scripts/easy-docker/lib/app/wizard/common/compose/build.sh
source "${repo_root}/scripts/easy-docker/lib/app/wizard/common/compose/build.sh"
}
easy_docker_test_source_docker_modules() {
local repo_root=""
repo_root="$(easy_docker_test_repo_root)"
easy_docker_test_source_common_modules
# shellcheck source=scripts/easy-docker/lib/checks/docker.sh
source "${repo_root}/scripts/easy-docker/lib/checks/docker.sh"
}
easy_docker_test_source_jq_modules() {
local repo_root=""
repo_root="$(easy_docker_test_repo_root)"
easy_docker_test_source_common_modules
# shellcheck source=scripts/easy-docker/lib/checks/jq.sh
source "${repo_root}/scripts/easy-docker/lib/checks/jq.sh"
}
easy_docker_test_source_gum_modules() {
local repo_root=""
repo_root="$(easy_docker_test_repo_root)"
easy_docker_test_source_common_modules
# shellcheck source=scripts/easy-docker/lib/install/gum/package_manager.sh
source "${repo_root}/scripts/easy-docker/lib/install/gum/package_manager.sh"
# shellcheck source=scripts/easy-docker/lib/install/gum/github_release.sh
source "${repo_root}/scripts/easy-docker/lib/install/gum/github_release.sh"
# shellcheck source=scripts/easy-docker/lib/install/gum/ensure.sh
source "${repo_root}/scripts/easy-docker/lib/install/gum/ensure.sh"
}
easy_docker_test_source_screen_modules() {
local repo_root=""
repo_root="$(easy_docker_test_repo_root)"
easy_docker_test_source_common_modules
# shellcheck source=scripts/easy-docker/lib/app/screen.sh
source "${repo_root}/scripts/easy-docker/lib/app/screen.sh"
}
easy_docker_test_source_screen_modules_with_tty_stdout() {
easy_docker_test_source_screen_modules
# shellcheck disable=SC2317
stdout_is_terminal() {
return 0
}
}
easy_docker_test_create_repo_sandbox() {
local sandbox_name="${1}"
local sandbox_root=""
sandbox_root="${EASY_DOCKER_TEST_TMPDIR}/repo-${sandbox_name}"
mkdir -p "${sandbox_root}/.easy-docker/stacks" "${sandbox_root}/overrides"
printf '%s\n' "${sandbox_root}"
}
easy_docker_test_override_repo_root() {
EASY_DOCKER_REPO_ROOT_OVERRIDE="${1}"
export EASY_DOCKER_REPO_ROOT_OVERRIDE
}
easy_docker_test_stack_dir() {
local stack_name="${1}"
printf '%s/.easy-docker/stacks/%s\n' "${EASY_DOCKER_REPO_ROOT_OVERRIDE}" "${stack_name}"
}
easy_docker_test_install_docker_stub() {
local log_file=""
log_file="${EASY_DOCKER_TEST_TMPDIR}/docker.invocations"
# shellcheck disable=SC2016
easy_docker_test_write_bin_command docker \
'set -euo pipefail' \
"log_file=\"${log_file}\"" \
'printf '"'"'%s\n'"'"' "docker $*" >>"${log_file}"' \
'if [ "${1:-}" != "compose" ]; then' \
' echo "unexpected docker subcommand: ${1:-}" >&2' \
' exit 64' \
'fi' \
'if [ "${!#}" != "config" ]; then' \
' echo "expected docker compose config invocation" >&2' \
' exit 65' \
'fi' \
'printf '"'"'invocation=%s\n'"'"' "docker $*"' \
'printf '"'"'erpnext=%s\n'"'"' "${ERPNEXT_VERSION:-}"'
easy_docker_test_prepend_bin_dir
}
easy_docker_test_install_jq_stub() {
# shellcheck disable=SC2016
easy_docker_test_write_bin_command jq \
'set -euo pipefail' \
'raw_output=0' \
'exit_status=0' \
'filter_expr=""' \
'file_path=""' \
'arg_field_name=""' \
'arg_key=""' \
'arg_app_id=""' \
'while [ "$#" -gt 0 ]; do' \
' case "${1}" in' \
' -r)' \
' raw_output=1' \
' shift' \
' ;;' \
' -e)' \
' exit_status=1' \
' shift' \
' ;;' \
' --arg)' \
' case "${2}" in' \
' field_name) arg_field_name="${3}" ;;' \
' key) arg_key="${3}" ;;' \
' app_id) arg_app_id="${3}" ;;' \
' esac' \
' shift 3' \
' ;;' \
' --indent)' \
' shift 2' \
' ;;' \
' -*)' \
' shift' \
' ;;' \
' *)' \
' if [ -z "${filter_expr}" ]; then' \
' filter_expr="${1}"' \
' elif [ -z "${file_path}" ]; then' \
' file_path="${1}"' \
' else' \
' echo "unsupported jq stub arguments" >&2' \
' exit 2' \
' fi' \
' shift' \
' ;;' \
' esac' \
'done' \
'if [ -z "${filter_expr}" ]; then' \
' echo "missing jq filter" >&2' \
' exit 2' \
'fi' \
'cleanup_file=""' \
'if [ -n "${file_path}" ] && [ "${file_path}" != "-" ]; then' \
' payload_path="${file_path}"' \
'else' \
' payload_path="$(mktemp)"' \
' cleanup_file="${payload_path}"' \
' cat >"${payload_path}"' \
'fi' \
'jq_stub_cleanup() {' \
' if [ -n "${cleanup_file}" ] && [ -f "${cleanup_file}" ]; then' \
' rm -f "${cleanup_file}"' \
' fi' \
'}' \
'trap jq_stub_cleanup EXIT' \
'jq_stub_is_object() {' \
' awk '"'"'BEGIN { found=0 } /^[[:space:]]*$/ { next } { if ($0 ~ /^[[:space:]]*{/) found=1; exit } END { exit(found ? 0 : 1) }'"'"' "${payload_path}"' \
'}' \
'jq_stub_first_string_field() {' \
' local field_name="${1}"' \
' awk -v field_name="${field_name}" '"'"'match($0, "\"" field_name "\"[[:space:]]*:[[:space:]]*\"([^\"]*)\"", parts) { print parts[1]; exit }'"'"' "${payload_path}"' \
'}' \
'jq_stub_array_strings() {' \
' local key="${1}"' \
' awk -v key="${key}" '"'"'' \
' function emit_matches(segment, parts) {' \
' while (match(segment, /"([^"]+)"/, parts)) {' \
' print parts[1]' \
' segment = substr(segment, RSTART + RLENGTH)' \
' }' \
' }' \
' $0 ~ "\"" key "\"[[:space:]]*:[[:space:]]*\\[" {' \
' segment = $0' \
' sub(/^.*\[[[:space:]]*/, "", segment)' \
' emit_matches(segment)' \
' if (segment ~ /\]/) {' \
' exit' \
' }' \
' in_array = 1' \
' next' \
' }' \
' in_array {' \
' emit_matches($0)' \
' if ($0 ~ /\]/) {' \
' exit' \
' }' \
' }' \
' '"'"' "${payload_path}"' \
'}' \
'jq_stub_object_entries() {' \
' local key="${1}"' \
' awk -v key="${key}" '"'"'' \
' $0 ~ "\"" key "\"[[:space:]]*:[[:space:]]*\\{" { in_object = 1; next }' \
' in_object && /^[[:space:]]*}/ { exit }' \
' in_object && match($0, /"([^"]+)"[[:space:]]*:[[:space:]]*"([^"]+)"/, parts) { print parts[1] "|" parts[2] }' \
' '"'"' "${payload_path}"' \
'}' \
'jq_stub_lookup_object_value() {' \
' local object_key="${1}"' \
' local lookup_key="${2}"' \
' awk -v object_key="${object_key}" -v lookup_key="${lookup_key}" '"'"'' \
' $0 ~ "\"" object_key "\"[[:space:]]*:[[:space:]]*\\{" { in_object = 1; next }' \
' in_object && /^[[:space:]]*}/ { exit }' \
' in_object && match($0, /"([^"]+)"[[:space:]]*:[[:space:]]*"([^"]+)"/, parts) {' \
' if (parts[1] == lookup_key) {' \
' print parts[2]' \
' exit' \
' }' \
' }' \
' '"'"' "${payload_path}"' \
'}' \
'jq_stub_top_level_keys() {' \
' awk '"'"'match($0, /^ "([^"]+)":/, parts) { print parts[1] }'"'"' "${payload_path}"' \
'}' \
'jq_stub_count_delta() {' \
' local line="${1}"' \
' local opens=0' \
' local closes=0' \
' local tmp=""' \
' tmp="${line//[^\{]/}"' \
' opens=$((opens + ${#tmp}))' \
' tmp="${line//[^\[]/}"' \
' opens=$((opens + ${#tmp}))' \
' tmp="${line//[^\}]/}"' \
' closes=$((closes + ${#tmp}))' \
' tmp="${line//[^\]]/}"' \
' closes=$((closes + ${#tmp}))' \
' printf "%s\n" "$((opens - closes))"' \
'}' \
'jq_stub_top_level_value() {' \
' local key="${1}"' \
' local line=""' \
' local value=""' \
' local in_block=0' \
' local depth=0' \
' local delta=0' \
' while IFS= read -r line || [ -n "${line}" ]; do' \
' if [ "${in_block}" -eq 0 ]; then' \
' case "${line}" in' \
' " \"${key}\":"*)' \
' value="${line#*: }"' \
' if [[ "${value}" == \{* || "${value}" == \[* ]]; then' \
' printf "%s\n" "${value}"' \
' depth="$(jq_stub_count_delta "${value}")"' \
' if [ "${depth}" -le 0 ]; then' \
' return 0' \
' fi' \
' in_block=1' \
' else' \
' value="${value%,}"' \
' printf "%s\n" "${value}"' \
' return 0' \
' fi' \
' ;;' \
' esac' \
' else' \
' delta="$(jq_stub_count_delta "${line}")"' \
' if [ $((depth + delta)) -le 0 ]; then' \
' printf "%s\n" "${line%,}"' \
' return 0' \
' fi' \
' printf "%s\n" "${line}"' \
' depth=$((depth + delta))' \
' fi' \
' done <"${payload_path}"' \
'}' \
'jq_stub_apps_custom_lines() {' \
' local repo=""' \
' local branch=""' \
' awk '"'"'' \
' /"custom"[[:space:]]*:[[:space:]]*\[/ { in_custom = 1; next }' \
' in_custom && /\]/ { exit }' \
' in_custom && match($0, /"repo"[[:space:]]*:[[:space:]]*"([^"]+)"/, parts) { repo = parts[1] }' \
' in_custom && match($0, /"branch"[[:space:]]*:[[:space:]]*"([^"]+)"/, parts) { branch = parts[1] }' \
' in_custom && repo != "" && branch != "" { print repo "|" branch; repo = ""; branch = "" }' \
' '"'"' "${payload_path}"' \
'}' \
'jq_stub_apps_json_refs() {' \
' awk '"'"'match($0, /"url"[[:space:]]*:[[:space:]]*"([^"]+)".*"branch"[[:space:]]*:[[:space:]]*"([^"]+)"/, parts) { print parts[1] "|" parts[2] }'"'"' "${payload_path}"' \
'}' \
'case "${filter_expr}" in' \
' "(.apps.predefined // []) | join(\",\")")' \
' output="$(jq_stub_array_strings "predefined" | paste -sd, -)"' \
' [ -n "${output}" ] && printf "%s\n" "${output}"' \
' ;;' \
' "(.apps.custom // [])[]? | select(has(\"repo\") and has(\"branch\")) | \"\\(.repo)|\\(.branch)\"")' \
' jq_stub_apps_custom_lines' \
' ;;' \
' "(.apps.predefined_branches // {}) | to_entries[]? | \"\\(.key)|\\(.value)\"")' \
' jq_stub_object_entries "predefined_branches"' \
' ;;' \
' ".apps.predefined_branches[\$app_id] // empty")' \
' jq_stub_lookup_object_value "predefined_branches" "${arg_app_id}"' \
' ;;' \
' "[.. | objects | .[\$field_name]? | select(type == \"string\")][0] // empty")' \
' jq_stub_first_string_field "${arg_field_name}"' \
' ;;' \
' "([.. | objects | .compose_files? | select(type == \"array\")] | .[0] // [])[]?")' \
' jq_stub_array_strings "compose_files"' \
' ;;' \
' ".site[\$field_name] // empty")' \
' jq_stub_first_string_field "${arg_field_name}"' \
' ;;' \
' "(.site.apps_installed // [])[]? | select(type == \"string\")")' \
' jq_stub_array_strings "apps_installed"' \
' ;;' \
' "type == \"object\"")' \
' if jq_stub_is_object; then' \
' [ "${exit_status}" -eq 0 ] && printf "true\n"' \
' exit 0' \
' fi' \
' [ "${exit_status}" -eq 1 ] && exit 1' \
' printf "false\n"' \
' exit 0' \
' ;;' \
' "keys_unsorted[]")' \
' jq_stub_top_level_keys' \
' ;;' \
' ".[\$key]")' \
' jq_stub_top_level_value "${arg_key}"' \
' ;;' \
' ".[]? | select((.url // \"\") != \"\" and (.branch // \"\") != \"\") | \"\\(.url)|\\(.branch)\"")' \
' jq_stub_apps_json_refs' \
' ;;' \
' *)' \
' echo "unsupported jq filter in stub: ${filter_expr}" >&2' \
' exit 2' \
' ;;' \
'esac'
easy_docker_test_prepend_bin_dir
}