diff --git a/scripts/da-integration/alt_mysql_restore_payload.sh b/scripts/da-integration/alt_mysql_restore_payload.sh index 0fc7954..759a591 100644 --- a/scripts/da-integration/alt_mysql_restore_payload.sh +++ b/scripts/da-integration/alt_mysql_restore_payload.sh @@ -4,7 +4,7 @@ set -Eeuo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PLUGIN_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" COMMON_FILE="$PLUGIN_DIR/scripts/setup/mysql_common.sh" -SETTINGS_FILE="$PLUGIN_DIR/plugin-settings.conf" +SETTINGS_FILE="${DA_ALT_MYSQL_SETTINGS_FILE:-$PLUGIN_DIR/plugin-settings.conf}" LOG_FILE="${DA_ALT_MYSQL_RESTORE_LOG:-$PLUGIN_DIR/data/logs/da-restore.log}" DA_USER="" PAYLOAD_DIR="" diff --git a/tests/alt_mysql_restore_payload_test.sh b/tests/alt_mysql_restore_payload_test.sh new file mode 100644 index 0000000..a764a02 --- /dev/null +++ b/tests/alt_mysql_restore_payload_test.sh @@ -0,0 +1,159 @@ +#!/bin/bash +set -euo pipefail + +PLUGIN_DIR="$(cd "$(dirname "$0")/.." && pwd)" +SCRIPT="$PLUGIN_DIR/scripts/da-integration/alt_mysql_restore_payload.sh" +TMP_DIR="$(mktemp -d)" +trap 'rm -rf "$TMP_DIR"' EXIT + +fail() { + echo "FAIL: $*" >&2 + exit 1 +} + +sha256_of() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" | awk '{print $1}' + else + shasum -a 256 "$1" | awk '{print $1}' + fi +} + +# --- Fake alt-mariadb client binaries + credentials, isolated from the real system --- +# The stub tracks a simple "existing databases" state file so CREATE/DROP +# DATABASE during the test actually affects what a later existence check sees. +mkdir -p "$TMP_DIR/mariadb/bin" +cat > "$TMP_DIR/mariadb/bin/mariadb" <<'STUB' +#!/bin/bash +STATE_FILE="${FAKE_DB_STATE_FILE:?FAKE_DB_STATE_FILE must be set}" + +sql="" +mode="" +for arg in "$@"; do + case "$arg" in + -e) mode="sql" ;; + -Nse) mode="sql" ;; + --database=*|--user=*|--socket=*|--host=*|--port=*|--protocol=*) ;; + *) + if [ "$mode" = "sql" ]; then + sql="$arg" + mode="" + fi + ;; + esac +done + +if [ -n "$sql" ]; then + case "$sql" in + *"SELECT 1 FROM information_schema.SCHEMATA"*) + name="$(printf '%s' "$sql" | sed -n "s/.*SCHEMA_NAME='\([^']*\)'.*/\1/p")" + grep -qx "$name" "$STATE_FILE" 2>/dev/null && echo 1 + ;; + *"DROP DATABASE"*) + name="$(printf '%s' "$sql" | sed -n 's/.*DATABASE[^`]*`\([A-Za-z0-9_]*\)`.*/\1/p')" + if [ -n "$name" ]; then + grep -vx "$name" "$STATE_FILE" > "$STATE_FILE.tmp" 2>/dev/null || true + mv -f "$STATE_FILE.tmp" "$STATE_FILE" + fi + ;; + *"CREATE DATABASE"*) + name="$(printf '%s' "$sql" | sed -n 's/.*DATABASE[^`]*`\([A-Za-z0-9_]*\)`.*/\1/p')" + [ -n "$name" ] && echo "$name" >> "$STATE_FILE" + ;; + esac + exit 0 +fi + +# No -e/-Nse: reading an import from stdin +cat >/dev/null +if [ "${FAKE_MYSQL_IMPORT_FAIL:-0}" = "1" ]; then + echo "fake mariadb: simulated import failure" >&2 + exit 1 +fi +exit 0 +STUB +chmod 755 "$TMP_DIR/mariadb/bin/mariadb" + +cat > "$TMP_DIR/mariadb/bin/mariadb-dump" <<'STUB' +#!/bin/bash +echo "-- fake rollback dump" +echo "SELECT 1;" +exit 0 +STUB +chmod 755 "$TMP_DIR/mariadb/bin/mariadb-dump" + +cat > "$TMP_DIR/alt-mysql.conf" < "$TMP_DIR/settings.conf" < "$payload_dir/databases/${db_name}.sql" + gzip -9 -f "$payload_dir/databases/${db_name}.sql" + printf '%s\n' '-- users' | gzip -9 > "$payload_dir/grants/users.sql.gz" + printf '%s\n' '-- grants' | gzip -9 > "$payload_dir/grants/grants.sql.gz" + local sha + sha="$(sha256_of "$payload_dir/databases/${db_name}.sql.gz")" + cat > "$payload_dir/manifest.json" < "$db_state" + + DA_ALT_MYSQL_SETTINGS_FILE="$TMP_DIR/settings.conf" \ + DA_ALT_MYSQL_RESTORE_LOG="$TMP_DIR/restore.log" \ + FAKE_DB_STATE_FILE="$db_state" \ + bash "$SCRIPT" "$@" +} + +# --- Scenario 1: checksum mismatch must be rejected before any restore happens --- +PAYLOAD1="$TMP_DIR/payload1" +build_payload "$PAYLOAD1" "demo_app" +echo 'CORRUPTED CONTENT' > "$PAYLOAD1/databases/demo_app.sql.gz" + +OUTPUT1="$(run_restore --user demo --payload "$PAYLOAD1" --overwrite allow 2>&1)" && fail "checksum mismatch must cause a non-zero exit, output: $OUTPUT1" +echo "$OUTPUT1" | grep -qi "checksum mismatch" \ + || fail "expected a checksum mismatch error, got: $OUTPUT1" +[ ! -f "$TMP_DIR/restore.log" ] || ! grep -qi "restoring user definitions" "$TMP_DIR/restore.log" \ + || fail "restore must not proceed past checksum verification on mismatch" + +# --- Scenario 2: overwrite=deny must refuse to touch an existing database --- +PAYLOAD2="$TMP_DIR/payload2" +build_payload "$PAYLOAD2" "demo_existing" + +OUTPUT2="$(FAKE_EXISTING_DBS="demo_existing" run_restore --user demo --payload "$PAYLOAD2" --overwrite deny 2>&1)" && fail "overwrite=deny must reject an existing database, output: $OUTPUT2" +echo "$OUTPUT2" | grep -qi "overwrite is denied" \ + || fail "expected an overwrite-denied error, got: $OUTPUT2" + +# --- Scenario 3: happy path - valid checksum, no conflict, overwrite=allow --- +PAYLOAD3="$TMP_DIR/payload3" +build_payload "$PAYLOAD3" "demo_new" + +run_restore --user demo --payload "$PAYLOAD3" --overwrite allow >/dev/null 2>&1 \ + || fail "a valid payload with no conflicts and overwrite=allow should succeed" +grep -qi "restore payload completed" "$TMP_DIR/restore.log" \ + || fail "expected the restore to log completion" + +echo "alt_mysql_restore_payload_test: OK" diff --git a/tests/da_integration_install_test.sh b/tests/da_integration_install_test.sh index 3fb5d3a..8622d5c 100644 --- a/tests/da_integration_install_test.sh +++ b/tests/da_integration_install_test.sh @@ -27,9 +27,19 @@ grep -Fq "managed by DirectAdmin MySQL plugin hook dispatcher" "$TMP_DIR/custom/ [ -L "$TMP_DIR/custom/user_backup_compress_pre.sh.d/10-alt-mysql.sh" ] || fail "alt backup hook link missing" [ -L "$TMP_DIR/custom/user_restore_post_pre_cleanup.sh.d/10-alt-mysql.sh" ] || fail "alt restore hook link missing" [ -L "$TMP_DIR/custom/database_create_pre.sh.d/20-alt-mysql-block-native-db.sh" ] || fail "native DB blocker link missing" +[ -L "$TMP_DIR/custom/database_delete_pre.sh.d/20-alt-mysql-block-native-db.sh" ] || fail "native DB delete blocker link missing" +[ -L "$TMP_DIR/custom/database_user_password_change_pre.sh.d/20-alt-mysql-block-native-db.sh" ] || fail "native DB password-change blocker link missing" +[ -L "$TMP_DIR/custom/database_user_create_post.sh.d/20-alt-mysql-observe-native-db.sh" ] || fail "native DB user-create observer link missing" +[ -L "$TMP_DIR/custom/database_destroy_user_post.sh.d/20-alt-mysql-observe-native-db.sh" ] || fail "native DB user-destroy observer link missing" DA_CUSTOM_HOOK_DIR="$TMP_DIR/custom" bash "$INSTALLER" uninstall >/dev/null [ ! -e "$TMP_DIR/custom/user_backup_compress_pre.sh.d/10-alt-mysql.sh" ] || fail "alt backup hook link not removed" +[ ! -e "$TMP_DIR/custom/user_restore_post_pre_cleanup.sh.d/10-alt-mysql.sh" ] || fail "alt restore hook link not removed" +[ ! -e "$TMP_DIR/custom/database_create_pre.sh.d/20-alt-mysql-block-native-db.sh" ] || fail "native DB blocker link not removed" +[ ! -e "$TMP_DIR/custom/database_delete_pre.sh.d/20-alt-mysql-block-native-db.sh" ] || fail "native DB delete blocker link not removed" +[ ! -e "$TMP_DIR/custom/database_user_password_change_pre.sh.d/20-alt-mysql-block-native-db.sh" ] || fail "native DB password-change blocker link not removed" +[ ! -e "$TMP_DIR/custom/database_user_create_post.sh.d/20-alt-mysql-observe-native-db.sh" ] || fail "native DB user-create observer link not removed" +[ ! -e "$TMP_DIR/custom/database_destroy_user_post.sh.d/20-alt-mysql-observe-native-db.sh" ] || fail "native DB user-destroy observer link not removed" [ -f "$TMP_DIR/custom/user_backup_compress_pre.sh.d/00-existing.sh" ] || fail "preserved existing hook should remain" echo "da_integration_install_test: OK"