#!/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 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" \ "
This is an HTML email test.
This email includes CSS styling.
" \ true \ 15 # Test 3: HTML with images run_test "HTML with Images" "send" \ "html-img@test.local" \ "sender@test.local" \ "HTML Image Test" \ "This email includes an embedded image:
If you see an image placeholder, HTML parsing is working.
" \ 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 "$@"