MDL-1626 mod_forum: Add unit tests for forum cron mail behaviour
authorAndrew Nicols <andrew@nicols.co.uk>
Thu, 5 Jun 2014 12:59:18 +0000 (20:59 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Tue, 24 Jun 2014 04:16:33 +0000 (12:16 +0800)
mod/forum/tests/mail_test.php [new file with mode: 0644]

diff --git a/mod/forum/tests/mail_test.php b/mod/forum/tests/mail_test.php
new file mode 100644 (file)
index 0000000..909ed6f
--- /dev/null
@@ -0,0 +1,390 @@
+<?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/>.
+
+/**
+ * The forum module mail generation tests.
+ *
+ * @package    mod_forum
+ * @category   external
+ * @copyright  2013 Andrew Nicols
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+
+class mod_forum_mail_testcase extends advanced_testcase {
+
+    protected $helper;
+
+    public function setUp() {
+        global $CFG;
+        require_once($CFG->dirroot . '/mod/forum/lib.php');
+
+        $helper = new stdClass();
+
+        // Messaging is not compatible with transactions...
+        $this->preventResetByRollback();
+
+        // Catch all messages.
+        $helper->messagesink = $this->redirectMessages();
+        $helper->mailsink = $this->redirectEmails();
+
+        // Confirm that we have an empty message sink so far.
+        $messages = $helper->messagesink->get_messages();
+        $this->assertEquals(0, count($messages));
+
+        $messages = $helper->mailsink->get_messages();
+        $this->assertEquals(0, count($messages));
+
+        // Forcibly reduce the maxeditingtime to a one second to ensure that
+        // messages are sent out.
+        $CFG->maxeditingtime = 1;
+
+        // Ensure that we don't prevent e-mail as this will cause unit test failures.
+        $CFG->noemailever = false;
+
+        $this->helper = $helper;
+    }
+
+    public function tearDown() {
+        $this->helper->messagesink->clear();
+        $this->helper->messagesink->close();
+
+        $this->helper->mailsink->clear();
+        $this->helper->mailsink->close();
+    }
+
+    /**
+     * Helper to create the required number of users in the specified
+     * course.
+     * Users are enrolled as students.
+     *
+     * @param stdClass $course The course object
+     * @param integer $count The number of users to create
+     * @return array The users created
+     */
+    protected function helper_create_users($course, $count) {
+        $users = array();
+
+        for ($i = 0; $i < $count; $i++) {
+            $user = $this->getDataGenerator()->create_user();
+            $this->getDataGenerator()->enrol_user($user->id, $course->id);
+            $users[] = $user;
+        }
+
+        return $users;
+    }
+
+    /**
+     * Create a new discussion and post within the specified forum, as the
+     * specified author.
+     *
+     * @param stdClass $forum The forum to post in
+     * @param stdClass $author The author to post as
+     * @param array An array containing the discussion object, and the post object
+     */
+    protected function helper_post_to_forum($forum, $author) {
+        $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
+
+        // Create a discussion in the forum, and then add a post to that discussion.
+        $record = new stdClass();
+        $record->course = $forum->course;
+        $record->userid = $author->id;
+        $record->forum = $forum->id;
+        $discussion = $generator->create_discussion($record);
+
+        $record = new stdClass();
+        $record->course = $forum->course;
+        $record->userid = $author->id;
+        $record->forum = $forum->id;
+        $record->discussion = $discussion->id;
+        $record->mailnow = 1;
+        $post = $generator->create_post($record);
+
+        return array($discussion, $post);
+    }
+
+    /**
+     * Run the forum cron, and check that the specified post was sent the
+     * specified number of times.
+     *
+     * @param stdClass $post The forum post object
+     * @param integer $expected The number of times that the post should have been sent
+     * @return array An array of the messages caught by the message sink
+     */
+    protected function helper_run_cron_check_count($post, $expected) {
+        // Clear the sinks before running cron.
+        $this->helper->messagesink->clear();
+        $this->helper->mailsink->clear();
+
+        // Cron daily uses mtrace, turn on buffering to silence output.
+        $this->expectOutputRegex("/{$expected} users were sent post {$post->id}, '{$post->subject}'/");
+        forum_cron();
+
+        // Now check the results in the message sink.
+        $messages = $this->helper->messagesink->get_messages();
+
+        // There should be the expected number of messages.
+        $this->assertEquals($expected, count($messages));
+
+        return $messages;
+    }
+
+    public function test_forced_subscription() {
+        $this->resetAfterTest(true);
+
+        // Create a course, with a forum.
+        $course = $this->getDataGenerator()->create_course();
+
+        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
+        $forum = $this->getDataGenerator()->create_module('forum', $options);
+
+        // Create two users enrolled in the course as students.
+        list($author, $recipient) = $this->helper_create_users($course, 2);
+
+        // Post a discussion to the forum.
+        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
+
+        // We expect both users to receive this post.
+        $expected = 2;
+
+        // Run cron and check that the expected number of users received the notification.
+        $messages = $this->helper_run_cron_check_count($post, $expected);
+
+        $seenauthor = false;
+        $seenrecipient = false;
+        foreach ($messages as $message) {
+            // They should both be from our user.
+            $this->assertEquals($author->id, $message->useridfrom);
+
+            if ($message->useridto == $author->id) {
+                $seenauthor = true;
+            } else if ($message->useridto = $recipient->id) {
+                $seenrecipient = true;
+            }
+        }
+
+        // Check we saw messages for both users.
+        $this->assertTrue($seenauthor);
+        $this->assertTrue($seenrecipient);
+    }
+
+    public function test_subscription_disabled() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        // Create a course, with a forum.
+        $course = $this->getDataGenerator()->create_course();
+
+        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
+        $forum = $this->getDataGenerator()->create_module('forum', $options);
+
+        // Create two users enrolled in the course as students.
+        list($author, $recipient) = $this->helper_create_users($course, 2);
+
+        // Post a discussion to the forum.
+        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
+
+        // We expect both users to receive this post.
+        $expected = 0;
+
+        // Run cron and check that the expected number of users received the notification.
+        $messages = $this->helper_run_cron_check_count($post, $expected);
+
+        // A user with the manageactivities capability within the course can subscribe.
+        $expected = 1;
+        $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
+        assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], context_course::instance($course->id));
+        \mod_forum\subscriptions::subscribe_user($author->id, $forum);
+
+        $this->assertEquals($expected, $DB->count_records('forum_subscriptions', array(
+            'userid'        => $author->id,
+            'forum'         => $forum->id,
+        )));
+
+        // Run cron and check that the expected number of users received the notification.
+        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
+        $messages = $this->helper_run_cron_check_count($post, $expected);
+
+        // Unsubscribe the user again.
+        \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
+
+        $expected = 0;
+        $this->assertEquals($expected, $DB->count_records('forum_subscriptions', array(
+            'userid'        => $author->id,
+            'forum'         => $forum->id,
+        )));
+
+        // Run cron and check that the expected number of users received the notification.
+        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
+        $messages = $this->helper_run_cron_check_count($post, $expected);
+
+        // And unsubscribe the user from a new discussion
+        $expected = 1;
+        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
+        \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
+        $messages = $this->helper_run_cron_check_count($post, $expected);
+    }
+
+    public function test_automatic() {
+        $this->resetAfterTest(true);
+
+        // Create a course, with a forum.
+        $course = $this->getDataGenerator()->create_course();
+
+        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
+        $forum = $this->getDataGenerator()->create_module('forum', $options);
+
+        // Create two users enrolled in the course as students.
+        list($author, $recipient) = $this->helper_create_users($course, 2);
+
+        // Post a discussion to the forum.
+        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
+
+        // We expect both users to receive this post.
+        $expected = 2;
+
+        // Run cron and check that the expected number of users received the notification.
+        $messages = $this->helper_run_cron_check_count($post, $expected);
+
+        $seenauthor = false;
+        $seenrecipient = false;
+        foreach ($messages as $message) {
+            // They should both be from our user.
+            $this->assertEquals($author->id, $message->useridfrom);
+
+            if ($message->useridto == $author->id) {
+                $seenauthor = true;
+            } else if ($message->useridto = $recipient->id) {
+                $seenrecipient = true;
+            }
+        }
+
+        // Check we saw messages for both users.
+        $this->assertTrue($seenauthor);
+        $this->assertTrue($seenrecipient);
+    }
+
+    public function test_optional() {
+        $this->resetAfterTest(true);
+
+        // Create a course, with a forum.
+        $course = $this->getDataGenerator()->create_course();
+
+        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
+        $forum = $this->getDataGenerator()->create_module('forum', $options);
+
+        // Create two users enrolled in the course as students.
+        list($author, $recipient) = $this->helper_create_users($course, 2);
+
+        // Post a discussion to the forum.
+        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
+
+        // We expect both users to receive this post.
+        $expected = 0;
+
+        // Run cron and check that the expected number of users received the notification.
+        $messages = $this->helper_run_cron_check_count($post, $expected);
+    }
+
+    public function test_automatic_with_unsubscribed_user() {
+        $this->resetAfterTest(true);
+
+        // Create a course, with a forum.
+        $course = $this->getDataGenerator()->create_course();
+
+        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
+        $forum = $this->getDataGenerator()->create_module('forum', $options);
+
+        // Create two users enrolled in the course as students.
+        list($author, $recipient) = $this->helper_create_users($course, 2);
+
+        // Unsubscribe the 'author' user from the forum.
+        forum_unsubscribe($author->id, $forum->id);
+
+        // Post a discussion to the forum.
+        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
+
+        // We expect only one user to receive this post.
+        $expected = 1;
+
+        // Run cron and check that the expected number of users received the notification.
+        $messages = $this->helper_run_cron_check_count($post, $expected);
+
+        $seenauthor = false;
+        $seenrecipient = false;
+        foreach ($messages as $message) {
+            // They should both be from our user.
+            $this->assertEquals($author->id, $message->useridfrom);
+
+            if ($message->useridto == $author->id) {
+                $seenauthor = true;
+            } else if ($message->useridto = $recipient->id) {
+                $seenrecipient = true;
+            }
+        }
+
+        // Check we only saw one user.
+        $this->assertFalse($seenauthor);
+        $this->assertTrue($seenrecipient);
+    }
+
+    public function test_optional_with_subscribed_user() {
+        $this->resetAfterTest(true);
+
+        // Create a course, with a forum.
+        $course = $this->getDataGenerator()->create_course();
+
+        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
+        $forum = $this->getDataGenerator()->create_module('forum', $options);
+
+        // Create two users enrolled in the course as students.
+        list($author, $recipient) = $this->helper_create_users($course, 2);
+
+        // Subscribe the 'recipient' user from the forum.
+        forum_subscribe($recipient->id, $forum->id);
+
+        // Post a discussion to the forum.
+        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
+
+        // We expect only one user to receive this post.
+        $expected = 1;
+
+        // Run cron and check that the expected number of users received the notification.
+        $messages = $this->helper_run_cron_check_count($post, $expected);
+
+        $seenauthor = false;
+        $seenrecipient = false;
+        foreach ($messages as $message) {
+            // They should both be from our user.
+            $this->assertEquals($author->id, $message->useridfrom);
+
+            if ($message->useridto == $author->id) {
+                $seenauthor = true;
+            } else if ($message->useridto = $recipient->id) {
+                $seenrecipient = true;
+            }
+        }
+
+        // Check we only saw one user.
+        $this->assertFalse($seenauthor);
+        $this->assertTrue($seenrecipient);
+    }
+
+}