Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2534447
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
43 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/app/Jobs/Password/RetentionEmailJob.php b/src/app/Jobs/Password/RetentionEmailJob.php
index e8d6ccf6..e086c64e 100644
--- a/src/app/Jobs/Password/RetentionEmailJob.php
+++ b/src/app/Jobs/Password/RetentionEmailJob.php
@@ -1,68 +1,73 @@
<?php
namespace App\Jobs\Password;
use App\Mail\PasswordExpirationReminder;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
class RetentionEmailJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/** @var bool Delete the job if its models no longer exist. */
public $deleteWhenMissingModels = true;
/** @var int The number of times the job may be attempted. */
public $tries = 2;
/** @var int The number of seconds to wait before retrying the job. */
public $retryAfter = 10;
/** @var string Password expiration date */
protected $expiresOn;
/** @var \App\User User object */
protected $user;
/**
* Create a new job instance.
*
* @param \App\User $user User object
* @param string $expiresOn Password expiration date
*
* @return void
*/
public function __construct(\App\User $user, string $expiresOn)
{
$this->user = $user;
$this->expiresOn = $expiresOn;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
+ if (!$this->user->isLdapReady() || !$this->user->isImapReady()) {
+ // The account isn't ready for mail delivery
+ return;
+ }
+
// TODO: Should we check if the password didn't update since
// the job has been created?
\App\Mail\Helper::sendMail(
new PasswordExpirationReminder($this->user, $this->expiresOn),
$this->user->tenant_id,
['to' => $this->user->email]
);
// Remember when we sent the email notification
$this->user->setSetting('password_expiration_warning', \now()->toDateTimeString());
}
}
diff --git a/src/app/Jobs/PaymentEmail.php b/src/app/Jobs/PaymentEmail.php
index 423c4e5f..36c2ef83 100644
--- a/src/app/Jobs/PaymentEmail.php
+++ b/src/app/Jobs/PaymentEmail.php
@@ -1,102 +1,102 @@
<?php
namespace App\Jobs;
use App\Payment;
use App\Providers\PaymentProvider;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
class PaymentEmail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/** @var int The number of times the job may be attempted. */
public $tries = 2;
/** @var int The number of seconds to wait before retrying the job. */
public $backoff = 10;
/** @var bool Delete the job if the wallet no longer exist. */
public $deleteWhenMissingModels = true;
/** @var \App\Payment A payment object */
protected $payment;
/** @var ?\App\User A wallet controller */
protected $controller;
/**
* Create a new job instance.
*
* @param \App\Payment $payment A payment object
* @param \App\User $controller A wallet controller
*
* @return void
*/
public function __construct(Payment $payment, User $controller = null)
{
$this->payment = $payment;
$this->controller = $controller;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$wallet = $this->payment->wallet;
if (empty($this->controller)) {
$this->controller = $wallet->owner;
}
if (empty($this->controller)) {
return;
}
if ($this->payment->status == PaymentProvider::STATUS_PAID) {
$mail = new \App\Mail\PaymentSuccess($this->payment, $this->controller);
$label = "Success";
} elseif (
$this->payment->status == PaymentProvider::STATUS_EXPIRED
|| $this->payment->status == PaymentProvider::STATUS_FAILED
) {
$mail = new \App\Mail\PaymentFailure($this->payment, $this->controller);
$label = "Failure";
} else {
return;
}
list($to, $cc) = \App\Mail\Helper::userEmails($this->controller);
- if (!empty($to)) {
+ if (!empty($to) || !empty($cc)) {
$params = [
'to' => $to,
'cc' => $cc,
'add' => " for {$wallet->id}",
];
\App\Mail\Helper::sendMail($mail, $this->controller->tenant_id, $params);
}
/*
// Send the email to all wallet controllers too
if ($wallet->owner->id == $this->controller->id) {
$this->wallet->controllers->each(function ($controller) {
self::dispatch($this->payment, $controller);
}
});
*/
}
}
diff --git a/src/app/Jobs/PaymentMandateDisabledEmail.php b/src/app/Jobs/PaymentMandateDisabledEmail.php
index 4a2d97cf..d26fed03 100644
--- a/src/app/Jobs/PaymentMandateDisabledEmail.php
+++ b/src/app/Jobs/PaymentMandateDisabledEmail.php
@@ -1,89 +1,89 @@
<?php
namespace App\Jobs;
use App\Mail\PaymentMandateDisabled;
use App\User;
use App\Wallet;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
class PaymentMandateDisabledEmail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/** @var int The number of times the job may be attempted. */
public $tries = 2;
/** @var int The number of seconds to wait before retrying the job. */
public $backoff = 10;
/** @var bool Delete the job if the wallet no longer exist. */
public $deleteWhenMissingModels = true;
/** @var \App\Wallet A wallet object */
protected $wallet;
/** @var ?\App\User A wallet controller */
protected $controller;
/**
* Create a new job instance.
*
* @param \App\Wallet $wallet A wallet object
* @param \App\User $controller An email recipient (wallet controller)
*
* @return void
*/
public function __construct(Wallet $wallet, User $controller = null)
{
$this->wallet = $wallet;
$this->controller = $controller;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
if (empty($this->controller)) {
$this->controller = $this->wallet->owner;
}
if (empty($this->controller)) {
return;
}
$mail = new PaymentMandateDisabled($this->wallet, $this->controller);
list($to, $cc) = \App\Mail\Helper::userEmails($this->controller);
- if (!empty($to)) {
+ if (!empty($to) || !empty($cc)) {
$params = [
'to' => $to,
'cc' => $cc,
'add' => " for {$this->wallet->id}",
];
\App\Mail\Helper::sendMail($mail, $this->controller->tenant_id, $params);
}
/*
// Send the email to all controllers too
if ($this->controller->id == $this->wallet->owner->id) {
$this->wallet->controllers->each(function ($controller) {
self::dispatch($this->wallet, $controller);
}
});
*/
}
}
diff --git a/src/app/Jobs/TrialEndEmail.php b/src/app/Jobs/TrialEndEmail.php
index 526b1209..73d3280f 100644
--- a/src/app/Jobs/TrialEndEmail.php
+++ b/src/app/Jobs/TrialEndEmail.php
@@ -1,65 +1,68 @@
<?php
namespace App\Jobs;
use App\Mail\TrialEnd;
use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
class TrialEndEmail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/** @var int The number of times the job may be attempted. */
public $tries = 3;
/** @var bool Delete the job if its models no longer exist. */
public $deleteWhenMissingModels = true;
/** @var \App\User The account owner */
protected $account;
/**
* Create a new job instance.
*
* @param \App\User $account The account owner
*
* @return void
*/
public function __construct(User $account)
{
$this->account = $account;
}
/**
* Determine the time at which the job should timeout.
*
* @return \DateTime
*/
public function retryUntil()
{
// FIXME: I think it does not make sense to continue trying after 24 hours
return now()->addHours(24);
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
- \App\Mail\Helper::sendMail(
- new TrialEnd($this->account),
- $this->account->tenant_id,
- ['to' => $this->account->email]
- );
+ // Skip accounts that aren't ready for mail delivery
+ if ($this->account->isLdapReady() && $this->account->isImapReady()) {
+ \App\Mail\Helper::sendMail(
+ new TrialEnd($this->account),
+ $this->account->tenant_id,
+ ['to' => $this->account->email]
+ );
+ }
}
}
diff --git a/src/app/Mail/Helper.php b/src/app/Mail/Helper.php
index dda1117b..bb7c8905 100644
--- a/src/app/Mail/Helper.php
+++ b/src/app/Mail/Helper.php
@@ -1,130 +1,134 @@
<?php
namespace App\Mail;
use App\Tenant;
use Illuminate\Mail\Mailable;
use Illuminate\Support\Facades\Mail;
class Helper
{
/**
* Render the mail template.
*
* @param \Illuminate\Mail\Mailable $mail The mailable object
* @param string $type Output format ('html' or 'text')
*
* @return string HTML or Plain Text output
*/
public static function render(Mailable $mail, string $type = 'html'): string
{
// Plain text output
if ($type == 'text') {
$mail->build(); // @phpstan-ignore-line
$mailer = \Illuminate\Container\Container::getInstance()->make('mailer');
return $mailer->render(['text' => $mail->textView], $mail->buildViewData());
} elseif ($type != 'html') {
throw new \Exception("Unsupported output format");
}
// HTML output
return $mail->build()->render(); // @phpstan-ignore-line
}
/**
* Sends an email
*
* @param Mailable $mail Email content generator
* @param int|null $tenantId Tenant identifier
* @param array $params Email parameters: to, cc
*
* @throws \Exception
*/
public static function sendMail(Mailable $mail, $tenantId = null, array $params = []): void
{
$class = explode("\\", get_class($mail));
$class = end($class);
$getRecipients = function () use ($params) {
$recipients = [];
// For now we do not support addresses + names, only addresses
foreach (['to', 'cc'] as $idx) {
if (!empty($params[$idx])) {
if (is_array($params[$idx])) {
$recipients = array_merge($recipients, $params[$idx]);
} else {
$recipients[] = $params[$idx];
}
}
}
return implode(', ', $recipients);
};
try {
if (!empty($params['to'])) {
$mail->to($params['to']);
}
if (!empty($params['cc'])) {
$mail->cc($params['cc']);
}
$fromAddress = Tenant::getConfig($tenantId, 'mail.from.address');
$fromName = Tenant::getConfig($tenantId, 'mail.from.name');
$replytoAddress = Tenant::getConfig($tenantId, 'mail.reply_to.address');
$replytoName = Tenant::getConfig($tenantId, 'mail.reply_to.name');
if ($fromAddress) {
$mail->from($fromAddress, $fromName);
}
if ($replytoAddress) {
$mail->replyTo($replytoAddress, $replytoName);
}
Mail::send($mail);
$msg = sprintf("[%s] Sent mail to %s%s", $class, $getRecipients(), $params['add'] ?? '');
\Log::info($msg);
} catch (\Exception $e) {
$format = "[%s] Failed to send mail to %s%s: %s";
$msg = sprintf($format, $class, $getRecipients(), $params['add'] ?? '', $e->getMessage());
\Log::error($msg);
throw $e;
}
}
/**
* Return user's email addresses, separately for use in To and Cc.
*
* @param \App\User $user The user
* @param bool $external Include users's external email
*
* @return array To address as the first element, Cc address(es) as the second.
*/
public static function userEmails(\App\User $user, bool $external = false): array
{
- $to = $user->email;
+ $active = $user->isLdapReady() && $user->isImapReady();
+
+ // Sending an email to non-(ldap|imap)-ready user will fail, skip it
+ // (or send to the external email only, when appropriate)
+ $to = $active ? $user->email : null;
$cc = [];
// If user has no mailbox entitlement we should not send
// the email to his main address, but use external address, if defined
- if (!$user->hasSku('mailbox')) {
+ if ($active && !$user->hasSku('mailbox')) {
$to = $user->getSetting('external_email');
} elseif ($external) {
$ext_email = $user->getSetting('external_email');
if ($ext_email && $ext_email != $to) {
$cc[] = $ext_email;
}
}
return [$to, $cc];
}
}
diff --git a/src/tests/Feature/Jobs/Password/RetentionEmailJobTest.php b/src/tests/Feature/Jobs/Password/RetentionEmailJobTest.php
index c897c6ac..171106b3 100644
--- a/src/tests/Feature/Jobs/Password/RetentionEmailJobTest.php
+++ b/src/tests/Feature/Jobs/Password/RetentionEmailJobTest.php
@@ -1,73 +1,75 @@
<?php
namespace Tests\Feature\Jobs\Password;
use App\Jobs\Password\RetentionEmailJob;
use App\Mail\PasswordExpirationReminder;
+use App\User;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class RetentionEmailJobTest extends TestCase
{
/**
* {@inheritDoc}
*
* @return void
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('PasswordRetention@UserAccount.com');
}
/**
* {@inheritDoc}
*
* @return void
*/
public function tearDown(): void
{
$this->deleteTestUser('PasswordRetention@UserAccount.com');
parent::tearDown();
}
/**
* Test job handle
*
* @return void
*/
public function testHandle()
{
- $user = $this->getTestUser('PasswordRetention@UserAccount.com');
+ $status = User::STATUS_ACTIVE | User::STATUS_LDAP_READY | User::STATUS_IMAP_READY;
+ $user = $this->getTestUser('PasswordRetention@UserAccount.com', ['status' => $status]);
$expiresOn = now()->copy()->addDays(7)->toDateString();
Mail::fake();
// Assert that no jobs were pushed...
Mail::assertNothingSent();
$job = new RetentionEmailJob($user, $expiresOn);
$job->handle();
$this->assertMatchesRegularExpression(
'/^' . now()->format('Y-m-d') . ' [0-9]{2}:[0-9]{2}:[0-9]{2}$/',
$user->getSetting('password_expiration_warning')
);
// Assert the email sending job was pushed once
Mail::assertSent(PasswordExpirationReminder::class, 1);
// Assert the mail was sent to the code's email
Mail::assertSent(PasswordExpirationReminder::class, function ($mail) use ($user) {
return $mail->hasTo($user->email);
});
// Assert sender
Mail::assertSent(PasswordExpirationReminder::class, function ($mail) {
return $mail->hasFrom(\config('mail.from.address'), \config('mail.from.name'))
&& $mail->hasReplyTo(\config('mail.reply_to.address'), \config('mail.reply_to.name'));
});
}
}
diff --git a/src/tests/Feature/Jobs/PaymentEmailTest.php b/src/tests/Feature/Jobs/PaymentEmailTest.php
index b2813a87..bbf52205 100644
--- a/src/tests/Feature/Jobs/PaymentEmailTest.php
+++ b/src/tests/Feature/Jobs/PaymentEmailTest.php
@@ -1,122 +1,124 @@
<?php
namespace Tests\Feature\Jobs;
use App\Jobs\PaymentEmail;
use App\Mail\PaymentFailure;
use App\Mail\PaymentSuccess;
use App\Payment;
use App\Providers\PaymentProvider;
+use App\User;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class PaymentEmailTest extends TestCase
{
/**
* {@inheritDoc}
*
* @return void
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('PaymentEmail@UserAccount.com');
}
/**
* {@inheritDoc}
*
* @return void
*/
public function tearDown(): void
{
$this->deleteTestUser('PaymentEmail@UserAccount.com');
parent::tearDown();
}
/**
* Test job handle
*
* @return void
*/
public function testHandle()
{
- $user = $this->getTestUser('PaymentEmail@UserAccount.com');
+ $status = User::STATUS_ACTIVE | User::STATUS_LDAP_READY | User::STATUS_IMAP_READY;
+ $user = $this->getTestUser('PaymentEmail@UserAccount.com', ['status' => $status]);
$user->setSetting('external_email', 'ext@email.tld');
$wallet = $user->wallets()->first();
$payment = new Payment();
$payment->id = 'test-payment';
$payment->wallet_id = $wallet->id;
$payment->amount = 100;
$payment->currency_amount = 100;
$payment->currency = 'CHF';
$payment->status = PaymentProvider::STATUS_PAID;
$payment->description = 'test';
$payment->provider = 'stripe';
$payment->type = PaymentProvider::TYPE_ONEOFF;
$payment->save();
Mail::fake();
// Assert that no jobs were pushed...
Mail::assertNothingSent();
$job = new PaymentEmail($payment);
$job->handle();
// Assert the email sending job was pushed once
Mail::assertSent(PaymentSuccess::class, 1);
// Assert the mail was sent to the user's email
Mail::assertSent(PaymentSuccess::class, function ($mail) {
return $mail->hasTo('ext@email.tld') && !$mail->hasCc('ext@email.tld');
});
$payment->status = PaymentProvider::STATUS_FAILED;
$payment->save();
$job = new PaymentEmail($payment);
$job->handle();
// Assert the email sending job was pushed once
Mail::assertSent(PaymentFailure::class, 1);
// Assert the mail was sent to the user's email
Mail::assertSent(PaymentFailure::class, function ($mail) {
return $mail->hasTo('ext@email.tld') && !$mail->hasCc('ext@email.tld');
});
$payment->status = PaymentProvider::STATUS_EXPIRED;
$payment->save();
$job = new PaymentEmail($payment);
$job->handle();
// Assert the email sending job was pushed twice
Mail::assertSent(PaymentFailure::class, 2);
// None of statuses below should trigger an email
Mail::fake();
$states = [
PaymentProvider::STATUS_OPEN,
PaymentProvider::STATUS_CANCELED,
PaymentProvider::STATUS_PENDING,
PaymentProvider::STATUS_AUTHORIZED,
];
foreach ($states as $state) {
$payment->status = $state;
$payment->save();
$job = new PaymentEmail($payment);
$job->handle();
}
// Assert that no mailables were sent...
Mail::assertNothingSent();
}
}
diff --git a/src/tests/Feature/Jobs/PaymentMandateDisabledEmailTest.php b/src/tests/Feature/Jobs/PaymentMandateDisabledEmailTest.php
index f2c90556..5e0502ce 100644
--- a/src/tests/Feature/Jobs/PaymentMandateDisabledEmailTest.php
+++ b/src/tests/Feature/Jobs/PaymentMandateDisabledEmailTest.php
@@ -1,63 +1,65 @@
<?php
namespace Tests\Feature\Jobs;
use App\Jobs\PaymentMandateDisabledEmail;
use App\Mail\PaymentMandateDisabled;
+use App\User;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class PaymentMandateDisabledEmailTest extends TestCase
{
/**
* {@inheritDoc}
*
* @return void
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('PaymentEmail@UserAccount.com');
}
/**
* {@inheritDoc}
*
* @return void
*/
public function tearDown(): void
{
$this->deleteTestUser('PaymentEmail@UserAccount.com');
parent::tearDown();
}
/**
* Test job handle
*
* @return void
*/
public function testHandle()
{
- $user = $this->getTestUser('PaymentEmail@UserAccount.com');
+ $status = User::STATUS_ACTIVE | User::STATUS_LDAP_READY | User::STATUS_IMAP_READY;
+ $user = $this->getTestUser('PaymentEmail@UserAccount.com', ['status' => $status]);
$user->setSetting('external_email', 'ext@email.tld');
$wallet = $user->wallets()->first();
Mail::fake();
// Assert that no jobs were pushed...
Mail::assertNothingSent();
$job = new PaymentMandateDisabledEmail($wallet);
$job->handle();
// Assert the email sending job was pushed once
Mail::assertSent(PaymentMandateDisabled::class, 1);
// Assert the mail was sent to the user's email
Mail::assertSent(PaymentMandateDisabled::class, function ($mail) {
return $mail->hasTo('ext@email.tld') && !$mail->hasCc('ext@email.tld');
});
}
}
diff --git a/src/tests/Feature/Jobs/TrialEndEmailTest.php b/src/tests/Feature/Jobs/TrialEndEmailTest.php
index 017e3d9a..bc3d33c7 100644
--- a/src/tests/Feature/Jobs/TrialEndEmailTest.php
+++ b/src/tests/Feature/Jobs/TrialEndEmailTest.php
@@ -1,62 +1,64 @@
<?php
namespace Tests\Feature\Jobs;
use App\Jobs\TrialEndEmail;
use App\Mail\TrialEnd;
+use App\User;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class TrialEndEmailTest extends TestCase
{
/**
* {@inheritDoc}
*
* @return void
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('PaymentEmail@UserAccount.com');
}
/**
* {@inheritDoc}
*
* @return void
*/
public function tearDown(): void
{
$this->deleteTestUser('PaymentEmail@UserAccount.com');
parent::tearDown();
}
/**
* Test job handle
*
* @return void
*/
public function testHandle()
{
- $user = $this->getTestUser('PaymentEmail@UserAccount.com');
+ $status = User::STATUS_ACTIVE | User::STATUS_LDAP_READY | User::STATUS_IMAP_READY;
+ $user = $this->getTestUser('PaymentEmail@UserAccount.com', ['status' => $status]);
$user->setSetting('external_email', 'ext@email.tld');
Mail::fake();
// Assert that no jobs were pushed...
Mail::assertNothingSent();
$job = new TrialEndEmail($user);
$job->handle();
// Assert the email sending job was pushed once
Mail::assertSent(TrialEnd::class, 1);
// Assert the mail was sent to the user's email
Mail::assertSent(TrialEnd::class, function ($mail) {
return $mail->hasTo('paymentemail@useraccount.com') && !$mail->hasCc('ext@email.tld');
});
}
}
diff --git a/src/tests/Feature/Jobs/WalletCheckTest.php b/src/tests/Feature/Jobs/WalletCheckTest.php
index db8bebee..c57317db 100644
--- a/src/tests/Feature/Jobs/WalletCheckTest.php
+++ b/src/tests/Feature/Jobs/WalletCheckTest.php
@@ -1,399 +1,400 @@
<?php
namespace Tests\Feature\Jobs;
use App\Jobs\WalletCheck;
use App\User;
use App\Wallet;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class WalletCheckTest extends TestCase
{
/**
* {@inheritDoc}
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('wallet-check@kolabnow.com');
}
/**
* {@inheritDoc}
*/
public function tearDown(): void
{
$this->deleteTestUser('wallet-check@kolabnow.com');
parent::tearDown();
}
/**
* Test job handle, initial negative-balance notification
*/
public function testHandleInitial(): void
{
Mail::fake();
$user = $this->prepareTestUser($wallet);
$now = Carbon::now();
$wallet->balance = 0;
$wallet->save();
$job = new WalletCheck($wallet);
$job->handle();
Mail::assertNothingSent();
// Balance is negative now
$wallet->balance = -100;
$wallet->save();
$job = new WalletCheck($wallet);
$job->handle();
Mail::assertNothingSent();
// Balance turned negative 2 hours ago, expect mail sent
$wallet->setSetting('balance_negative_since', $now->subHours(2)->toDateTimeString());
$wallet->setSetting('balance_warning_initial', null);
$job = new WalletCheck($wallet);
$job->handle();
// Assert the mail was sent to the user's email, but not to his external email
Mail::assertSent(\App\Mail\NegativeBalance::class, 1);
Mail::assertSent(\App\Mail\NegativeBalance::class, function ($mail) use ($user) {
return $mail->hasTo($user->email) && !$mail->hasCc('external@test.com');
});
// Run the job again to make sure the notification is not sent again
Mail::fake();
$job = new WalletCheck($wallet);
$job->handle();
Mail::assertNothingSent();
// Test the migration scenario where a negative wallet has no balance_negative_since set yet
Mail::fake();
$wallet->setSetting('balance_negative_since', null);
$wallet->setSetting('balance_warning_initial', null);
$job = new WalletCheck($wallet);
$job->handle();
// Assert the mail was sent to the user's email, but not to his external email
Mail::assertSent(\App\Mail\NegativeBalance::class, 1);
Mail::assertSent(\App\Mail\NegativeBalance::class, function ($mail) use ($user) {
return $mail->hasTo($user->email) && !$mail->hasCc('external@test.com');
});
$wallet->refresh();
$today_regexp = '/' . Carbon::now()->toDateString() . ' [0-9]{2}:[0-9]{2}:[0-9]{2}/';
$this->assertMatchesRegularExpression($today_regexp, $wallet->getSetting('balance_negative_since'));
$this->assertMatchesRegularExpression($today_regexp, $wallet->getSetting('balance_warning_initial'));
}
/**
* Test job handle, top-up before reminder notification
*
* @depends testHandleInitial
*/
public function testHandleBeforeReminder(): void
{
Mail::fake();
$user = $this->prepareTestUser($wallet);
$now = Carbon::now();
// Balance turned negative 7-1 days ago
$wallet->setSetting('balance_negative_since', $now->subDays(7 - 1)->toDateTimeString());
$job = new WalletCheck($wallet);
$res = $job->handle();
Mail::assertNothingSent();
// TODO: Test that it actually executed the topUpWallet()
$this->assertSame(WalletCheck::THRESHOLD_BEFORE_REMINDER, $res);
$this->assertFalse($user->fresh()->isSuspended());
}
/**
* Test job handle, reminder notification
*
* @depends testHandleBeforeReminder
*/
public function testHandleReminder(): void
{
Mail::fake();
$user = $this->prepareTestUser($wallet);
$now = Carbon::now();
// Balance turned negative 7+1 days ago, expect mail sent
$wallet->setSetting('balance_negative_since', $now->subDays(7 + 1)->toDateTimeString());
$job = new WalletCheck($wallet);
$job->handle();
// Assert the mail was sent to the user's email and to his external email
Mail::assertSent(\App\Mail\NegativeBalanceReminderDegrade::class, 1);
Mail::assertSent(\App\Mail\NegativeBalanceReminderDegrade::class, function ($mail) use ($user) {
return $mail->hasTo($user->email) && $mail->hasCc('external@test.com');
});
// Run the job again to make sure the notification is not sent again
Mail::fake();
$job = new WalletCheck($wallet);
$job->handle();
Mail::assertNothingSent();
}
/**
* Test job handle, top-up wallet before account suspending
*
* @depends testHandleReminder
*/
/*
public function testHandleBeforeSuspended(): void
{
Mail::fake();
$user = $this->prepareTestUser($wallet);
$now = Carbon::now();
// Balance turned negative 7+14-1 days ago
$days = 7 + 14 - 1;
$wallet->setSetting('balance_negative_since', $now->subDays($days)->toDateTimeString());
$job = new WalletCheck($wallet);
$res = $job->handle();
Mail::assertNothingSent();
// TODO: Test that it actually executed the topUpWallet()
$this->assertSame(WalletCheck::THRESHOLD_BEFORE_SUSPEND, $res);
$this->assertFalse($user->fresh()->isSuspended());
}
*/
/**
* Test job handle, account suspending
*
* @depends testHandleBeforeSuspended
*/
/*
public function testHandleSuspended(): void
{
Mail::fake();
$user = $this->prepareTestUser($wallet);
$now = Carbon::now();
// Balance turned negative 7+14+1 days ago, expect mail sent
$days = 7 + 14 + 1;
$wallet->setSetting('balance_negative_since', $now->subDays($days)->toDateTimeString());
$job = new WalletCheck($wallet);
$job->handle();
// Assert the mail was sent to the user's email, but not to his external email
Mail::assertSent(\App\Mail\NegativeBalanceSuspended::class, 1);
Mail::assertSent(\App\Mail\NegativeBalanceSuspended::class, function ($mail) use ($user) {
return $mail->hasTo($user->email) && $mail->hasCc('external@test.com');
});
// Check that it has been suspended
$this->assertTrue($user->fresh()->isSuspended());
// TODO: Test that group account members/domain are also being suspended
// Run the job again to make sure the notification is not sent again
Mail::fake();
$job = new WalletCheck($wallet);
$job->handle();
Mail::assertNothingSent();
}
*/
/**
* Test job handle, final warning before delete
*
* @depends testHandleSuspended
*/
/*
public function testHandleBeforeDelete(): void
{
Mail::fake();
$user = $this->prepareTestUser($wallet);
$now = Carbon::now();
// Balance turned negative 7+14+21-3+1 days ago, expect mail sent
$days = 7 + 14 + 21 - 3 + 1;
$wallet->setSetting('balance_negative_since', $now->subDays($days)->toDateTimeString());
$job = new WalletCheck($wallet);
$job->handle();
// Assert the mail was sent to the user's email, and his external email
Mail::assertSent(\App\Mail\NegativeBalanceBeforeDelete::class, 1);
Mail::assertSent(\App\Mail\NegativeBalanceBeforeDelete::class, function ($mail) use ($user) {
return $mail->hasTo($user->email) && $mail->hasCc('external@test.com');
});
// Check that it has not been deleted yet
$this->assertFalse($user->fresh()->isDeleted());
// Run the job again to make sure the notification is not sent again
Mail::fake();
$job = new WalletCheck($wallet);
$job->handle();
Mail::assertNothingSent();
}
*/
/**
* Test job handle, account delete
*
* @depends testHandleBeforeDelete
*/
/*
public function testHandleDelete(): void
{
Mail::fake();
$user = $this->prepareTestUser($wallet);
$now = Carbon::now();
$this->assertFalse($user->isDeleted());
$this->assertCount(7, $user->entitlements()->get());
// Balance turned negative 7+14+21+1 days ago, expect mail sent
$days = 7 + 14 + 21 + 1;
$wallet->setSetting('balance_negative_since', $now->subDays($days)->toDateTimeString());
$job = new WalletCheck($wallet);
$job->handle();
Mail::assertNothingSent();
// Check that it has not been deleted
$this->assertTrue($user->fresh()->trashed());
$this->assertCount(0, $user->entitlements()->get());
// TODO: Test it deletes all members of the group account
}
*/
/**
* Test job handle, account degrade
*
* @depends testHandleReminder
*/
public function testHandleDegrade(): void
{
Mail::fake();
$user = $this->prepareTestUser($wallet);
$now = Carbon::now();
$this->assertFalse($user->isDegraded());
// Balance turned negative 7+7+1 days ago, expect mail sent
$days = 7 + 7 + 1;
$wallet->setSetting('balance_negative_since', $now->subDays($days)->toDateTimeString());
$job = new WalletCheck($wallet);
$job->handle();
// Assert the mail was sent to the user's email, and his external email
Mail::assertSent(\App\Mail\NegativeBalanceDegraded::class, 1);
Mail::assertSent(\App\Mail\NegativeBalanceDegraded::class, function ($mail) use ($user) {
return $mail->hasTo($user->email) && $mail->hasCc('external@test.com');
});
// Check that it has been degraded
$this->assertTrue($user->fresh()->isDegraded());
}
/**
* Test job handle, periodic reminder to a degraded account
*
* @depends testHandleDegrade
*/
public function testHandleDegradeReminder(): void
{
Mail::fake();
$user = $this->prepareTestUser($wallet);
$user->update(['status' => $user->status | User::STATUS_DEGRADED]);
$now = Carbon::now();
$this->assertTrue($user->isDegraded());
// Test degraded_last_reminder not set
$wallet->setSetting('degraded_last_reminder', null);
$job = new WalletCheck($wallet);
$res = $job->handle();
Mail::assertNothingSent();
$_last = Wallet::find($wallet->id)->getSetting('degraded_last_reminder');
$this->assertSame(Carbon::now()->toDateTimeString(), $_last);
$this->assertSame(WalletCheck::THRESHOLD_DEGRADE_REMINDER, $res);
// Test degraded_last_reminder set, but 14 days didn't pass yet
$last = $now->copy()->subDays(10);
$wallet->setSetting('degraded_last_reminder', $last->toDateTimeString());
$job = new WalletCheck($wallet);
$res = $job->handle();
Mail::assertNothingSent();
$_last = $wallet->fresh()->getSetting('degraded_last_reminder');
$this->assertSame(WalletCheck::THRESHOLD_DEGRADE_REMINDER, $res);
$this->assertSame($last->toDateTimeString(), $_last);
// Test degraded_last_reminder set, and 14 days passed
$wallet->setSetting('degraded_last_reminder', $now->copy()->subDays(14)->setSeconds(0));
$job = new WalletCheck($wallet);
$res = $job->handle();
// Assert the mail was sent to the user's email, and his external email
Mail::assertSent(\App\Mail\DegradedAccountReminder::class, 1);
Mail::assertSent(\App\Mail\DegradedAccountReminder::class, function ($mail) use ($user) {
return $mail->hasTo($user->email) && $mail->hasCc('external@test.com');
});
$_last = $wallet->fresh()->getSetting('degraded_last_reminder');
$this->assertSame(Carbon::now()->toDateTimeString(), $_last);
$this->assertSame(WalletCheck::THRESHOLD_DEGRADE_REMINDER, $res);
}
/**
* A helper to prepare a user for tests
*/
private function prepareTestUser(&$wallet)
{
- $user = $this->getTestUser('wallet-check@kolabnow.com');
+ $status = User::STATUS_ACTIVE | User::STATUS_LDAP_READY | User::STATUS_IMAP_READY;
+ $user = $this->getTestUser('wallet-check@kolabnow.com', ['status' => $status]);
$user->setSetting('external_email', 'external@test.com');
$wallet = $user->wallets()->first();
$package = \App\Package::withObjectTenantContext($user)->where('title', 'kolab')->first();
$user->assignPackage($package);
$wallet->balance = -100;
$wallet->save();
return $user;
}
}
diff --git a/src/tests/Unit/Mail/HelperTest.php b/src/tests/Unit/Mail/HelperTest.php
index 62735b25..8a027160 100644
--- a/src/tests/Unit/Mail/HelperTest.php
+++ b/src/tests/Unit/Mail/HelperTest.php
@@ -1,152 +1,164 @@
<?php
namespace Tests\Unit\Mail;
use App\Mail\Helper;
+use App\User;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class HelperTest extends TestCase
{
/**
* {@inheritDoc}
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('mail-helper-test@kolabnow.com');
\App\TenantSetting::truncate();
}
/**
* {@inheritDoc}
*/
public function tearDown(): void
{
$this->deleteTestUser('mail-helper-test@kolabnow.com');
\App\TenantSetting::truncate();
parent::tearDown();
}
/**
* Test Helper::sendMail()
*/
public function testSendMail(): void
{
Mail::fake();
$tenant = \App\Tenant::whereNotIn('id', [1])->first();
$invitation = new \App\SignupInvitation();
$invitation->id = 'test';
$mail = new \App\Mail\SignupInvitation($invitation);
Helper::sendMail($mail, null, ['to' => 'to@test.com', 'cc' => 'cc@test.com']);
Mail::assertSent(\App\Mail\SignupInvitation::class, 1);
Mail::assertSent(\App\Mail\SignupInvitation::class, function ($mail) {
return $mail->hasTo('to@test.com')
&& $mail->hasCc('cc@test.com')
&& $mail->hasFrom(\config('mail.from.address'), \config('mail.from.name'))
&& $mail->hasReplyTo(\config('mail.reply_to.address'), \config('mail.reply_to.name'));
});
// Test with a tenant (but no per-tenant settings)
Mail::fake();
$invitation->tenant_id = $tenant->id;
$mail = new \App\Mail\SignupInvitation($invitation);
Helper::sendMail($mail, $tenant->id, ['to' => 'to@test.com', 'cc' => 'cc@test.com']);
Mail::assertSent(\App\Mail\SignupInvitation::class, 1);
Mail::assertSent(\App\Mail\SignupInvitation::class, function ($mail) {
return $mail->hasTo('to@test.com')
&& $mail->hasCc('cc@test.com')
&& $mail->hasFrom(\config('mail.from.address'), \config('mail.from.name'))
&& $mail->hasReplyTo(\config('mail.reply_to.address'), \config('mail.reply_to.name'));
});
// Test with a tenant (but with per-tenant settings)
Mail::fake();
$tenant->setSettings([
'mail.from.address' => 'from@test.com',
'mail.from.name' => 'from name',
'mail.reply_to.address' => 'replyto@test.com',
'mail.reply_to.name' => 'replyto name',
]);
$mail = new \App\Mail\SignupInvitation($invitation);
Helper::sendMail($mail, $tenant->id, ['to' => 'to@test.com']);
Mail::assertSent(\App\Mail\SignupInvitation::class, 1);
Mail::assertSent(\App\Mail\SignupInvitation::class, function ($mail) {
return $mail->hasTo('to@test.com')
&& $mail->hasFrom('from@test.com', 'from name')
&& $mail->hasReplyTo('replyto@test.com', 'replyto name');
});
// TODO: Test somehow log entries, maybe with timacdonald/log-fake package
// TODO: Test somehow exception case
}
/**
* Test Helper::userEmails()
*/
public function testUserEmails(): void
{
- $user = $this->getTestUser('mail-helper-test@kolabnow.com');
+ $status = User::STATUS_ACTIVE | User::STATUS_LDAP_READY | User::STATUS_IMAP_READY;
+ $user = $this->getTestUser('mail-helper-test@kolabnow.com', ['status' => $status]);
// User with no mailbox and no external email
list($to, $cc) = Helper::userEmails($user);
$this->assertSame(null, $to);
$this->assertSame([], $cc);
list($to, $cc) = Helper::userEmails($user, true);
$this->assertSame(null, $to);
$this->assertSame([], $cc);
// User with no mailbox but with external email
$user->setSetting('external_email', 'external@test.com');
list($to, $cc) = Helper::userEmails($user);
$this->assertSame('external@test.com', $to);
$this->assertSame([], $cc);
list($to, $cc) = Helper::userEmails($user, true);
$this->assertSame('external@test.com', $to);
$this->assertSame([], $cc);
// User with mailbox and external email
$sku = \App\Sku::withEnvTenantContext()->where('title', 'mailbox')->first();
$user->assignSku($sku);
list($to, $cc) = Helper::userEmails($user);
$this->assertSame($user->email, $to);
$this->assertSame([], $cc);
list($to, $cc) = Helper::userEmails($user, true);
$this->assertSame($user->email, $to);
$this->assertSame(['external@test.com'], $cc);
// User with mailbox, but no external email
$user->setSetting('external_email', null);
list($to, $cc) = Helper::userEmails($user);
$this->assertSame($user->email, $to);
$this->assertSame([], $cc);
list($to, $cc) = Helper::userEmails($user, true);
$this->assertSame($user->email, $to);
$this->assertSame([], $cc);
+
+ // Use with mailbox, but not ready
+ $user->setSetting('external_email', 'external@test.com');
+ $user->status = User::STATUS_ACTIVE | User::STATUS_LDAP_READY;
+ $user->save();
+
+ list($to, $cc) = Helper::userEmails($user, true);
+
+ $this->assertSame(null, $to);
+ $this->assertSame(['external@test.com'], $cc);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Feb 6, 12:58 PM (1 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
428269
Default Alt Text
(43 KB)
Attached To
Mode
R2 kolab
Attached
Detach File
Event Timeline
Log In to Comment