MDL-1626 mod_forum: Add unit tests for forum cron mail behaviour
[moodle.git] / mod / forum / tests / mail_test.php
CommitLineData
a7a84903
AN
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * The forum module mail generation tests.
19 *
20 * @package mod_forum
21 * @category external
22 * @copyright 2013 Andrew Nicols
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28global $CFG;
29
30class mod_forum_mail_testcase extends advanced_testcase {
31
32 protected $helper;
33
34 public function setUp() {
35 global $CFG;
36 require_once($CFG->dirroot . '/mod/forum/lib.php');
37
38 $helper = new stdClass();
39
40 // Messaging is not compatible with transactions...
41 $this->preventResetByRollback();
42
43 // Catch all messages.
44 $helper->messagesink = $this->redirectMessages();
45 $helper->mailsink = $this->redirectEmails();
46
47 // Confirm that we have an empty message sink so far.
48 $messages = $helper->messagesink->get_messages();
49 $this->assertEquals(0, count($messages));
50
51 $messages = $helper->mailsink->get_messages();
52 $this->assertEquals(0, count($messages));
53
54 // Forcibly reduce the maxeditingtime to a one second to ensure that
55 // messages are sent out.
56 $CFG->maxeditingtime = 1;
57
58 // Ensure that we don't prevent e-mail as this will cause unit test failures.
59 $CFG->noemailever = false;
60
61 $this->helper = $helper;
62 }
63
64 public function tearDown() {
65 $this->helper->messagesink->clear();
66 $this->helper->messagesink->close();
67
68 $this->helper->mailsink->clear();
69 $this->helper->mailsink->close();
70 }
71
72 /**
73 * Helper to create the required number of users in the specified
74 * course.
75 * Users are enrolled as students.
76 *
77 * @param stdClass $course The course object
78 * @param integer $count The number of users to create
79 * @return array The users created
80 */
81 protected function helper_create_users($course, $count) {
82 $users = array();
83
84 for ($i = 0; $i < $count; $i++) {
85 $user = $this->getDataGenerator()->create_user();
86 $this->getDataGenerator()->enrol_user($user->id, $course->id);
87 $users[] = $user;
88 }
89
90 return $users;
91 }
92
93 /**
94 * Create a new discussion and post within the specified forum, as the
95 * specified author.
96 *
97 * @param stdClass $forum The forum to post in
98 * @param stdClass $author The author to post as
99 * @param array An array containing the discussion object, and the post object
100 */
101 protected function helper_post_to_forum($forum, $author) {
102 $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
103
104 // Create a discussion in the forum, and then add a post to that discussion.
105 $record = new stdClass();
106 $record->course = $forum->course;
107 $record->userid = $author->id;
108 $record->forum = $forum->id;
109 $discussion = $generator->create_discussion($record);
110
111 $record = new stdClass();
112 $record->course = $forum->course;
113 $record->userid = $author->id;
114 $record->forum = $forum->id;
115 $record->discussion = $discussion->id;
116 $record->mailnow = 1;
117 $post = $generator->create_post($record);
118
119 return array($discussion, $post);
120 }
121
122 /**
123 * Run the forum cron, and check that the specified post was sent the
124 * specified number of times.
125 *
126 * @param stdClass $post The forum post object
127 * @param integer $expected The number of times that the post should have been sent
128 * @return array An array of the messages caught by the message sink
129 */
130 protected function helper_run_cron_check_count($post, $expected) {
131 // Clear the sinks before running cron.
132 $this->helper->messagesink->clear();
133 $this->helper->mailsink->clear();
134
135 // Cron daily uses mtrace, turn on buffering to silence output.
136 $this->expectOutputRegex("/{$expected} users were sent post {$post->id}, '{$post->subject}'/");
137 forum_cron();
138
139 // Now check the results in the message sink.
140 $messages = $this->helper->messagesink->get_messages();
141
142 // There should be the expected number of messages.
143 $this->assertEquals($expected, count($messages));
144
145 return $messages;
146 }
147
148 public function test_forced_subscription() {
149 $this->resetAfterTest(true);
150
151 // Create a course, with a forum.
152 $course = $this->getDataGenerator()->create_course();
153
154 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
155 $forum = $this->getDataGenerator()->create_module('forum', $options);
156
157 // Create two users enrolled in the course as students.
158 list($author, $recipient) = $this->helper_create_users($course, 2);
159
160 // Post a discussion to the forum.
161 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
162
163 // We expect both users to receive this post.
164 $expected = 2;
165
166 // Run cron and check that the expected number of users received the notification.
167 $messages = $this->helper_run_cron_check_count($post, $expected);
168
169 $seenauthor = false;
170 $seenrecipient = false;
171 foreach ($messages as $message) {
172 // They should both be from our user.
173 $this->assertEquals($author->id, $message->useridfrom);
174
175 if ($message->useridto == $author->id) {
176 $seenauthor = true;
177 } else if ($message->useridto = $recipient->id) {
178 $seenrecipient = true;
179 }
180 }
181
182 // Check we saw messages for both users.
183 $this->assertTrue($seenauthor);
184 $this->assertTrue($seenrecipient);
185 }
186
187 public function test_subscription_disabled() {
188 global $DB;
189
190 $this->resetAfterTest(true);
191
192 // Create a course, with a forum.
193 $course = $this->getDataGenerator()->create_course();
194
195 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
196 $forum = $this->getDataGenerator()->create_module('forum', $options);
197
198 // Create two users enrolled in the course as students.
199 list($author, $recipient) = $this->helper_create_users($course, 2);
200
201 // Post a discussion to the forum.
202 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
203
204 // We expect both users to receive this post.
205 $expected = 0;
206
207 // Run cron and check that the expected number of users received the notification.
208 $messages = $this->helper_run_cron_check_count($post, $expected);
209
210 // A user with the manageactivities capability within the course can subscribe.
211 $expected = 1;
212 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
213 assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], context_course::instance($course->id));
214 \mod_forum\subscriptions::subscribe_user($author->id, $forum);
215
216 $this->assertEquals($expected, $DB->count_records('forum_subscriptions', array(
217 'userid' => $author->id,
218 'forum' => $forum->id,
219 )));
220
221 // Run cron and check that the expected number of users received the notification.
222 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
223 $messages = $this->helper_run_cron_check_count($post, $expected);
224
225 // Unsubscribe the user again.
226 \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
227
228 $expected = 0;
229 $this->assertEquals($expected, $DB->count_records('forum_subscriptions', array(
230 'userid' => $author->id,
231 'forum' => $forum->id,
232 )));
233
234 // Run cron and check that the expected number of users received the notification.
235 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
236 $messages = $this->helper_run_cron_check_count($post, $expected);
237
238 // And unsubscribe the user from a new discussion
239 $expected = 1;
240 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
241 \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
242 $messages = $this->helper_run_cron_check_count($post, $expected);
243 }
244
245 public function test_automatic() {
246 $this->resetAfterTest(true);
247
248 // Create a course, with a forum.
249 $course = $this->getDataGenerator()->create_course();
250
251 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
252 $forum = $this->getDataGenerator()->create_module('forum', $options);
253
254 // Create two users enrolled in the course as students.
255 list($author, $recipient) = $this->helper_create_users($course, 2);
256
257 // Post a discussion to the forum.
258 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
259
260 // We expect both users to receive this post.
261 $expected = 2;
262
263 // Run cron and check that the expected number of users received the notification.
264 $messages = $this->helper_run_cron_check_count($post, $expected);
265
266 $seenauthor = false;
267 $seenrecipient = false;
268 foreach ($messages as $message) {
269 // They should both be from our user.
270 $this->assertEquals($author->id, $message->useridfrom);
271
272 if ($message->useridto == $author->id) {
273 $seenauthor = true;
274 } else if ($message->useridto = $recipient->id) {
275 $seenrecipient = true;
276 }
277 }
278
279 // Check we saw messages for both users.
280 $this->assertTrue($seenauthor);
281 $this->assertTrue($seenrecipient);
282 }
283
284 public function test_optional() {
285 $this->resetAfterTest(true);
286
287 // Create a course, with a forum.
288 $course = $this->getDataGenerator()->create_course();
289
290 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
291 $forum = $this->getDataGenerator()->create_module('forum', $options);
292
293 // Create two users enrolled in the course as students.
294 list($author, $recipient) = $this->helper_create_users($course, 2);
295
296 // Post a discussion to the forum.
297 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
298
299 // We expect both users to receive this post.
300 $expected = 0;
301
302 // Run cron and check that the expected number of users received the notification.
303 $messages = $this->helper_run_cron_check_count($post, $expected);
304 }
305
306 public function test_automatic_with_unsubscribed_user() {
307 $this->resetAfterTest(true);
308
309 // Create a course, with a forum.
310 $course = $this->getDataGenerator()->create_course();
311
312 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
313 $forum = $this->getDataGenerator()->create_module('forum', $options);
314
315 // Create two users enrolled in the course as students.
316 list($author, $recipient) = $this->helper_create_users($course, 2);
317
318 // Unsubscribe the 'author' user from the forum.
319 forum_unsubscribe($author->id, $forum->id);
320
321 // Post a discussion to the forum.
322 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
323
324 // We expect only one user to receive this post.
325 $expected = 1;
326
327 // Run cron and check that the expected number of users received the notification.
328 $messages = $this->helper_run_cron_check_count($post, $expected);
329
330 $seenauthor = false;
331 $seenrecipient = false;
332 foreach ($messages as $message) {
333 // They should both be from our user.
334 $this->assertEquals($author->id, $message->useridfrom);
335
336 if ($message->useridto == $author->id) {
337 $seenauthor = true;
338 } else if ($message->useridto = $recipient->id) {
339 $seenrecipient = true;
340 }
341 }
342
343 // Check we only saw one user.
344 $this->assertFalse($seenauthor);
345 $this->assertTrue($seenrecipient);
346 }
347
348 public function test_optional_with_subscribed_user() {
349 $this->resetAfterTest(true);
350
351 // Create a course, with a forum.
352 $course = $this->getDataGenerator()->create_course();
353
354 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
355 $forum = $this->getDataGenerator()->create_module('forum', $options);
356
357 // Create two users enrolled in the course as students.
358 list($author, $recipient) = $this->helper_create_users($course, 2);
359
360 // Subscribe the 'recipient' user from the forum.
361 forum_subscribe($recipient->id, $forum->id);
362
363 // Post a discussion to the forum.
364 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
365
366 // We expect only one user to receive this post.
367 $expected = 1;
368
369 // Run cron and check that the expected number of users received the notification.
370 $messages = $this->helper_run_cron_check_count($post, $expected);
371
372 $seenauthor = false;
373 $seenrecipient = false;
374 foreach ($messages as $message) {
375 // They should both be from our user.
376 $this->assertEquals($author->id, $message->useridfrom);
377
378 if ($message->useridto == $author->id) {
379 $seenauthor = true;
380 } else if ($message->useridto = $recipient->id) {
381 $seenrecipient = true;
382 }
383 }
384
385 // Check we only saw one user.
386 $this->assertFalse($seenauthor);
387 $this->assertTrue($seenrecipient);
388 }
389
390}