Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:05:59 +08:00
commit 37120368e6
13 changed files with 5177 additions and 0 deletions

View File

@@ -0,0 +1,855 @@
#!/bin/bash
# test_email_workflow.sh - Automated email testing workflow
# Usage: ./test_email_workflow.sh [OPTIONS]
set -e
# Default values
MAILHOG_URL="http://localhost:8025"
SMTP_HOST="localhost"
SMTP_PORT="1025"
CONFIG_FILE=""
TEST_SCENARIO="basic"
CLEANUP=true
VERBOSE=false
TIMEOUT=30
OUTPUT_FORMAT="text"
REPORT_FILE="email_test_report.txt"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Global variables
TOTAL_TESTS=0
PASSED_TESTS=0
FAILED_TESTS=0
TEST_RESULTS=()
# Print colored output
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
[[ "$VERBOSE" == true ]] && echo "[$(date '+%Y-%m-%d %H:%M:%S')] INFO: $1" >> "$REPORT_FILE"
}
print_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
[[ "$VERBOSE" == true ]] && echo "[$(date '+%Y-%m-%d %H:%M:%S')] SUCCESS: $1" >> "$REPORT_FILE"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
[[ "$VERBOSE" == true ]] && echo "[$(date '+%Y-%m-%d %H:%M:%S')] WARNING: $1" >> "$REPORT_FILE"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
[[ "$VERBOSE" == true ]] && echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >> "$REPORT_FILE"
}
print_test() {
echo -e "${CYAN}[TEST]${NC} $1"
[[ "$VERBOSE" == true ]] && echo "[$(date '+%Y-%m-%d %H:%M:%S')] TEST: $1" >> "$REPORT_FILE"
}
# Show usage information
show_usage() {
cat << EOF
Automated Email Testing Workflow
Usage: $0 [OPTIONS]
OPTIONS:
-h, --help Show this help message
-c, --config FILE Load test configuration from file
-s, --scenario SCENARIO Test scenario to run (default: basic)
--url URL MailHog API URL (default: http://localhost:8025)
--smtp-host HOST SMTP server host (default: localhost)
--smtp-port PORT SMTP server port (default: 1025)
-t, --timeout SECONDS Timeout for each test (default: 30)
-v, --verbose Enable verbose output
--no-cleanup Don't clean up test emails after testing
--output FORMAT Report format: text, json, csv (default: text)
--report-file FILE Save report to file (default: email_test_report.txt)
TEST SCENARIOS:
basic - Basic email sending and receiving tests
html - HTML email formatting tests
attachments - Email attachment tests
bulk - Bulk email sending tests
performance - Performance and load tests
integration - Integration testing with different configurations
CONFIGURATION FILE FORMAT (JSON):
{
"tests": [
{
"name": "Test Name",
"type": "send|receive|search",
"to": "recipient@test.local",
"from": "sender@test.local",
"subject": "Test Subject",
"body": "Test body",
"html": false,
"expected_count": 1,
"timeout": 30
}
]
}
EXAMPLES:
# Run basic email tests
$0 --scenario basic
# Run HTML email tests with verbose output
$0 --scenario html --verbose
# Run tests with custom configuration
$0 --config test_config.json --timeout 60
# Run performance tests and save JSON report
$0 --scenario performance --output json --report-file perf_results.json
EOF
}
# Initialize test environment
init_test_environment() {
print_info "Initializing email test environment..."
# Initialize report file
if [[ "$VERBOSE" == true ]]; then
echo "Email Test Report - $(date)" > "$REPORT_FILE"
echo "==============================" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
fi
# Check if MailHog is accessible
check_mailhog_connection
# Clear existing messages if cleanup is enabled
if [[ "$CLEANUP" == true ]]; then
clear_mailhog_messages
fi
# Check SMTP connection
check_smtp_connection
print_success "Test environment initialized successfully"
}
# Check MailHog connection
check_mailhog_connection() {
print_test "Checking MailHog API connection..."
local response=$(curl -s --max-time 5 "$MAILHOG_URL/api/v1/status" 2>/dev/null)
if [[ $? -eq 0 ]] && echo "$response" | grep -q "messages"; then
print_success "MailHog API is accessible"
return 0
else
print_error "Cannot connect to MailHog API at $MAILHOG_URL"
return 1
fi
}
# Check SMTP connection
check_smtp_connection() {
print_test "Checking SMTP connection to $SMTP_HOST:$SMTP_PORT..."
if command -v nc >/dev/null 2>&1; then
if echo "" | nc -w 5 "$SMTP_HOST" "$SMTP_PORT" >/dev/null 2>&1; then
print_success "SMTP connection successful"
return 0
fi
elif command -v telnet >/dev/null 2>&1; then
timeout 5 telnet "$SMTP_HOST" "$SMTP_PORT" </dev/null >/dev/null 2>&1
if [[ $? -eq 0 || $? -eq 124 ]]; then
print_success "SMTP connection successful"
return 0
fi
fi
print_error "Failed to connect to SMTP server"
return 1
}
# Clear all MailHog messages
clear_mailhog_messages() {
print_test "Clearing existing MailHog messages..."
local response=$(curl -s -X DELETE "$MAILHOG_URL/api/v1/messages" 2>/dev/null)
if [[ $? -eq 0 ]]; then
print_success "MailHog messages cleared"
else
print_warning "Failed to clear MailHog messages"
fi
}
# Send test email
send_test_email() {
local to="$1"
local from="$2"
local subject="$3"
local body="$4"
local html="${5:-false}"
print_test "Sending test email to $to..."
# Create email content
local email_content="From: $from
To: $to
Subject: $subject
Date: $(date -R)
MIME-Version: 1.0"
if [[ "$html" == true ]]; then
email_content="$email_content
Content-Type: text/html; charset=UTF-8
$body"
else
email_content="$email_content
Content-Type: text/plain; charset=UTF-8
$body"
fi
# Send email via SMTP
(
echo "EHLO mailhog-test.local"
echo "MAIL FROM:<$from>"
echo "RCPT TO:<$to>"
echo "DATA"
echo "$email_content"
echo "."
echo "QUIT"
) | nc "$SMTP_HOST" "$SMTP_PORT" 2>/dev/null
return $?
}
# Wait for email to be received
wait_for_email() {
local to="$1"
local subject="$2"
local timeout="$3"
local start_time=$(date +%s)
local end_time=$((start_time + timeout))
print_test "Waiting for email to be received (timeout: ${timeout}s)..."
while [[ $(date +%s) -lt $end_time ]]; do
local response=$(curl -s "$MAILHOG_URL/api/v1/messages" 2>/dev/null)
if [[ $? -eq 0 ]]; then
local message_count=$(echo "$response" | jq -r '.total // 0' 2>/dev/null)
if [[ "$message_count" -gt 0 ]]; then
# Search for specific email
local search_response=$(curl -s -X POST "$MAILHOG_URL/api/v1/search" \
-H "Content-Type: application/json" \
-d "{\"query\":\"to:$to subject:$subject\"}" 2>/dev/null)
if [[ $? -eq 0 ]]; then
local found_count=$(echo "$search_response" | jq -r '.total // 0' 2>/dev/null)
if [[ "$found_count" -gt 0 ]]; then
print_success "Email received and found"
return 0
fi
fi
# If search fails, check if any new email was received
if [[ "$message_count" -gt 0 ]]; then
print_success "Email received (search failed, but count > 0)"
return 0
fi
fi
fi
sleep 1
done
print_error "Email not received within timeout period"
return 1
}
# Run a single test
run_test() {
local test_name="$1"
local test_type="$2"
shift 2
local test_params=("$@")
print_test "Running test: $test_name"
((TOTAL_TESTS++))
case "$test_type" in
"send")
run_send_test "$test_name" "${test_params[@]}"
;;
"receive")
run_receive_test "$test_name" "${test_params[@]}"
;;
"search")
run_search_test "$test_name" "${test_params[@]}"
;;
"bulk")
run_bulk_test "$test_name" "${test_params[@]}"
;;
*)
print_error "Unknown test type: $test_type"
record_test_result "$test_name" "FAILED" "Unknown test type"
;;
esac
}
# Run send email test
run_send_test() {
local test_name="$1"
local to="$2"
local from="$3"
local subject="$4"
local body="$5"
local html="${6:-false}"
local timeout="${7:-30}"
print_test "Sending email test: $test_name"
# Send email
if send_test_email "$to" "$from" "$subject" "$body" "$html"; then
# Wait for email to be received
if wait_for_email "$to" "$subject" "$timeout"; then
print_success "Test passed: $test_name"
record_test_result "$test_name" "PASSED" "Email sent and received successfully"
((PASSED_TESTS++))
else
print_error "Test failed: $test_name - Email not received"
record_test_result "$test_name" "FAILED" "Email sent but not received"
((FAILED_TESTS++))
fi
else
print_error "Test failed: $test_name - Email sending failed"
record_test_result "$test_name" "FAILED" "Email sending failed"
((FAILED_TESTS++))
fi
}
# Run receive test
run_receive_test() {
local test_name="$1"
local timeout="$2"
print_test "Receive test: $test_name"
# Send a test email first
local test_email="test-$(date +%s)@receiver.local"
if send_test_email "$test_email" "sender@test.local" "Receive Test" "Test email for receive test"; then
if wait_for_email "$test_email" "Receive Test" "$timeout"; then
print_success "Test passed: $test_name"
record_test_result "$test_name" "PASSED" "Email received successfully"
((PASSED_TESTS++))
else
print_error "Test failed: $test_name - Email not received"
record_test_result "$test_name" "FAILED" "Email not received within timeout"
((FAILED_TESTS++))
fi
else
print_error "Test failed: $test_name - Test email sending failed"
record_test_result "$test_name" "FAILED" "Test email sending failed"
((FAILED_TESTS++))
fi
}
# Run search test
run_search_test() {
local test_name="$1"
local query="$2"
local expected_count="$3"
print_test "Search test: $test_name"
# Send a test email first
local test_email="search-test-$(date +%s)@search.local"
if send_test_email "$test_email" "sender@test.local" "Search Test" "Test email for search test"; then
sleep 2 # Wait for email to be indexed
# Search for the email
local search_response=$(curl -s -X POST "$MAILHOG_URL/api/v1/search" \
-H "Content-Type: application/json" \
-d "{\"query\":\"$query\"}" 2>/dev/null)
if [[ $? -eq 0 ]]; then
local found_count=$(echo "$search_response" | jq -r '.total // 0' 2>/dev/null)
if [[ "$found_count" -eq "$expected_count" ]]; then
print_success "Test passed: $test_name - Found $found_count emails"
record_test_result "$test_name" "PASSED" "Found expected $expected_count emails"
((PASSED_TESTS++))
else
print_error "Test failed: $test_name - Expected $expected_count, found $found_count"
record_test_result "$test_name" "FAILED" "Expected $expected_count emails, found $found_count"
((FAILED_TESTS++))
fi
else
print_error "Test failed: $test_name - Search request failed"
record_test_result "$test_name" "FAILED" "Search request failed"
((FAILED_TESTS++))
fi
else
print_error "Test failed: $test_name - Test email sending failed"
record_test_result "$test_name" "FAILED" "Test email sending failed"
((FAILED_TESTS++))
fi
}
# Run bulk test
run_bulk_test() {
local test_name="$1"
local email_count="$2"
local timeout="$3"
print_test "Bulk test: $test_name - Sending $email_count emails"
local sent_count=0
local start_time=$(date +%s)
for ((i=1; i<=email_count; i++)); do
local test_email="bulk-$i-$(date +%s)@bulk.local"
if send_test_email "$test_email" "bulk@sender.local" "Bulk Test #$i" "Bulk test email #$i"; then
((sent_count++))
fi
# Small delay to avoid overwhelming
sleep 0.1
done
local send_time=$(($(date +%s) - start_time))
# Wait for all emails to be received
sleep "$timeout"
# Check total message count
local response=$(curl -s "$MAILHOG_URL/api/v1/messages" 2>/dev/null)
local received_count=0
if [[ $? -eq 0 ]]; then
received_count=$(echo "$response" | jq -r '.total // 0' 2>/dev/null)
fi
if [[ "$sent_count" -eq "$email_count" && "$received_count" -ge "$email_count" ]]; then
print_success "Test passed: $test_name - Sent $sent_count/$email_count, Received $received_count"
record_test_result "$test_name" "PASSED" "Bulk test: Sent $sent_count/$email_count emails in ${send_time}s"
((PASSED_TESTS++))
else
print_error "Test failed: $test_name - Sent $sent_count/$email_count, Received $received_count"
record_test_result "$test_name" "FAILED" "Bulk test: Expected $email_count, sent $sent_count, received $received_count"
((FAILED_TESTS++))
fi
}
# Record test result
record_test_result() {
local test_name="$1"
local result="$2"
local details="$3"
TEST_RESULTS+=("{\"name\":\"$test_name\",\"result\":\"$result\",\"details\":\"$details\"}")
}
# Run predefined test scenarios
run_basic_scenario() {
print_info "Running basic email tests..."
# Test 1: Simple text email
run_test "Simple Text Email" "send" \
"basic-test@test.local" \
"sender@test.local" \
"Basic Test Email" \
"This is a basic test email from the email testing workflow." \
false \
15
# Test 2: Different sender and recipient
run_test "Different Sender/Recipient" "send" \
"recipient1@test.local" \
"sender1@test.local" \
"Different Addresses Test" \
"Testing different sender and recipient addresses." \
false \
15
# Test 3: Special characters in subject
run_test "Special Characters Subject" "send" \
"special@test.local" \
"sender@test.local" \
"Subject with Special chars: àáâãäåæçèéêë" \
"Testing special characters in email subject line." \
false \
15
}
run_html_scenario() {
print_info "Running HTML email tests..."
# Test 1: Basic HTML email
run_test "Basic HTML Email" "send" \
"html@test.local" \
"sender@test.local" \
"HTML Test Email" \
"<h1>HTML Test</h1><p>This is an <strong>HTML</strong> email test.</p><ul><li>Item 1</li><li>Item 2</li></ul>" \
true \
15
# Test 2: HTML with CSS styling
run_test "HTML with CSS" "send" \
"html-css@test.local" \
"sender@test.local" \
"HTML + CSS Test" \
"<html><head><style>body{font-family:Arial,sans-serif} .highlight{background-color:yellow}</style></head><body><h1 class='highlight'>Styled HTML</h1><p>This email includes <span class='highlight'>CSS styling</span>.</p></body></html>" \
true \
15
# Test 3: HTML with images
run_test "HTML with Images" "send" \
"html-img@test.local" \
"sender@test.local" \
"HTML Image Test" \
"<h1>Image Test</h1><p>This email includes an embedded image:</p><img src='cid:test-image' alt='Test Image'><p>If you see an image placeholder, HTML parsing is working.</p>" \
true \
15
}
run_attachments_scenario() {
print_info "Running attachment tests..."
# Note: This is a simplified test. Real attachment testing would require multipart MIME construction
print_warning "Attachment tests require more complex MIME construction. This is a placeholder test."
run_test "Simple Attachment Test" "send" \
"attachment@test.local" \
"sender@test.local" \
"Attachment Test" \
"This test would normally include an attachment, but requires more complex MIME construction." \
false \
15
}
run_bulk_scenario() {
print_info "Running bulk email tests..."
# Test sending multiple emails
run_test "Bulk Send Test" "bulk" 10 30
# Test with more emails
run_test "Bulk Send Large Test" "bulk" 50 60
}
run_performance_scenario() {
print_info "Running performance tests..."
# Test performance with larger volume
run_test "Performance Test" "bulk" 100 120
# Test with timeout constraints
run_test "Quick Response Test" "send" \
"perf@test.local" \
"sender@test.local" \
"Performance Test" \
"Quick performance test email." \
false \
5
}
run_integration_scenario() {
print_info "Running integration tests..."
# Test with various timeouts
run_test "Long Timeout Test" "send" \
"integration@test.local" \
"sender@test.local" \
"Integration Test" \
"Integration test with extended timeout." \
false \
60
# Test search functionality
run_test "Email Search Test" "search" \
"search-test@test.local" \
1
# Test receive functionality
run_test "Receive Test" "receive" \
30
}
# Load configuration from file
load_configuration() {
local config_file="$1"
if [[ ! -f "$config_file" ]]; then
print_error "Configuration file not found: $config_file"
exit 1
fi
print_info "Loading configuration from $config_file"
if command -v jq >/dev/null 2>&1; then
local tests=$(jq -c '.tests[]' "$config_file" 2>/dev/null)
if [[ $? -eq 0 ]]; then
echo "$tests" | while read -r test; do
local name=$(echo "$test" | jq -r '.name // "Unnamed Test"')
local type=$(echo "$test" | jq -r '.type // "send"')
local to=$(echo "$test" | jq -r '.to // "test@local"')
local from=$(echo "$test" | jq -r '.from // "sender@local"')
local subject=$(echo "$test" | jq -r '.subject // "Test Email"')
local body=$(echo "$test" | jq -r '.body // "Test body"')
local html=$(echo "$test" | jq -r '.html // false')
local timeout=$(echo "$test" | jq -r '.timeout // 30')
run_test "$name" "$type" "$to" "$from" "$subject" "$body" "$html" "$timeout"
done
else
print_error "Invalid JSON in configuration file"
exit 1
fi
else
print_error "jq is required for JSON configuration files"
exit 1
fi
}
# Generate test report
generate_report() {
print_info "Generating test report..."
echo ""
echo "=========================================="
echo " EMAIL TEST REPORT "
echo "=========================================="
echo "Total Tests: $TOTAL_TESTS"
echo "Passed Tests: $PASSED_TESTS"
echo "Failed Tests: $FAILED_TESTS"
if [[ $TOTAL_TESTS -gt 0 ]]; then
local success_rate=$(( (PASSED_TESTS * 100) / TOTAL_TESTS ))
echo "Success Rate: ${success_rate}%"
fi
echo "Duration: $((SECONDS)) seconds"
echo "=========================================="
# Save detailed report if verbose mode
if [[ "$VERBOSE" == true ]]; then
echo "" >> "$REPORT_FILE"
echo "Test Summary:" >> "$REPORT_FILE"
echo "Total Tests: $TOTAL_TESTS" >> "$REPORT_FILE"
echo "Passed Tests: $PASSED_TESTS" >> "$REPORT_FILE"
echo "Failed Tests: $FAILED_TESTS" >> "$REPORT_FILE"
if [[ $TOTAL_TESTS -gt 0 ]]; then
local success_rate=$(( (PASSED_TESTS * 100) / TOTAL_TESTS ))
echo "Success Rate: ${success_rate}%" >> "$REPORT_FILE"
fi
echo "Duration: ${SECONDS} seconds" >> "$REPORT_FILE"
fi
# Generate JSON report if requested
if [[ "$OUTPUT_FORMAT" == "json" ]]; then
generate_json_report
elif [[ "$OUTPUT_FORMAT" == "csv" ]]; then
generate_csv_report
fi
}
# Generate JSON report
generate_json_report() {
local json_report_file="${REPORT_FILE%.*}.json"
local json="{\"timestamp\":\"$(date -Iseconds)\",\"total\":$TOTAL_TESTS,\"passed\":$PASSED_TESTS,\"failed\":$FAILED_TESTS,\"duration\":$SECONDS,\"tests\":["
local first=true
for result in "${TEST_RESULTS[@]}"; do
if [[ "$first" == true ]]; then
first=false
else
json+=","
fi
json+="$result"
done
json+="]}"
echo "$json" > "$json_report_file"
print_info "JSON report saved to: $json_report_file"
}
# Generate CSV report
generate_csv_report() {
local csv_report_file="${REPORT_FILE%.*}.csv"
echo "Test Name,Result,Details,Timestamp" > "$csv_report_file"
for result in "${TEST_RESULTS[@]}"; do
local name=$(echo "$result" | jq -r '.name' 2>/dev/null || echo "Unknown")
local res=$(echo "$result" | jq -r '.result' 2>/dev/null || echo "UNKNOWN")
local details=$(echo "$result" | jq -r '.details' 2>/dev/null || echo "No details")
local timestamp=$(date -Iseconds)
echo "\"$name\",\"$res\",\"$details\",\"$timestamp\"" >> "$csv_report_file"
done
print_info "CSV report saved to: $csv_report_file"
}
# Clean up test environment
cleanup_test_environment() {
if [[ "$CLEANUP" == true ]]; then
print_info "Cleaning up test environment..."
clear_mailhog_messages
fi
# Remove temporary files
rm -f /tmp/email_test_*
}
# Main execution
main() {
local start_time=$(date +%s)
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_usage
exit 0
;;
-c|--config)
CONFIG_FILE="$2"
shift 2
;;
-s|--scenario)
TEST_SCENARIO="$2"
shift 2
;;
--url)
MAILHOG_URL="$2"
shift 2
;;
--smtp-host)
SMTP_HOST="$2"
shift 2
;;
--smtp-port)
SMTP_PORT="$2"
shift 2
;;
-t|--timeout)
TIMEOUT="$2"
shift 2
;;
-v|--verbose)
VERBOSE=true
shift
;;
--no-cleanup)
CLEANUP=false
shift
;;
--output)
OUTPUT_FORMAT="$2"
shift 2
;;
--report-file)
REPORT_FILE="$2"
shift 2
;;
*)
print_error "Unknown option: $1"
show_usage
exit 1
;;
esac
done
# Validate output format
case "$OUTPUT_FORMAT" in
"text"|"json"|"csv")
;;
*)
print_error "Invalid output format: $OUTPUT_FORMAT. Use text, json, or csv."
exit 1
;;
esac
print_info "Starting email testing workflow..."
print_info "Scenario: $TEST_SCENARIO"
print_info "MailHog URL: $MAILHOG_URL"
print_info "SMTP Server: $SMTP_HOST:$SMTP_PORT"
# Initialize test environment
init_test_environment
# Run tests based on scenario or configuration
if [[ -n "$CONFIG_FILE" ]]; then
load_configuration "$CONFIG_FILE"
else
case "$TEST_SCENARIO" in
"basic")
run_basic_scenario
;;
"html")
run_html_scenario
;;
"attachments")
run_attachments_scenario
;;
"bulk")
run_bulk_scenario
;;
"performance")
run_performance_scenario
;;
"integration")
run_integration_scenario
;;
*)
print_error "Unknown test scenario: $TEST_SCENARIO"
show_usage
exit 1
;;
esac
fi
# Generate report
generate_report
# Clean up
cleanup_test_environment
# Exit with appropriate code
local end_time=$(date +%s)
local duration=$((end_time - start_time))
print_info "Test workflow completed in ${duration} seconds"
if [[ $FAILED_TESTS -eq 0 ]]; then
print_success "All tests passed!"
exit 0
else
print_error "$FAILED_TESTS test(s) failed"
exit 1
fi
}
# Run main function
main "$@"