Page MenuHomePhorge

No OneTemporary

Size
43 KB
Referenced Files
None
Subscribers
None
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

Mime Type
text/x-diff
Expires
Fri, Feb 6, 12:58 PM (12 m, 17 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
428269
Default Alt Text
(43 KB)

Event Timeline