Initial commit
This commit is contained in:
227
app/Console/Commands/TestBounceSystem.php
Normal file
227
app/Console/Commands/TestBounceSystem.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\MailingBounce;
|
||||
use App\Models\User;
|
||||
use App\Models\Organization;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class TestBounceSystem extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*/
|
||||
protected $signature = 'test:bounce-system
|
||||
{--email= : Email to test (default: creates test emails)}
|
||||
{--scenario= : Test scenario: single, threshold-verification, threshold-suppression, multiple}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*/
|
||||
protected $description = 'Test the bounce handling system with simulated bounces';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$scenario = $this->option('scenario') ?: 'single';
|
||||
$email = $this->option('email');
|
||||
|
||||
switch ($scenario) {
|
||||
case 'single':
|
||||
$this->testSingleBounce($email);
|
||||
break;
|
||||
case 'threshold-verification':
|
||||
$this->testVerificationThreshold($email);
|
||||
break;
|
||||
case 'threshold-suppression':
|
||||
$this->testSuppressionThreshold($email);
|
||||
break;
|
||||
case 'multiple':
|
||||
$this->testMultipleEmails();
|
||||
break;
|
||||
default:
|
||||
$this->error("Unknown scenario: {$scenario}");
|
||||
$this->info("Available scenarios: single, threshold-verification, threshold-suppression, multiple");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a single bounce (should not trigger any thresholds)
|
||||
*/
|
||||
protected function testSingleBounce(?string $email): void
|
||||
{
|
||||
$testEmail = $email ?: 'test-single@example.com';
|
||||
|
||||
$this->info("🧪 Testing Single Bounce for: {$testEmail}");
|
||||
|
||||
// Create a test user with verified email
|
||||
$this->createTestUser($testEmail);
|
||||
|
||||
// Record a single hard bounce with definitive pattern
|
||||
$bounce = MailingBounce::recordBounce(
|
||||
$testEmail,
|
||||
'hard',
|
||||
'user unknown - mailbox does not exist'
|
||||
);
|
||||
|
||||
$this->line("✅ Created bounce record ID: {$bounce->id}");
|
||||
|
||||
// Check the results
|
||||
$stats = MailingBounce::getBounceStats($testEmail);
|
||||
$this->displayResults($testEmail, $stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test verification threshold (2 bounces)
|
||||
*/
|
||||
protected function testVerificationThreshold(?string $email): void
|
||||
{
|
||||
$testEmail = $email ?: 'test-verification@example.com';
|
||||
|
||||
$this->info("🧪 Testing Verification Reset Threshold for: {$testEmail}");
|
||||
|
||||
// Create test user with verified email
|
||||
$user = $this->createTestUser($testEmail);
|
||||
$this->line("📧 Created test user with verified email: {$user->email_verified_at}");
|
||||
|
||||
// Record first bounce with exact pattern from config
|
||||
$this->line("1️⃣ Recording first hard bounce...");
|
||||
MailingBounce::recordBounce($testEmail, 'hard', 'user unknown - definitive bounce');
|
||||
|
||||
$user->refresh();
|
||||
$this->line(" User email_verified_at: " . ($user->email_verified_at ?: 'NULL'));
|
||||
|
||||
// Record second bounce (should trigger verification reset)
|
||||
$this->line("2️⃣ Recording second hard bounce (should reset verification)...");
|
||||
MailingBounce::recordBounce($testEmail, 'hard', 'mailbox unavailable - permanent failure');
|
||||
|
||||
$user->refresh();
|
||||
$this->line(" User email_verified_at: " . ($user->email_verified_at ?: 'NULL'));
|
||||
|
||||
// Check results
|
||||
$stats = MailingBounce::getBounceStats($testEmail);
|
||||
$this->displayResults($testEmail, $stats);
|
||||
|
||||
if (!$user->email_verified_at) {
|
||||
$this->info("✅ SUCCESS: Email verification was reset!");
|
||||
} else {
|
||||
$this->error("❌ FAILED: Email verification was NOT reset!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test suppression threshold (3 bounces)
|
||||
*/
|
||||
protected function testSuppressionThreshold(?string $email): void
|
||||
{
|
||||
$testEmail = $email ?: 'test-suppression@example.com';
|
||||
|
||||
$this->info("🧪 Testing Suppression Threshold for: {$testEmail}");
|
||||
|
||||
// Create test user
|
||||
$user = $this->createTestUser($testEmail);
|
||||
|
||||
// Record three bounces
|
||||
for ($i = 1; $i <= 3; $i++) {
|
||||
$this->line("{$i}️⃣ Recording hard bounce #{$i}...");
|
||||
MailingBounce::recordBounce($testEmail, 'hard', "user unknown - attempt {$i}");
|
||||
|
||||
$user->refresh();
|
||||
$isSuppressed = MailingBounce::isSuppressed($testEmail);
|
||||
|
||||
$this->line(" Suppressed: " . ($isSuppressed ? 'YES' : 'NO'));
|
||||
$this->line(" Email verified: " . ($user->email_verified_at ? 'YES' : 'NO'));
|
||||
}
|
||||
|
||||
// Check final results
|
||||
$stats = MailingBounce::getBounceStats($testEmail);
|
||||
$this->displayResults($testEmail, $stats);
|
||||
|
||||
if ($stats['is_suppressed']) {
|
||||
$this->info("✅ SUCCESS: Email was suppressed after 3 bounces!");
|
||||
} else {
|
||||
$this->error("❌ FAILED: Email was NOT suppressed!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test multiple emails with different scenarios
|
||||
*/
|
||||
protected function testMultipleEmails(): void
|
||||
{
|
||||
$this->info("🧪 Testing Multiple Email Scenarios");
|
||||
|
||||
$scenarios = [
|
||||
'no-bounce@example.com' => 0,
|
||||
'one-bounce@example.com' => 1,
|
||||
'verification-reset@example.com' => 2,
|
||||
'suppressed@example.com' => 3,
|
||||
'over-threshold@example.com' => 5
|
||||
];
|
||||
|
||||
foreach ($scenarios as $email => $bounceCount) {
|
||||
$this->line("Setting up {$email} with {$bounceCount} bounces...");
|
||||
|
||||
$this->createTestUser($email);
|
||||
|
||||
for ($i = 1; $i <= $bounceCount; $i++) {
|
||||
MailingBounce::recordBounce($email, 'hard', "user unknown - bounce {$i}");
|
||||
}
|
||||
}
|
||||
|
||||
$this->info("\n📊 Results Summary:");
|
||||
foreach ($scenarios as $email => $expectedBounces) {
|
||||
$stats = MailingBounce::getBounceStats($email);
|
||||
$user = User::where('email', $email)->first();
|
||||
|
||||
$this->line("📧 {$email}:");
|
||||
$this->line(" Hard bounces: {$stats['recent_hard_bounces']}");
|
||||
$this->line(" Suppressed: " . ($stats['is_suppressed'] ? 'YES' : 'NO'));
|
||||
$this->line(" Verified: " . ($user && $user->email_verified_at ? 'YES' : 'NO'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a test user with verified email
|
||||
*/
|
||||
protected function createTestUser(string $email): User
|
||||
{
|
||||
// Remove existing test user if any
|
||||
User::where('email', $email)->delete();
|
||||
|
||||
$user = User::create([
|
||||
'name' => 'Test User ' . substr($email, 0, strpos($email, '@')),
|
||||
'email' => $email,
|
||||
'password' => bcrypt('password'),
|
||||
]);
|
||||
|
||||
// Use forceFill since email_verified_at isn't in fillable
|
||||
$user->forceFill(['email_verified_at' => now()])->save();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display test results
|
||||
*/
|
||||
protected function displayResults(string $email, array $stats): void
|
||||
{
|
||||
$this->info("\n📊 Test Results for {$email}:");
|
||||
$this->line("Total bounces: {$stats['total_bounces']}");
|
||||
$this->line("Recent hard bounces: {$stats['recent_hard_bounces']}");
|
||||
$this->line("Is suppressed: " . ($stats['is_suppressed'] ? 'YES' : 'NO'));
|
||||
|
||||
$user = User::where('email', $email)->first();
|
||||
if ($user) {
|
||||
$this->line("Email verified: " . ($user->email_verified_at ? 'YES' : 'NO'));
|
||||
}
|
||||
|
||||
$this->line("");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user