6f85ad95334062736a6f3fdb0c44bbac10e8116a
[moodle.git] / mod / lti / tests / lib_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 mod_lti lib
19  *
20  * @package    mod_lti
21  * @category   external
22  * @copyright  2015 Juan Leyva <juan@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  * @since      Moodle 3.0
25  */
27 defined('MOODLE_INTERNAL') || die();
30 /**
31  * Unit tests for mod_lti lib
32  *
33  * @package    mod_lti
34  * @category   external
35  * @copyright  2015 Juan Leyva <juan@moodle.com>
36  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  * @since      Moodle 3.0
38  */
39 class mod_lti_lib_testcase extends advanced_testcase {
41     /**
42      * Prepares things before this test case is initialised
43      * @return void
44      */
45     public static function setUpBeforeClass() {
46         global $CFG;
47         require_once($CFG->dirroot . '/mod/lti/lib.php');
48     }
50     /**
51      * Test lti_view
52      * @return void
53      */
54     public function test_lti_view() {
55         global $CFG;
57         $CFG->enablecompletion = 1;
58         $this->resetAfterTest();
60         $this->setAdminUser();
61         // Setup test data.
62         $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
63         $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id),
64                                                             array('completion' => 2, 'completionview' => 1));
65         $context = context_module::instance($lti->cmid);
66         $cm = get_coursemodule_from_instance('lti', $lti->id);
68         // Trigger and capture the event.
69         $sink = $this->redirectEvents();
71         lti_view($lti, $course, $cm, $context);
73         $events = $sink->get_events();
74         // 2 additional events thanks to completion.
75         $this->assertCount(3, $events);
76         $event = array_shift($events);
78         // Checking that the event contains the expected values.
79         $this->assertInstanceOf('\mod_lti\event\course_module_viewed', $event);
80         $this->assertEquals($context, $event->get_context());
81         $moodleurl = new \moodle_url('/mod/lti/view.php', array('id' => $cm->id));
82         $this->assertEquals($moodleurl, $event->get_url());
83         $this->assertEventContextNotUsed($event);
84         $this->assertNotEmpty($event->get_name());
86         // Check completion status.
87         $completion = new completion_info($course);
88         $completiondata = $completion->get_data($cm);
89         $this->assertEquals(1, $completiondata->completionstate);
91     }
93     /**
94      * Test deleting LTI instance.
95      */
96     public function test_lti_delete_instance() {
97         $this->resetAfterTest();
99         $this->setAdminUser();
100         $course = $this->getDataGenerator()->create_course(array());
101         $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
102         $cm = get_coursemodule_from_instance('lti', $lti->id);
104         // Must not throw notices.
105         course_delete_module($cm->id);
106     }
108     public function test_lti_core_calendar_provide_event_action() {
109         $this->resetAfterTest();
110         $this->setAdminUser();
112         // Create the activity.
113         $course = $this->getDataGenerator()->create_course();
114         $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
116         // Create a calendar event.
117         $event = $this->create_action_event($course->id, $lti->id,
118             \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
120         // Create an action factory.
121         $factory = new \core_calendar\action_factory();
123         // Decorate action event.
124         $actionevent = mod_lti_core_calendar_provide_event_action($event, $factory);
126         // Confirm the event was decorated.
127         $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
128         $this->assertEquals(get_string('view'), $actionevent->get_name());
129         $this->assertInstanceOf('moodle_url', $actionevent->get_url());
130         $this->assertEquals(1, $actionevent->get_item_count());
131         $this->assertTrue($actionevent->is_actionable());
132     }
134     public function test_lti_core_calendar_provide_event_action_as_non_user() {
135         global $CFG;
137         $this->resetAfterTest();
138         $this->setAdminUser();
140         // Create the activity.
141         $course = $this->getDataGenerator()->create_course();
142         $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
144         // Create a calendar event.
145         $event = $this->create_action_event($course->id, $lti->id,
146             \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
148         // Now, log out.
149         $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
150         $this->setUser();
152         // Create an action factory.
153         $factory = new \core_calendar\action_factory();
155         // Decorate action event.
156         $actionevent = mod_lti_core_calendar_provide_event_action($event, $factory);
158         // Confirm the event is not shown at all.
159         $this->assertNull($actionevent);
160     }
162     public function test_lti_core_calendar_provide_event_action_for_user() {
163         global $CFG;
165         $this->resetAfterTest();
166         $this->setAdminUser();
168         // Create the activity.
169         $course = $this->getDataGenerator()->create_course();
170         $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
172         // Enrol a student in the course.
173         $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
175         // Create a calendar event.
176         $event = $this->create_action_event($course->id, $lti->id,
177             \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
179         // Now, log out.
180         $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
181         $this->setUser();
183         // Create an action factory.
184         $factory = new \core_calendar\action_factory();
186         // Decorate action event for the student.
187         $actionevent = mod_lti_core_calendar_provide_event_action($event, $factory, $student->id);
189         // Confirm the event was decorated.
190         $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
191         $this->assertEquals(get_string('view'), $actionevent->get_name());
192         $this->assertInstanceOf('moodle_url', $actionevent->get_url());
193         $this->assertEquals(1, $actionevent->get_item_count());
194         $this->assertTrue($actionevent->is_actionable());
195     }
197     public function test_lti_core_calendar_provide_event_action_already_completed() {
198         global $CFG;
200         $this->resetAfterTest();
201         $this->setAdminUser();
203         $CFG->enablecompletion = 1;
205         // Create the activity.
206         $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
207         $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id),
208             array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
210         // Get some additional data.
211         $cm = get_coursemodule_from_instance('lti', $lti->id);
213         // Create a calendar event.
214         $event = $this->create_action_event($course->id, $lti->id,
215             \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
217         // Mark the activity as completed.
218         $completion = new completion_info($course);
219         $completion->set_module_viewed($cm);
221         // Create an action factory.
222         $factory = new \core_calendar\action_factory();
224         // Decorate action event.
225         $actionevent = mod_lti_core_calendar_provide_event_action($event, $factory);
227         // Ensure result was null.
228         $this->assertNull($actionevent);
229     }
231     public function test_lti_core_calendar_provide_event_action_already_completed_as_non_user() {
232         global $CFG;
234         $this->resetAfterTest();
235         $this->setAdminUser();
237         $CFG->enablecompletion = 1;
239         // Create the activity.
240         $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
241         $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id),
242             array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
244         // Get some additional data.
245         $cm = get_coursemodule_from_instance('lti', $lti->id);
247         // Create a calendar event.
248         $event = $this->create_action_event($course->id, $lti->id,
249             \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
251         // Mark the activity as completed.
252         $completion = new completion_info($course);
253         $completion->set_module_viewed($cm);
255         // Now, log out.
256         $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
257         $this->setUser();
259         // Create an action factory.
260         $factory = new \core_calendar\action_factory();
262         // Decorate action event.
263         $actionevent = mod_lti_core_calendar_provide_event_action($event, $factory);
265         // Ensure result was null.
266         $this->assertNull($actionevent);
267     }
269     public function test_lti_core_calendar_provide_event_action_already_completed_for_user() {
270         global $CFG;
272         $this->resetAfterTest();
273         $this->setAdminUser();
275         $CFG->enablecompletion = 1;
277         // Create the activity.
278         $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
279         $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id),
280             array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
282         // Enrol 2 students in the course.
283         $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
284         $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
286         // Get some additional data.
287         $cm = get_coursemodule_from_instance('lti', $lti->id);
289         // Create a calendar event.
290         $event = $this->create_action_event($course->id, $lti->id,
291             \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
293         // Mark the activity as completed for $student1.
294         $completion = new completion_info($course);
295         $completion->set_module_viewed($cm, $student1->id);
297         // Now, log in as $student2.
298         $this->setUser($student2);
300         // Create an action factory.
301         $factory = new \core_calendar\action_factory();
303         // Decorate action event for $student1.
304         $actionevent = mod_lti_core_calendar_provide_event_action($event, $factory, $student1->id);
306         // Ensure result was null.
307         $this->assertNull($actionevent);
308     }
310     /**
311      * Creates an action event.
312      *
313      * @param int $courseid The course id.
314      * @param int $instanceid The instance id.
315      * @param string $eventtype The event type.
316      * @return bool|calendar_event
317      */
318     private function create_action_event($courseid, $instanceid, $eventtype) {
319         $event = new stdClass();
320         $event->name = 'Calendar event';
321         $event->modulename  = 'lti';
322         $event->courseid = $courseid;
323         $event->instance = $instanceid;
324         $event->type = CALENDAR_EVENT_TYPE_ACTION;
325         $event->eventtype = $eventtype;
326         $event->timestart = time();
328         return calendar_event::create($event);
329     }
331     /**
332      * Test verifying the output of the lti_get_course_content_items and lti_get_all_content_items callbacks.
333      */
334     public function test_content_item_callbacks() {
335         $this->resetAfterTest();
336         global $DB, $CFG;
337         require_once($CFG->dirroot . '/mod/lti/locallib.php');
339         $admin = get_admin();
340         $time = time();
341         $course = $this->getDataGenerator()->create_course();
342         $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
343         $course2 = $this->getDataGenerator()->create_course();
344         $teacher2 = $this->getDataGenerator()->create_and_enrol($course2, 'editingteacher');
346         // Create some preconfigured tools.
347         $sitetoolrecord = (object) [
348             'name' => 'Site level tool which is available in the activity chooser',
349             'baseurl' => 'http://example.com',
350             'createdby' => $admin->id,
351             'course' => SITEID,
352             'ltiversion' => 'LTI-1p0',
353             'timecreated' => $time,
354             'timemodified' => $time,
355             'state' => LTI_TOOL_STATE_CONFIGURED,
356             'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER
357         ];
358         $sitetoolrecordnonchooser = (object) [
359             'name' => 'Site level tool which is NOT available in the course activity chooser',
360             'baseurl' => 'http://example2.com',
361             'createdby' => $admin->id,
362             'course' => SITEID,
363             'ltiversion' => 'LTI-1p0',
364             'timecreated' => $time,
365             'timemodified' => $time,
366             'state' => LTI_TOOL_STATE_CONFIGURED,
367             'coursevisible' => LTI_COURSEVISIBLE_PRECONFIGURED
368         ];
369         $course1toolrecord = (object) [
370             'name' => 'Course created tool which is available in the activity chooser',
371             'baseurl' => 'http://example3.com',
372             'createdby' => $teacher->id,
373             'course' => $course->id,
374             'ltiversion' => 'LTI-1p0',
375             'timecreated' => $time,
376             'timemodified' => $time,
377             'state' => LTI_TOOL_STATE_CONFIGURED,
378             'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER
379         ];
380         $course2toolrecord = (object) [
381             'name' => 'Course created tool which is available in the activity chooser',
382             'baseurl' => 'http://example4.com',
383             'createdby' => $teacher2->id,
384             'course' => $course2->id,
385             'ltiversion' => 'LTI-1p0',
386             'timecreated' => $time,
387             'timemodified' => $time,
388             'state' => LTI_TOOL_STATE_CONFIGURED,
389             'coursevisible' => LTI_COURSEVISIBLE_ACTIVITYCHOOSER
390         ];
391         $tool1id = $DB->insert_record('lti_types', $sitetoolrecord);
392         $tool2id = $DB->insert_record('lti_types', $sitetoolrecordnonchooser);
393         $tool3id = $DB->insert_record('lti_types', $course1toolrecord);
394         $tool4id = $DB->insert_record('lti_types', $course2toolrecord);
395         $sitetoolrecord->id = $tool1id;
396         $sitetoolrecordnonchooser->id = $tool2id;
397         $course1toolrecord->id = $tool3id;
398         $course2toolrecord->id = $tool4id;
400         $defaultmodulecontentitem = new \core_course\local\entity\content_item(
401             '1',
402             'default module content item',
403             new \core_course\local\entity\string_title('Content item title'),
404             new moodle_url(''),
405             'icon',
406             'Description of the module',
407             MOD_ARCHETYPE_OTHER,
408             'mod_lti'
409         );
411         // The lti_get_lti_types_by_course method (used by the callbacks) assumes the global user.
412         $this->setUser($teacher);
414         // Teacher in course1 should be able to see the default module item ('external tool'),
415         // the site preconfigured tool and the tool created in course1.
416         $courseitems = lti_get_course_content_items($defaultmodulecontentitem, $teacher, $course);
417         $this->assertCount(3, $courseitems);
418         $ids = [];
419         foreach ($courseitems as $item) {
420             $ids[] = $item->get_id();
421         }
422         $this->assertContains(1, $ids);
423         $this->assertContains($sitetoolrecord->id + 1, $ids);
424         $this->assertContains($course1toolrecord->id + 1, $ids);
425         $this->assertNotContains($sitetoolrecordnonchooser->id + 1, $ids);
427         // The content items for teacher2 in course2 include the default module content item ('external tool'),
428         // the site preconfigured tool and the tool created in course2.
429         $this->setUser($teacher2);
430         $course2items = lti_get_course_content_items($defaultmodulecontentitem, $teacher2, $course2);
431         $this->assertCount(3, $course2items);
432         $ids = [];
433         foreach ($course2items as $item) {
434             $ids[] = $item->get_id();
435         }
436         $this->assertContains(1, $ids);
437         $this->assertContains($sitetoolrecord->id + 1, $ids);
438         $this->assertContains($course2toolrecord->id + 1, $ids);
439         $this->assertNotContains($sitetoolrecordnonchooser->id + 1, $ids);
440     }