Files
timebank-cc-public/tests/Feature/MailingBounceRecordTest.php
T

122 lines
4.7 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Mailing;
use App\Models\MailingBounce;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
/**
* Also: blocks against our sending server (Microsoft's S3150 block list,
* "550 5.7.1") suppressed hotmail addresses on 2026-10-10, and a backlog of
* August bounces was recorded as if it bounced that day.
*
* A second bounce report for the same address and mailing (unique index)
* updates the first. Inserting it threw, and from 2026-09-27 at the latest
* that stopped every hourly bounce run at the same message.
*/
class MailingBounceRecordTest extends TestCase
{
use DatabaseTransactions;
private function mailing(): Mailing
{
return Mailing::create([
'title' => 'Bounce test',
'type' => 'general_newsletter',
'subject' => ['en' => 'Bounce test'],
'status' => 'sent',
]);
}
public function test_a_second_report_for_the_same_mailing_updates_the_first(): void
{
$mailing = $this->mailing();
MailingBounce::recordBounce('Bounce.Test@Example.test', 'soft', 'mailbox full', $mailing->id);
$bounce = MailingBounce::recordBounce('bounce.test@example.test ', 'hard', 'user unknown', $mailing->id);
$rows = MailingBounce::where('email', 'bounce.test@example.test')->get();
$this->assertCount(1, $rows);
$this->assertSame($bounce->id, $rows->first()->id);
$this->assertSame('hard', $rows->first()->bounce_type);
$this->assertSame('user unknown', $rows->first()->bounce_reason);
}
public function test_bounces_without_a_mailing_still_add_up(): void
{
MailingBounce::recordBounce('bounce.test@example.test', 'hard', 'user unknown');
MailingBounce::recordBounce('bounce.test@example.test', 'hard', 'user unknown');
$this->assertSame(2, MailingBounce::where('email', 'bounce.test@example.test')->count());
}
public function test_other_mailings_get_their_own_row(): void
{
$first = $this->mailing();
$second = $this->mailing();
MailingBounce::recordBounce('bounce.test@example.test', 'hard', 'user unknown', $first->id);
MailingBounce::recordBounce('bounce.test@example.test', 'hard', 'user unknown', $second->id);
$this->assertSame(2, MailingBounce::where('email', 'bounce.test@example.test')->count());
}
private function thresholds(int $windowDays = 30): void
{
$key = config('app.timebank_config') . '.mailing.bounce_thresholds';
config([
"{$key}.suppression_threshold" => 1,
"{$key}.verification_reset_threshold" => 1,
"{$key}.counting_window_days" => $windowDays,
]);
}
public function test_a_block_against_our_server_does_not_suppress_the_address(): void
{
$this->thresholds();
$user = User::factory()->create(['email' => 'bounce.test@example.test', 'email_verified_at' => now()]);
MailingBounce::recordBounce('bounce.test@example.test', 'hard',
"smtp; 550 5.7.1 Unfortunately, messages from [195.190.28.89] weren't sent. Please contact your Internet service provider since part of their network is on our block list (S3150).");
$this->assertFalse(MailingBounce::isSuppressed('bounce.test@example.test'));
$this->assertNotNull($user->fresh()->email_verified_at);
}
public function test_an_unknown_user_still_suppresses_the_address(): void
{
$this->thresholds();
$user = User::factory()->create(['email' => 'bounce.test@example.test', 'email_verified_at' => now()]);
MailingBounce::recordBounce('bounce.test@example.test', 'hard', 'smtp; 550 5.1.1 user unknown');
$this->assertTrue(MailingBounce::isSuppressed('bounce.test@example.test'));
$this->assertNull($user->fresh()->email_verified_at);
}
public function test_a_missing_address_counts_even_with_a_5_7_code(): void
{
$this->thresholds();
MailingBounce::recordBounce('bounce.test@example.test', 'hard',
'smtp; 550 5.7.1 <bounce.test@example.test>: Recipient address rejected: Recipient not found');
$this->assertTrue(MailingBounce::isSuppressed('bounce.test@example.test'));
}
public function test_a_bounce_is_dated_when_it_happened_and_old_ones_do_not_count(): void
{
$this->thresholds(30);
$when = now()->subDays(70)->startOfSecond();
$bounce = MailingBounce::recordBounce('bounce.test@example.test', 'hard', 'smtp; 550 5.1.1 user unknown', null, $when);
$this->assertTrue($bounce->fresh()->bounced_at->equalTo($when));
$this->assertFalse(MailingBounce::isSuppressed('bounce.test@example.test'));
}
}