171 lines
7.1 KiB
PHP
171 lines
7.1 KiB
PHP
<?php
|
|
|
|
use App\Mail\MaintenanceAnnouncementMail;
|
|
use App\Mail\MaintenanceCancelledMail;
|
|
use App\Models\Customer;
|
|
use App\Models\Instance;
|
|
use App\Models\Mailbox;
|
|
use App\Models\MaintenanceWindow;
|
|
use App\Notifications\CloudReady;
|
|
use App\Services\Mail\MailPurpose;
|
|
use App\Services\Maintenance\MaintenanceNotifier;
|
|
use App\Support\Settings;
|
|
use Illuminate\Mail\SendQueuedMailable;
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
beforeEach(function () {
|
|
config()->set('admin_access.secrets_key', 'base64:'.base64_encode(random_bytes(32)));
|
|
clearMailboxSeed();
|
|
});
|
|
|
|
it('sends from the mailbox its purpose points at', function () {
|
|
Mailbox::factory()->create([
|
|
'key' => 'no-reply',
|
|
'address' => 'no-reply@clupilot.com',
|
|
'display_name' => 'CluPilot',
|
|
'no_reply' => true,
|
|
]);
|
|
Settings::set(MailPurpose::settingKey(MailPurpose::MAINTENANCE), 'no-reply');
|
|
|
|
$mail = new MaintenanceAnnouncementMail(
|
|
MaintenanceWindow::factory()->create(),
|
|
Customer::factory()->create(),
|
|
);
|
|
|
|
$envelope = $mail->envelope();
|
|
|
|
expect($envelope->from->address)->toBe('no-reply@clupilot.com')
|
|
->and($envelope->from->name)->toBe('CluPilot');
|
|
});
|
|
|
|
it('omits Reply-To on a no-reply mailbox, because that is what the name promises', function () {
|
|
Mailbox::factory()->create(['key' => 'no-reply', 'address' => 'no-reply@clupilot.com', 'no_reply' => true]);
|
|
Settings::set(MailPurpose::settingKey(MailPurpose::MAINTENANCE), 'no-reply');
|
|
|
|
$envelope = (new MaintenanceAnnouncementMail(
|
|
MaintenanceWindow::factory()->create(),
|
|
Customer::factory()->create(),
|
|
))->envelope();
|
|
|
|
expect($envelope->replyTo)->toBeEmpty();
|
|
});
|
|
|
|
it('sets Reply-To on an answerable mailbox, so a customer reply reaches a human', function () {
|
|
Mailbox::factory()->create(['key' => 'support', 'address' => 'support@clupilot.com', 'no_reply' => false]);
|
|
Settings::set(MailPurpose::settingKey(MailPurpose::MAINTENANCE), 'support');
|
|
|
|
$envelope = (new MaintenanceAnnouncementMail(
|
|
MaintenanceWindow::factory()->create(),
|
|
Customer::factory()->create(),
|
|
))->envelope();
|
|
|
|
expect($envelope->replyTo[0]->address)->toBe('support@clupilot.com');
|
|
});
|
|
|
|
it('names the purpose mailer so the queue worker picks the right transport', function () {
|
|
// A bare, never-queued mailable proves nothing here: the constructor sets
|
|
// $this->mailer correctly regardless of what MaintenanceNotifier does.
|
|
// Illuminate\Mail\Mailer::queue() (vendor Mailer.php) runs
|
|
// `$view->mailer($this->name)->queue(...)` — if MaintenanceNotifier queues
|
|
// through the bare `Mail` facade instead of `Mail::mailer($mail->mailer)`,
|
|
// that resolves the DEFAULT mailer ('array' in tests) and overwrites the
|
|
// mailable's own 'cp_maintenance' with it BEFORE the queued job is even
|
|
// built. The only way to see that is to actually queue it and inspect the
|
|
// job that comes out the other side.
|
|
Mailbox::factory()->create(['key' => 'no-reply', 'address' => 'no-reply@clupilot.com', 'no_reply' => true]);
|
|
Settings::set(MailPurpose::settingKey(MailPurpose::MAINTENANCE), 'no-reply');
|
|
|
|
Queue::fake();
|
|
|
|
$window = MaintenanceWindow::factory()->create();
|
|
$customer = Customer::factory()->create();
|
|
|
|
app(MaintenanceNotifier::class)->deliver(
|
|
$window,
|
|
$customer,
|
|
'announcement',
|
|
new MaintenanceAnnouncementMail($window, $customer),
|
|
);
|
|
|
|
Queue::assertPushed(SendQueuedMailable::class, function (SendQueuedMailable $job) {
|
|
return $job->mailable->mailer === 'cp_maintenance';
|
|
});
|
|
});
|
|
|
|
it('gives the cancellation mail the same sender as the announcement', function () {
|
|
Mailbox::factory()->create(['key' => 'no-reply', 'address' => 'no-reply@clupilot.com', 'no_reply' => true]);
|
|
Settings::set(MailPurpose::settingKey(MailPurpose::MAINTENANCE), 'no-reply');
|
|
|
|
$mail = new MaintenanceCancelledMail(
|
|
MaintenanceWindow::factory()->create(),
|
|
Customer::factory()->create(),
|
|
);
|
|
|
|
expect($mail->envelope()->from->address)->toBe('no-reply@clupilot.com')
|
|
->and($mail->mailer)->toBe('cp_maintenance');
|
|
});
|
|
|
|
it('falls back to the framework default envelope when no mailbox is configured for the purpose', function () {
|
|
// Neither a purpose mapping nor a system fallback exists anywhere in
|
|
// Settings — MailboxResolver::for() returns null. An installation
|
|
// mid-setup must still hand back a usable Envelope (subject intact)
|
|
// rather than dereferencing a null mailbox.
|
|
$window = MaintenanceWindow::factory()->create();
|
|
|
|
$envelope = (new MaintenanceAnnouncementMail($window, Customer::factory()->create()))->envelope();
|
|
|
|
expect($envelope->from)->toBeNull()
|
|
->and($envelope->replyTo)->toBeEmpty()
|
|
->and($envelope->subject)->toBe(__('maintenance.mail_subject', ['title' => $window->title]));
|
|
});
|
|
|
|
it('sends the provisioning notification from the provisioning mailbox', function () {
|
|
Mailbox::factory()->create([
|
|
'key' => 'no-reply',
|
|
'address' => 'no-reply@clupilot.com',
|
|
'display_name' => 'CluPilot',
|
|
'no_reply' => true,
|
|
]);
|
|
Settings::set(MailPurpose::settingKey(MailPurpose::PROVISIONING), 'no-reply');
|
|
|
|
$notification = new CloudReady(Instance::factory()->create());
|
|
|
|
$message = $notification->toMail(new stdClass);
|
|
|
|
expect($message->from[0])->toBe('no-reply@clupilot.com')
|
|
->and($message->mailer)->toBe('cp_provisioning')
|
|
// no_reply is set, so no Reply-To — the same promise as everywhere else.
|
|
->and($message->replyTo)->toBeEmpty();
|
|
});
|
|
|
|
it('sets Reply-To on the provisioning notification when its mailbox allows replies', function () {
|
|
// The mirror image of the no-reply case above: a support-style mailbox
|
|
// behind PROVISIONING must still get a Reply-To, the same as maintenance
|
|
// does. Without this, CloudReady's `if (! $box->no_reply)` branch is only
|
|
// ever exercised by the no_reply=true side and a deleted replyTo() call
|
|
// would go unnoticed.
|
|
Mailbox::factory()->create(['key' => 'support', 'address' => 'support@clupilot.com', 'no_reply' => false]);
|
|
Settings::set(MailPurpose::settingKey(MailPurpose::PROVISIONING), 'support');
|
|
|
|
$notification = new CloudReady(Instance::factory()->create());
|
|
|
|
$message = $notification->toMail(new stdClass);
|
|
|
|
expect($message->replyTo[0][0])->toBe('support@clupilot.com');
|
|
});
|
|
|
|
it("leaves the provisioning notification's sender fields at MailMessage's defaults when no mailbox is configured", function () {
|
|
// The mirror of the trait's own null-mailbox fallback test above, but for
|
|
// CloudReady's own guard: neither a mailbox row nor a provisioning/system
|
|
// Settings mapping exists, so MailboxResolver::for() returns null. Must
|
|
// not throw dereferencing a null mailbox, and must leave from/replyTo
|
|
// exactly as `new MailMessage` starts them — [] and [].
|
|
$notification = new CloudReady(Instance::factory()->create());
|
|
|
|
$message = $notification->toMail(new stdClass);
|
|
|
|
expect($message->from)->toBeEmpty()
|
|
->and($message->replyTo)->toBeEmpty()
|
|
->and($message->mailer)->toBe('cp_provisioning');
|
|
});
|