diff --git a/classes/core/PKPApplication.php b/classes/core/PKPApplication.php index e02dd22d551..124aa474f84 100644 --- a/classes/core/PKPApplication.php +++ b/classes/core/PKPApplication.php @@ -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()) { diff --git a/classes/core/PKPContainer.php b/classes/core/PKPContainer.php index 26f798f3a98..fcdecfc84db 100644 --- a/classes/core/PKPContainer.php +++ b/classes/core/PKPContainer.php @@ -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 */ @@ -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. */ @@ -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) { diff --git a/classes/mail/mailables/EditorialReminder.php b/classes/mail/mailables/EditorialReminder.php index 80fd188e66a..10411380e37 100644 --- a/classes/mail/mailables/EditorialReminder.php +++ b/classes/mail/mailables/EditorialReminder.php @@ -74,7 +74,7 @@ public function setOutstandingTasks(array $outstanding, array $submissions, int $this->context->getPath(), 'workflow', 'access', - $submission->getId() + [$submission->getId()] ); $outstandingTasks[] = ' diff --git a/classes/notification/NotificationManagerDelegate.php b/classes/notification/NotificationManagerDelegate.php index 37b4dd2842e..2223fb76eb8 100644 --- a/classes/notification/NotificationManagerDelegate.php +++ b/classes/notification/NotificationManagerDelegate.php @@ -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); diff --git a/classes/notification/PKPNotificationOperationManager.php b/classes/notification/PKPNotificationOperationManager.php index 00e5c026591..751caf7a9db 100644 --- a/classes/notification/PKPNotificationOperationManager.php +++ b/classes/notification/PKPNotificationOperationManager.php @@ -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; diff --git a/tests/PKPTestCase.php b/tests/PKPTestCase.php index f17b6adc4d3..95bc000a4cf 100644 --- a/tests/PKPTestCase.php +++ b/tests/PKPTestCase.php @@ -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. */ @@ -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 = []; @@ -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!). @@ -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(); } /** @@ -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. @@ -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'])) { @@ -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'; @@ -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) { diff --git a/tests/jobs/bulk/BulkEmailSenderTest.php b/tests/jobs/bulk/BulkEmailSenderTest.php new file mode 100644 index 00000000000..7c7c86e4380 --- /dev/null +++ b/tests/jobs/bulk/BulkEmailSenderTest.php @@ -0,0 +1,75 @@ +Test body
";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()); + } +} diff --git a/tests/jobs/doi/DepositContextTest.php b/tests/jobs/doi/DepositContextTest.php new file mode 100644 index 00000000000..b0e7c19d422 --- /dev/null +++ b/tests/jobs/doi/DepositContextTest.php @@ -0,0 +1,84 @@ +assertInstanceOf( + DepositContext::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + /** @var DepositContext $depositContextJob */ + $depositContextJob = unserialize($this->serializedJobData); + + $contextDaoClass = get_class(Application::getContextDAO()); + + $contextMock = Mockery::mock(get_class(Application::getContextDAO()->newDataObject())) + ->makePartial() + ->shouldReceive('getData') + ->with(Context::SETTING_DOI_AUTOMATIC_DEPOSIT) + ->andReturn(true) + ->getMock(); + + $contextDaoMock = Mockery::mock($contextDaoClass) + ->makePartial() + ->shouldReceive('getById') + ->withAnyArgs() + ->andReturn($contextMock) + ->getMock(); + + DAORegistry::registerDAO(substr(strrchr($contextDaoClass, '\\'), 1), $contextDaoMock); + + $doiRepoMock = Mockery::mock(app(DoiRepository::class)) + ->makePartial() + ->shouldReceive('depositAll') + ->with($contextMock) + ->andReturn(null) + ->getMock(); + + app()->instance(DoiRepository::class, $doiRepoMock); + + $this->assertNull($depositContextJob->handle()); + } +} diff --git a/tests/jobs/doi/DepositSubmissionTest.php b/tests/jobs/doi/DepositSubmissionTest.php new file mode 100644 index 00000000000..b20dbe534a9 --- /dev/null +++ b/tests/jobs/doi/DepositSubmissionTest.php @@ -0,0 +1,140 @@ +This is an automated message from Journal of Public Knowledge.
";s:19:"enableAnnouncements";b:1;s:15:"enabledDoiTypes";a:2:{i:0;s:11:"publication";i:1;s:5:"issue";}s:10:"enableDois";b:1;s:19:"enableGeoUsageStats";s:8:"disabled";s:27:"enableInstitutionUsageStats";b:0;s:9:"enableOai";b:1;s:16:"isSushiApiPublic";b:1;s:12:"itemsPerPage";i:25;s:8:"keywords";s:7:"request";s:14:"mailingAddress";s:49:"123 456th Street Burnaby, British Columbia Canada";s:13:"membershipFee";d:0;s:16:"notifyAllAuthors";b:1;s:12:"numPageLinks";i:10;s:19:"numWeeksPerResponse";i:4;s:17:"numWeeksPerReview";i:4;s:10:"onlineIssn";s:9:"0378-5955";s:17:"paymentPluginName";s:13:"PaypalPayment";s:15:"paymentsEnabled";b:1;s:9:"printIssn";s:9:"0378-5955";s:14:"publicationFee";d:0;s:20:"publisherInstitution";s:24:"Public Knowledge Project";s:18:"purchaseArticleFee";d:0;s:18:"registrationAgency";s:14:"dataciteplugin";s:25:"submissionAcknowledgement";s:10:"allAuthors";s:20:"submitWithCategories";b:0;s:20:"supportedFormLocales";a:2:{i:0;s:2:"en";i:1;s:5:"fr_CA";}s:16:"supportedLocales";a:2:{i:0;s:2:"en";i:1;s:5:"fr_CA";}s:26:"supportedSubmissionLocales";a:2:{i:0;s:2:"en";i:1;s:5:"fr_CA";}s:12:"supportEmail";s:20:"rvaca@mailinator.com";s:11:"supportName";s:11:"Ramiro Vaca";s:15:"themePluginPath";s:7:"default";s:12:"abbreviation";a:1:{s:2:"en";s:25:"publicknowledgeJ Pub Know";}s:7:"acronym";a:1:{s:2:"en";s:6:"JPKJPK";}s:16:"authorGuidelines";a:2:{s:2:"en";s:1209:"Authors are invited to make a submission to this journal. All submissions will be assessed by an editor to determine whether they meet the aims and scope of this journal. Those considered to be a good fit will be sent for peer review before determining whether they will be accepted or rejected.
Before making a submission, authors are responsible for obtaining permission to publish any material included with the submission, such as photos, documents and datasets. All authors identified on the submission must consent to be identified as an author. Where appropriate, research should be approved by an appropriate ethics committee in accordance with the legal requirements of the study's country.
An editor may desk reject a submission if it does not meet minimum standards of quality. Before submitting, please ensure that the study design and research argument are structured and articulated properly. The title should be concise and the abstract should be able to stand on its own. This will increase the likelihood of reviewers agreeing to review the paper. When you're satisfied that your submission meets this standard, please follow the checklist below to prepare your submission.
";s:5:"fr_CA";s:44:"##default.contextSettings.authorGuidelines##";}s:17:"authorInformation";a:2:{s:2:"en";s:586:"Interested in submitting to this journal? We recommend that you review the About the Journal page for the journal's section policies, as well as the Author Guidelines. Authors need to register with the journal prior to submitting or, if already registered, can simply log in and begin the five-step process.";s:5:"fr_CA";s:715:"Intéressé-e à soumettre à cette revue ? Nous vous recommandons de consulter les politiques de rubrique de la revue à la page À propos de la revue ainsi que les Directives aux auteurs. Les auteurs-es doivent s'inscrire auprès de la revue avant de présenter une soumission, ou s'ils et elles sont déjà inscrits-es, simplement ouvrir une session et accéder au tableau de bord pour commencer les 5 étapes du processus.";}s:19:"beginSubmissionHelp";a:2:{s:2:"en";s:611:"Thank you for submitting to the Journal of Public Knowledge. You will be asked to upload files, identify co-authors, and provide information such as the title and abstract.
Please read our Submission Guidelines if you have not done so already. When filling out the forms, provide as many details as possible in order to help our editors evaluate your work.
Once you begin, you can save your submission and come back to it later. You will be able to review and correct any information before you submit.
";s:5:"fr_CA";s:42:"##default.submission.step.beforeYouBegin##";}s:14:"clockssLicense";a:2:{s:2:"en";s:271:"This journal utilizes the CLOCKSS system to create a distributed archiving system among participating libraries and permits those libraries to create permanent archives of the journal for purposes of preservation and restoration. More...";s:5:"fr_CA";s:315:"Cette revue utilise le système CLOCKSS pour créer un système d'archivage distribué parmi les bibliothèques participantes et permet à ces bibliothèques de créer des archives permanentes de la revue à des fins de conservation et de reconstitution. En apprendre davantage... ";}s:16:"contributorsHelp";a:2:{s:2:"en";s:504:"Add details for all of the contributors to this submission. Contributors added here will be sent an email confirmation of the submission, as well as a copy of all editorial decisions recorded against this submission.
If a contributor can not be contacted by email, because they must remain anonymous or do not have an email account, please do not enter a fake email address. You can add information about this contributor in a message to the editor at a later step in the submission process.
";s:5:"fr_CA";s:40:"##default.submission.step.contributors##";}s:13:"customHeaders";a:1:{s:2:"en";s:41:"";}s:11:"description";a:2:{s:2:"en";s:123:"The Journal of Public Knowledge is a peer-reviewed quarterly publication on the subject of public access to science.
";s:5:"fr_CA";s:146:"Le Journal de Public Knowledge est une publication trimestrielle évaluée par les pairs sur le thème de l'accès du public à la science.
";}s:11:"detailsHelp";a:2:{s:2:"en";s:92:"Please provide the following details to help us manage your submission in our system.
";s:5:"fr_CA";s:35:"##default.submission.step.details##";}s:17:"forTheEditorsHelp";a:2:{s:2:"en";s:278:"Please provide the following details in order to help our editorial team manage your submission.
When entering metadata, provide entries that you think would be most helpful to the person managing your submission. This information can be changed before publication.
";s:5:"fr_CA";s:41:"##default.submission.step.forTheEditors##";}s:20:"librarianInformation";a:2:{s:2:"en";s:361:"We encourage research librarians to list this journal among their library's electronic journal holdings. As well, it may be worth noting that this journal's open source publishing system is suitable for libraries to host for their faculty members to use with journals they are involved in editing (see Open Journal Systems).";s:5:"fr_CA";s:434:"Nous incitons les bibliothécaires à lister cette revue dans leur fonds de revues numériques. Aussi, il peut être pertinent de mentionner que ce système de publication en libre accès est conçu pour être hébergé par les bibliothèques de recherche pour que les membres de leurs facultés l'utilisent avec les revues dans lesquelles elles ou ils sont impliqués (voir Open Journal Systems).";}s:13:"lockssLicense";a:2:{s:2:"en";s:273:"This journal utilizes the LOCKSS system to create a distributed archiving system among participating libraries and permits those libraries to create permanent archives of the journal for purposes of preservation and restoration. More...";s:5:"fr_CA";s:314:"Cette revue utilise le système LOCKSS pour créer un système de distribution des archives parmi les bibliothèques participantes et afin de permettre à ces bibliothèques de créer des archives permanentes pour fins de préservation et de restauration. En apprendre davantage...";}s:4:"name";a:2:{s:2:"en";s:27:"Journal of Public Knowledge";s:5:"fr_CA";s:36:"Journal de la connaissance du public";}s:16:"openAccessPolicy";a:2:{s:2:"en";s:176:"This journal provides immediate open access to its content on the principle that making research freely available to the public supports a greater global exchange of knowledge.";s:5:"fr_CA";s:217:"Cette revue fournit le libre accès immédiat à son contenu se basant sur le principe que rendre la recherche disponible au public gratuitement facilite un plus grand échange du savoir, à l'échelle de la planète.";}s:16:"privacyStatement";a:2:{s:2:"en";s:206:"The names and email addresses entered in this journal site will be used exclusively for the stated purposes of this journal and will not be made available for any other purpose or to any other party.
";s:5:"fr_CA";s:193:"Les noms et courriels saisis dans le site de cette revue seront utilisés exclusivement aux fins indiquées par cette revue et ne serviront à aucune autre fin, ni à toute autre partie.
";}s:17:"readerInformation";a:2:{s:2:"en";s:654:"We encourage readers to sign up for the publishing notification service for this journal. Use the Register link at the top of the home page for the journal. This registration will result in the reader receiving the Table of Contents by email for each new issue of the journal. This list also allows the journal to claim a certain level of support or readership. See the journal's Privacy Statement, which assures readers that their name and email address will not be used for other purposes.";s:5:"fr_CA";s:716:"Nous invitons les lecteurs-trices à s'inscrire pour recevoir les avis de publication de cette revue. Utiliser le lien S'inscrire en haut de la page d'accueil de la revue. Cette inscription permettra au,à la lecteur-trice de recevoir par courriel le sommaire de chaque nouveau numéro de la revue. Cette liste permet aussi à la revue de revendiquer un certain niveau de soutien ou de lectorat. Voir la Déclaration de confidentialité de la revue qui certifie aux lecteurs-trices que leur nom et leur courriel ne seront pas utilisés à d'autres fins.";}s:10:"reviewHelp";a:2:{s:2:"en";s:368:"Review the information you have entered before you complete your submission. You can change any of the details displayed here by clicking the edit button at the top of each section.
Once you complete your submission, a member of our editorial team will be assigned to review it. Please ensure the details you have entered here are as accurate as possible.
";s:5:"fr_CA";s:34:"##default.submission.step.review##";}s:17:"searchDescription";a:1:{s:2:"en";s:116:"The Journal of Public Knowledge is a peer-reviewed quarterly publication on the subject of public access to science.";}s:19:"submissionChecklist";a:2:{s:2:"en";s:591:"All submissions must meet the following requirements.
Provide any files our editorial team may need to evaluate your submission. In addition to the main work, you may wish to submit data sets, conflict of interest statements, or other supplementary files if these will be helpful for our editors.
";s:5:"fr_CA";s:39:"##default.submission.step.uploadFiles##";}}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:9:"\0*\0agency";O:43:"APP\\plugins\\generic\\datacite\\DatacitePlugin":4:{s:10:"pluginPath";s:24:"plugins/generic/datacite";s:14:"pluginCategory";s:7:"generic";s:7:"request";N;s:58:"\0APP\\plugins\\generic\\datacite\\DatacitePlugin\0_exportPlugin";N;}s:10:"connection";s:8:"database";s:5:"queue";s:5:"queue";} + END; + + /** + * Test job is a proper instance + */ + public function testUnserializationGetProperJobInstance(): void + { + $this->assertInstanceOf( + DepositSubmission::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + // need to mock request so that a valid context information is set and can be retrived + $this->mockRequest(); + + $this->mockGuzzleClient(); + + /** @var DepositSubmission $depositSubmissionJob */ + $depositSubmissionJob = unserialize($this->serializedJobData); + + /** + * @disregard P1013 PHP Intelephense error suppression + * @see https://github.com/bmewburn/vscode-intelephense/issues/568 + */ + $publicationMock = Mockery::mock(\APP\publication\Publication::class) + ->makePartial() + ->shouldReceive('getData') + ->with('authors') + ->andReturn(\Illuminate\Support\LazyCollection::make([new \PKP\author\Author()])) + ->shouldReceive('getData') + ->with('issueId') + ->andReturn(1) + ->shouldReceive('getData') + ->with('title') + ->andReturn(['en' => 'Test title', 'fr_CA' => 'Test title']) + ->shouldReceive('getTitles') + ->with('text') + ->andReturn(['en' => 'Test title', 'fr_CA' => 'Test title']) + ->shouldReceive('getLocalizedTitle') + ->withAnyArgs() + ->andReturn('Test title') + ->shouldReceive('getData') + ->with('galleys') + ->andReturn( + \Illuminate\Support\LazyCollection::make([Mockery::mock(\PKP\galley\Galley::class) + ->makePartial() + ->shouldReceive('getDoi') + ->withAnyArgs() + ->andReturn('10.1234/mq45t6723') + ->getMock()]) + ) + ->shouldReceive('getData') + ->with('doiObject') + ->andReturn(new \PKP\doi\Doi) + ->getMock(); + + $submissionMock = Mockery::mock(\APP\submission\Submission::class) + ->makePartial() + ->shouldReceive([ + 'getDatePublished' => \Carbon\Carbon::today() + ->startOfYear() + ->format('Y-m-d H:i:s'), + 'getStoredPubId' => '10.1234/mq45t6723', + // 'getData' => '', + 'getCurrentPublication' => $publicationMock, + ]) + ->shouldReceive('getData') + ->with('doiObject') + ->andReturn(new \PKP\doi\Doi()) + ->getMock(); + + $submissionDaoMock = Mockery::mock(\APP\submission\DAO::class, [ + new \PKP\services\PKPSchemaService() + ]) + ->makePartial() + ->shouldReceive([ + 'fromRow' => $submissionMock + ]) + ->withAnyArgs() + ->getMock(); + + $submissionRepoMock = Mockery::mock(app(SubmissionRepository::class)) + ->makePartial() + ->shouldReceive('get') + ->withAnyArgs() + ->andReturn($submissionMock) + ->set('dao', $submissionDaoMock) + ->getMock(); + + app()->instance(SubmissionRepository::class, $submissionRepoMock); + + $doiRepoMock = Mockery::mock(app(DoiRepository::class)) + ->makePartial() + ->shouldReceive('edit') + ->withAnyArgs() + ->andReturn(null) + ->getMock(); + + app()->instance(DoiRepository::class, $doiRepoMock); + + $this->assertNull($depositSubmissionJob->handle()); + } +} diff --git a/tests/jobs/email/EditorialReminderTest.php b/tests/jobs/email/EditorialReminderTest.php new file mode 100644 index 00000000000..33389eedc86 --- /dev/null +++ b/tests/jobs/email/EditorialReminderTest.php @@ -0,0 +1,213 @@ +assertInstanceOf( + EditorialReminder::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + $this->mockRequest(); + + $this->mockMail(); + + /** @var EditorialReminder $editorialReminderJob*/ + $editorialReminderJob = unserialize($this->serializedJobData); + + $notificationSubscriptionSettingsDAO = Mockery::mock( + \PKP\notification\NotificationSubscriptionSettingsDAO::class + ) + ->makePartial() + ->shouldReceive('getNotificationSubscriptionSettings') + ->withAnyArgs() + ->andReturn([]) + ->getMock(); + + DAORegistry::registerDAO( + 'NotificationSubscriptionSettingsDAO', + $notificationSubscriptionSettingsDAO + ); + + $contextMock = Mockery::mock(\PKP\context\Context::class) + ->makePartial() + ->shouldReceive([ + 'getPath' => '', + 'getId' => 0, + 'getContactEmail' => 'testmail@test.com', + 'getLocalizedName' => 'Test Context', + 'getPrimaryLocale' => 'en', + 'getSupportedLocales' => ['en', 'fr_CA'], + ]) + ->withAnyArgs() + ->getMock(); + + $contextServiceMock = Mockery::mock(\APP\services\ContextService::class) + ->makePartial() + ->shouldReceive('get') + ->withAnyArgs() + ->andReturn($contextMock) + ->getMock(); + + app()->instance('context', $contextServiceMock); + + $userRepoMock = Mockery::mock(app(UserRepository::class)) + ->makePartial() + ->shouldReceive('get') + ->withAnyArgs() + ->andReturn(new \PKP\user\User) + ->getMock(); + + app()->instance(UserRepository::class, $userRepoMock); + + /** + * @disregard P1013 PHP Intelephense error suppression + * @see https://github.com/bmewburn/vscode-intelephense/issues/568 + */ + Locale::shouldReceive('getLocale') + ->withAnyArgs() + ->andReturn('en') + ->shouldReceive('get') + ->withAnyArgs() + ->andReturn('') + ->shouldReceive('setLocale') + ->withAnyArgs() + ->andReturn(null); + + /** + * @disregard P1013 PHP Intelephense error suppression + * @see https://github.com/bmewburn/vscode-intelephense/issues/568 + */ + $submissionCollectorMock = Mockery::mock(app(SubmissionCollector::class)) + ->makePartial() + ->shouldReceive('assignedTo') + ->withAnyArgs() + ->andReturnSelf() + ->shouldReceive('filterByContextIds') + ->withAnyArgs() + ->andReturnSelf() + ->shouldReceive('filterByStatus') + ->withAnyArgs() + ->andReturnSelf() + ->shouldReceive('filterByIncomplete') + ->withAnyArgs() + ->andReturnSelf() + ->shouldReceive('getIds') + ->withAnyArgs() + ->andReturn(collect([1,2])) + ->getMock(); + + app()->instance(SubmissionCollector::class, $submissionCollectorMock); + + $publicationMock = Mockery::mock(\APP\publication\Publication::class) + ->makePartial() + ->shouldReceive([ + 'getLocalizedFullTitle' => 'Submission Full Title', + 'getShortAuthorString' => 'Author', + ]) + ->withAnyArgs() + ->getMock(); + + $submissionMock = Mockery::mock(\APP\submission\Submission::class) + ->makePartial() + ->shouldReceive([ + 'getId' => 0, + 'getCurrentPublication' => $publicationMock, + ]) + ->shouldReceive('getData') + ->with('stageId') + ->andReturn(WORKFLOW_STAGE_ID_INTERNAL_REVIEW) + ->getMock(); + + $submissionRepoMock = Mockery::mock(app(SubmissionRepository::class)) + ->makePartial() + ->shouldReceive([ + 'get' => $submissionMock, + ]) + ->withAnyArgs() + ->getMock(); + + app()->instance(SubmissionRepository::class, $submissionRepoMock); + + $reviewRoundMock = Mockery::mock(\PKP\submission\reviewRound\ReviewRound::class) + ->makePartial() + ->shouldReceive([ + 'getStatus' => ReviewRound::REVIEW_ROUND_STATUS_PENDING_REVIEWERS, + ]) + ->withAnyArgs() + ->getMock(); + + $reviewRoundDaoMock = Mockery::mock(\PKP\submission\reviewRound\ReviewRoundDAO::class) + ->makePartial() + ->shouldReceive('getLastReviewRoundBySubmissionId') + ->withAnyArgs() + ->andReturn($reviewRoundMock) + ->getMock(); + + DAORegistry::registerDAO('ReviewRoundDAO', $reviewRoundDaoMock); + + $emailTemplateMock = Mockery::mock(\PKP\emailTemplate\EmailTemplate::class) + ->makePartial() + ->shouldReceive([ + 'getLocalizedData' => '', + ]) + ->withAnyArgs() + ->getMock(); + + $emailTemplateRepoMock = Mockery::mock(app(EmailTemplateRepository::class)) + ->makePartial() + ->shouldReceive([ + 'getByKey' => $emailTemplateMock, + ]) + ->withAnyArgs() + ->getMock(); + + app()->instance(EmailTemplateRepository::class, $emailTemplateRepoMock); + + $this->assertNull($editorialReminderJob->handle()); + } +} diff --git a/tests/jobs/metadata/BatchMetadataChangedJobTest.php b/tests/jobs/metadata/BatchMetadataChangedJobTest.php new file mode 100644 index 00000000000..c074e6d8db0 --- /dev/null +++ b/tests/jobs/metadata/BatchMetadataChangedJobTest.php @@ -0,0 +1,107 @@ +assertInstanceOf( + BatchMetadataChangedJob::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + $this->mockRequest(); + + /** @var BatchMetadataChangedJob $batchMetadataChangedJob */ + $batchMetadataChangedJob = unserialize($this->serializedJobData); + + /** + * @disregard P1013 PHP Intelephense error suppression + * @see https://github.com/bmewburn/vscode-intelephense/issues/568 + */ + $publicationMock = Mockery::mock(\APP\publication\Publication::class) + ->makePartial() + ->shouldReceive('getData') + ->with('authors') + ->andReturn(\Illuminate\Support\LazyCollection::make([new \PKP\author\Author()])) + ->shouldReceive('getData') + ->with('subject') + ->andReturn([]) + ->shouldReceive('getData') + ->with('subjects') + ->andReturn([]) + ->shouldReceive('getData') + ->with('keywords') + ->andReturn([]) + ->shouldReceive('getData') + ->with('disciplines') + ->andReturn([]) + ->getMock(); + + $submissionMock = Mockery::mock(\APP\submission\Submission::class) + ->makePartial() + ->shouldReceive(['getCurrentPublication' => $publicationMock,]) + ->shouldReceive('getData') + ->with('doiObject') + ->andReturn(new \PKP\doi\Doi()) + ->getMock(); + + $submissionRepoMock = Mockery::mock(app(SubmissionRepository::class)) + ->makePartial() + ->shouldReceive('get') + ->withAnyArgs() + ->andReturn($submissionMock) + ->getMock(); + + app()->instance(SubmissionRepository::class, $submissionRepoMock); + + $submissionSearchDAOMock = Mockery::mock(\PKP\search\SubmissionSearchDAO::class) + ->makePartial() + ->shouldReceive(['insertObject' => 0, 'insertObjectKeywords' => null,]) + ->withAnyArgs() + ->getMock(); + + DAORegistry::registerDAO('ArticleSearchDAO', $submissionSearchDAOMock); // for OJS + DAORegistry::registerDAO('MonographSearchDAO', $submissionSearchDAOMock); // for OMP + DAORegistry::registerDAO('PreprintSearchDAO', $submissionSearchDAOMock); // for OPS + + $this->assertNull($batchMetadataChangedJob->handle()); + } +} diff --git a/tests/jobs/metadata/MetadataChangedJobTest.php b/tests/jobs/metadata/MetadataChangedJobTest.php new file mode 100644 index 00000000000..696104d69e8 --- /dev/null +++ b/tests/jobs/metadata/MetadataChangedJobTest.php @@ -0,0 +1,107 @@ +assertInstanceOf( + MetadataChangedJob::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + $this->mockRequest(); + + /** @var MetadataChangedJob $metadataChangedJob */ + $metadataChangedJob = unserialize($this->serializedJobData); + + /** + * @disregard P1013 PHP Intelephense error suppression + * @see https://github.com/bmewburn/vscode-intelephense/issues/568 + */ + $publicationMock = Mockery::mock(\APP\publication\Publication::class) + ->makePartial() + ->shouldReceive('getData') + ->with('authors') + ->andReturn(\Illuminate\Support\LazyCollection::make([new \PKP\author\Author()])) + ->shouldReceive('getData') + ->with('subject') + ->andReturn([]) + ->shouldReceive('getData') + ->with('subjects') + ->andReturn([]) + ->shouldReceive('getData') + ->with('keywords') + ->andReturn([]) + ->shouldReceive('getData') + ->with('disciplines') + ->andReturn([]) + ->getMock(); + + $submissionMock = Mockery::mock(\APP\submission\Submission::class) + ->makePartial() + ->shouldReceive(['getCurrentPublication' => $publicationMock,]) + ->shouldReceive('getData') + ->with('doiObject') + ->andReturn(new \PKP\doi\Doi()) + ->getMock(); + + $submissionRepoMock = Mockery::mock(app(SubmissionRepository::class)) + ->makePartial() + ->shouldReceive('get') + ->withAnyArgs() + ->andReturn($submissionMock) + ->getMock(); + + app()->instance(SubmissionRepository::class, $submissionRepoMock); + + $submissionSearchDAOMock = Mockery::mock(\PKP\search\SubmissionSearchDAO::class) + ->makePartial() + ->shouldReceive(['insertObject' => 0, 'insertObjectKeywords' => null,]) + ->withAnyArgs() + ->getMock(); + + DAORegistry::registerDAO('ArticleSearchDAO', $submissionSearchDAOMock); // for OJS + DAORegistry::registerDAO('MonographSearchDAO', $submissionSearchDAOMock); // for OMP + DAORegistry::registerDAO('PreprintSearchDAO', $submissionSearchDAOMock); // for OPS + + $this->assertNull($metadataChangedJob->handle()); + } +} diff --git a/tests/jobs/notifications/NewAnnouncementNotifyUsersTest.php b/tests/jobs/notifications/NewAnnouncementNotifyUsersTest.php new file mode 100644 index 00000000000..c687c8939cf --- /dev/null +++ b/tests/jobs/notifications/NewAnnouncementNotifyUsersTest.php @@ -0,0 +1,126 @@ +assertInstanceOf( + NewAnnouncementNotifyUsers::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + $this->mockMail(); + + $this->mockRequest(); + + /** @var NewAnnouncementNotifyUsers $newAnnouncementNotifyUsersJob */ + $newAnnouncementNotifyUsersJob = unserialize($this->serializedJobData); + + $announcementMock = Mockery::mock(\PKP\announcement\Announcement::class) + ->makePartial() + ->shouldReceive([ + 'getAssocId' => 0, + 'getLocalizedTitle' => '', + ]) + ->withAnyArgs() + ->getMock(); + + $announcementRepoMock = Mockery::mock(app(AnnouncementRepository::class)) + ->makePartial() + ->shouldReceive('get') + ->withAnyArgs() + ->andReturn($announcementMock) + ->getMock(); + + app()->instance(AnnouncementRepository::class, $announcementRepoMock); + + $contextDaoClass = get_class(Application::getContextDAO()); + + $contextMock = Mockery::mock(get_class(Application::getContextDAO()->newDataObject())) + ->makePartial() + ->shouldReceive([ + 'getId' => 0, + ]) + ->withAnyArgs() + ->getMock(); + + $contextDaoMock = Mockery::mock($contextDaoClass) + ->makePartial() + ->shouldReceive('getById') + ->withAnyArgs() + ->andReturn($contextMock) + ->getMock(); + + DAORegistry::registerDAO(substr(strrchr($contextDaoClass, '\\'), 1), $contextDaoMock); + + $emailTemplateMock = Mockery::mock(\PKP\emailTemplate\EmailTemplate::class) + ->makePartial() + ->shouldReceive([ + 'getLocalizedData' => '', + ]) + ->withAnyArgs() + ->getMock(); + + $emailTemplateRepoMock = Mockery::mock(app(EmailTemplateRepository::class)) + ->makePartial() + ->shouldReceive([ + 'getByKey' => $emailTemplateMock, + ]) + ->withAnyArgs() + ->getMock(); + + app()->instance(EmailTemplateRepository::class, $emailTemplateRepoMock); + + $userRepoMock = Mockery::mock(app(UserRepository::class)) + ->makePartial() + ->shouldReceive('get') + ->withAnyArgs() + ->andReturn(new \PKP\user\User) + ->getMock(); + + app()->instance(UserRepository::class, $userRepoMock); + + $this->assertNull($newAnnouncementNotifyUsersJob->handle()); + } +} diff --git a/tests/jobs/notifications/StatisticsReportMailTest.php b/tests/jobs/notifications/StatisticsReportMailTest.php new file mode 100644 index 00000000000..73b30958c3c --- /dev/null +++ b/tests/jobs/notifications/StatisticsReportMailTest.php @@ -0,0 +1,149 @@ +assertInstanceOf( + StatisticsReportMail::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + /** @var StatisticsReportMail $statisticsReportMailJob */ + $statisticsReportMailJob = unserialize($this->serializedJobData); + + $this->mockRequest(); + + $this->mockMail(); + + $contextDaoClass = get_class(Application::getContextDAO()); + + $contextMock = Mockery::mock(get_class(Application::getContextDAO()->newDataObject())) + ->makePartial() + ->shouldReceive([ + 'getId' => 0, + 'getPrimaryLocale' => 'en', + 'getContactEmail' => 'testmail@mail.test', + 'getContactName' => 'Test User', + ]) + ->withAnyArgs() + ->getMock(); + + $contextDaoMock = Mockery::mock($contextDaoClass) + ->makePartial() + ->shouldReceive('getById') + ->withAnyArgs() + ->andReturn($contextMock) + ->getMock(); + + DAORegistry::registerDAO(substr(strrchr($contextDaoClass, '\\'), 1), $contextDaoMock); + + $emailTemplateMock = Mockery::mock(\PKP\emailTemplate\EmailTemplate::class) + ->makePartial() + ->shouldReceive([ + 'getLocalizedData' => '', + ]) + ->withAnyArgs() + ->getMock(); + + $emailTemplateRepoMock = Mockery::mock(app(EmailTemplateRepository::class)) + ->makePartial() + ->shouldReceive([ + 'getByKey' => $emailTemplateMock, + ]) + ->withAnyArgs() + ->getMock(); + + app()->instance(EmailTemplateRepository::class, $emailTemplateRepoMock); + + $statsEditorialServiceMock = Mockery::mock(\APP\services\StatsEditorialService::class) + ->makePartial() + ->shouldReceive([ + 'getOverview' => [ + [ + 'key' => 'submissionsReceived', + 'name' => 'stats.name.submissionsReceived', + 'value' => 0, + ], + [ + 'key' => 'submissionsAccepted', + 'name' => 'stats.name.submissionsAccepted', + 'value' => 0, + ], + [ + 'key' => 'submissionsDeclined', + 'name' => 'stats.name.submissionsDeclined', + 'value' => 0, + ], + [ + 'key' => 'submissionsSkipped', + 'name' => 'stats.name.submissionsSkipped', + 'value' => 0, + ], + ], + 'countSubmissionsReceived' => 0, + ]) + ->withAnyArgs() + ->getMock(); + + app()->instance('editorialStats', $statsEditorialServiceMock); + + $userMock = Mockery::mock(\PKP\user\User::class) + ->makePartial() + ->shouldReceive('getId') + ->withAnyArgs() + ->andReturn(0) + ->getMock(); + + $userRepoMock = Mockery::mock(app(UserRepository::class)) + ->makePartial() + ->shouldReceive('get') + ->withAnyArgs() + ->andReturn($userMock) + ->getMock(); + + app()->instance(UserRepository::class, $userRepoMock); + + $this->assertNull($statisticsReportMailJob->handle()); + } +} diff --git a/tests/jobs/notifications/StatisticsReportNotifyTest.php b/tests/jobs/notifications/StatisticsReportNotifyTest.php new file mode 100644 index 00000000000..dced89c03d8 --- /dev/null +++ b/tests/jobs/notifications/StatisticsReportNotifyTest.php @@ -0,0 +1,63 @@ +This is an automated message from Journal of Public Knowledge.
";s:19:"enableAnnouncements";b:1;s:15:"enabledDoiTypes";a:2:{i:0;s:11:"publication";i:1;s:5:"issue";}s:10:"enableDois";b:1;s:19:"enableGeoUsageStats";s:8:"disabled";s:27:"enableInstitutionUsageStats";b:0;s:9:"enableOai";b:1;s:16:"isSushiApiPublic";b:1;s:12:"itemsPerPage";i:25;s:8:"keywords";s:7:"request";s:14:"mailingAddress";s:49:"123 456th Street Burnaby, British Columbia Canada";s:13:"membershipFee";d:0;s:16:"notifyAllAuthors";b:1;s:12:"numPageLinks";i:10;s:19:"numWeeksPerResponse";i:4;s:17:"numWeeksPerReview";i:4;s:10:"onlineIssn";s:9:"0378-5955";s:17:"paymentPluginName";s:13:"PaypalPayment";s:15:"paymentsEnabled";b:1;s:9:"printIssn";s:9:"0378-5955";s:14:"publicationFee";d:0;s:20:"publisherInstitution";s:24:"Public Knowledge Project";s:18:"purchaseArticleFee";d:0;s:18:"registrationAgency";s:14:"dataciteplugin";s:25:"submissionAcknowledgement";s:10:"allAuthors";s:20:"submitWithCategories";b:0;s:20:"supportedFormLocales";a:2:{i:0;s:2:"en";i:1;s:5:"fr_CA";}s:16:"supportedLocales";a:2:{i:0;s:2:"en";i:1;s:5:"fr_CA";}s:26:"supportedSubmissionLocales";a:2:{i:0;s:2:"en";i:1;s:5:"fr_CA";}s:12:"supportEmail";s:20:"rvaca@mailinator.com";s:11:"supportName";s:11:"Ramiro Vaca";s:15:"themePluginPath";s:7:"default";s:12:"abbreviation";a:1:{s:2:"en";s:25:"publicknowledgeJ Pub Know";}s:7:"acronym";a:1:{s:2:"en";s:6:"JPKJPK";}s:16:"authorGuidelines";a:2:{s:2:"en";s:1209:"Authors are invited to make a submission to this journal. All submissions will be assessed by an editor to determine whether they meet the aims and scope of this journal. Those considered to be a good fit will be sent for peer review before determining whether they will be accepted or rejected.
Before making a submission, authors are responsible for obtaining permission to publish any material included with the submission, such as photos, documents and datasets. All authors identified on the submission must consent to be identified as an author. Where appropriate, research should be approved by an appropriate ethics committee in accordance with the legal requirements of the study's country.
An editor may desk reject a submission if it does not meet minimum standards of quality. Before submitting, please ensure that the study design and research argument are structured and articulated properly. The title should be concise and the abstract should be able to stand on its own. This will increase the likelihood of reviewers agreeing to review the paper. When you're satisfied that your submission meets this standard, please follow the checklist below to prepare your submission.
";s:5:"fr_CA";s:44:"##default.contextSettings.authorGuidelines##";}s:17:"authorInformation";a:2:{s:2:"en";s:586:"Interested in submitting to this journal? We recommend that you review the About the Journal page for the journal's section policies, as well as the Author Guidelines. Authors need to register with the journal prior to submitting or, if already registered, can simply log in and begin the five-step process.";s:5:"fr_CA";s:715:"Intéressé-e à soumettre à cette revue ? Nous vous recommandons de consulter les politiques de rubrique de la revue à la page À propos de la revue ainsi que les Directives aux auteurs. Les auteurs-es doivent s'inscrire auprès de la revue avant de présenter une soumission, ou s'ils et elles sont déjà inscrits-es, simplement ouvrir une session et accéder au tableau de bord pour commencer les 5 étapes du processus.";}s:19:"beginSubmissionHelp";a:2:{s:2:"en";s:611:"Thank you for submitting to the Journal of Public Knowledge. You will be asked to upload files, identify co-authors, and provide information such as the title and abstract.
Please read our Submission Guidelines if you have not done so already. When filling out the forms, provide as many details as possible in order to help our editors evaluate your work.
Once you begin, you can save your submission and come back to it later. You will be able to review and correct any information before you submit.
";s:5:"fr_CA";s:42:"##default.submission.step.beforeYouBegin##";}s:14:"clockssLicense";a:2:{s:2:"en";s:271:"This journal utilizes the CLOCKSS system to create a distributed archiving system among participating libraries and permits those libraries to create permanent archives of the journal for purposes of preservation and restoration. More...";s:5:"fr_CA";s:315:"Cette revue utilise le système CLOCKSS pour créer un système d'archivage distribué parmi les bibliothèques participantes et permet à ces bibliothèques de créer des archives permanentes de la revue à des fins de conservation et de reconstitution. En apprendre davantage... ";}s:16:"contributorsHelp";a:2:{s:2:"en";s:504:"Add details for all of the contributors to this submission. Contributors added here will be sent an email confirmation of the submission, as well as a copy of all editorial decisions recorded against this submission.
If a contributor can not be contacted by email, because they must remain anonymous or do not have an email account, please do not enter a fake email address. You can add information about this contributor in a message to the editor at a later step in the submission process.
";s:5:"fr_CA";s:40:"##default.submission.step.contributors##";}s:13:"customHeaders";a:1:{s:2:"en";s:41:"";}s:11:"description";a:2:{s:2:"en";s:123:"The Journal of Public Knowledge is a peer-reviewed quarterly publication on the subject of public access to science.
";s:5:"fr_CA";s:146:"Le Journal de Public Knowledge est une publication trimestrielle évaluée par les pairs sur le thème de l'accès du public à la science.
";}s:11:"detailsHelp";a:2:{s:2:"en";s:92:"Please provide the following details to help us manage your submission in our system.
";s:5:"fr_CA";s:35:"##default.submission.step.details##";}s:17:"forTheEditorsHelp";a:2:{s:2:"en";s:278:"Please provide the following details in order to help our editorial team manage your submission.
When entering metadata, provide entries that you think would be most helpful to the person managing your submission. This information can be changed before publication.
";s:5:"fr_CA";s:41:"##default.submission.step.forTheEditors##";}s:20:"librarianInformation";a:2:{s:2:"en";s:361:"We encourage research librarians to list this journal among their library's electronic journal holdings. As well, it may be worth noting that this journal's open source publishing system is suitable for libraries to host for their faculty members to use with journals they are involved in editing (see Open Journal Systems).";s:5:"fr_CA";s:434:"Nous incitons les bibliothécaires à lister cette revue dans leur fonds de revues numériques. Aussi, il peut être pertinent de mentionner que ce système de publication en libre accès est conçu pour être hébergé par les bibliothèques de recherche pour que les membres de leurs facultés l'utilisent avec les revues dans lesquelles elles ou ils sont impliqués (voir Open Journal Systems).";}s:13:"lockssLicense";a:2:{s:2:"en";s:273:"This journal utilizes the LOCKSS system to create a distributed archiving system among participating libraries and permits those libraries to create permanent archives of the journal for purposes of preservation and restoration. More...";s:5:"fr_CA";s:314:"Cette revue utilise le système LOCKSS pour créer un système de distribution des archives parmi les bibliothèques participantes et afin de permettre à ces bibliothèques de créer des archives permanentes pour fins de préservation et de restauration. En apprendre davantage...";}s:4:"name";a:2:{s:2:"en";s:27:"Journal of Public Knowledge";s:5:"fr_CA";s:36:"Journal de la connaissance du public";}s:16:"openAccessPolicy";a:2:{s:2:"en";s:176:"This journal provides immediate open access to its content on the principle that making research freely available to the public supports a greater global exchange of knowledge.";s:5:"fr_CA";s:217:"Cette revue fournit le libre accès immédiat à son contenu se basant sur le principe que rendre la recherche disponible au public gratuitement facilite un plus grand échange du savoir, à l'échelle de la planète.";}s:16:"privacyStatement";a:2:{s:2:"en";s:206:"The names and email addresses entered in this journal site will be used exclusively for the stated purposes of this journal and will not be made available for any other purpose or to any other party.
";s:5:"fr_CA";s:193:"Les noms et courriels saisis dans le site de cette revue seront utilisés exclusivement aux fins indiquées par cette revue et ne serviront à aucune autre fin, ni à toute autre partie.
";}s:17:"readerInformation";a:2:{s:2:"en";s:654:"We encourage readers to sign up for the publishing notification service for this journal. Use the Register link at the top of the home page for the journal. This registration will result in the reader receiving the Table of Contents by email for each new issue of the journal. This list also allows the journal to claim a certain level of support or readership. See the journal's Privacy Statement, which assures readers that their name and email address will not be used for other purposes.";s:5:"fr_CA";s:716:"Nous invitons les lecteurs-trices à s'inscrire pour recevoir les avis de publication de cette revue. Utiliser le lien S'inscrire en haut de la page d'accueil de la revue. Cette inscription permettra au,à la lecteur-trice de recevoir par courriel le sommaire de chaque nouveau numéro de la revue. Cette liste permet aussi à la revue de revendiquer un certain niveau de soutien ou de lectorat. Voir la Déclaration de confidentialité de la revue qui certifie aux lecteurs-trices que leur nom et leur courriel ne seront pas utilisés à d'autres fins.";}s:10:"reviewHelp";a:2:{s:2:"en";s:368:"Review the information you have entered before you complete your submission. You can change any of the details displayed here by clicking the edit button at the top of each section.
Once you complete your submission, a member of our editorial team will be assigned to review it. Please ensure the details you have entered here are as accurate as possible.
";s:5:"fr_CA";s:34:"##default.submission.step.review##";}s:17:"searchDescription";a:1:{s:2:"en";s:116:"The Journal of Public Knowledge is a peer-reviewed quarterly publication on the subject of public access to science.";}s:19:"submissionChecklist";a:2:{s:2:"en";s:591:"All submissions must meet the following requirements.
Provide any files our editorial team may need to evaluate your submission. In addition to the main work, you may wish to submit data sets, conflict of interest statements, or other supplementary files if these will be helpful for our editors.
";s:5:"fr_CA";s:39:"##default.submission.step.uploadFiles##";}}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:77:"\0PKP\\notification\\managerDelegate\\EditorialReportNotificationManager\0_request";O:16:"APP\\core\\Request":10:{s:7:"_router";O:19:"APP\\core\\PageRouter":10:{s:15:"\0*\0_application";O:20:"APP\\core\\Application":2:{s:15:"enabledProducts";a:1:{i:0;a:10:{s:16:"plugins.metadata";a:1:{s:4:"dc11";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:16:"plugins.metadata";s:7:"product";s:4:"dc11";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}}s:14:"plugins.blocks";a:1:{s:14:"languageToggle";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:14:"plugins.blocks";s:7:"product";s:14:"languageToggle";s:16:"productClassName";s:25:"LanguageToggleBlockPlugin";s:8:"lazyLoad";i:1;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}}s:16:"plugins.gateways";a:1:{s:8:"resolver";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:16:"plugins.gateways";s:7:"product";s:8:"resolver";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}}s:15:"plugins.generic";a:5:{s:8:"datacite";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:2;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:15:"plugins.generic";s:7:"product";s:8:"datacite";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:10:"usageEvent";O:16:"PKP\site\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:15:"plugins.generic";s:7:"product";s:10:"usageEvent";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:5:"acron";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:3;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:15:"plugins.generic";s:7:"product";s:5:"acron";s:16:"productClassName";s:11:"AcronPlugin";s:8:"lazyLoad";i:1;s:8:"sitewide";i:1;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:7:"tinymce";O:16:"PKP\site\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:15:"plugins.generic";s:7:"product";s:7:"tinymce";s:16:"productClassName";s:13:"TinyMCEPlugin";s:8:"lazyLoad";i:1;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:8:"crossref";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:3;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:15:"plugins.generic";s:7:"product";s:8:"crossref";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}}s:20:"plugins.importexport";a:4:{s:5:"users";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:20:"plugins.importexport";s:7:"product";s:5:"users";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:6:"native";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:20:"plugins.importexport";s:7:"product";s:6:"native";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:6:"pubmed";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:20:"plugins.importexport";s:7:"product";s:6:"pubmed";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:4:"doaj";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:1;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:33";s:7:"current";i:1;s:11:"productType";s:20:"plugins.importexport";s:7:"product";s:4:"doaj";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}}s:26:"plugins.oaiMetadataFormats";a:4:{s:7:"rfc1807";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:26:"plugins.oaiMetadataFormats";s:7:"product";s:7:"rfc1807";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:7:"marcxml";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:26:"plugins.oaiMetadataFormats";s:7:"product";s:7:"marcxml";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:4:"marc";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:26:"plugins.oaiMetadataFormats";s:7:"product";s:4:"marc";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:2:"dc";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:26:"plugins.oaiMetadataFormats";s:7:"product";s:2:"dc";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}}s:17:"plugins.paymethod";a:2:{s:6:"manual";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:17:"plugins.paymethod";s:7:"product";s:6:"manual";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:6:"paypal";O:16:"PKP\site\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:17:"plugins.paymethod";s:7:"product";s:6:"paypal";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}}s:15:"plugins.reports";a:4:{s:13:"counterReport";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:1;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:15:"plugins.reports";s:7:"product";s:13:"counterReport";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:8:"articles";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:15:"plugins.reports";s:7:"product";s:8:"articles";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:12:"reviewReport";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:2;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:15:"plugins.reports";s:7:"product";s:12:"reviewReport";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}s:13:"subscriptions";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:15:"plugins.reports";s:7:"product";s:13:"subscriptions";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}}s:14:"plugins.themes";a:1:{s:7:"default";O:16:"PKP\\site\\Version":6:{s:5:"_data";a:11:{s:5:"major";i:1;s:5:"minor";i:0;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:07";s:7:"current";i:1;s:11:"productType";s:14:"plugins.themes";s:7:"product";s:7:"default";s:16:"productClassName";s:18:"DefaultThemePlugin";s:8:"lazyLoad";i:1;s:8:"sitewide";i:0;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}}s:4:"core";a:1:{s:4:"ojs2";O:16:"PKP\site\Version":6:{s:5:"_data";a:11:{s:5:"major";i:3;s:5:"minor";i:4;s:8:"revision";i:0;s:5:"build";i:0;s:13:"dateInstalled";s:19:"2023-02-28 20:19:00";s:7:"current";i:1;s:11:"productType";s:4:"core";s:7:"product";s:4:"ojs2";s:16:"productClassName";s:0:"";s:8:"lazyLoad";i:0;s:8:"sitewide";i:1;}s:20:"_hasLoadableAdapters";b:0;s:27:"_metadataExtractionAdapters";a:0:{}s:25:"_extractionAdaptersLoaded";b:0;s:26:"_metadataInjectionAdapters";a:0:{}s:24:"_injectionAdaptersLoaded";b:0;}}}}s:11:"allProducts";N;}s:14:"\0*\0_dispatcher";O:19:"PKP\\core\\Dispatcher":5:{s:12:"_application";r:141;s:12:"_routerNames";a:3:{s:3:"api";s:19:"\\PKP\\core\\APIRouter";s:9:"component";s:28:"\PKP\core\PKPComponentRouter";s:4:"page";s:20:"\\APP\\core\\PageRouter";}s:16:"_routerInstances";a:3:{s:3:"api";O:18:"PKP\\core\\APIRouter":6:{s:15:"\0*\0_application";r:141;s:14:"\0*\0_dispatcher";r:587;s:15:"\0*\0_contextPath";N;s:8:"_context";N;s:8:"_handler";N;s:9:"_indexUrl";s:80:"http://ojs-stable-3_4_0.test/Users/abir/.composer/vendor/laravel/valet/index.php";}s:9:"component";O:27:"PKP\core\PKPComponentRouter":10:{s:15:"\0*\0_application";r:141;s:14:"\0*\0_dispatcher";r:587;s:15:"\0*\0_contextPath";s:5:"index";s:8:"_context";N;s:8:"_handler";N;s:9:"_indexUrl";s:80:"http://ojs-stable-3_4_0.test/Users/abir/.composer/vendor/laravel/valet/index.php";s:10:"_component";N;s:3:"_op";N;s:24:"_rpcServiceEndpointParts";b:0;s:19:"_rpcServiceEndpoint";b:0;}s:4:"page";R:140;}s:7:"_router";R:140;s:20:"_requestCallbackHack";r:139;}s:15:"\0*\0_contextPath";s:5:"index";s:8:"_context";N;s:8:"_handler";O:28:"APP\\pages\\index\\IndexHandler":9:{s:12:"\0*\0_apiToken";N;s:3:"_id";s:0:"";s:11:"_dispatcher";r:587;s:7:"_checks";a:0:{}s:16:"_roleAssignments";a:0:{}s:29:"_authorizationDecisionManager";O:55:"PKP\\security\\authorization\\AuthorizationDecisionManager":3:{s:14:"_rootPolicySet";O:36:"PKP\\security\\authorization\\PolicySet":3:{s:9:"_policies";a:3:{i:0;O:45:"PKP\\security\\authorization\\AllowedHostsPolicy":3:{s:7:"_advice";a:1:{i:2;a:3:{i:0;r:624;i:1;s:10:"callOnDeny";i:2;a:0:{}}}s:18:"_authorizedContext";a:0:{}s:8:"_request";r:139;}i:1;O:38:"PKP\\security\\authorization\\HttpsPolicy":3:{s:7:"_advice";a:1:{i:2;a:3:{i:0;r:139;i:1;s:11:"redirectSSL";i:2;a:0:{}}}s:18:"_authorizedContext";R:630;s:8:"_request";r:139;}i:2;O:53:"PKP\\security\\authorization\\RestrictedSiteAccessPolicy":4:{s:7:"_advice";a:1:{i:1;s:39:"user.authorization.restrictedSiteAccess";}s:18:"_authorizedContext";R:630;s:62:"\0PKP\\security\\authorization\\RestrictedSiteAccessPolicy\0_router";r:140;s:63:"\0PKP\\security\\authorization\\RestrictedSiteAccessPolicy\0_request";r:139;}}s:19:"_combiningAlgorithm";i:1;s:24:"_effectIfNoPolicyApplies";i:1;}s:22:"_authorizationMessages";a:0:{}s:18:"_authorizedContext";R:630;}s:22:"_enforceRestrictedSite";b:1;s:23:"_roleAssignmentsChecked";b:0;s:14:"_isBackendPage";b:0;}s:9:"_indexUrl";s:80:"http://ojs-stable-3_4_0.test/Users/abir/.composer/vendor/laravel/valet/index.php";s:18:"_installationPages";a:4:{i:0;s:7:"install";i:1;s:4:"help";i:2;s:6:"header";i:3;s:7:"sidebar";}s:5:"_page";s:0:"";s:3:"_op";s:5:"index";s:14:"_cacheFilename";s:78:"/Users/abir/Sites/code/ojs-main/cache/wc-ff7472e8e7ded5f1751e73a64fa00139.html";}s:11:"_dispatcher";r:587;s:12:"_requestVars";a:0:{}s:9:"_basePath";s:42:"/Users/abir/.composer/vendor/laravel/valet";s:12:"_requestPath";s:70:"/Applications/Tinkerwell.app/Contents/Resources/tinkerwell/tinker.phar";s:21:"_isRestfulUrlsEnabled";b:0;s:11:"_serverHost";s:21:"ojs-stable-3_4_0.test";s:9:"_protocol";s:4:"http";s:6:"_isBot";b:1;s:10:"_userAgent";s:117:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36";}}s:10:"connection";s:8:"database";s:5:"queue";s:5:"queue";s:7:"batchId";s:36:"9c872d03-b95f-47f8-ab10-78d2772697d6";} + END; + + /** + * Test job is a proper instance + */ + public function testUnserializationGetProperJobInstance(): void + { + $this->assertInstanceOf( + StatisticsReportNotify::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + /** @var StatisticsReportNotify $statisticsReportNotifyJob */ + $statisticsReportNotifyJob = unserialize($this->serializedJobData); + + $userRepoMock = Mockery::mock(app(UserRepository::class)) + ->makePartial() + ->shouldReceive('get') + ->withAnyArgs() + ->andReturn(new \PKP\user\User) + ->getMock(); + + app()->instance(UserRepository::class, $userRepoMock); + + $this->assertNull($statisticsReportNotifyJob->handle()); + } +} diff --git a/tests/jobs/statistics/ArchiveUsageStatsLogFileTest.php b/tests/jobs/statistics/ArchiveUsageStatsLogFileTest.php new file mode 100644 index 00000000000..53da0ec1d43 --- /dev/null +++ b/tests/jobs/statistics/ArchiveUsageStatsLogFileTest.php @@ -0,0 +1,96 @@ +assertInstanceOf( + ArchiveUsageStatsLogFile::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + /** @var ArchiveUsageStatsLogFile $archiveUsageStatsLogFileJob */ + $archiveUsageStatsLogFileJob = unserialize($this->serializedJobData); + + // we need to create a dummy file if not existed as to avoid mocking PHP's built in functions + $dummyFileName = $this->createDummyFileIfNeeded($archiveUsageStatsLogFileJob, 'loadId'); + + $this->assertNull($archiveUsageStatsLogFileJob->handle()); + + if ($dummyFileName) { + unlink( + StatisticsHelper::getUsageStatsDirPath() + . '/' + . FileLoader::FILE_LOADER_PATH_ARCHIVE + . '/' + .$dummyFileName + ); + } + } + + /** + * Create the dummy file with dummy content if required + */ + protected function createDummyFileIfNeeded(ArchiveUsageStatsLogFile $job, string $propertyName): ?string + { + $reflection = new ReflectionClass($job); + $property = $reflection->getProperty($propertyName); + $property->setAccessible(true); + $fileName = $property->getValue($job); + + $filePath = StatisticsHelper::getUsageStatsDirPath() + . DIRECTORY_SEPARATOR + . FileLoader::FILE_LOADER_PATH_DISPATCH + . DIRECTORY_SEPARATOR; + + if (!file_exists($filePath . $fileName)) { + file_put_contents($filePath . $fileName, $this->dummyFileContent); + return $fileName; + } + + return null; + } +} diff --git a/tests/jobs/statistics/CompileContextMetricsTest.php b/tests/jobs/statistics/CompileContextMetricsTest.php new file mode 100644 index 00000000000..33112915af4 --- /dev/null +++ b/tests/jobs/statistics/CompileContextMetricsTest.php @@ -0,0 +1,64 @@ +assertInstanceOf( + CompileContextMetrics::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + /** @var CompileContextMetrics $compileContextMetricsJob */ + $compileContextMetricsJob = unserialize($this->serializedJobData); + + $temporaryTotalsDAOMock = Mockery::mock(\APP\statistics\TemporaryTotalsDAO::class) + ->makePartial() + ->shouldReceive([ + 'removeDoubleClicks' => null, + ]) + ->withAnyArgs() + ->getMock(); + + DAORegistry::registerDAO('TemporaryTotalsDAO', $temporaryTotalsDAOMock); + + $this->assertNull($compileContextMetricsJob->handle()); + } +} diff --git a/tests/jobs/statistics/CompileMonthlyMetricsTest.php b/tests/jobs/statistics/CompileMonthlyMetricsTest.php new file mode 100644 index 00000000000..e2a5efa0527 --- /dev/null +++ b/tests/jobs/statistics/CompileMonthlyMetricsTest.php @@ -0,0 +1,77 @@ +assertInstanceOf( + CompileMonthlyMetrics::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + /** @var CompileMonthlyMetrics $compileMonthlyMetricsJob */ + $compileMonthlyMetricsJob = unserialize($this->serializedJobData); + + $statsGeoServiceMock = Mockery::mock(\PKP\services\PKPStatsGeoService::class) + ->makePartial() + ->shouldReceive([ + 'deleteMonthlyMetrics' => null, + 'addMonthlyMetrics' => null, + 'deleteDailyMetrics' => null, + ]) + ->withAnyArgs() + ->getMock(); + + app()->instance('geoStats', $statsGeoServiceMock); + + $statsSushiService = Mockery::mock(\PKP\services\PKPStatsSushiService::class) + ->makePartial() + ->shouldReceive([ + 'deleteMonthlyMetrics' => null, + 'addMonthlyMetrics' => null, + 'deleteDailyMetrics' => null, + ]) + ->withAnyArgs() + ->getMock(); + + app()->instance('sushiStats', $statsSushiService); + + $this->assertNull($compileMonthlyMetricsJob->handle()); + } +} diff --git a/tests/jobs/statistics/CompileSubmissionMetricsTest.php b/tests/jobs/statistics/CompileSubmissionMetricsTest.php new file mode 100644 index 00000000000..5c3644dd7af --- /dev/null +++ b/tests/jobs/statistics/CompileSubmissionMetricsTest.php @@ -0,0 +1,64 @@ +assertInstanceOf( + CompileSubmissionMetrics::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + /** @var CompileSubmissionMetrics $compileSubmissionMetricsJob */ + $compileSubmissionMetricsJob = unserialize($this->serializedJobData); + + $temporaryTotalsDAOMock = Mockery::mock(\APP\statistics\TemporaryTotalsDAO::class) + ->makePartial() + ->shouldReceive([ + 'removeDoubleClicks' => null, + ]) + ->withAnyArgs() + ->getMock(); + + DAORegistry::registerDAO('TemporaryTotalsDAO', $temporaryTotalsDAOMock); + + $this->assertNull($compileSubmissionMetricsJob->handle()); + } +} diff --git a/tests/jobs/statistics/RemoveDoubleClicksTest.php b/tests/jobs/statistics/RemoveDoubleClicksTest.php new file mode 100644 index 00000000000..417c491367a --- /dev/null +++ b/tests/jobs/statistics/RemoveDoubleClicksTest.php @@ -0,0 +1,64 @@ +assertInstanceOf( + RemoveDoubleClicks::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + /** @var RemoveDoubleClicks $removeDoubleClicksJob */ + $removeDoubleClicksJob = unserialize($this->serializedJobData); + + $temporaryTotalsDAOMock = Mockery::mock(\APP\statistics\TemporaryTotalsDAO::class) + ->makePartial() + ->shouldReceive([ + 'removeDoubleClicks' => null, + ]) + ->withAnyArgs() + ->getMock(); + + DAORegistry::registerDAO('TemporaryTotalsDAO', $temporaryTotalsDAOMock); + + $this->assertNull($removeDoubleClicksJob->handle()); + } +} diff --git a/tests/jobs/submissions/RemoveSubmissionFileFromSearchIndexJobTest.php b/tests/jobs/submissions/RemoveSubmissionFileFromSearchIndexJobTest.php new file mode 100644 index 00000000000..3a79c33c3e4 --- /dev/null +++ b/tests/jobs/submissions/RemoveSubmissionFileFromSearchIndexJobTest.php @@ -0,0 +1,66 @@ +assertInstanceOf( + RemoveSubmissionFileFromSearchIndexJob::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + /** @var RemoveSubmissionFileFromSearchIndexJob $removeSubmissionFileFromSearchIndexJob */ + $removeSubmissionFileFromSearchIndexJob = unserialize($this->serializedJobData); + + $submissionSearchDAOMock = Mockery::mock(\PKP\search\SubmissionSearchDAO::class) + ->makePartial() + ->shouldReceive(['deleteSubmissionKeywords' => null]) + ->withAnyArgs() + ->getMock(); + + DAORegistry::registerDAO('ArticleSearchDAO', $submissionSearchDAOMock); // for OJS + DAORegistry::registerDAO('MonographSearchDAO', $submissionSearchDAOMock); // for OMP + DAORegistry::registerDAO('PreprintSearchDAO', $submissionSearchDAOMock); // for OPS + + // Test that the job can be handled without causing an exception. + $this->assertNull($removeSubmissionFileFromSearchIndexJob->handle()); + } +} + diff --git a/tests/jobs/submissions/RemoveSubmissionFromSearchIndexJobTest.php b/tests/jobs/submissions/RemoveSubmissionFromSearchIndexJobTest.php new file mode 100644 index 00000000000..08e41be835a --- /dev/null +++ b/tests/jobs/submissions/RemoveSubmissionFromSearchIndexJobTest.php @@ -0,0 +1,66 @@ +assertInstanceOf( + RemoveSubmissionFromSearchIndexJob::class, + unserialize($this->serializedJobData) + ); + } + + /** + * Ensure that a serialized job can be unserialized and executed + */ + public function testRunSerializedJob(): void + { + /** @var RemoveSubmissionFromSearchIndexJob $removeSubmissionFromSearchIndexJob */ + $removeSubmissionFromSearchIndexJob = unserialize($this->serializedJobData); + + $submissionSearchDAOMock = Mockery::mock(\PKP\search\SubmissionSearchDAO::class) + ->makePartial() + ->shouldReceive(['deleteSubmissionKeywords' => null]) + ->withAnyArgs() + ->getMock(); + + DAORegistry::registerDAO('ArticleSearchDAO', $submissionSearchDAOMock); // for OJS + DAORegistry::registerDAO('MonographSearchDAO', $submissionSearchDAOMock); // for OMP + DAORegistry::registerDAO('PreprintSearchDAO', $submissionSearchDAOMock); // for OPS + + // Test that the job can be handled without causing an exception. + $this->assertNull($removeSubmissionFromSearchIndexJob->handle()); + } +} + diff --git a/tests/jobs/submissions/UpdateSubmissionSearchJobTest.php b/tests/jobs/submissions/UpdateSubmissionSearchJobTest.php index 82f20b80bae..2b7e2482fb5 100644 Binary files a/tests/jobs/submissions/UpdateSubmissionSearchJobTest.php and b/tests/jobs/submissions/UpdateSubmissionSearchJobTest.php differ