File: /home/xedaptot/be.naniguide.com/tests/Unit/QuotaTest.php
<?php
namespace Tests\Unit;
use Tests\TestCase;
use Acelle\Model\SendingServer;
use Acelle\Model\Subscription;
use Acelle\Model\EmailVerificationServer;
use Acelle\Model\Subscriber;
use Acelle\Model\Customer;
use Acelle\Model\Campaign;
use Acelle\Model\MailList;
use Acelle\Library\Exception\RateLimitExceeded;
use Acelle\Library\Exception\OutOfCredits;
use Acelle\Jobs\SendMessage;
use Acelle\Jobs\VerifySubscriber;
use Exception;
use Mockery;
use Acelle\Library\RateTracker;
use Acelle\Library\RateLimit;
use Acelle\Library\CreditTracker;
use Carbon\Carbon;
use Acelle\Library\RouletteWheel;
use function Acelle\Helpers\withQuota;
class QuotaTest extends TestCase
{
public function initServer()
{
$server = new SendingServer();
$server->generateUid();
return $server;
}
public function initSubscription()
{
$subscription = new Subscription();
$subscription->generateUid();
return $subscription;
}
public function initEmailVerificationServer()
{
$server = new EmailVerificationServer();
$server->generateUid();
$server = Mockery::mock($server);
$server->shouldReceive('verify')->andReturn(null);
return $server;
}
private function prepareSendMessageTest()
{
// Server
$server = $this->initServer();
$server = Mockery::mock($server);
$server->shouldReceive('send')->andReturn(null);
$server->shouldReceive('dryrun')->andReturn(null);
// Subscription
$subscription = Mockery::mock($this->initSubscription());
// Subscriber type is required as the new SendMessage(Subscriber $sub) has strict type parameter
$subscriber = Mockery::mock(new Subscriber());
$subscriber->shouldReceive('getEmail')->andReturn('[email protected]');
// Logger
$logger = Mockery::mock('logger');
$logger->shouldReceive('info')->andReturn(null);
$logger->shouldReceive('warning')->andReturn(null);
$logger->shouldReceive('error')->andReturn(null);
// Campaign
$campaign = Mockery::mock(new Campaign());
$campaign->shouldReceive('logger')->andReturn($logger);
$campaign->shouldReceive('prepareEmail')->andReturn(null);
$campaign->shouldReceive('trackMessage')->andReturn(null);
return [$campaign, $subscriber, $server, $subscription];
}
public function test_send_message_job_with_rate_limit_exceeded()
{
list($campaign, $subscriber, $server, $subscription) = $this->prepareSendMessageTest();
$server->shouldReceive('getRateLimitTracker')->andReturn(
new RateTracker('/tmp/test-server-rate-log-'.uniqid(), [
new RateLimit(
$amount = 2,
$periodValue = 1,
$periodUnit = 'minute',
)
])
);
$subscription->shouldReceive('getSendEmailRateTracker')->andReturn(
new RateTracker('/tmp/test-subscription-rate-log-'.$subscription->uid, [
new RateLimit(
$amount = 1,
$periodValue = 1,
$periodUnit = 'minute',
)
])
);
$subscription->shouldReceive('getSendEmailCreditTracker')->andReturn(
CreditTracker::load('/tmp/test-subscription-credit-'.$subscription->uid, $createFileIfNotExist = true)->setCredits(100)
);
// Record exception to test
$outcome = null;
$pool = new RouletteWheel();
$pool->add($server, 100);
// 1st execution
$sendMsgJob = new SendMessage($campaign, $subscriber, $pool, $subscription);
$sendMsgJob->send(function ($exception) use (&$outcome) {
$outcome = $exception;
});
$this->assertNull($outcome);
$this->assertEquals($server->getRateLimitTracker()->getCreditsUsed(Carbon::parse('5 minutes ago')), 1);
$this->assertEquals($subscription->getSendEmailRateTracker()->getCreditsUsed(Carbon::parse('5 minutes ago')), 1);
// FAILED, as credits tracking is commented out in SendMessage
$this->assertEquals($subscription->getSendEmailCreditTracker()->getRemainingCredits(), 99);
// 2st execution, hit subscription rate limit
$sendMsgJob = new SendMessage($campaign, $subscriber, $pool, $subscription);
$sendMsgJob->send(function ($exception) use (&$outcome) {
$outcome = $exception;
});
$this->assertEquals($server->getRateLimitTracker()->getCreditsUsed(Carbon::parse('5 minutes ago')), 1); // Server counts
$this->assertEquals($subscription->getSendEmailRateTracker()->getCreditsUsed(Carbon::parse('5 minutes ago')), 1); // sub fails to count, so still 1 (max 1/minute)
$this->assertEquals(RateLimitExceeded::class, get_class($outcome));
// FAILED, as credits tracking is commented out in SendMessage
$this->assertEquals($subscription->getSendEmailCreditTracker()->getRemainingCredits(), 99);
}
public function test_send_message_job_with_credits_exceeded()
{
list($campaign, $subscriber, $server, $subscription) = $this->prepareSendMessageTest();
$server->shouldReceive('getRateLimitTracker')->andReturn(
new RateTracker('/tmp/test-server-rate-log-'.$server->uid, [
new RateLimit(
$amount = 3,
$periodValue = 1,
$periodUnit = 'minute',
)
])
);
$subscription->shouldReceive('getSendEmailRateTracker')->andReturn(
new RateTracker('/tmp/test-subscription-rate-log-'.$subscription->uid, [
new RateLimit(
$amount = 2,
$periodValue = 1,
$periodUnit = 'minute',
)
])
);
$subscription->shouldReceive('getSendEmailCreditTracker')->andReturn(
CreditTracker::load('/tmp/test-subscription-credit-'.$subscription->uid, $createFileIfNotExist = true)->setCredits(1)
);
$outcome = null;
$pool = new RouletteWheel();
$pool->add($server, 100);
// SendMessage job 1st time
$sendMsgJob = new SendMessage($campaign, $subscriber, $pool, $subscription);
$sendMsgJob->send(function ($exception) use (&$outcome) {
$outcome = $exception;
});
$this->assertEquals($server->getRateLimitTracker()->getCreditsUsed(Carbon::parse('10 minute ago')), 1);
$this->assertEquals($subscription->getSendEmailRateTracker()->getCreditsUsed(Carbon::parse('10 minute ago')), 1);
// FAILED, as credits tracking is commented out in SendMessage
// $this->assertEquals($subscription->getSendEmailCreditTracker()->getRemainingCredits(), 0);
$this->assertNull($outcome);
// SendMessage job 2nd time
$sendMsgJob = new SendMessage($campaign, $subscriber, $pool, $subscription);
$sendMsgJob->send(function ($exception) use (&$outcome) {
$outcome = $exception;
});
$this->assertEquals(OutOfCredits::class, get_class($outcome));
$this->assertEquals($server->getRateLimitTracker()->getCreditsUsed(Carbon::parse('10 minute ago')), 1);
$this->assertEquals($subscription->getSendEmailRateTracker()->getCreditsUsed(Carbon::parse('10 minute ago')), 1);
$this->assertEquals($subscription->getSendEmailCreditTracker()->getRemainingCredits(), 0);
}
public function test_email_verification_server_job_server_rate_limit_exceeded()
{
// Server
$server = $this->initEmailVerificationServer();
$subscription = $this->initSubscription();
$list = Mockery::mock(new MailList());
$customer = Mockery::mock(new Customer());
$list->customer = $customer;
$subscriber = Mockery::mock(new Subscriber());
$subscriber->mailList = $list;
$subscriber->shouldReceive('verify')->andReturn(null);
$subscription->getVerifyEmailCreditTracker()->setCredits(100);
// $server->setLimit($limit = 2, $unitValue = 1, $unit = 'minute');
$file = storage_path('app/quota/verification-server-verify-email-rate-tracking-log-'.$server->uid);
$server->shouldReceive('getRateTracker')->andReturn(
new RateTracker($file, [
new RateLimit(
$amount = 2,
$periodValue = 1,
$periodUnit = 'minute',
)
])
);
// SendMessage job
$verifyJob = new VerifySubscriber($subscriber, $server, $subscription);
// First shot
$verifyJob->doVerify(function ($exception) use (&$outcome) {
$outcome = $exception;
});
// Just fine
$this->assertNull($outcome);
$this->assertEquals($server->getRateTracker()->getCreditsUsed(), 1);
$this->assertEquals($subscription->getVerifyEmailCreditTracker()->getRemainingCredits(), 99);
// Second shot
$verifyJob->doVerify(function ($exception) use (&$outcome) {
$outcome = $exception;
});
// Just fine
$this->assertNull($outcome);
$this->assertEquals($server->getRateTracker()->getCreditsUsed(), 2);
$this->assertEquals($subscription->getVerifyEmailCreditTracker()->getRemainingCredits(), 98);
// Third short
$verifyJob->doVerify(function ($exception) use (&$outcome) {
$outcome = $exception;
});
// Credit does not count in case of any exception
$this->assertEquals($subscription->getVerifyEmailCreditTracker()->getRemainingCredits(), 98);
// Second, quota exceeded
$this->assertEquals(RateLimitExceeded::class, get_class($outcome));
$this->assertEquals($server->getRateTracker()->getCreditsUsed(), 2);
}
}