weekly release 3.1dev
[moodle.git] / admin / tool / monitor / tests / eventobservers_test.php
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/>.
17 /**
18  * Unit tests for event observers.
19  *
20  * @package    tool_monitor
21  * @category   test
22  * @copyright  2014 onwards Ankit Agarwal <ankit.agrr@gmail.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->dirroot . '/blog/locallib.php');
30 require_once($CFG->dirroot . '/blog/lib.php');
32 /**
33  * Class tool_monitor_eventobservers_testcase
34  *
35  * Tests for event observers
36  */
37 class tool_monitor_eventobservers_testcase extends advanced_testcase {
38     /**
39      * Set up method.
40      */
41     public function setUp() {
42         // Enable monitor.
43         set_config('enablemonitor', 1, 'tool_monitor');
44     }
46     /**
47      * Test observer for course delete event.
48      */
49     public function test_course_deleted() {
50         global $DB;
52         $this->setAdminUser();
53         $this->resetAfterTest(true);
55         $user = $this->getDataGenerator()->create_user();
56         $course1 = $this->getDataGenerator()->create_course();
57         $course2 = $this->getDataGenerator()->create_course();
58         $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
60         $rule = new stdClass();
61         $rule->userid = $user->id;
62         $rule->courseid = $course1->id;
63         $rule->plugin = 'test';
65         $sub = new stdClass();
66         $sub->courseid = $course1->id;
67         $sub->userid = $user->id;
69         // Add 10 rules for this course with subscriptions.
70         for ($i = 0; $i < 10; $i++) {
71             $createdrule = $monitorgenerator->create_rule($rule);
72             $sub->ruleid = $createdrule->id;
73             $monitorgenerator->create_subscription($sub);
74         }
76         // Add 10 random rules for course 2.
77         $rule->courseid = $course2->id;
78         for ($i = 0; $i < 10; $i++) {
79             $createdrule = $monitorgenerator->create_rule($rule);
80             $sub->courseid = $rule->courseid;
81             $sub->ruleid = $createdrule->id;
82             $monitorgenerator->create_subscription($sub);
83         }
85         // Add a site rule.
86         $rule = new stdClass();
87         $rule->userid = $user->id;
88         $rule->courseid = 0;
89         $rule->plugin = 'core';
90         $monitorgenerator->create_rule($rule);
92         // Verify that if we do not specify that we do not want the site rules, they are returned.
93         $courserules = \tool_monitor\rule_manager::get_rules_by_courseid($course1->id);
94         $this->assertCount(11, $courserules);
96         // Verify data before course delete.
97         $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
98         $this->assertCount(20, $totalrules);
99         $courserules = \tool_monitor\rule_manager::get_rules_by_courseid($course1->id, 0, 0, false);
100         $this->assertCount(10, $courserules);
101         $this->assertEquals(20, $DB->count_records('tool_monitor_subscriptions'));
102         $coursesubs = \tool_monitor\subscription_manager::get_user_subscriptions_for_course($course1->id, 0, 0, $user->id);
103         $this->assertCount(10, $coursesubs);
105         // Let us delete the course now.
106         delete_course($course1->id, false);
108         // Confirm the site rule still exists.
109         $this->assertEquals(1, $DB->count_records('tool_monitor_rules', array('courseid' => 0)));
111         // Verify data after course delete.
112         $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
113         $this->assertCount(10, $totalrules);
114         $courserules = \tool_monitor\rule_manager::get_rules_by_courseid($course1->id, 0, 0, false);
115         $this->assertCount(0, $courserules); // Making sure all rules are deleted.
116         $this->assertEquals(10, $DB->count_records('tool_monitor_subscriptions'));
117         $coursesubs = \tool_monitor\subscription_manager::get_user_subscriptions_for_course($course1->id, 0, 0, $user->id);
118         $this->assertCount(0, $coursesubs); // Making sure all subscriptions are deleted.
119     }
121     /**
122      * This tests if writing of the events to the table tool_monitor_events is working fine.
123      */
124     public function test_flush() {
125         global $DB;
127         $this->resetAfterTest();
129         // Create the necessary items for testing.
130         $user = $this->getDataGenerator()->create_user();
131         $course = $this->getDataGenerator()->create_course();
132         $course2 = $this->getDataGenerator()->create_course();
133         $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
135         // Fire a bunch of events.
136         // Trigger a bunch of other events.
137         $eventparams = array(
138             'context' => context_course::instance($course->id)
139         );
140         for ($i = 0; $i < 5; $i++) {
141             \core\event\course_viewed::create($eventparams)->trigger();
142             \mod_quiz\event\course_module_instance_list_viewed::create($eventparams)->trigger();
143             \mod_scorm\event\course_module_instance_list_viewed::create($eventparams)->trigger();
144         }
146         // Confirm that nothing was stored in the tool_monitor_events table
147         // as we do not have any subscriptions associated for the above events.
148         $this->assertEquals(0, $DB->count_records('tool_monitor_events'));
150         // Now, let's create a rule so an event can be stored.
151         $rule = new stdClass();
152         $rule->courseid = $course->id;
153         $rule->plugin = 'mod_book';
154         $rule->eventname = '\mod_book\event\course_module_instance_list_viewed';
155         $rule = $monitorgenerator->create_rule($rule);
157         // Let's subscribe to this rule.
158         $sub = new stdClass;
159         $sub->courseid = $course->id;
160         $sub->ruleid = $rule->id;
161         $sub->userid = $user->id;
162         $monitorgenerator->create_subscription($sub);
164         // Again, let's just fire more events to make sure they still aren't stored.
165         for ($i = 0; $i < 5; $i++) {
166             \core\event\course_viewed::create($eventparams)->trigger();
167             \mod_quiz\event\course_module_instance_list_viewed::create($eventparams)->trigger();
168             \mod_scorm\event\course_module_instance_list_viewed::create($eventparams)->trigger();
169         }
171         // Fire the event we want to capture.
172         $event = \mod_book\event\course_module_instance_list_viewed::create_from_course($course);
173         $event->trigger();
175         // Check that the event data is valid.
176         $events = $DB->get_records('tool_monitor_events');
177         $this->assertEquals(1, count($events));
178         $monitorevent = array_pop($events);
179         $this->assertEquals($event->eventname, $monitorevent->eventname);
180         $this->assertEquals($event->contextid, $monitorevent->contextid);
181         $this->assertEquals($event->contextlevel, $monitorevent->contextlevel);
182         $this->assertEquals($event->get_url()->out(), $monitorevent->link);
183         $this->assertEquals($event->courseid, $monitorevent->courseid);
184         $this->assertEquals($event->timecreated, $monitorevent->timecreated);
186         // Remove the stored events.
187         $DB->delete_records('tool_monitor_events');
189         // Now, let's create a site wide rule.
190         $rule = new stdClass();
191         $rule->courseid = 0;
192         $rule->plugin = 'mod_book';
193         $rule->eventname = '\mod_book\event\course_module_instance_list_viewed';
194         $rule = $monitorgenerator->create_rule($rule);
196         // Let's subscribe to this rule.
197         $sub = new stdClass;
198         $sub->courseid = 0;
199         $sub->ruleid = $rule->id;
200         $sub->userid = $user->id;
201         $monitorgenerator->create_subscription($sub);
203         // Fire the event we want to capture - but in a different course.
204         $event = \mod_book\event\course_module_instance_list_viewed::create_from_course($course2);
205         $event->trigger();
207         // Check that the event data is valid.
208         $events = $DB->get_records('tool_monitor_events');
209         $this->assertEquals(1, count($events));
210         $monitorevent = array_pop($events);
211         $this->assertEquals($event->eventname, $monitorevent->eventname);
212         $this->assertEquals($event->contextid, $monitorevent->contextid);
213         $this->assertEquals($event->contextlevel, $monitorevent->contextlevel);
214         $this->assertEquals($event->get_url()->out(), $monitorevent->link);
215         $this->assertEquals($event->courseid, $monitorevent->courseid);
216         $this->assertEquals($event->timecreated, $monitorevent->timecreated);
217     }
219     /**
220      * Test the notification sending features.
221      */
222     public function test_process_event() {
224         global $DB, $USER;
226         $this->resetAfterTest();
227         $this->setAdminUser();
228         $msgsink = $this->redirectMessages();
230         // Generate data.
231         $course = $this->getDataGenerator()->create_course();
232         $toolgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
234         $rulerecord = new stdClass();
235         $rulerecord->courseid = $course->id;
236         $rulerecord->eventname = '\mod_book\event\course_module_instance_list_viewed';
237         $rulerecord->frequency = 1;
239         $rule = $toolgenerator->create_rule($rulerecord);
241         $subrecord = new stdClass();
242         $subrecord->courseid = $course->id;
243         $subrecord->ruleid = $rule->id;
244         $subrecord->userid = $USER->id;
245         $toolgenerator->create_subscription($subrecord);
247         $recordexists = $DB->record_exists('task_adhoc', array('component' => 'tool_monitor'));
248         $this->assertFalse($recordexists);
250         // Now let us trigger the event.
251         $event = \mod_book\event\course_module_instance_list_viewed::create_from_course($course);
252         $event->trigger();
254         $this->verify_processed_data($msgsink);
256         // Clean up.
257         \tool_monitor\rule_manager::delete_rule($rule->id);
258         $DB->delete_records('tool_monitor_events');
260         // Let us create a rule with more than 1 frequency.
261         $rulerecord->frequency = 5;
262         $rule = $toolgenerator->create_rule($rulerecord);
263         $subrecord->ruleid = $rule->id;
264         $toolgenerator->create_subscription($subrecord);
266         // Let us trigger events.
267         for ($i = 0; $i < 5; $i++) {
268             $event = \mod_book\event\course_module_instance_list_viewed::create_from_course($course);
269             $event->trigger();
270             if ($i != 4) {
271                 $this->verify_message_not_sent_yet($msgsink);
272             }
273         }
275         $this->verify_processed_data($msgsink);
277         // Clean up.
278         \tool_monitor\rule_manager::delete_rule($rule->id);
279         $DB->delete_records('tool_monitor_events');
281         // Now let us create a rule specific to a module instance.
282         $cm = new stdClass();
283         $cm->course = $course->id;
284         $book = $this->getDataGenerator()->create_module('book', $cm);
285         $rulerecord->eventname = '\mod_book\event\course_module_viewed';
286         $rulerecord->cmid = $book->cmid;
287         $rule = $toolgenerator->create_rule($rulerecord);
288         $subrecord->ruleid = $rule->id;
289         $toolgenerator->create_subscription($subrecord);
291         // Let us trigger events.
292         $params = array(
293             'context' => context_module::instance($book->cmid),
294             'objectid' => $book->id
295         );
296         for ($i = 0; $i < 5; $i++) {
297             $event = \mod_book\event\course_module_viewed::create($params);
298             $event->trigger();
299             if ($i != 4) {
300                 $this->verify_message_not_sent_yet($msgsink);
301             }
302         }
304         $this->verify_processed_data($msgsink);
306         // Clean up.
307         \tool_monitor\rule_manager::delete_rule($rule->id);
308         $DB->delete_records('tool_monitor_events');
310         // Now let us create a rule for event that happens in category context events.
311         $rulerecord->eventname = '\core\event\course_category_created';
312         $rulerecord->courseid = 0;
313         $rule = $toolgenerator->create_rule($rulerecord);
314         $subrecord->courseid = 0;
315         $subrecord->ruleid = $rule->id;
316         $toolgenerator->create_subscription($subrecord);
318         // Let us trigger events.
319         for ($i = 0; $i < 5; $i++) {
320             $this->getDataGenerator()->create_category();
321             if ($i != 4) {
322                 $this->verify_message_not_sent_yet($msgsink);
323             }
324         }
325         $this->verify_processed_data($msgsink);
327         // Clean up.
328         \tool_monitor\rule_manager::delete_rule($rule->id);
329         $DB->delete_records('tool_monitor_events');
331         // Now let us create a rule at site level.
332         $rulerecord->eventname = '\core\event\blog_entry_created';
333         $rulerecord->courseid = 0;
334         $rule = $toolgenerator->create_rule($rulerecord);
335         $subrecord->courseid = 0;
336         $subrecord->ruleid = $rule->id;
337         $toolgenerator->create_subscription($subrecord);
339         // Let us trigger events.
340         $blog = new blog_entry();
341         $blog->subject = "Subject of blog";
342         $blog->userid = $USER->id;
343         $states = blog_entry::get_applicable_publish_states();
344         $blog->publishstate = reset($states);
345         for ($i = 0; $i < 5; $i++) {
346             $newblog = fullclone($blog);
347             $newblog->add();
348             if ($i != 4) {
349                 $this->verify_message_not_sent_yet($msgsink);
350             }
351         }
353         $this->verify_processed_data($msgsink);
354     }
356     /**
357      * Test that same events are not used twice to calculate conditions for a single subscription.
358      */
359     public function test_multiple_notification_not_sent() {
360         global $USER;
362         $this->resetAfterTest();
363         $this->setAdminUser();
364         $messagesink = $this->redirectMessages();
366         // Generate data.
367         $course = $this->getDataGenerator()->create_course();
368         $toolgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
370         $rulerecord = new stdClass();
371         $rulerecord->courseid = $course->id;
372         $rulerecord->eventname = '\mod_book\event\course_module_instance_list_viewed';
373         $rulerecord->frequency = 5;
375         $rule = $toolgenerator->create_rule($rulerecord);
377         $subrecord = new stdClass();
378         $subrecord->courseid = $course->id;
379         $subrecord->ruleid = $rule->id;
380         $subrecord->userid = $USER->id;
381         $toolgenerator->create_subscription($subrecord);
383         for ($i = 0; $i < 7; $i++) {
384             // Now let us trigger 7 instances of the event.
385             $event = \mod_book\event\course_module_instance_list_viewed::create_from_course($course);
386             $event->trigger();
387             sleep(1); // Add a second delay, to prevent time collisions.
388         }
389         $this->run_adhock_tasks();
390         $messages = $messagesink->get_messages();
391         $this->assertCount(1, $messages); // There should be only one message not 3.
392         for ($i = 0; $i < 3; $i++) {
393             // Now let us trigger 5 more instances of the event.
394             $event = \mod_book\event\course_module_instance_list_viewed::create_from_course($course);
395             $event->trigger();
396         }
398         $this->run_adhock_tasks();
399         $messages = $messagesink->get_messages();
400         $this->assertCount(2, $messages); // There should be two messages now.
401     }
403     /**
404      * Run adhoc tasks.
405      */
406     protected function run_adhock_tasks() {
407         while ($task = \core\task\manager::get_next_adhoc_task(time())) {
408             $task->execute();
409             \core\task\manager::adhoc_task_complete($task);
410         }
411         $this->expectOutputRegex("/^Sending message to the user with id \d+ for the subscription with id \d+\.\.\..Sent./ms");
412     }
414     /**
415      * Verify that task was scheduled and a message was sent as expected.
416      *
417      * @param phpunit_message_sink $msgsink Message sink
418      */
419     protected function verify_processed_data(phpunit_message_sink $msgsink) {
420         global $DB, $USER;
422         $recordexists = $DB->count_records('task_adhoc', array('component' => 'tool_monitor'));
423         $this->assertEquals(1, $recordexists); // We should have an adhock task now to send notifications.
424         $this->run_adhock_tasks();
425         $this->assertEquals(1, $msgsink->count());
426         $msgs = $msgsink->get_messages();
427         $msg = array_pop($msgs);
428         $this->assertEquals($USER->id, $msg->useridto);
429         $this->assertEquals(1, $msg->notification);
430         $msgsink->clear();
431     }
433     /**
434      * Verify that a message was not sent.
435      *
436      * @param phpunit_message_sink $msgsink Message sink
437      */
438     protected function verify_message_not_sent_yet(phpunit_message_sink $msgsink) {
439         $msgs = $msgsink->get_messages();
440         $this->assertCount(0, $msgs);
441         $msgsink->clear();
442     }
444     /**
445      * Tests for replace_placeholders method.
446      */
447     public function test_replace_placeholders() {
448         global $USER;
450         $this->resetAfterTest();
451         $this->setAdminUser();
452         $msgsink = $this->redirectMessages();
454         // Generate data.
455         $course = $this->getDataGenerator()->create_course();
456         $toolgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
457         $context = \context_user::instance($USER->id, IGNORE_MISSING);
459         // Creating book.
460         $cm = new stdClass();
461         $cm->course = $course->id;
462         $book = $this->getDataGenerator()->create_module('book', $cm);
464         // Creating rule.
465         $rulerecord = new stdClass();
466         $rulerecord->courseid = $course->id;
467         $rulerecord->eventname = '\mod_book\event\course_module_viewed';
468         $rulerecord->cmid = $book->cmid;
469         $rulerecord->frequency = 1;
470         $rulerecord->template = '{link} {modulelink} {rulename} {description} {eventname}';
472         $rule = $toolgenerator->create_rule($rulerecord);
474         // Creating subscription.
475         $subrecord = new stdClass();
476         $subrecord->courseid = $course->id;
477         $subrecord->ruleid = $rule->id;
478         $subrecord->userid = $USER->id;
479         $toolgenerator->create_subscription($subrecord);
481         // Now let us trigger the event.
482         $params = array(
483             'context' => context_module::instance($book->cmid),
484             'objectid' => $book->id
485         );
487         $event = \mod_book\event\course_module_viewed::create($params);
488         $event->trigger();
489         $this->run_adhock_tasks();
490         $msgs = $msgsink->get_messages();
491         $msg = array_pop($msgs);
493         $modurl = new moodle_url('/mod/book/view.php', array('id' => $book->cmid));
494         $expectedmsg = $event->get_url()->out() . ' ' .
495                         $modurl->out()  . ' ' .
496                         $rule->get_name($context) . ' ' .
497                         $rule->get_description($context) . ' ' .
498                         $rule->get_event_name();
500         $this->assertEquals($expectedmsg, $msg->fullmessage);
501     }
503     /**
504      * Test observer for user delete event.
505      */
506     public function test_user_deleted() {
507         global $DB;
509         $this->setAdminUser();
510         $this->resetAfterTest(true);
512         $user = $this->getDataGenerator()->create_user();
513         $course1 = $this->getDataGenerator()->create_course();
514         $course2 = $this->getDataGenerator()->create_course();
515         $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
517         $rule = new stdClass();
518         $rule->userid = $user->id;
519         $rule->courseid = $course1->id;
520         $rule->plugin = 'test';
522         $sub = new stdClass();
523         $sub->courseid = $course1->id;
524         $sub->userid = $user->id;
526         // Add 10 rules for this course with subscriptions.
527         for ($i = 0; $i < 10; $i++) {
528             $createdrule = $monitorgenerator->create_rule($rule);
529             $sub->ruleid = $createdrule->id;
530             $monitorgenerator->create_subscription($sub);
531         }
533         // Add 10 random rules for course 2.
534         $rule->courseid = $course2->id;
535         for ($i = 0; $i < 10; $i++) {
536             $createdrule = $monitorgenerator->create_rule($rule);
537             $sub->courseid = $rule->courseid;
538             $sub->ruleid = $createdrule->id;
539             $monitorgenerator->create_subscription($sub);
540         }
542         // Verify data before user delete.
543         $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
544         $this->assertCount(20, $totalrules);
545         $totalsubs = $DB->get_records('tool_monitor_subscriptions');
546         $this->assertCount(20, $totalsubs);
548         // Let us delete the user now.
549         delete_user($user);
551         // Verify data after course delete.
552         $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
553         $this->assertCount(20, $totalrules);
554         $totalsubs = $DB->get_records('tool_monitor_subscriptions');
555         $this->assertCount(0, $totalsubs); // Make sure all subscriptions are deleted.
556     }
558     /**
559      * Test observer for course module delete event.
560      */
561     public function test_course_module_deleted() {
562         global $DB;
564         $this->setAdminUser();
565         $this->resetAfterTest(true);
567         $user = $this->getDataGenerator()->create_user();
568         $course1 = $this->getDataGenerator()->create_course();
569         $course2 = $this->getDataGenerator()->create_course();
570         $monitorgenerator = $this->getDataGenerator()->get_plugin_generator('tool_monitor');
572         // Now let us create a rule specific to a module instance.
573         $cm = new stdClass();
574         $cm->course = $course1->id;
575         $book = $this->getDataGenerator()->create_module('book', $cm);
577         $rule = new stdClass();
578         $rule->userid = $user->id;
579         $rule->courseid = $course1->id;
580         $rule->plugin = 'test';
582         $sub = new stdClass();
583         $sub->courseid = $course1->id;
584         $sub->userid = $user->id;
585         $sub->cmid = $book->cmid;
587         // Add 10 rules for this course with subscriptions for this module.
588         for ($i = 0; $i < 10; $i++) {
589             $createdrule = $monitorgenerator->create_rule($rule);
590             $sub->ruleid = $createdrule->id;
591             $monitorgenerator->create_subscription($sub);
592         }
594         // Add 10 random rules for course 2.
595         $rule->courseid = $course2->id;
596         for ($i = 0; $i < 10; $i++) {
597             $createdrule = $monitorgenerator->create_rule($rule);
598             $sub->courseid = $rule->courseid;
599             $sub->ruleid = $createdrule->id;
600             $sub->cmid = 0;
601             $monitorgenerator->create_subscription($sub);
602         }
604         // Verify data before module delete.
605         $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
606         $this->assertCount(20, $totalrules);
607         $totalsubs = $DB->get_records('tool_monitor_subscriptions');
608         $this->assertCount(20, $totalsubs);
610         // Let us delete the user now.
611         course_delete_module($book->cmid);
613         // Verify data after course delete.
614         $totalrules = \tool_monitor\rule_manager::get_rules_by_plugin('test');
615         $this->assertCount(20, $totalrules);
616         $totalsubs = $DB->get_records('tool_monitor_subscriptions');
617         $this->assertCount(10, $totalsubs); // Make sure only relevant subscriptions are deleted.
618     }