Skip to content

Commit

Permalink
pkp#10306 unit tests for queue jobs
Browse files Browse the repository at this point in the history
  • Loading branch information
touhidurabir committed Sep 4, 2024
1 parent 41b38eb commit 7b04ccb
Show file tree
Hide file tree
Showing 23 changed files with 1,667 additions and 20 deletions.
7 changes: 7 additions & 0 deletions classes/core/PKPApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,13 @@ public function getUUID(): string
*/
public function getHttpClient()
{
if (PKPContainer::getInstance()->runningUnitTests()) {
$client = Registry::get(\PKP\tests\PKPTestCase::MOCKED_GUZZLE_CLIENT_NAME);
if ($client) {
return $client;
}
}

$application = Application::get();
$userAgent = $application->getName() . '/';
if (static::isInstalled() && !static::isUpgrading()) {
Expand Down
39 changes: 30 additions & 9 deletions classes/core/PKPContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@

class PKPContainer extends Container
{
/**
* Define if the app currently runing the unit test
*/
private bool $isRunningUnitTest = false;

/**
* The base path of the application, needed for base_path helper
*/
Expand Down Expand Up @@ -538,15 +543,6 @@ protected function settingProxyForStreamContext(): void
libxml_set_streams_context($context);
}

/**
* Override Laravel method; always false.
* Prevents the undefined method error when the Log Manager tries to determine the driver
*/
public function runningUnitTests(): bool
{
return false;
}

/**
* Determine if the application is currently down for maintenance.
*/
Expand Down Expand Up @@ -590,6 +586,31 @@ public function environment(string ...$environments): string|bool

return $this->get('config')['app']['env'];
}

/**
* Override Laravel method; always false.
* Prevents the undefined method error when the Log Manager tries to determine the driver
*/
public function runningUnitTests(): bool
{
return false;
}

/**
* Set the app running unit test
*/
public function setRunningUnitTests(): void
{
$this->isRunningUnitTest = true;
}

/**
* Unset the app running unit test
*/
public function unsetRunningUnitTests(): void
{
$this->isRunningUnitTest = false;
}
}

if (!PKP_STRICT_MODE) {
Expand Down
2 changes: 1 addition & 1 deletion classes/mail/mailables/EditorialReminder.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public function setOutstandingTasks(array $outstanding, array $submissions, int
$this->context->getPath(),
'workflow',
'access',
$submission->getId()
[$submission->getId()]
);

$outstandingTasks[] = '
Expand Down
2 changes: 1 addition & 1 deletion classes/notification/NotificationManagerDelegate.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function updateNotification(PKPRequest $request, ?array $userIds, int $as
*
* @copydoc PKPNotificationOperationManager::createNotification()
*/
public function createNotification(PKPRequest $request, ?int $userId = null, ?int $notificationType = null, ?int $contextId = Application::SITE_CONTEXT_ID, ?int $assocType = null, ?int $assocId = null, int $level = Notification::NOTIFICATION_LEVEL_NORMAL, ?array $params = null): ?Notification
public function createNotification(?PKPRequest $request = null, ?int $userId = null, ?int $notificationType = null, ?int $contextId = Application::SITE_CONTEXT_ID, ?int $assocType = null, ?int $assocId = null, int $level = Notification::NOTIFICATION_LEVEL_NORMAL, ?array $params = null): ?Notification
{
assert($notificationType == $this->getNotificationType() || $this->multipleTypesUpdate());
return parent::createNotification($request, $userId, $notificationType, $contextId, $assocType, $assocId, $level, $params);
Expand Down
2 changes: 1 addition & 1 deletion classes/notification/PKPNotificationOperationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public function getParamsForCurrentLocale(array $params): array
/**
* Create a new notification with the specified arguments and insert into DB
*/
public function createNotification(PKPRequest $request, ?int $userId = null, ?int $notificationType = null, ?int $contextId = Application::SITE_CONTEXT_ID, ?int $assocType = null, ?int $assocId = null, int $level = Notification::NOTIFICATION_LEVEL_NORMAL, ?array $params = null): ?Notification
public function createNotification(?PKPRequest $request = null, ?int $userId = null, ?int $notificationType = null, ?int $contextId = Application::SITE_CONTEXT_ID, ?int $assocType = null, ?int $assocId = null, int $level = Notification::NOTIFICATION_LEVEL_NORMAL, ?array $params = null): ?Notification
{
if ($userId && in_array($notificationType, $this->getUserBlockedNotifications($userId, $contextId))) {
return null;
Expand Down
74 changes: 66 additions & 8 deletions tests/PKPTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@
/**
* @file tests/PKPTestCase.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Copyright (c) 2014-2024 Simon Fraser University
* Copyright (c) 2000-2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class PKPTestCase
*
* @ingroup tests
*
* @brief Class that implements functionality common to all PKP unit test cases.
*/

Expand All @@ -23,16 +21,23 @@
use APP\core\Application;
use APP\core\PageRouter;
use APP\core\Request;
use Illuminate\Support\Facades\Mail;
use Mockery;
use Mockery\LegacyMockInterface;
use Mockery\MockInterface;
use PKP\core\PKPContainer;
use PHPUnit\Framework\TestCase;
use PKP\config\Config;
use PKP\core\Core;
use PKP\core\Dispatcher;
use PKP\core\PKPRequest;
use PKP\core\Registry;
use PKP\db\DAORegistry;

abstract class PKPTestCase extends TestCase
{
public const MOCKED_GUZZLE_CLIENT_NAME = 'GuzzleClient';

private array $daoBackup = [];
private array $registryBackup = [];
private array $containerBackup = [];
Expand Down Expand Up @@ -79,6 +84,9 @@ protected function setUp(): void
parent::setUp();
$this->setBackupGlobals(true);

// Set application running unit test
PKPContainer::getInstance()->setRunningUnitTests();

// Rather than using "include_once()", ADOdb uses
// a global variable to maintain the information
// whether its library has been included before (wtf!).
Expand Down Expand Up @@ -127,8 +135,15 @@ protected function tearDown(): void
DAORegistry::registerDAO($mockedDao, $this->daoBackup[$mockedDao]);
}

Mockery::close();
// Delete the mocked guzzle client from registry
Registry::delete(self::MOCKED_GUZZLE_CLIENT_NAME);

// Unset application running unit test
PKPContainer::getInstance()->unsetRunningUnitTests();

parent::tearDown();

Mockery::close();
}

/**
Expand All @@ -150,7 +165,7 @@ public function getActualOutput(): string
* @param string $config the id of the configuration to use
* @param string $configPath (optional) where to find the config file, default: 'config'
*/
protected function setTestConfiguration($config, $configPath = 'config')
protected function setTestConfiguration(string $config, string $configPath = 'config'): void
{
// Get the configuration file belonging to
// this test configuration.
Expand Down Expand Up @@ -181,7 +196,7 @@ protected function setTestConfiguration($config, $configPath = 'config')
*
* @return Request
*/
protected function mockRequest(string $path = 'index/test-page/test-op', int $userId = 0)
protected function mockRequest(string $path = 'index/test-page/test-op', int $userId = 0): PKPRequest
{
// Back up the default request.
if (!isset($this->registryBackup['request'])) {
Expand Down Expand Up @@ -222,7 +237,7 @@ protected function mockRequest(string $path = 'index/test-page/test-op', int $us
*
* @return string the resolved configuration file name
*/
private function getConfigFile($config, $configPath = 'config')
private function getConfigFile(string $config, string $configPath = 'config'): string
{
// Build the config file name.
return './lib/pkp/tests/' . $configPath . '/config.' . $config . '.inc.php';
Expand All @@ -238,6 +253,49 @@ protected function localeToRegExp(string $translation): string
$escapedPieces = array_map(fn ($piece) => preg_quote($piece, '/'), $pieces);
return '/^' . implode('.*?', $escapedPieces) . '$/u';
}

/**
* Mock the mail facade
* @see https://laravel.com/docs/10.x/mocking
*/
protected function mockMail(): void
{
/**
* @disregard P1013 PHP Intelephense error suppression
* @see https://github.com/bmewburn/vscode-intelephense/issues/568
*/
Mail::shouldReceive('send')
->withAnyArgs()
->andReturn(null)
->shouldReceive('compileParams')
->withAnyArgs()
->andReturn('');
}

/**
* Create mockable guzzle client
* @see https://docs.guzzlephp.org/en/stable/testing.html
*
* @param bool $setToRegistry Should store it in app registry to be used by call
* as `Application::get()->getHttpClient()`
*
* @return \Mockery\MockInterface|\Mockery\LegacyMockInterface
*/
protected function mockGuzzleClient(bool $setToRegistry = true): MockInterface|LegacyMockInterface
{
$guzzleClientMock = Mockery::mock(\GuzzleHttp\Client::class)
->makePartial()
->shouldReceive('request')
->withAnyArgs()
->andReturn(new \GuzzleHttp\Psr7\Response)
->getMock();

if ($setToRegistry) {
Registry::set(self::MOCKED_GUZZLE_CLIENT_NAME, $guzzleClientMock);
}

return $guzzleClientMock;
}
}

if (!PKP_STRICT_MODE) {
Expand Down
75 changes: 75 additions & 0 deletions tests/jobs/bulk/BulkEmailSenderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

/**
* @file tests/jobs/bulk/BulkEmailSenderTest.php
*
* Copyright (c) 2024 Simon Fraser University
* Copyright (c) 2024 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @brief Tests for bulk email sending job.
*/

namespace PKP\tests\jobs\bulk;

use Mockery;
use PKP\tests\PKPTestCase;
use PKP\jobs\bulk\BulkEmailSender;
use PKP\user\Collector as UserCollector;
use APP\user\Repository as UserRepository;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;

#[RunTestsInSeparateProcesses]
#[CoversClass(BulkEmailSender::class)]
class BulkEmailSenderTest extends PKPTestCase
{
/**
* serializion from OJS 3.4.0
*/
protected string $serializedJobData = <<<END
O:29:"PKP\\jobs\\bulk\BulkEmailSender":9:{s:10:"\0*\0userIds";a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}s:12:"\0*\0contextId";i:1;s:10:"\0*\0subject";s:12:"Test subject";s:7:"\0*\0body";s:16:"<p>Test body</p>";s:12:"\0*\0fromEmail";s:20:"rvaca@mailinator.com";s:11:"\0*\0fromName";s:11:"Ramiro Vaca";s:10:"connection";s:8:"database";s:5:"queue";s:5:"queue";s:7:"batchId";s:36:"9c1cbc05-017b-4a02-bd5a-b113c92a7735";}
END;

/**
* Test job is a proper instance
*/
public function testUnserializationGetProperJobInstance(): void
{
$this->assertInstanceOf(
BulkEmailSender::class,
unserialize($this->serializedJobData)
);
}

/**
* Ensure that a serialized job can be unserialized and executed
*/
public function testRunSerializedJob(): void
{
$this->mockMail();

/** @var BulkEmailSender $bulkEmailSenderJob*/
$bulkEmailSenderJob = unserialize($this->serializedJobData);

$userCollectorMock = Mockery::mock(app(UserCollector::class))
->makePartial()
->shouldReceive('getMany')
->withAnyArgs()
->andReturn(\Illuminate\Support\LazyCollection::make([new \PKP\user\User]))
->getMock();

app()->instance(UserCollector::class, $userCollectorMock);

$userRepoMock = Mockery::mock(app(UserRepository::class))
->makePartial()
->shouldReceive('getCollector')
->withAnyArgs()
->andReturn($userCollectorMock)
->getMock();

app()->instance(UserRepository::class, $userRepoMock);

$this->assertNull($bulkEmailSenderJob->handle());
}
}
Loading

0 comments on commit 7b04ccb

Please sign in to comment.