From 1272600825734431aad8779559587165f705ed47 Mon Sep 17 00:00:00 2001 From: addshore Date: Sat, 26 Sep 2026 15:26:42 +0100 Subject: [PATCH 1/2] dev: capture Compose email with MailHog Add a local MailHog inbox and route API Compose mail through it, keeping verification emails off external mail services and actually allowing out of the box registration of a user in development docker compose (along with the recaptcha change) --- README.md | 6 ++++++ docker-compose.yml | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8757149ac..b78994cdc 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,16 @@ Though the experience is not that refined... # Start the stack in the background docker compose up -d +# View locally sent email, including account verification links +# http://localhost:8025/ + # Stop the stack and delete all volumes docker compose down --volumes ``` +The Compose setup sends email to the local MailHog inbox instead of an external +mail provider. + ### Migrations Run everything in one go: diff --git a/docker-compose.yml b/docker-compose.yml index e14f0e183..5d25b55ea 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,11 +13,16 @@ services: restart: always depends_on: - sql + - mailhog links: - sql - redis environment: - - CONTAINER_ROLE=app + CONTAINER_ROLE: app + MAIL_MAILER: smtp + MAIL_HOST: mailhog + MAIL_PORT: 1025 + MAIL_ENCRYPTION: "" sql: image: "${DOCKER_DATABASE_IMAGE_NAME}" environment: @@ -41,6 +46,11 @@ services: restart: always expose: - 6379 + mailhog: + image: mailhog/mailhog:v1.0.1 + restart: always + ports: + - 127.0.0.1:8025:8025 volumes: sql-data: From 8db218c26082bdeae0e1a9d4263dda1d63dcf406 Mon Sep 17 00:00:00 2001 From: addshore Date: Tue, 29 Sep 2026 01:01:16 +0100 Subject: [PATCH 2/2] Make complaint mail failure test independent of SMTP availability MailHog intentionally makes local SMTP delivery succeed. MailHog now runs in CI... Simulate a transport failure explicitly, assert HTTP 500, and always verify the complaint remains recorded but undispatched. --- tests/Routes/Complaint/SendMessageTest.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/Routes/Complaint/SendMessageTest.php b/tests/Routes/Complaint/SendMessageTest.php index f92f23ea6..1b3a9c08d 100644 --- a/tests/Routes/Complaint/SendMessageTest.php +++ b/tests/Routes/Complaint/SendMessageTest.php @@ -7,8 +7,10 @@ use App\Rules\ReCaptchaValidation; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Notifications\AnonymousNotifiable; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; use Symfony\Component\Mailer\Exception\TransportException; +use Symfony\Component\Mailer\Transport\TransportInterface; use Tests\TestCase; class SendMessageTest extends TestCase { @@ -66,15 +68,16 @@ private function assertComplaintNotRecorded() { public function testRecordOnMailFail() { $this->mockReCaptchaValidation(); - $data = $this->postDataTemplateFilled; + $transport = $this->createMock(TransportInterface::class); + $transport->expects($this->once()) + ->method('send') + ->willThrowException(new TransportException('Simulated mail delivery failure')); + Mail::mailer()->setSymfonyTransport($transport); - try { - $response = $this->json('POST', $this->route, $data); - } catch (TransportException $e) { - return; - } + $data = $this->postDataTemplateFilled; - $this->assertNotEquals($response->status(), 200); + $response = $this->json('POST', $this->route, $data); + $response->assertStatus(500); $this->assertComplaintRecorded(); $this->assertComplaintNotMarkedAsDispatched();