896 lines
33 KiB
Bash
Executable File
896 lines
33 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
|
|
TEST_DIR="${SCRIPT_DIR}/test_tmp"
|
|
TEST_TOKEN="test-token-$(date +%s)"
|
|
CONTAINER_NAME="sos-test-$(date +%s)"
|
|
IMAGE="gitea.jde.nz/public/simple-object-server:latest"
|
|
TEST_PORT=""
|
|
SOS_TEST_HOST=""
|
|
CLEANUP_NEEDED=false
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
# Logging functions
|
|
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
|
|
log_warning() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
die() { log_error "$@"; exit 1; }
|
|
|
|
# Cleanup on exit
|
|
cleanup() {
|
|
[[ "$CLEANUP_NEEDED" != true ]] && return
|
|
log_info "Cleaning up test environment..."
|
|
docker stop "${CONTAINER_NAME}" &>/dev/null || true
|
|
docker rm "${CONTAINER_NAME}" &>/dev/null || true
|
|
rm -rf "${TEST_DIR}"
|
|
log_info "Cleanup complete"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# Check dependencies
|
|
check_dependencies() {
|
|
log_info "Checking dependencies..."
|
|
for cmd in docker jq curl; do
|
|
command -v $cmd &>/dev/null || die "$cmd is required but not installed"
|
|
done
|
|
docker info &>/dev/null || die "Docker daemon is not running"
|
|
log_info "All dependencies satisfied"
|
|
}
|
|
|
|
# Setup test environment
|
|
setup_test_environment() {
|
|
log_info "Setting up test environment..."
|
|
CLEANUP_NEEDED=true
|
|
|
|
# Create directories
|
|
rm -rf "${TEST_DIR}"
|
|
mkdir -p "${TEST_DIR}"/{test_files,config}
|
|
|
|
# Generate hashed token for authentication
|
|
log_info "Generating authentication token..."
|
|
local hashed_token=$(docker run --rm "$IMAGE" /sos/hash_token "${TEST_TOKEN}" 2>/dev/null | grep '^\$2[aby]\$' | head -1)
|
|
[[ -z "$hashed_token" ]] && die "Failed to generate hashed token"
|
|
|
|
# Create config
|
|
echo "{\"write_tokens\": [\"${hashed_token}\"]}" > "${TEST_DIR}/config/sos_config.json"
|
|
|
|
# Create test files
|
|
echo "This is test file 1" > "${TEST_DIR}/test_files/test1.txt"
|
|
echo "This is test file 2 with more content" > "${TEST_DIR}/test_files/test2.txt"
|
|
dd if=/dev/urandom of="${TEST_DIR}/test_files/binary_test.bin" bs=1024 count=10 &>/dev/null
|
|
cp "${SCRIPT_DIR}/sos" "${TEST_DIR}/test_files/sos_binary"
|
|
|
|
log_info "Test environment ready"
|
|
}
|
|
|
|
# Start and configure test server
|
|
start_test_server() {
|
|
log_info "Starting SOS test server..."
|
|
|
|
# Pull latest image
|
|
docker pull "$IMAGE" &>/dev/null || log_warning "Using cached image"
|
|
|
|
# Start container with dynamic port
|
|
local container_id=$(docker run -d --name "${CONTAINER_NAME}" -p 80 "$IMAGE" 2>&1)
|
|
[[ $? -ne 0 ]] && die "Failed to start container: ${container_id}"
|
|
|
|
log_info "Container started: ${container_id:0:12}"
|
|
|
|
# Get assigned port
|
|
TEST_PORT=$(docker port "${CONTAINER_NAME}" 80 | cut -d: -f2)
|
|
[[ -z "$TEST_PORT" ]] && die "Failed to get container port"
|
|
log_info "Container port: ${TEST_PORT}"
|
|
|
|
# Copy config and restart (for Docker-in-Docker compatibility)
|
|
docker cp "${TEST_DIR}/config/sos_config.json" "${CONTAINER_NAME}:/data/sos_config.json" || die "Failed to copy config"
|
|
docker exec "${CONTAINER_NAME}" mkdir -p /data/storage 2>/dev/null || true
|
|
docker restart "${CONTAINER_NAME}" &>/dev/null || die "Failed to restart container"
|
|
|
|
# ARM64 systems may need more time to restart
|
|
sleep 5 # Wait for restart
|
|
|
|
# Update port if changed after restart
|
|
local new_port=$(docker port "${CONTAINER_NAME}" 80 | cut -d: -f2)
|
|
[[ -z "$new_port" ]] && die "Lost port mapping after restart"
|
|
[[ "$new_port" != "$TEST_PORT" ]] && TEST_PORT="$new_port" && log_info "Port updated: ${TEST_PORT}"
|
|
|
|
# Get network details for connectivity
|
|
local container_ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${CONTAINER_NAME}")
|
|
local host_ip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}' "${CONTAINER_NAME}")
|
|
|
|
# Wait for server readiness and determine connection method
|
|
wait_for_server "$container_ip" "$host_ip"
|
|
}
|
|
|
|
# Wait for server and determine connection method
|
|
wait_for_server() {
|
|
local container_ip="${1:-}"
|
|
local host_ip="${2:-}"
|
|
|
|
log_info "Waiting for server readiness..."
|
|
|
|
for attempt in {1..60}; do
|
|
# Try all connection methods
|
|
local container_code=$(curl -s --max-time 2 -o /dev/null -w "%{http_code}" "http://${container_ip}:80/" 2>/dev/null || echo "000")
|
|
local host_code=$(curl -s --max-time 2 -o /dev/null -w "%{http_code}" "http://${host_ip}:${TEST_PORT}/" 2>/dev/null || echo "000")
|
|
local local_code=$(curl -s --max-time 2 -o /dev/null -w "%{http_code}" "http://localhost:${TEST_PORT}/" 2>/dev/null || echo "000")
|
|
|
|
# Debug first attempts
|
|
if [[ $attempt -le 3 ]]; then
|
|
log_info "Health check #${attempt}: container=${container_code}, host=${host_code}, localhost=${local_code}"
|
|
fi
|
|
|
|
# Check which method works
|
|
if [[ "$container_code" =~ ^(200|204|404)$ ]]; then
|
|
export SOS_TEST_HOST="${container_ip}:80"
|
|
log_info "Server ready! Using container IP: ${SOS_TEST_HOST}"
|
|
return 0
|
|
elif [[ "$host_code" =~ ^(200|204|404)$ ]]; then
|
|
export SOS_TEST_HOST="${host_ip}:${TEST_PORT}"
|
|
log_info "Server ready! Using host IP: ${SOS_TEST_HOST}"
|
|
return 0
|
|
elif [[ "$local_code" =~ ^(200|204|404)$ ]]; then
|
|
export SOS_TEST_HOST="localhost:${TEST_PORT}"
|
|
log_info "Server ready! Using localhost: ${SOS_TEST_HOST}"
|
|
return 0
|
|
fi
|
|
|
|
# Progress indicator
|
|
[[ $((attempt % 10)) -eq 0 ]] && log_info "Still waiting... (${attempt}/60)"
|
|
sleep 1
|
|
done
|
|
|
|
die "Server failed to become ready after 60 seconds"
|
|
}
|
|
|
|
# Execute SOS upload command
|
|
run_sos_upload() {
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
export SOS_WRITE_TOKEN="${TEST_TOKEN}"
|
|
export SOS_TEST_MODE=1
|
|
"${SCRIPT_DIR}/sos" upload "$host" "$@"
|
|
}
|
|
|
|
# Test functions
|
|
test_upload() {
|
|
local file="$1"
|
|
local label="$2"
|
|
shift 2
|
|
|
|
log_info "Testing upload: ${file##*/} with label ${label}"
|
|
|
|
local output=$(run_sos_upload "$file" "$label" "$@" 2>&1 | tee "${TEST_DIR}/upload_output.txt")
|
|
|
|
if echo "$output" | grep -q "Download URL:"; then
|
|
log_info "Upload successful"
|
|
return 0
|
|
else
|
|
log_error "Upload failed"
|
|
# Show error details for debugging ARM64 issues
|
|
echo "Error output:" >&2
|
|
echo "$output" | grep -E "(FATAL:|ERROR:|Failed|curl)" | head -5 >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_retrieval() {
|
|
local identifier="$1"
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
|
|
log_info "Testing retrieval: ${identifier}"
|
|
|
|
local response=$(curl -s "http://${host}/${identifier}")
|
|
[[ -z "$response" ]] && log_error "Retrieval failed" && return 1
|
|
|
|
# For hash identifiers, also test metadata endpoint
|
|
if [[ "$identifier" =~ ^[a-f0-9]{64}$ ]]; then
|
|
curl -s "http://${host}/meta/${identifier}" | jq -e '.metadata' &>/dev/null || {
|
|
log_error "Metadata retrieval failed"
|
|
return 1
|
|
}
|
|
fi
|
|
|
|
log_info "Retrieval successful"
|
|
return 0
|
|
}
|
|
|
|
test_deduplication() {
|
|
log_info "Testing deduplication..."
|
|
|
|
# Create duplicate file
|
|
cp "${TEST_DIR}/test_files/test1.txt" "${TEST_DIR}/test_files/test1_dup.txt"
|
|
|
|
# Upload original and duplicate
|
|
run_sos_upload "${TEST_DIR}/test_files/test1.txt" "dup:original" &>/dev/null
|
|
local output=$(run_sos_upload "${TEST_DIR}/test_files/test1_dup.txt" "dup:copy" 2>&1)
|
|
|
|
if echo "$output" | grep -q "File already exists, skipping upload"; then
|
|
log_info "Deduplication working"
|
|
return 0
|
|
else
|
|
log_error "Deduplication failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_update() {
|
|
log_info "Testing metadata update..."
|
|
|
|
run_sos_upload "${TEST_DIR}/test_files/test2.txt" "meta:v1" &>/dev/null
|
|
local output=$(run_sos_upload "${TEST_DIR}/test_files/test2.txt" "meta:v2" "meta:updated" 2>&1)
|
|
|
|
if echo "$output" | grep -q "File already exists"; then
|
|
log_info "Metadata update working"
|
|
return 0
|
|
else
|
|
log_error "Metadata update failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_api_endpoints() {
|
|
log_info "Testing API endpoints..."
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
|
|
# Test hash endpoint
|
|
local hash=$(curl -s "http://${host}/hash/test:file1" | jq -r '.hash')
|
|
[[ -z "$hash" || "$hash" == "null" ]] && log_error "Hash endpoint failed" && return 1
|
|
|
|
# Test exists endpoint
|
|
local exists=$(curl -s "http://${host}/exists/${hash}" | jq -r '.exists')
|
|
[[ "$exists" != "true" ]] && log_error "Exists endpoint failed" && return 1
|
|
|
|
log_info "API endpoints working"
|
|
return 0
|
|
}
|
|
|
|
test_invalid_auth() {
|
|
log_info "Testing invalid authentication..."
|
|
|
|
echo "auth test" > "${TEST_DIR}/test_files/auth_test.txt"
|
|
export SOS_WRITE_TOKEN="invalid-token"
|
|
export SOS_TEST_MODE=1
|
|
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
|
|
# Run upload with invalid token and properly capture exit code
|
|
set +e
|
|
"${SCRIPT_DIR}/sos" upload "$host" "${TEST_DIR}/test_files/auth_test.txt" "auth:test" >"${TEST_DIR}/auth_output.txt" 2>&1
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
local output=$(cat "${TEST_DIR}/auth_output.txt")
|
|
|
|
# The sos script should fail with non-zero exit code and show error message
|
|
if [[ $exit_code -ne 0 ]] && echo "$output" | grep -qE "(FATAL:|Invalid write token|HTTP 403|HTTP 401)"; then
|
|
log_info "Invalid auth correctly rejected (exit code: $exit_code)"
|
|
return 0
|
|
else
|
|
log_error "Invalid auth not rejected properly (exit code: $exit_code)"
|
|
echo "Output:" && echo "$output" | head -5
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_single() {
|
|
log_info "Testing single metadata field..."
|
|
|
|
echo "metadata test single" > "${TEST_DIR}/test_files/meta_single.txt"
|
|
local output=$(run_sos_upload "${TEST_DIR}/test_files/meta_single.txt" "meta:single" --metadata "customField=testValue" 2>&1)
|
|
|
|
# Check if upload succeeded
|
|
if ! echo "$output" | grep -q "Download URL:"; then
|
|
log_error "Upload with metadata failed"
|
|
echo "$output" | head -5
|
|
return 1
|
|
fi
|
|
|
|
# Verify metadata was included
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
local hash=$(curl -s "http://${host}/hash/meta:single" | jq -r '.hash')
|
|
local meta=$(curl -s "http://${host}/meta/${hash}" | jq -r '.metadata.customField')
|
|
|
|
if [[ "$meta" == "testValue" ]]; then
|
|
log_info "Single metadata field working"
|
|
return 0
|
|
else
|
|
log_error "Metadata not found or incorrect (got: $meta)"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_multiple() {
|
|
log_info "Testing multiple metadata fields..."
|
|
|
|
echo "metadata test multiple" > "${TEST_DIR}/test_files/meta_multiple.txt"
|
|
local output=$(run_sos_upload "${TEST_DIR}/test_files/meta_multiple.txt" "meta:multiple" \
|
|
--metadata "field1=value1" \
|
|
--metadata "field2=value2" \
|
|
--metadata "unpackedhash=abc123def456" 2>&1)
|
|
|
|
# Check if upload succeeded
|
|
if ! echo "$output" | grep -q "Download URL:"; then
|
|
log_error "Upload with multiple metadata failed"
|
|
echo "$output" | head -5
|
|
return 1
|
|
fi
|
|
|
|
# Verify all metadata fields were included
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
local hash=$(curl -s "http://${host}/hash/meta:multiple" | jq -r '.hash')
|
|
local metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
|
|
local field1=$(echo "$metadata" | jq -r '.metadata.field1')
|
|
local field2=$(echo "$metadata" | jq -r '.metadata.field2')
|
|
local xxhash=$(echo "$metadata" | jq -r '.metadata.unpackedhash')
|
|
|
|
if [[ "$field1" == "value1" && "$field2" == "value2" && "$xxhash" == "abc123def456" ]]; then
|
|
log_info "Multiple metadata fields working"
|
|
return 0
|
|
else
|
|
log_error "Metadata fields incorrect"
|
|
echo "field1=$field1, field2=$field2, unpackedhash=$xxhash"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_special_chars() {
|
|
log_info "Testing metadata with special characters..."
|
|
|
|
echo "metadata test special" > "${TEST_DIR}/test_files/meta_special.txt"
|
|
local output=$(run_sos_upload "${TEST_DIR}/test_files/meta_special.txt" "meta:special" \
|
|
--metadata "description=This is a test with spaces" \
|
|
--metadata "formula=a=b+c" \
|
|
--metadata "path=/usr/local/bin" \
|
|
--metadata "quote=He said \"hello\"" 2>&1)
|
|
|
|
# Check if upload succeeded
|
|
if ! echo "$output" | grep -q "Download URL:"; then
|
|
log_error "Upload with special chars metadata failed"
|
|
echo "$output" | head -5
|
|
return 1
|
|
fi
|
|
|
|
# Verify metadata with special characters
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
local hash=$(curl -s "http://${host}/hash/meta:special" | jq -r '.hash')
|
|
local metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
|
|
local desc=$(echo "$metadata" | jq -r '.metadata.description')
|
|
local formula=$(echo "$metadata" | jq -r '.metadata.formula')
|
|
local path=$(echo "$metadata" | jq -r '.metadata.path')
|
|
local quote=$(echo "$metadata" | jq -r '.metadata.quote')
|
|
|
|
if [[ "$desc" == "This is a test with spaces" && \
|
|
"$formula" == "a=b+c" && \
|
|
"$path" == "/usr/local/bin" && \
|
|
"$quote" == 'He said "hello"' ]]; then
|
|
log_info "Metadata with special characters working"
|
|
return 0
|
|
else
|
|
log_error "Special character metadata incorrect"
|
|
echo "description='$desc'"
|
|
echo "formula='$formula'"
|
|
echo "path='$path'"
|
|
echo "quote='$quote'"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_mixed_labels() {
|
|
log_info "Testing metadata mixed with multiple labels..."
|
|
|
|
echo "metadata test mixed" > "${TEST_DIR}/test_files/meta_mixed.txt"
|
|
local output=$(run_sos_upload "${TEST_DIR}/test_files/meta_mixed.txt" \
|
|
"meta:mixed1" \
|
|
--metadata "author=test-user" \
|
|
"meta:mixed2" \
|
|
--metadata "version=1.2.3" \
|
|
"meta:mixed3" 2>&1)
|
|
|
|
# Check if upload succeeded
|
|
if ! echo "$output" | grep -q "Download URL:"; then
|
|
log_error "Upload with mixed labels and metadata failed"
|
|
echo "$output" | head -5
|
|
return 1
|
|
fi
|
|
|
|
# Verify metadata and labels
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
local hash=$(curl -s "http://${host}/hash/meta:mixed1" | jq -r '.hash')
|
|
local metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
|
|
local author=$(echo "$metadata" | jq -r '.metadata.author')
|
|
local version=$(echo "$metadata" | jq -r '.metadata.version')
|
|
local labels=$(echo "$metadata" | jq -r '.metadata.labeltags | length')
|
|
|
|
# Check all three labels exist
|
|
local has_mixed1=$(echo "$metadata" | jq -r '.metadata.labeltags | map(select(. == "meta:mixed1")) | length')
|
|
local has_mixed2=$(echo "$metadata" | jq -r '.metadata.labeltags | map(select(. == "meta:mixed2")) | length')
|
|
local has_mixed3=$(echo "$metadata" | jq -r '.metadata.labeltags | map(select(. == "meta:mixed3")) | length')
|
|
|
|
if [[ "$author" == "test-user" && "$version" == "1.2.3" && \
|
|
"$has_mixed1" == "1" && "$has_mixed2" == "1" && "$has_mixed3" == "1" ]]; then
|
|
log_info "Mixed labels and metadata working"
|
|
return 0
|
|
else
|
|
log_error "Mixed metadata/labels incorrect"
|
|
echo "author=$author, version=$version"
|
|
echo "Labels: mixed1=$has_mixed1, mixed2=$has_mixed2, mixed3=$has_mixed3"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_empty_value() {
|
|
log_info "Testing metadata with empty value..."
|
|
|
|
echo "metadata test empty" > "${TEST_DIR}/test_files/meta_empty.txt"
|
|
local output=$(run_sos_upload "${TEST_DIR}/test_files/meta_empty.txt" "meta:empty" \
|
|
--metadata "emptyField=" \
|
|
--metadata "normalField=hasValue" 2>&1)
|
|
|
|
# Check if upload succeeded
|
|
if ! echo "$output" | grep -q "Download URL:"; then
|
|
log_error "Upload with empty metadata value failed"
|
|
echo "$output" | head -5
|
|
return 1
|
|
fi
|
|
|
|
# Verify metadata
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
local hash=$(curl -s "http://${host}/hash/meta:empty" | jq -r '.hash')
|
|
local metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
|
|
local empty=$(echo "$metadata" | jq -r '.metadata.emptyField')
|
|
local normal=$(echo "$metadata" | jq -r '.metadata.normalField')
|
|
|
|
if [[ "$empty" == "" && "$normal" == "hasValue" ]]; then
|
|
log_info "Empty metadata value handled correctly"
|
|
return 0
|
|
else
|
|
log_error "Empty metadata value not handled correctly"
|
|
echo "emptyField='$empty', normalField='$normal'"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_invalid_format() {
|
|
log_info "Testing invalid metadata format rejection..."
|
|
|
|
echo "metadata test invalid" > "${TEST_DIR}/test_files/meta_invalid.txt"
|
|
|
|
# Test invalid format (no equals sign) - directly call sos to capture exit code
|
|
export SOS_WRITE_TOKEN="${TEST_TOKEN}"
|
|
export SOS_TEST_MODE=1
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
|
|
set +e
|
|
"${SCRIPT_DIR}/sos" upload "$host" "${TEST_DIR}/test_files/meta_invalid.txt" "meta:invalid" --metadata "noequals" >"${TEST_DIR}/invalid_meta_output.txt" 2>&1
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
local output=$(cat "${TEST_DIR}/invalid_meta_output.txt")
|
|
|
|
if [[ $exit_code -ne 0 ]] && echo "$output" | grep -q "Metadata must be in \"key=value\" format"; then
|
|
log_info "Invalid metadata format correctly rejected (exit code: $exit_code)"
|
|
return 0
|
|
else
|
|
log_error "Invalid metadata format not rejected properly"
|
|
echo "Exit code: $exit_code"
|
|
echo "$output" | head -5
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_no_labels() {
|
|
log_info "Testing metadata without any labels (should fail)..."
|
|
|
|
echo "metadata no labels" > "${TEST_DIR}/test_files/meta_no_labels.txt"
|
|
|
|
# Test with only metadata, no labels - should fail
|
|
export SOS_WRITE_TOKEN="${TEST_TOKEN}"
|
|
export SOS_TEST_MODE=1
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
|
|
set +e
|
|
"${SCRIPT_DIR}/sos" upload "$host" "${TEST_DIR}/test_files/meta_no_labels.txt" \
|
|
--metadata "field1=value1" \
|
|
--metadata "field2=value2" >"${TEST_DIR}/no_labels_output.txt" 2>&1
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
local output=$(cat "${TEST_DIR}/no_labels_output.txt")
|
|
|
|
if [[ $exit_code -ne 0 ]] && echo "$output" | grep -q "At least one label:tag is required"; then
|
|
log_info "Metadata without labels correctly rejected (exit code: $exit_code)"
|
|
return 0
|
|
else
|
|
log_error "Metadata without labels not rejected properly"
|
|
echo "Exit code: $exit_code"
|
|
echo "$output" | head -5
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_realistic_usage() {
|
|
log_info "Testing realistic usage with variables (like templates.dropshell.app example)..."
|
|
|
|
# Simulate real-world usage with variables
|
|
local SERVER="templates.dropshell.app" # Will actually use test server
|
|
local TARBALL="${TEST_DIR}/test_files/template.tar.gz"
|
|
local TEMPLATE="caddy"
|
|
local XXHASH="d4a7f3b2e1c9f8a5"
|
|
|
|
# Create a test tarball
|
|
echo "test template content" > "${TEST_DIR}/test_files/template_content.txt"
|
|
tar -czf "$TARBALL" -C "${TEST_DIR}/test_files" template_content.txt 2>/dev/null
|
|
|
|
# Test the exact pattern from the user's example
|
|
export SOS_WRITE_TOKEN="${TEST_TOKEN}"
|
|
export SOS_TEST_MODE=1
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
|
|
# Run the command with the same structure as the user's example
|
|
set +e
|
|
local output=$("${SCRIPT_DIR}/sos" upload "$host" "$TARBALL" "${TEMPLATE}:latest" --metadata "unpackedhash=$XXHASH" 2>&1)
|
|
local exit_code=$?
|
|
set -e
|
|
|
|
if [[ $exit_code -eq 0 ]] && echo "$output" | grep -q "Download URL:"; then
|
|
# Verify the upload worked correctly
|
|
local hash=$(curl -s "http://${host}/hash/${TEMPLATE}:latest" | jq -r '.hash')
|
|
local metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
|
|
local xxhash_value=$(echo "$metadata" | jq -r '.metadata.unpackedhash')
|
|
local has_label=$(echo "$metadata" | jq -r '.metadata.labeltags | map(select(. == "caddy:latest")) | length')
|
|
|
|
if [[ "$xxhash_value" == "$XXHASH" && "$has_label" == "1" ]]; then
|
|
log_info "Realistic usage pattern working correctly"
|
|
return 0
|
|
else
|
|
log_error "Realistic usage pattern metadata incorrect"
|
|
echo "Expected unpackedhash=$XXHASH, got=$xxhash_value"
|
|
echo "Expected label caddy:latest, has_label=$has_label"
|
|
return 1
|
|
fi
|
|
else
|
|
log_error "Realistic usage pattern upload failed"
|
|
echo "Exit code: $exit_code"
|
|
echo "$output" | head -5
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_realistic_multi() {
|
|
log_info "Testing realistic usage with multiple metadata fields..."
|
|
|
|
# More complex real-world scenario with multiple metadata fields
|
|
local TARBALL="${TEST_DIR}/test_files/app.tar.gz"
|
|
local APP_NAME="nginx"
|
|
local VERSION="1.25.3"
|
|
local BUILD_ID="build-$(date +%s)"
|
|
local COMMIT_SHA="abc123def456"
|
|
local AUTHOR="deploy-bot"
|
|
|
|
# Create test tarball
|
|
echo "app content v${VERSION}" > "${TEST_DIR}/test_files/app_content.txt"
|
|
tar -czf "$TARBALL" -C "${TEST_DIR}/test_files" app_content.txt 2>/dev/null
|
|
|
|
# Test with multiple metadata fields in realistic order
|
|
local output=$(run_sos_upload "$TARBALL" \
|
|
"${APP_NAME}:${VERSION}" \
|
|
"${APP_NAME}:latest" \
|
|
--metadata "version=$VERSION" \
|
|
--metadata "buildId=$BUILD_ID" \
|
|
--metadata "commitSha=$COMMIT_SHA" \
|
|
--metadata "deployedBy=$AUTHOR" \
|
|
--metadata "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" 2>&1)
|
|
|
|
if echo "$output" | grep -q "Download URL:"; then
|
|
# Verify all metadata was stored correctly
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
local hash=$(curl -s "http://${host}/hash/${APP_NAME}:${VERSION}" | jq -r '.hash')
|
|
local metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
|
|
local stored_version=$(echo "$metadata" | jq -r '.metadata.version')
|
|
local stored_build=$(echo "$metadata" | jq -r '.metadata.buildId')
|
|
local stored_commit=$(echo "$metadata" | jq -r '.metadata.commitSha')
|
|
local stored_author=$(echo "$metadata" | jq -r '.metadata.deployedBy')
|
|
local has_version_label=$(echo "$metadata" | jq -r ".metadata.labeltags | map(select(. == \"${APP_NAME}:${VERSION}\")) | length")
|
|
local has_latest_label=$(echo "$metadata" | jq -r ".metadata.labeltags | map(select(. == \"${APP_NAME}:latest\")) | length")
|
|
|
|
if [[ "$stored_version" == "$VERSION" && \
|
|
"$stored_build" == "$BUILD_ID" && \
|
|
"$stored_commit" == "$COMMIT_SHA" && \
|
|
"$stored_author" == "$AUTHOR" && \
|
|
"$has_version_label" == "1" && \
|
|
"$has_latest_label" == "1" ]]; then
|
|
log_info "Realistic multi-metadata usage working correctly"
|
|
return 0
|
|
else
|
|
log_error "Realistic multi-metadata usage incorrect"
|
|
echo "version: expected=$VERSION, got=$stored_version"
|
|
echo "buildId: expected=$BUILD_ID, got=$stored_build"
|
|
echo "commitSha: expected=$COMMIT_SHA, got=$stored_commit"
|
|
echo "deployedBy: expected=$AUTHOR, got=$stored_author"
|
|
return 1
|
|
fi
|
|
else
|
|
log_error "Realistic multi-metadata upload failed"
|
|
echo "$output" | head -5
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
test_metadata_ordering_comprehensive() {
|
|
log_info "Testing comprehensive metadata and label ordering combinations..."
|
|
|
|
local all_passed=true
|
|
local host="${SOS_TEST_HOST:-localhost:${TEST_PORT}}"
|
|
|
|
# Test 1: Metadata before all labels
|
|
echo "order test 1" > "${TEST_DIR}/test_files/order1.txt"
|
|
local output=$(run_sos_upload "${TEST_DIR}/test_files/order1.txt" \
|
|
--metadata "position=before" \
|
|
--metadata "test=1" \
|
|
"order:test1a" \
|
|
"order:test1b" 2>&1)
|
|
|
|
if echo "$output" | grep -q "Download URL:"; then
|
|
local hash=$(curl -s "http://${host}/hash/order:test1a" | jq -r '.hash')
|
|
local metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
local position=$(echo "$metadata" | jq -r '.metadata.position')
|
|
local test=$(echo "$metadata" | jq -r '.metadata.test')
|
|
local labels=$(echo "$metadata" | jq -r '.metadata.labeltags | length')
|
|
|
|
if [[ "$position" == "before" && "$test" == "1" && "$labels" == "2" ]]; then
|
|
log_info " ✓ Metadata before all labels: PASSED"
|
|
else
|
|
log_error " ✗ Metadata before all labels: FAILED"
|
|
echo " Got: position=$position, test=$test, labels=$labels"
|
|
all_passed=false
|
|
fi
|
|
else
|
|
log_error " ✗ Metadata before all labels: Upload failed"
|
|
all_passed=false
|
|
fi
|
|
|
|
# Test 2: Metadata after all labels
|
|
echo "order test 2" > "${TEST_DIR}/test_files/order2.txt"
|
|
output=$(run_sos_upload "${TEST_DIR}/test_files/order2.txt" \
|
|
"order:test2a" \
|
|
"order:test2b" \
|
|
--metadata "position=after" \
|
|
--metadata "test=2" 2>&1)
|
|
|
|
if echo "$output" | grep -q "Download URL:"; then
|
|
hash=$(curl -s "http://${host}/hash/order:test2a" | jq -r '.hash')
|
|
metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
position=$(echo "$metadata" | jq -r '.metadata.position')
|
|
test=$(echo "$metadata" | jq -r '.metadata.test')
|
|
labels=$(echo "$metadata" | jq -r '.metadata.labeltags | length')
|
|
|
|
if [[ "$position" == "after" && "$test" == "2" && "$labels" == "2" ]]; then
|
|
log_info " ✓ Metadata after all labels: PASSED"
|
|
else
|
|
log_error " ✗ Metadata after all labels: FAILED"
|
|
echo " Got: position=$position, test=$test, labels=$labels"
|
|
all_passed=false
|
|
fi
|
|
else
|
|
log_error " ✗ Metadata after all labels: Upload failed"
|
|
all_passed=false
|
|
fi
|
|
|
|
# Test 3: Alternating pattern - label, metadata, label, metadata
|
|
echo "order test 3" > "${TEST_DIR}/test_files/order3.txt"
|
|
output=$(run_sos_upload "${TEST_DIR}/test_files/order3.txt" \
|
|
"order:test3a" \
|
|
--metadata "first=1" \
|
|
"order:test3b" \
|
|
--metadata "second=2" 2>&1)
|
|
|
|
if echo "$output" | grep -q "Download URL:"; then
|
|
hash=$(curl -s "http://${host}/hash/order:test3a" | jq -r '.hash')
|
|
metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
first=$(echo "$metadata" | jq -r '.metadata.first')
|
|
second=$(echo "$metadata" | jq -r '.metadata.second')
|
|
labels=$(echo "$metadata" | jq -r '.metadata.labeltags | length')
|
|
|
|
if [[ "$first" == "1" && "$second" == "2" && "$labels" == "2" ]]; then
|
|
log_info " ✓ Alternating label-metadata pattern: PASSED"
|
|
else
|
|
log_error " ✗ Alternating label-metadata pattern: FAILED"
|
|
echo " Got: first=$first, second=$second, labels=$labels"
|
|
all_passed=false
|
|
fi
|
|
else
|
|
log_error " ✗ Alternating label-metadata pattern: Upload failed"
|
|
all_passed=false
|
|
fi
|
|
|
|
# Test 4: Alternating pattern - metadata, label, metadata, label
|
|
echo "order test 4" > "${TEST_DIR}/test_files/order4.txt"
|
|
output=$(run_sos_upload "${TEST_DIR}/test_files/order4.txt" \
|
|
--metadata "alpha=a" \
|
|
"order:test4a" \
|
|
--metadata "beta=b" \
|
|
"order:test4b" 2>&1)
|
|
|
|
if echo "$output" | grep -q "Download URL:"; then
|
|
hash=$(curl -s "http://${host}/hash/order:test4a" | jq -r '.hash')
|
|
metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
alpha=$(echo "$metadata" | jq -r '.metadata.alpha')
|
|
beta=$(echo "$metadata" | jq -r '.metadata.beta')
|
|
labels=$(echo "$metadata" | jq -r '.metadata.labeltags | length')
|
|
|
|
if [[ "$alpha" == "a" && "$beta" == "b" && "$labels" == "2" ]]; then
|
|
log_info " ✓ Alternating metadata-label pattern: PASSED"
|
|
else
|
|
log_error " ✗ Alternating metadata-label pattern: FAILED"
|
|
echo " Got: alpha=$alpha, beta=$beta, labels=$labels"
|
|
all_passed=false
|
|
fi
|
|
else
|
|
log_error " ✗ Alternating metadata-label pattern: Upload failed"
|
|
all_passed=false
|
|
fi
|
|
|
|
# Test 5: Complex mixed pattern
|
|
echo "order test 5" > "${TEST_DIR}/test_files/order5.txt"
|
|
output=$(run_sos_upload "${TEST_DIR}/test_files/order5.txt" \
|
|
--metadata "start=yes" \
|
|
"order:test5a" \
|
|
"order:test5b" \
|
|
--metadata "middle=true" \
|
|
"order:test5c" \
|
|
--metadata "end=done" \
|
|
--metadata "extra=value" 2>&1)
|
|
|
|
if echo "$output" | grep -q "Download URL:"; then
|
|
hash=$(curl -s "http://${host}/hash/order:test5a" | jq -r '.hash')
|
|
metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
start=$(echo "$metadata" | jq -r '.metadata.start')
|
|
middle=$(echo "$metadata" | jq -r '.metadata.middle')
|
|
end=$(echo "$metadata" | jq -r '.metadata.end')
|
|
extra=$(echo "$metadata" | jq -r '.metadata.extra')
|
|
labels=$(echo "$metadata" | jq -r '.metadata.labeltags | length')
|
|
|
|
if [[ "$start" == "yes" && "$middle" == "true" && "$end" == "done" && \
|
|
"$extra" == "value" && "$labels" == "3" ]]; then
|
|
log_info " ✓ Complex mixed pattern: PASSED"
|
|
else
|
|
log_error " ✗ Complex mixed pattern: FAILED"
|
|
echo " Got: start=$start, middle=$middle, end=$end, extra=$extra, labels=$labels"
|
|
all_passed=false
|
|
fi
|
|
else
|
|
log_error " ✗ Complex mixed pattern: Upload failed"
|
|
all_passed=false
|
|
fi
|
|
|
|
# Test 6: Multiple consecutive metadata flags at different positions
|
|
echo "order test 6" > "${TEST_DIR}/test_files/order6.txt"
|
|
output=$(run_sos_upload "${TEST_DIR}/test_files/order6.txt" \
|
|
--metadata "group1a=1" \
|
|
--metadata "group1b=2" \
|
|
"order:test6a" \
|
|
--metadata "group2a=3" \
|
|
--metadata "group2b=4" \
|
|
"order:test6b" \
|
|
--metadata "group3a=5" \
|
|
--metadata "group3b=6" 2>&1)
|
|
|
|
if echo "$output" | grep -q "Download URL:"; then
|
|
hash=$(curl -s "http://${host}/hash/order:test6a" | jq -r '.hash')
|
|
metadata=$(curl -s "http://${host}/meta/${hash}")
|
|
g1a=$(echo "$metadata" | jq -r '.metadata.group1a')
|
|
g1b=$(echo "$metadata" | jq -r '.metadata.group1b')
|
|
g2a=$(echo "$metadata" | jq -r '.metadata.group2a')
|
|
g2b=$(echo "$metadata" | jq -r '.metadata.group2b')
|
|
g3a=$(echo "$metadata" | jq -r '.metadata.group3a')
|
|
g3b=$(echo "$metadata" | jq -r '.metadata.group3b')
|
|
labels=$(echo "$metadata" | jq -r '.metadata.labeltags | length')
|
|
|
|
if [[ "$g1a" == "1" && "$g1b" == "2" && "$g2a" == "3" && \
|
|
"$g2b" == "4" && "$g3a" == "5" && "$g3b" == "6" && "$labels" == "2" ]]; then
|
|
log_info " ✓ Multiple consecutive metadata groups: PASSED"
|
|
else
|
|
log_error " ✗ Multiple consecutive metadata groups: FAILED"
|
|
echo " Got: g1a=$g1a, g1b=$g1b, g2a=$g2a, g2b=$g2b, g3a=$g3a, g3b=$g3b, labels=$labels"
|
|
all_passed=false
|
|
fi
|
|
else
|
|
log_error " ✗ Multiple consecutive metadata groups: Upload failed"
|
|
all_passed=false
|
|
fi
|
|
|
|
if [[ "$all_passed" == true ]]; then
|
|
log_info "All ordering tests passed!"
|
|
return 0
|
|
else
|
|
log_error "Some ordering tests failed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Run all tests
|
|
run_tests() {
|
|
local tests=(
|
|
"test_upload ${TEST_DIR}/test_files/test1.txt test:file1"
|
|
"test_upload ${TEST_DIR}/test_files/test2.txt test:file2 version:1.0 env:test"
|
|
"test_upload ${TEST_DIR}/test_files/binary_test.bin binary:test"
|
|
"test_upload ${TEST_DIR}/test_files/sos_binary sos:test sos:latest"
|
|
"test_retrieval test:file1"
|
|
"test_deduplication"
|
|
"test_metadata_update"
|
|
"test_api_endpoints"
|
|
"test_invalid_auth"
|
|
"test_metadata_single"
|
|
"test_metadata_multiple"
|
|
"test_metadata_special_chars"
|
|
"test_metadata_mixed_labels"
|
|
"test_metadata_empty_value"
|
|
"test_metadata_invalid_format"
|
|
"test_metadata_no_labels"
|
|
"test_metadata_realistic_usage"
|
|
"test_metadata_realistic_multi"
|
|
"test_metadata_ordering_comprehensive"
|
|
)
|
|
|
|
local total=${#tests[@]}
|
|
local passed=0
|
|
|
|
log_info "Running ${total} tests..."
|
|
echo ""
|
|
|
|
set +e # Don't exit on test failures
|
|
for test in "${tests[@]}"; do
|
|
eval "$test" && ((passed++)) || true
|
|
echo ""
|
|
# Small delay between tests for ARM64 stability
|
|
sleep 0.5
|
|
done
|
|
set -e
|
|
|
|
# Summary
|
|
echo "=================================="
|
|
if [[ $passed -eq $total ]]; then
|
|
echo -e "${GREEN}All tests passed!${NC} (${passed}/${total})"
|
|
return 0
|
|
else
|
|
echo -e "${RED}Some tests failed!${NC} (${passed}/${total})"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Main execution
|
|
main() {
|
|
echo "SOS Test Suite"
|
|
echo "=============="
|
|
echo ""
|
|
|
|
check_dependencies
|
|
setup_test_environment
|
|
start_test_server
|
|
|
|
echo ""
|
|
local result=0
|
|
run_tests || result=$?
|
|
|
|
echo ""
|
|
log_info "Test suite completed"
|
|
|
|
[[ $result -ne 0 ]] && log_warning "Server logs:" && docker logs "${CONTAINER_NAME}" 2>&1 | tail -10
|
|
|
|
exit $result
|
|
}
|
|
|
|
main "$@" |