Merge branch 'MDL-47806' of github.com:mr-russ/moodle
[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() {
59075a43
AN
35 // We must clear the subscription caches. This has to be done both before each test, and after in case of other
36 // tests using these functions.
37 \mod_forum\subscriptions::reset_forum_cache();
a54702b2 38 \mod_forum\subscriptions::reset_discussion_cache();
59075a43 39
a7a84903
AN
40 global $CFG;
41 require_once($CFG->dirroot . '/mod/forum/lib.php');
42
43 $helper = new stdClass();
44
45 // Messaging is not compatible with transactions...
46 $this->preventResetByRollback();
47
48 // Catch all messages.
49 $helper->messagesink = $this->redirectMessages();
50 $helper->mailsink = $this->redirectEmails();
51
52 // Confirm that we have an empty message sink so far.
53 $messages = $helper->messagesink->get_messages();
54 $this->assertEquals(0, count($messages));
55
56 $messages = $helper->mailsink->get_messages();
57 $this->assertEquals(0, count($messages));
58
555837c5
AN
59 // Forcibly reduce the maxeditingtime to a second in the past to
60 // ensure that messages are sent out.
61 $CFG->maxeditingtime = -1;
a7a84903
AN
62
63 // Ensure that we don't prevent e-mail as this will cause unit test failures.
64 $CFG->noemailever = false;
65
66 $this->helper = $helper;
67 }
68
69 public function tearDown() {
59075a43
AN
70 // We must clear the subscription caches. This has to be done both before each test, and after in case of other
71 // tests using these functions.
72 \mod_forum\subscriptions::reset_forum_cache();
73
a7a84903
AN
74 $this->helper->messagesink->clear();
75 $this->helper->messagesink->close();
76
77 $this->helper->mailsink->clear();
78 $this->helper->mailsink->close();
79 }
80
81 /**
82 * Helper to create the required number of users in the specified
83 * course.
84 * Users are enrolled as students.
85 *
86 * @param stdClass $course The course object
87 * @param integer $count The number of users to create
88 * @return array The users created
89 */
90 protected function helper_create_users($course, $count) {
91 $users = array();
92
93 for ($i = 0; $i < $count; $i++) {
94 $user = $this->getDataGenerator()->create_user();
95 $this->getDataGenerator()->enrol_user($user->id, $course->id);
96 $users[] = $user;
97 }
98
99 return $users;
100 }
101
102 /**
103 * Create a new discussion and post within the specified forum, as the
104 * specified author.
105 *
106 * @param stdClass $forum The forum to post in
107 * @param stdClass $author The author to post as
108 * @param array An array containing the discussion object, and the post object
109 */
110 protected function helper_post_to_forum($forum, $author) {
555837c5 111 global $DB;
a7a84903
AN
112 $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
113
114 // Create a discussion in the forum, and then add a post to that discussion.
115 $record = new stdClass();
116 $record->course = $forum->course;
117 $record->userid = $author->id;
118 $record->forum = $forum->id;
119 $discussion = $generator->create_discussion($record);
120
555837c5
AN
121 // Retrieve the post which was created by create_discussion.
122 $post = $DB->get_record('forum_posts', array('discussion' => $discussion->id));
a7a84903
AN
123
124 return array($discussion, $post);
125 }
126
127 /**
128 * Run the forum cron, and check that the specified post was sent the
129 * specified number of times.
130 *
131 * @param stdClass $post The forum post object
132 * @param integer $expected The number of times that the post should have been sent
133 * @return array An array of the messages caught by the message sink
134 */
135 protected function helper_run_cron_check_count($post, $expected) {
eb451c79 136
a7a84903
AN
137 // Clear the sinks before running cron.
138 $this->helper->messagesink->clear();
139 $this->helper->mailsink->clear();
140
141 // Cron daily uses mtrace, turn on buffering to silence output.
142 $this->expectOutputRegex("/{$expected} users were sent post {$post->id}, '{$post->subject}'/");
143 forum_cron();
144
145 // Now check the results in the message sink.
146 $messages = $this->helper->messagesink->get_messages();
147
148 // There should be the expected number of messages.
149 $this->assertEquals($expected, count($messages));
150
151 return $messages;
152 }
153
eb451c79
AN
154 /**
155 * Run the forum cron, and check that the specified posts were sent the
156 * specified number of times.
157 *
158 * @param stdClass $post The forum post object
159 * @param integer $expected The number of times that the post should have been sent
160 * @return array An array of the messages caught by the message sink
161 */
162 protected function helper_run_cron_check_counts($posts, $expected) {
163
164 // Clear the sinks before running cron.
165 $this->helper->messagesink->clear();
166 $this->helper->mailsink->clear();
167
168 // Cron daily uses mtrace, turn on buffering to silence output.
169 foreach ($posts as $post) {
170 $this->expectOutputRegex("/{$post['count']} users were sent post {$post['id']}, '{$post['subject']}'/");
171 }
172 forum_cron();
173
174 // Now check the results in the message sink.
175 $messages = $this->helper->messagesink->get_messages();
176
177 // There should be the expected number of messages.
178 $this->assertEquals($expected, count($messages));
179
180 return $messages;
181 }
182
a7a84903
AN
183 public function test_forced_subscription() {
184 $this->resetAfterTest(true);
185
186 // Create a course, with a forum.
187 $course = $this->getDataGenerator()->create_course();
188
189 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
190 $forum = $this->getDataGenerator()->create_module('forum', $options);
191
192 // Create two users enrolled in the course as students.
193 list($author, $recipient) = $this->helper_create_users($course, 2);
194
195 // Post a discussion to the forum.
196 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
197
198 // We expect both users to receive this post.
199 $expected = 2;
200
201 // Run cron and check that the expected number of users received the notification.
202 $messages = $this->helper_run_cron_check_count($post, $expected);
203
204 $seenauthor = false;
205 $seenrecipient = false;
206 foreach ($messages as $message) {
207 // They should both be from our user.
208 $this->assertEquals($author->id, $message->useridfrom);
209
210 if ($message->useridto == $author->id) {
211 $seenauthor = true;
212 } else if ($message->useridto = $recipient->id) {
213 $seenrecipient = true;
214 }
215 }
216
217 // Check we saw messages for both users.
218 $this->assertTrue($seenauthor);
219 $this->assertTrue($seenrecipient);
220 }
221
222 public function test_subscription_disabled() {
223 global $DB;
224
225 $this->resetAfterTest(true);
226
227 // Create a course, with a forum.
228 $course = $this->getDataGenerator()->create_course();
229
230 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
231 $forum = $this->getDataGenerator()->create_module('forum', $options);
232
233 // Create two users enrolled in the course as students.
234 list($author, $recipient) = $this->helper_create_users($course, 2);
235
236 // Post a discussion to the forum.
237 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
238
239 // We expect both users to receive this post.
240 $expected = 0;
241
242 // Run cron and check that the expected number of users received the notification.
243 $messages = $this->helper_run_cron_check_count($post, $expected);
244
245 // A user with the manageactivities capability within the course can subscribe.
246 $expected = 1;
247 $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
248 assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleids['student'], context_course::instance($course->id));
249 \mod_forum\subscriptions::subscribe_user($author->id, $forum);
250
251 $this->assertEquals($expected, $DB->count_records('forum_subscriptions', array(
252 'userid' => $author->id,
253 'forum' => $forum->id,
254 )));
255
256 // Run cron and check that the expected number of users received the notification.
257 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
258 $messages = $this->helper_run_cron_check_count($post, $expected);
259
260 // Unsubscribe the user again.
261 \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
262
263 $expected = 0;
264 $this->assertEquals($expected, $DB->count_records('forum_subscriptions', array(
265 'userid' => $author->id,
266 'forum' => $forum->id,
267 )));
268
269 // Run cron and check that the expected number of users received the notification.
270 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
271 $messages = $this->helper_run_cron_check_count($post, $expected);
272
273 // And unsubscribe the user from a new discussion
274 $expected = 1;
275 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
276 \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
277 $messages = $this->helper_run_cron_check_count($post, $expected);
278 }
279
280 public function test_automatic() {
281 $this->resetAfterTest(true);
282
283 // Create a course, with a forum.
284 $course = $this->getDataGenerator()->create_course();
285
286 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
287 $forum = $this->getDataGenerator()->create_module('forum', $options);
288
289 // Create two users enrolled in the course as students.
290 list($author, $recipient) = $this->helper_create_users($course, 2);
291
292 // Post a discussion to the forum.
293 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
294
295 // We expect both users to receive this post.
296 $expected = 2;
297
298 // Run cron and check that the expected number of users received the notification.
299 $messages = $this->helper_run_cron_check_count($post, $expected);
300
301 $seenauthor = false;
302 $seenrecipient = false;
303 foreach ($messages as $message) {
304 // They should both be from our user.
305 $this->assertEquals($author->id, $message->useridfrom);
306
307 if ($message->useridto == $author->id) {
308 $seenauthor = true;
309 } else if ($message->useridto = $recipient->id) {
310 $seenrecipient = true;
311 }
312 }
313
314 // Check we saw messages for both users.
315 $this->assertTrue($seenauthor);
316 $this->assertTrue($seenrecipient);
317 }
318
319 public function test_optional() {
320 $this->resetAfterTest(true);
321
322 // Create a course, with a forum.
323 $course = $this->getDataGenerator()->create_course();
324
325 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
326 $forum = $this->getDataGenerator()->create_module('forum', $options);
327
328 // Create two users enrolled in the course as students.
329 list($author, $recipient) = $this->helper_create_users($course, 2);
330
331 // Post a discussion to the forum.
332 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
333
334 // We expect both users to receive this post.
335 $expected = 0;
336
337 // Run cron and check that the expected number of users received the notification.
338 $messages = $this->helper_run_cron_check_count($post, $expected);
339 }
340
341 public function test_automatic_with_unsubscribed_user() {
342 $this->resetAfterTest(true);
343
344 // Create a course, with a forum.
345 $course = $this->getDataGenerator()->create_course();
346
347 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
348 $forum = $this->getDataGenerator()->create_module('forum', $options);
349
350 // Create two users enrolled in the course as students.
351 list($author, $recipient) = $this->helper_create_users($course, 2);
352
353 // Unsubscribe the 'author' user from the forum.
59075a43 354 \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
a7a84903
AN
355
356 // Post a discussion to the forum.
357 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
358
359 // We expect only one user to receive this post.
360 $expected = 1;
361
362 // Run cron and check that the expected number of users received the notification.
363 $messages = $this->helper_run_cron_check_count($post, $expected);
364
365 $seenauthor = false;
366 $seenrecipient = false;
367 foreach ($messages as $message) {
368 // They should both be from our user.
369 $this->assertEquals($author->id, $message->useridfrom);
370
371 if ($message->useridto == $author->id) {
372 $seenauthor = true;
373 } else if ($message->useridto = $recipient->id) {
374 $seenrecipient = true;
375 }
376 }
377
378 // Check we only saw one user.
379 $this->assertFalse($seenauthor);
380 $this->assertTrue($seenrecipient);
381 }
382
383 public function test_optional_with_subscribed_user() {
384 $this->resetAfterTest(true);
385
386 // Create a course, with a forum.
387 $course = $this->getDataGenerator()->create_course();
388
389 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
390 $forum = $this->getDataGenerator()->create_module('forum', $options);
391
392 // Create two users enrolled in the course as students.
393 list($author, $recipient) = $this->helper_create_users($course, 2);
394
395 // Subscribe the 'recipient' user from the forum.
59075a43 396 \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
a7a84903
AN
397
398 // Post a discussion to the forum.
399 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
400
401 // We expect only one user to receive this post.
402 $expected = 1;
403
404 // Run cron and check that the expected number of users received the notification.
405 $messages = $this->helper_run_cron_check_count($post, $expected);
406
407 $seenauthor = false;
408 $seenrecipient = false;
409 foreach ($messages as $message) {
410 // They should both be from our user.
411 $this->assertEquals($author->id, $message->useridfrom);
412
413 if ($message->useridto == $author->id) {
414 $seenauthor = true;
415 } else if ($message->useridto = $recipient->id) {
416 $seenrecipient = true;
417 }
418 }
419
420 // Check we only saw one user.
421 $this->assertFalse($seenauthor);
422 $this->assertTrue($seenrecipient);
423 }
424
49566c8a
AN
425 public function test_automatic_with_unsubscribed_discussion() {
426 $this->resetAfterTest(true);
427
428 // Create a course, with a forum.
429 $course = $this->getDataGenerator()->create_course();
430
431 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
432 $forum = $this->getDataGenerator()->create_module('forum', $options);
433
434 // Create two users enrolled in the course as students.
435 list($author, $recipient) = $this->helper_create_users($course, 2);
436
437 // Post a discussion to the forum.
438 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
439
440 // Unsubscribe the 'author' user from the discussion.
441 \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
442
eb451c79
AN
443 $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
444 $this->assertTrue(\mod_forum\subscriptions::is_subscribed($recipient->id, $forum, $discussion->id));
445
49566c8a
AN
446 // We expect only one user to receive this post.
447 $expected = 1;
448
449 // Run cron and check that the expected number of users received the notification.
450 $messages = $this->helper_run_cron_check_count($post, $expected);
451
452 $seenauthor = false;
453 $seenrecipient = false;
454 foreach ($messages as $message) {
455 // They should both be from our user.
456 $this->assertEquals($author->id, $message->useridfrom);
457
458 if ($message->useridto == $author->id) {
459 $seenauthor = true;
460 } else if ($message->useridto = $recipient->id) {
461 $seenrecipient = true;
462 }
463 }
464
465 // Check we only saw one user.
466 $this->assertFalse($seenauthor);
467 $this->assertTrue($seenrecipient);
468 }
469
470 public function test_optional_with_subscribed_discussion() {
471 $this->resetAfterTest(true);
472
473 // Create a course, with a forum.
474 $course = $this->getDataGenerator()->create_course();
475
476 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
477 $forum = $this->getDataGenerator()->create_module('forum', $options);
478
479 // Create two users enrolled in the course as students.
480 list($author, $recipient) = $this->helper_create_users($course, 2);
481
482 // Post a discussion to the forum.
483 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
484
485 // Subscribe the 'recipient' user to the discussion.
486 \mod_forum\subscriptions::subscribe_user_to_discussion($recipient->id, $discussion);
487
488 // We expect only one user to receive this post.
489 $expected = 1;
490
491 // Run cron and check that the expected number of users received the notification.
492 $messages = $this->helper_run_cron_check_count($post, $expected);
493
494 $seenauthor = false;
495 $seenrecipient = false;
496 foreach ($messages as $message) {
497 // They should both be from our user.
498 $this->assertEquals($author->id, $message->useridfrom);
499
500 if ($message->useridto == $author->id) {
501 $seenauthor = true;
502 } else if ($message->useridto = $recipient->id) {
503 $seenrecipient = true;
504 }
505 }
506
507 // Check we only saw one user.
508 $this->assertFalse($seenauthor);
509 $this->assertTrue($seenrecipient);
510 }
511
512 public function test_automatic_with_subscribed_discussion_in_unsubscribed_forum() {
513 $this->resetAfterTest(true);
514
515 // Create a course, with a forum.
516 $course = $this->getDataGenerator()->create_course();
517
518 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
519 $forum = $this->getDataGenerator()->create_module('forum', $options);
520
521 // Create two users enrolled in the course as students.
522 list($author, $recipient) = $this->helper_create_users($course, 2);
523
524 // Post a discussion to the forum.
525 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
526
527 // Unsubscribe the 'author' user from the discussion.
528 \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
529
530 // Then re-subscribe them to the discussion.
531 \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
532
533 // We expect two users to receive this post.
534 $expected = 2;
535
536 // Run cron and check that the expected number of users received the notification.
537 $messages = $this->helper_run_cron_check_count($post, $expected);
538
539 $seenauthor = false;
540 $seenrecipient = false;
541 foreach ($messages as $message) {
542 // They should both be from our user.
543 $this->assertEquals($author->id, $message->useridfrom);
544
545 if ($message->useridto == $author->id) {
546 $seenauthor = true;
547 } else if ($message->useridto = $recipient->id) {
548 $seenrecipient = true;
549 }
550 }
551
552 // Check we only saw one user.
553 $this->assertTrue($seenauthor);
554 $this->assertTrue($seenrecipient);
555 }
556
557 public function test_optional_with_unsubscribed_discussion_in_subscribed_forum() {
558 $this->resetAfterTest(true);
559
560 // Create a course, with a forum.
561 $course = $this->getDataGenerator()->create_course();
562
563 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
564 $forum = $this->getDataGenerator()->create_module('forum', $options);
565
566 // Create two users enrolled in the course as students.
567 list($author, $recipient) = $this->helper_create_users($course, 2);
568
569 // Post a discussion to the forum.
570 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
571
572 // Unsubscribe the 'recipient' user from the discussion.
573 \mod_forum\subscriptions::subscribe_user($recipient->id, $forum);
574
575 // Then unsubscribe them from the discussion.
576 \mod_forum\subscriptions::unsubscribe_user_from_discussion($recipient->id, $discussion);
577
578 // We don't expect any users to receive this post.
579 $expected = 0;
580
581 // Run cron and check that the expected number of users received the notification.
582 $messages = $this->helper_run_cron_check_count($post, $expected);
583 }
eb451c79
AN
584
585 /**
586 * Test that a user unsubscribed from a forum who has subscribed to a discussion, only receives posts made after
587 * they subscribed to the discussion.
588 */
589 public function test_forum_discussion_subscription_forum_unsubscribed_discussion_subscribed_after_post() {
590 $this->resetAfterTest(true);
591 global $DB;
592
593 // Create a course, with a forum.
594 $course = $this->getDataGenerator()->create_course();
595
596 $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
597 $forum = $this->getDataGenerator()->create_module('forum', $options);
598
599 $expectedmessages = array();
600
601 // Create a user enrolled in the course as a student.
602 list($author) = $this->helper_create_users($course, 1);
603
604 // Post a discussion to the forum.
605 list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
606
607 // Update the original post to have a timecreated in the past by a few minutes.
608 $post->created = $post->created - 600;
609 $DB->update_record('forum_posts', $post);
610
611 $expectedmessages[] = array(
612 'id' => $post->id,
613 'subject' => $post->subject,
614 'count' => 0,
615 );
616
617 // Then subscribe the user to the discussion.
618 $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));
619
620 // Then post a reply to the first discussion.
621 $record = new stdClass();
622 $record->course = $forum->course;
623 $record->userid = $author->id;
624 $record->forum = $forum->id;
625 $record->discussion = $discussion->id;
626 $record->mailnow = 1;
627 $record->created = time();
628 $generator = $this->getDataGenerator()->get_plugin_generator('mod_forum');
629 $reply = $generator->create_post($record);
630
631 $expectedmessages[] = array(
632 'id' => $reply->id,
633 'subject' => $reply->subject,
634 'count' => 1,
635 );
636
637 // We expect there to be two notifications, and not three.
638 $expectedcount = 1;
639
640 // Run cron and check that the expected number of users received the notification.
641 $messages = $this->helper_run_cron_check_counts($expectedmessages, $expectedcount);
642
643 }
a7a84903 644}