MDL-80484 mod_forum : Add a check for an empty email address

Added a check for empty email address so that if the adhoc task
for sending email notifcation to users for forum update fails
because of empty email address an exception is not thrown as
the exception was causing the the task for user with empty email
address to keep requeuing again and again after failing.

Wrote two test cases for testing if the fix has now prevented
the task for empty email address to be requeued after failing
and to test that if its still requeuing for other cases when
the adhoc task fails for some other reason in this cause we are
testing for bounce threshold.
This commit is contained in:
waleedhassan 2024-06-17 11:44:41 +01:00 committed by Mark Johnson
parent ec7711b9a6
commit 6ee711fb6c
No known key found for this signature in database
GPG Key ID: EB30E1468CFAE242
2 changed files with 184 additions and 2 deletions

View File

@ -184,9 +184,15 @@ class send_user_notifications extends \core\task\adhoc_task {
}
}
if ($errorcount > 0 and $sentcount === 0) {
if ($errorcount > 0 && $sentcount === 0) {
// All messages errored. So fail.
throw new \moodle_exception('Error sending posts.');
// Checking if the task failed because of empty email address so that it doesn't get rescheduled.
if (!empty($this->recipient->email)) {
throw new \moodle_exception('Error sending posts.');
} else {
mtrace("Failed to send emails for the user with ID ".
$this->recipient->id ." due to an empty email address. Skipping re-queuing of the task.");
}
} else if ($errorcount > 0) {
// Requeue failed messages as a new task.
$task = new send_user_notifications();

View File

@ -0,0 +1,176 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace mod_forum\task;
use Exception;
use InvalidArgumentException;
use RuntimeException;
/**
* Unit tests for the send_user_notifications task in the forum module.
*
* This class contains test cases to ensure that the forum module's
* send_user_notifications task functions as expected, particularly
* when handling email notifications to users after forum posts.
*
* It tests different scenarios related to user email configurations,
* such as when a user has an empty email address, when a user has exceeded
* the bounce threshold, and how the system behaves when posts are attempted
* to be sent under these conditions.
*
* Each test verifies that the appropriate exceptions are thrown, that
* messages are correctly sent (or skipped), and that the task requeues
* appropriately based on the user's email settings and other related conditions.
*
* @package mod_forum
* @copyright 2024 Waleed ul hassan <waleed.hassan@catalyst-eu.net>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class send_user_notifications_test extends \advanced_testcase {
/**
* Testcase to check send notification for post via email
*
* @covers \mod_forum\task\send_user_notifications
* @dataProvider send_user_notifications_cases
* @param array $userdata Test user for the case.
* @param string $expectedstring Expected string during the test case.
* @param array $expecteddebuggingstrings Expected debugging strings array.
* @param bool $expectedassertion Expected adhoc task to be re queued or not.
* @param array $userpreferences (optional) User preferences for the test case.
* @throws InvalidArgumentException If the user data is invalid.
* @throws RuntimeException If the notification fails to send.
* @throws Exception For any other general errors.
*/
public function test_send_user_notifications(
array $userdata,
string $expectedstring,
array $expecteddebuggingstrings,
bool $expectedassertion,
array $userpreferences = [],
): void {
global $CFG;
require_once($CFG->dirroot . '/mod/forum/lib.php');
$CFG->handlebounces = true;
$this->resetAfterTest(true);
$this->preventResetByRollback();
$this->redirectEmails();
// Creating a user.
$user = $this->getDataGenerator()->create_user($userdata);
// Set user preferences.
foreach ($userpreferences as $name => $value) {
set_user_preference($name, $value, $user);
}
// Create a course and a forum.
$course = $this->getDataGenerator()->create_course();
$forum = $this->getDataGenerator()->create_module('forum', [
'course' => $course->id,
'forcesubscribe' => \FORUM_FORCESUBSCRIBE,
]);
// Create a discussion in the forum.
$discussion = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion([
'course' => $course->id,
'forum' => $forum->id,
'userid' => $user->id,
'message' => 'Test discussion',
]);
// Create a post in the discussion.
$post = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_post([
'course' => $course->id,
'discussion' => $discussion->id,
'userid' => $user->id,
'message' => 'Test post',
]);
// Setting placeholders for user id and post id.
$expectedstring = sprintf($expectedstring, $user->id, $post->id, $user->id);
$expecteddebuggingstrings = array_map(function($expecteddebuggingstring) use ($user) {
return sprintf($expecteddebuggingstring, $user->id, $user->firstname . " " . $user->lastname);
}, $expecteddebuggingstrings);
// Enroll the user in the course.
$this->getDataGenerator()->enrol_user($user->id, $course->id);
// Trigger the send_user_notifications task.
$task = new send_user_notifications();
$task->set_userid($user->id);
$task->set_custom_data($post->id);
$this->expectOutputString($expectedstring);
// Testing if an exception is thrown because the task is re queued if an exception is thrown in the adhoc task.
$expectedexception = 'Error sending posts.';
try {
$task->execute();
} catch (\Exception $ex) {
$this->assertEquals($expectedexception, $ex->errorcode);
}
if (count($expecteddebuggingstrings)) {
$this->assertdebuggingcalledcount(count($expecteddebuggingstrings), $expecteddebuggingstrings);
}
}
/**
* Data provider for test cases related to sending user notifications.
*
* This data provider generates various test cases for the `test_send_user_notifications` function.
* Each test case consists of a user configuration, expected output strings, debugging messages, and assertions.
*
* @return array[] Array of test cases.
*/
public static function send_user_notifications_cases(): array {
return [
[
// Create a user with an empty email address.
[
'email' => '',
'username' => 'testuser',
],
"Sending messages to testuser (%d)\n" .
" Failed to send post %d\n" .
"Sent 0 messages with 1 failures\n" .
"Failed to send emails for the user with ID %d" .
" due to an empty email address. Skipping re-queuing of the task.\n",
[
"Can not send email to user without email: %d",
"Error calling message processor email",
],
false,
],
[
// Create a user with bounce threshold.
[
'email' => 'bounce@example.com',
'username' => 'bounceuser',
],
"Sending messages to bounceuser (%d)\n" .
" Failed to send post %d\n" .
"Sent 0 messages with 1 failures\n",
[
"email_to_user: User %d (%s) is over bounce threshold! Not sending.",
"Error calling message processor email",
],
true,
[
'email_bounce_count' => 20,
'email_send_count' => 20,
],
],
];
}
}