diff --git a/tests/linux/CMakeLists.txt b/tests/linux/CMakeLists.txt index 4f6c572..2de8534 100644 --- a/tests/linux/CMakeLists.txt +++ b/tests/linux/CMakeLists.txt @@ -18,6 +18,14 @@ if(NOT NCPFS_INCLUDE_DIR OR NOT NCPFS_LIBRARY) ) endif() +file(COPY afp_smoke_suite.sh + DESTINATION ${CMAKE_CURRENT_BINARY_DIR} + FILE_PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE +) + add_executable(afp_entry_id_smoke afp_entry_id_smoke.c) target_include_directories(afp_entry_id_smoke PRIVATE ${NCPFS_INCLUDE_DIR}) target_link_libraries(afp_entry_id_smoke ${NCPFS_LIBRARY}) diff --git a/tests/linux/README.md b/tests/linux/README.md index 242f5a9..61314de 100644 --- a/tests/linux/README.md +++ b/tests/linux/README.md @@ -33,6 +33,43 @@ cmake --build . --target afp_temp_dir_handle_smoke cmake --build . --target afp_set_file_info_smoke ``` + +## AFP smoke-suite report helper + +`afp_smoke_suite.sh` runs the currently verified AFP Linux smoke helpers as one +collectable report. It is meant for interactive runtime validation after a +server rebuild: the script prints each helper with the password masked, captures +new AFP lines appended to the mars_nwe server log while the suite runs, and +adds `getfattr -e hex` checks for the mars_nwe AFP xattrs on the tested Unix +file. + +Example from the build `tests/linux` directory: + +```sh +./afp_smoke_suite.sh \ + -S MARS -U SUPERVISOR -P secret \ + --path SYS:PUBLIC/pmdflts.ini \ + --unix-path /var/mars_nwe/SYS/public/pmdflts.ini \ + --log /var/log/mars_nwe/nw.log \ + --out /tmp/mars-afp-smoke.txt +``` + +The report includes AFP Entry ID, Entry ID From NetWare Handle, Get File +Information, Scan File Information, Alloc Temporary Directory Handle, Open File +Fork, FinderInfo Set File Information, Invisible Set/Clear File Information, +and the Linux xattr checks for: + +```text +user.org.mars-nwe.afp.finder-info +user.org.mars-nwe.afp.attributes +user.org.mars-nwe.afp.entry-id +``` + +Use `--no-log` when the log file is unavailable or when the server log is being +collected separately. Use `--stop-on-failure` for strict bisect-style runs; by +default the script keeps going so one failing endpoint does not hide later AFP +output from the report. + ## AFP Entry ID smoke test `afp_entry_id_smoke` sends the WebSDK-documented NetWare AFP request: diff --git a/tests/linux/afp_smoke_suite.sh b/tests/linux/afp_smoke_suite.sh new file mode 100755 index 0000000..e4bc5cb --- /dev/null +++ b/tests/linux/afp_smoke_suite.sh @@ -0,0 +1,276 @@ +#!/usr/bin/env bash +# Run the mars_nwe AFP Linux smoke helpers as one collectable report. +# +# The helper intentionally keeps the password out of the printed command lines, +# captures AFP server diagnostics from the mars_nwe log while the probes run, and +# appends Linux xattr checks for the tested Unix file. + +set -u + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +SERVER="MARS" +USER_NAME="SUPERVISOR" +PASSWORD="" +NETWARE_PATH="SYS:PUBLIC/pmdflts.ini" +UNIX_PATH="/var/mars_nwe/SYS/public/pmdflts.ini" +LOG_FILE="/var/log/mars_nwe/nw.log" +OUT_FILE="" +FINDER_TYPE="TEXT" +FINDER_CREATOR="MARS" +KEEP_GOING=1 +CAPTURE_LOG=1 + +usage() { + cat <&2 + usage >&2 + exit 2 ;; + esac +done + +if [ -z "$PASSWORD" ]; then + echo "Missing password. Use -P PASSWORD." >&2 + usage >&2 + exit 2 +fi + +case "$FINDER_TYPE" in + ???? ) ;; + *) echo "--type must be exactly four characters" >&2; exit 2 ;; +esac +case "$FINDER_CREATOR" in + ???? ) ;; + *) echo "--creator must be exactly four characters" >&2; exit 2 ;; +esac + +DIR_PATH=$NETWARE_PATH +case "$NETWARE_PATH" in + */*) DIR_PATH=${NETWARE_PATH%/*} ;; +esac + +REPORT_TMP=$(mktemp "${TMPDIR:-/tmp}/mars-afp-smoke.XXXXXX") +LOG_TMP=$(mktemp "${TMPDIR:-/tmp}/mars-afp-log.XXXXXX") +LOG_PID="" +FAILURES=0 + +cleanup() { + if [ -n "$LOG_PID" ] && kill -0 "$LOG_PID" 2>/dev/null; then + kill "$LOG_PID" 2>/dev/null || true + wait "$LOG_PID" 2>/dev/null || true + fi + rm -f "$LOG_TMP" +} +trap cleanup EXIT INT TERM + +emit() { + printf '%s\n' "$*" | tee -a "$REPORT_TMP" +} + +section() { + emit "" + emit "## $*" +} + +run_cmd() { + local label=$1 + local printable=$2 + shift 2 + + section "$label" + emit "\$ $printable" + "$@" 2>&1 | tee -a "$REPORT_TMP" + local status=${PIPESTATUS[0]} + emit "[exit=$status]" + if [ "$status" -ne 0 ]; then + FAILURES=$((FAILURES + 1)) + if [ "$KEEP_GOING" -eq 0 ]; then + finish_report + exit "$status" + fi + fi +} + +finish_report() { + if [ "$CAPTURE_LOG" -eq 1 ]; then + sleep 1 + if [ -n "$LOG_PID" ] && kill -0 "$LOG_PID" 2>/dev/null; then + kill "$LOG_PID" 2>/dev/null || true + wait "$LOG_PID" 2>/dev/null || true + LOG_PID="" + fi + section "AFP server log excerpt" + if [ -s "$LOG_TMP" ]; then + cat "$LOG_TMP" | tee -a "$REPORT_TMP" + else + emit "No AFP lines captured from $LOG_FILE." + fi + fi + + section "Summary" + emit "failures=$FAILURES" + if [ -n "$OUT_FILE" ]; then + cp "$REPORT_TMP" "$OUT_FILE" + emit "report=$OUT_FILE" + fi +} + +section "mars_nwe AFP smoke suite" +emit "date=$(date -Is)" +emit "server=$SERVER" +emit "user=$USER_NAME" +emit "path=$NETWARE_PATH" +emit "dir_path=$DIR_PATH" +emit "unix_path=$UNIX_PATH" +emit "log=$LOG_FILE" +emit "finder_type=$FINDER_TYPE" +emit "finder_creator=$FINDER_CREATOR" + +for helper in \ + afp_entry_id_smoke \ + afp_file_info_smoke \ + afp_scan_info_smoke \ + afp_temp_dir_handle_smoke \ + afp_open_file_fork_smoke \ + afp_set_file_info_smoke + do + if [ ! -x "$SCRIPT_DIR/$helper" ]; then + emit "Missing executable: $SCRIPT_DIR/$helper" + exit 2 + fi +done + +if [ "$CAPTURE_LOG" -eq 1 ]; then + if [ -r "$LOG_FILE" ]; then + # Capture only lines appended while this script is running. + tail -n 0 -F "$LOG_FILE" 2>/dev/null | grep --line-buffered AFP >"$LOG_TMP" & + LOG_PID=$! + sleep 1 + else + emit "Log file is not readable, disabling log capture: $LOG_FILE" + CAPTURE_LOG=0 + fi +fi + +COMMON_PRINT="-S $SERVER -U $USER_NAME -P ******" + +run_cmd \ + "AFP Entry ID From Path Name" \ + "./afp_entry_id_smoke $COMMON_PRINT '$NETWARE_PATH'" \ + "$SCRIPT_DIR/afp_entry_id_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" + +run_cmd \ + "AFP Entry ID From NetWare Handle" \ + "./afp_entry_id_smoke --from-handle $COMMON_PRINT '$NETWARE_PATH'" \ + "$SCRIPT_DIR/afp_entry_id_smoke" --from-handle -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" + +run_cmd \ + "AFP 2.0 Get File Information" \ + "./afp_file_info_smoke $COMMON_PRINT '$NETWARE_PATH'" \ + "$SCRIPT_DIR/afp_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" + +run_cmd \ + "AFP 2.0 Scan File Information" \ + "./afp_scan_info_smoke $COMMON_PRINT '$DIR_PATH'" \ + "$SCRIPT_DIR/afp_scan_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$DIR_PATH" + +run_cmd \ + "AFP Alloc Temporary Directory Handle" \ + "./afp_temp_dir_handle_smoke $COMMON_PRINT '$DIR_PATH'" \ + "$SCRIPT_DIR/afp_temp_dir_handle_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$DIR_PATH" + +run_cmd \ + "AFP Open File Fork" \ + "./afp_open_file_fork_smoke $COMMON_PRINT '$NETWARE_PATH'" \ + "$SCRIPT_DIR/afp_open_file_fork_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" "$NETWARE_PATH" + +run_cmd \ + "AFP Set File Information FinderInfo" \ + "./afp_set_file_info_smoke $COMMON_PRINT --finder-info-only --type '$FINDER_TYPE' --creator '$FINDER_CREATOR' '$NETWARE_PATH'" \ + "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ + --finder-info-only --type "$FINDER_TYPE" --creator "$FINDER_CREATOR" "$NETWARE_PATH" + +run_cmd \ + "AFP Set File Information Invisible" \ + "./afp_set_file_info_smoke $COMMON_PRINT --attributes-only --invisible '$NETWARE_PATH'" \ + "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ + --attributes-only --invisible "$NETWARE_PATH" + +run_cmd \ + "AFP Set File Information Clear Invisible" \ + "./afp_set_file_info_smoke $COMMON_PRINT --attributes-only --clear-invisible '$NETWARE_PATH'" \ + "$SCRIPT_DIR/afp_set_file_info_smoke" -S "$SERVER" -U "$USER_NAME" -P "$PASSWORD" \ + --attributes-only --clear-invisible "$NETWARE_PATH" + +if command -v getfattr >/dev/null 2>&1; then + run_cmd \ + "Linux xattr: AFP FinderInfo" \ + "getfattr -n user.org.mars-nwe.afp.finder-info -e hex '$UNIX_PATH'" \ + getfattr -n user.org.mars-nwe.afp.finder-info -e hex "$UNIX_PATH" + + run_cmd \ + "Linux xattr: AFP Attributes" \ + "getfattr -n user.org.mars-nwe.afp.attributes -e hex '$UNIX_PATH'" \ + getfattr -n user.org.mars-nwe.afp.attributes -e hex "$UNIX_PATH" + + run_cmd \ + "Linux xattr: AFP Entry ID" \ + "getfattr -n user.org.mars-nwe.afp.entry-id -e hex '$UNIX_PATH'" \ + getfattr -n user.org.mars-nwe.afp.entry-id -e hex "$UNIX_PATH" +else + section "Linux xattr checks" + emit "getfattr not found; install the attr package to collect xattr output." + FAILURES=$((FAILURES + 1)) +fi + +finish_report + +if [ "$FAILURES" -ne 0 ]; then + exit 1 +fi +exit 0