Integration tests (tests/): - 9 categories covering config, lifecycle, signals, supervision, cache, writeback, network faults, crash recovery, and CLI - Shell-based harness with mock NAS (network namespace + SFTP), fault injection (tc netem), and power loss simulation - TAP format runner (run-all.sh) with proper SKIP detection Rust unit tests (warpgate/src/): - 110 tests across 14 modules, all passing in 0.01s - Config parsing, defaults validation, RestartTracker logic, RC API response parsing, rclone arg generation, service config generation, CLI output formatting, warmup path logic Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
167 lines
4.6 KiB
Bash
Executable File
167 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Warpgate Integration Test — Mock NAS (SFTP server in network namespace)
|
|
#
|
|
# Creates a Linux network namespace with a veth pair and runs an SFTP-only
|
|
# SSH daemon. This lets tests control "network" behavior (down/up/latency)
|
|
# without affecting the host.
|
|
#
|
|
# Topology:
|
|
# Host namespace nas-sim namespace
|
|
# veth-wg (10.99.0.1/24) <---> veth-nas (10.99.0.2/24)
|
|
# └─ sshd (SFTP on :22)
|
|
# └─ NAS root: $NAS_ROOT
|
|
|
|
set -euo pipefail
|
|
|
|
MOCK_NAS_NS="${MOCK_NAS_NS:-nas-sim}"
|
|
MOCK_NAS_IP="${MOCK_NAS_IP:-10.99.0.2}"
|
|
HOST_IP="${HOST_IP:-10.99.0.1}"
|
|
MOCK_NAS_SSHD_PID=""
|
|
|
|
start_mock_nas() {
|
|
require_root
|
|
|
|
local sshd_config="$TEST_DIR/sshd_config"
|
|
local host_key="$TEST_DIR/ssh_host_key"
|
|
local auth_keys="$TEST_DIR/authorized_keys"
|
|
|
|
# Generate host key for sshd
|
|
ssh-keygen -t ed25519 -f "$host_key" -N "" -q
|
|
|
|
# Set up authorized_keys from the test key
|
|
cp "$TEST_SSH_PUBKEY" "$auth_keys"
|
|
chmod 600 "$auth_keys"
|
|
|
|
# Create network namespace
|
|
ip netns add "$MOCK_NAS_NS" 2>/dev/null || true
|
|
|
|
# Create veth pair
|
|
ip link add veth-wg type veth peer name veth-nas 2>/dev/null || true
|
|
|
|
# Move one end into the namespace
|
|
ip link set veth-nas netns "$MOCK_NAS_NS"
|
|
|
|
# Configure host side
|
|
ip addr add "$HOST_IP/24" dev veth-wg 2>/dev/null || true
|
|
ip link set veth-wg up
|
|
|
|
# Configure namespace side
|
|
ip netns exec "$MOCK_NAS_NS" ip addr add "$MOCK_NAS_IP/24" dev veth-nas 2>/dev/null || true
|
|
ip netns exec "$MOCK_NAS_NS" ip link set veth-nas up
|
|
ip netns exec "$MOCK_NAS_NS" ip link set lo up
|
|
|
|
# Write sshd config (SFTP-only, no password auth, restricted to NAS_ROOT)
|
|
cat > "$sshd_config" <<SSHD_EOF
|
|
Port 22
|
|
ListenAddress $MOCK_NAS_IP
|
|
HostKey $host_key
|
|
PidFile $TEST_DIR/sshd.pid
|
|
AuthorizedKeysFile $auth_keys
|
|
PasswordAuthentication no
|
|
PubkeyAuthentication yes
|
|
ChallengeResponseAuthentication no
|
|
UsePAM no
|
|
Subsystem sftp internal-sftp
|
|
ForceCommand internal-sftp -d /
|
|
ChrootDirectory $NAS_ROOT
|
|
StrictModes no
|
|
LogLevel ERROR
|
|
SSHD_EOF
|
|
|
|
# Ensure NAS root is owned by root (sshd ChrootDirectory requirement)
|
|
chown root:root "$NAS_ROOT"
|
|
chmod 755 "$NAS_ROOT"
|
|
|
|
# Start sshd inside the namespace
|
|
ip netns exec "$MOCK_NAS_NS" /usr/sbin/sshd -f "$sshd_config" -E "$TEST_DIR/sshd.log"
|
|
|
|
# Read PID
|
|
if [[ -f "$TEST_DIR/sshd.pid" ]]; then
|
|
MOCK_NAS_SSHD_PID=$(cat "$TEST_DIR/sshd.pid")
|
|
fi
|
|
|
|
# Wait for sshd to be ready
|
|
local deadline=$((SECONDS + 10))
|
|
while [[ $SECONDS -lt $deadline ]]; do
|
|
if ip netns exec "$MOCK_NAS_NS" bash -c "echo > /dev/tcp/$MOCK_NAS_IP/22" 2>/dev/null; then
|
|
break
|
|
fi
|
|
sleep 0.5
|
|
done
|
|
|
|
export MOCK_NAS_SSHD_PID
|
|
}
|
|
|
|
stop_mock_nas() {
|
|
# Kill sshd
|
|
if [[ -n "${MOCK_NAS_SSHD_PID:-}" ]] && kill -0 "$MOCK_NAS_SSHD_PID" 2>/dev/null; then
|
|
kill "$MOCK_NAS_SSHD_PID" 2>/dev/null || true
|
|
wait "$MOCK_NAS_SSHD_PID" 2>/dev/null || true
|
|
fi
|
|
|
|
# Also kill by PID file
|
|
if [[ -f "${TEST_DIR:-}/sshd.pid" ]]; then
|
|
local pid
|
|
pid=$(cat "$TEST_DIR/sshd.pid" 2>/dev/null || true)
|
|
if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
|
|
kill "$pid" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
|
|
# Clean up veth pair (deleting one end removes both)
|
|
ip link del veth-wg 2>/dev/null || true
|
|
|
|
# Delete network namespace
|
|
ip netns del "$MOCK_NAS_NS" 2>/dev/null || true
|
|
|
|
MOCK_NAS_SSHD_PID=""
|
|
}
|
|
|
|
# Verify mock NAS is reachable via SFTP
|
|
verify_mock_nas() {
|
|
sftp -i "$TEST_SSH_KEY" \
|
|
-o StrictHostKeyChecking=no \
|
|
-o UserKnownHostsFile=/dev/null \
|
|
-o ConnectTimeout=5 \
|
|
-P 22 \
|
|
"root@$MOCK_NAS_IP" <<< "ls" > /dev/null 2>&1
|
|
}
|
|
|
|
# Create a file directly on the mock NAS filesystem
|
|
nas_create_file() {
|
|
local path="$1"
|
|
local size_kb="${2:-1}"
|
|
|
|
local full_path="$NAS_ROOT/$path"
|
|
mkdir -p "$(dirname "$full_path")"
|
|
dd if=/dev/urandom of="$full_path" bs=1K count="$size_kb" 2>/dev/null
|
|
}
|
|
|
|
# Create a file with specific content on the mock NAS
|
|
nas_create_file_content() {
|
|
local path="$1"
|
|
local content="$2"
|
|
|
|
local full_path="$NAS_ROOT/$path"
|
|
mkdir -p "$(dirname "$full_path")"
|
|
echo -n "$content" > "$full_path"
|
|
}
|
|
|
|
# Read a file from the mock NAS
|
|
nas_read_file() {
|
|
local path="$1"
|
|
cat "$NAS_ROOT/$path"
|
|
}
|
|
|
|
# Check if a file exists on the mock NAS
|
|
nas_file_exists() {
|
|
local path="$1"
|
|
[[ -f "$NAS_ROOT/$path" ]]
|
|
}
|
|
|
|
# Get file checksum on the mock NAS
|
|
nas_file_checksum() {
|
|
local path="$1"
|
|
md5sum "$NAS_ROOT/$path" | awk '{print $1}'
|
|
}
|