#!/usr/bin/env node /** * Test script to verify resend verification email functionality * This script tests the complete flow of resending verification emails */ const readline = require('readline'); const API_BASE_URL = 'http://localhost:3001/api'; // Colors for console output const colors = { green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', blue: '\x1b[34m', reset: '\x1b[0m', bold: '\x1b[1m' }; function log(message, color = colors.reset) { console.log(`${color}${message}${colors.reset}`); } function success(message) { log(`✅ ${message}`, colors.green); } function error(message) { log(`❌ ${message}`, colors.red); } function warning(message) { log(`⚠️ ${message}`, colors.yellow); } function info(message) { log(`ℹ️ ${message}`, colors.blue); } class ResendVerificationTester { constructor() { this.testEmail = `test-resend-${Date.now()}@example.com`; this.testPassword = 'TestPassword123!'; } /** * Check if the server is running */ async checkServerHealth() { try { const response = await fetch(`${API_BASE_URL.replace('/api', '')}/health`); if (response.ok) { const data = await response.json(); success('Server is running and healthy'); return true; } else { error('Server health check failed'); return false; } } catch (err) { error(`Server is not running: ${err.message}`); return false; } } /** * Register a test user */ async registerTestUser() { try { info(`Registering test user: ${this.testEmail}`); const response = await fetch(`${API_BASE_URL}/auth/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: this.testEmail, password: this.testPassword }) }); const data = await response.json(); if (response.ok) { success('Test user registered successfully'); info(`User ID: ${data.user?.id}`); return { success: true, user: data.user }; } else { error(`Registration failed: ${data.error}`); return { success: false, error: data.error }; } } catch (err) { error(`Registration error: ${err.message}`); return { success: false, error: err.message }; } } /** * Test resend verification email with valid email */ async testResendVerificationValid() { try { info('Testing resend verification with valid unverified email...'); const response = await fetch(`${API_BASE_URL}/auth/resend-verification`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: this.testEmail }) }); const data = await response.json(); if (response.ok) { success('Resend verification request successful'); info(`Message: ${data.message}`); return { success: true, message: data.message }; } else { error(`Resend verification failed: ${data.error}`); return { success: false, error: data.error }; } } catch (err) { error(`Resend verification error: ${err.message}`); return { success: false, error: err.message }; } } /** * Test resend verification email with non-existent email */ async testResendVerificationNonExistent() { try { info('Testing resend verification with non-existent email...'); const nonExistentEmail = `nonexistent-${Date.now()}@example.com`; const response = await fetch(`${API_BASE_URL}/auth/resend-verification`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: nonExistentEmail }) }); const data = await response.json(); if (response.ok) { success('Resend verification with non-existent email handled correctly'); info(`Message: ${data.message}`); return { success: true, message: data.message }; } else { warning(`Unexpected response for non-existent email: ${data.error}`); return { success: false, error: data.error }; } } catch (err) { error(`Resend verification error: ${err.message}`); return { success: false, error: err.message }; } } /** * Test resend verification email with missing email */ async testResendVerificationMissingEmail() { try { info('Testing resend verification with missing email...'); const response = await fetch(`${API_BASE_URL}/auth/resend-verification`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }); const data = await response.json(); if (response.status === 400) { success('Missing email validation working correctly'); info(`Error: ${data.error}`); return { success: true, error: data.error }; } else { error(`Expected 400 status for missing email, got ${response.status}`); return { success: false, error: 'Unexpected response status' }; } } catch (err) { error(`Resend verification error: ${err.message}`); return { success: false, error: err.message }; } } /** * Test resend verification email with invalid email format */ async testResendVerificationInvalidEmail() { try { info('Testing resend verification with invalid email format...'); const response = await fetch(`${API_BASE_URL}/auth/resend-verification`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'invalid-email-format' }) }); const data = await response.json(); // The endpoint should still return success for security reasons if (response.ok) { success('Invalid email format handled correctly (security response)'); info(`Message: ${data.message}`); return { success: true, message: data.message }; } else { warning(`Unexpected response for invalid email: ${data.error}`); return { success: false, error: data.error }; } } catch (err) { error(`Resend verification error: ${err.message}`); return { success: false, error: err.message }; } } /** * Test rate limiting on resend verification */ async testResendVerificationRateLimit() { try { info('Testing rate limiting on resend verification...'); const requests = []; const maxRequests = 6; // Should exceed the rate limit for (let i = 0; i < maxRequests; i++) { requests.push( fetch(`${API_BASE_URL}/auth/resend-verification`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: this.testEmail }) }) ); } const responses = await Promise.all(requests); const rateLimitedResponses = responses.filter(r => r.status === 429); if (rateLimitedResponses.length > 0) { success(`Rate limiting working: ${rateLimitedResponses.length} requests were rate limited`); return { success: true, rateLimited: rateLimitedResponses.length }; } else { warning('Rate limiting may not be working as expected'); return { success: false, error: 'No rate limiting detected' }; } } catch (err) { error(`Rate limit test error: ${err.message}`); return { success: false, error: err.message }; } } /** * Check email service configuration */ async checkEmailServiceConfig() { try { info('Checking email service configuration...'); // This would require a dedicated endpoint to check email config // For now, we'll just check if the service responds properly const response = await fetch(`${API_BASE_URL}/auth/resend-verification`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'test@example.com' }) }); if (response.ok || response.status === 400) { success('Email service endpoint is accessible'); return { success: true }; } else { error('Email service endpoint may have issues'); return { success: false }; } } catch (err) { error(`Email service check error: ${err.message}`); return { success: false, error: err.message }; } } /** * Clean up test data */ async cleanup() { try { info('Cleaning up test data...'); // Note: In a real scenario, you'd want to clean up the test user // This would require a dedicated cleanup endpoint or direct database access warning('Manual cleanup may be required for test user data'); return { success: true }; } catch (err) { error(`Cleanup error: ${err.message}`); return { success: false, error: err.message }; } } /** * Run all tests */ async runAllTests() { log('\n' + '='.repeat(60), colors.bold); log('🧪 RESEND VERIFICATION EMAIL FUNCTIONALITY TEST', colors.bold); log('='.repeat(60), colors.bold); const results = { total: 0, passed: 0, failed: 0 }; const tests = [ { name: 'Server Health Check', fn: () => this.checkServerHealth() }, { name: 'Email Service Configuration', fn: () => this.checkEmailServiceConfig() }, { name: 'User Registration', fn: () => this.registerTestUser() }, { name: 'Resend Verification (Valid Email)', fn: () => this.testResendVerificationValid() }, { name: 'Resend Verification (Non-existent Email)', fn: () => this.testResendVerificationNonExistent() }, { name: 'Resend Verification (Missing Email)', fn: () => this.testResendVerificationMissingEmail() }, { name: 'Resend Verification (Invalid Email)', fn: () => this.testResendVerificationInvalidEmail() }, { name: 'Rate Limiting Test', fn: () => this.testResendVerificationRateLimit() }, { name: 'Cleanup', fn: () => this.cleanup() } ]; for (const test of tests) { log(`\n📋 Running: ${test.name}`, colors.yellow); log('-'.repeat(40)); try { const result = await test.fn(); results.total++; if (result.success) { results.passed++; success(`${test.name} - PASSED`); } else { results.failed++; error(`${test.name} - FAILED: ${result.error || 'Unknown error'}`); } } catch (err) { results.total++; results.failed++; error(`${test.name} - ERROR: ${err.message}`); } } // Final results log('\n' + '='.repeat(60), colors.bold); log('📊 TEST RESULTS SUMMARY', colors.bold); log('='.repeat(60), colors.bold); log(`Total Tests: ${results.total}`); success(`Passed: ${results.passed}`); if (results.failed > 0) { error(`Failed: ${results.failed}`); } else { log(`Failed: ${results.failed}`); } const successRate = ((results.passed / results.total) * 100).toFixed(1); log(`Success Rate: ${successRate}%`); if (results.failed === 0) { success('\n🎉 All tests passed! Resend verification functionality is working correctly.'); } else { warning('\n⚠️ Some tests failed. Please review the issues above.'); } return results; } } // Main execution async function main() { const tester = new ResendVerificationTester(); try { await tester.runAllTests(); } catch (err) { error(`Test execution failed: ${err.message}`); process.exit(1); } } // Run if called directly if (require.main === module) { main().catch(console.error); } module.exports = ResendVerificationTester;