Merge branch 'MDL-57643-master' of git://github.com/jleyva/moodle
[moodle.git] / mod / lesson / tests / external_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  * Lesson module external functions tests
19  *
20  * @package    mod_lesson
21  * @category   external
22  * @copyright  2017 Juan Leyva <juan@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  * @since      Moodle 3.3
25  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
31 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
32 require_once($CFG->dirroot . '/mod/lesson/locallib.php');
34 /**
35  * Silly class to access mod_lesson_external internal methods.
36  *
37  * @package mod_lesson
38  * @copyright 2017 Juan Leyva <juan@moodle.com>
39  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40  * @since  Moodle 3.3
41  */
42 class testable_mod_lesson_external extends mod_lesson_external {
44     /**
45      * Validates a new attempt.
46      *
47      * @param  lesson  $lesson lesson instance
48      * @param  array   $params request parameters
49      * @param  boolean $return whether to return the errors or throw exceptions
50      * @return [array          the errors (if return set to true)
51      * @since  Moodle 3.3
52      */
53     public static function validate_attempt(lesson $lesson, $params, $return = false) {
54         return parent::validate_attempt($lesson, $params, $return);
55     }
56 }
58 /**
59  * Lesson module external functions tests
60  *
61  * @package    mod_lesson
62  * @category   external
63  * @copyright  2017 Juan Leyva <juan@moodle.com>
64  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
65  * @since      Moodle 3.3
66  */
67 class mod_lesson_external_testcase extends externallib_advanced_testcase {
69     /**
70      * Set up for every test
71      */
72     public function setUp() {
73         global $DB;
74         $this->resetAfterTest();
75         $this->setAdminUser();
77         // Setup test data.
78         $this->course = $this->getDataGenerator()->create_course();
79         $this->lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $this->course->id));
80         $lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
81         $this->page1 = $lessongenerator->create_content($this->lesson);
82         $this->page2 = $lessongenerator->create_question_truefalse($this->lesson);
83         $this->context = context_module::instance($this->lesson->cmid);
84         $this->cm = get_coursemodule_from_instance('lesson', $this->lesson->id);
86         // Create users.
87         $this->student = self::getDataGenerator()->create_user();
88         $this->teacher = self::getDataGenerator()->create_user();
90         // Users enrolments.
91         $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
92         $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
93         $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual');
94         $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
95     }
98     /**
99      * Test test_mod_lesson_get_lessons_by_courses
100      */
101     public function test_mod_lesson_get_lessons_by_courses() {
102         global $DB;
104         // Create additional course.
105         $course2 = self::getDataGenerator()->create_course();
107         // Second lesson.
108         $record = new stdClass();
109         $record->course = $course2->id;
110         $lesson2 = self::getDataGenerator()->create_module('lesson', $record);
112         // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
113         $enrol = enrol_get_plugin('manual');
114         $enrolinstances = enrol_get_instances($course2->id, true);
115         foreach ($enrolinstances as $courseenrolinstance) {
116             if ($courseenrolinstance->enrol == "manual") {
117                 $instance2 = $courseenrolinstance;
118                 break;
119             }
120         }
121         $enrol->enrol_user($instance2, $this->student->id, $this->studentrole->id);
123         self::setUser($this->student);
125         $returndescription = mod_lesson_external::get_lessons_by_courses_returns();
127         // Create what we expect to be returned when querying the two courses.
128         // First for the student user.
129         $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles', 'practice',
130                                 'modattempts', 'usepassword', 'grade', 'custom', 'ongoing', 'usemaxgrade',
131                                 'maxanswers', 'maxattempts', 'review', 'nextpagedefault', 'feedback', 'minquestions',
132                                 'maxpages', 'timelimit', 'retake', 'mediafile', 'mediafiles', 'mediaheight', 'mediawidth',
133                                 'mediaclose', 'slideshow', 'width', 'height', 'bgcolor', 'displayleft', 'displayleftif',
134                                 'progressbar');
136         // Add expected coursemodule and data.
137         $lesson1 = $this->lesson;
138         $lesson1->coursemodule = $lesson1->cmid;
139         $lesson1->introformat = 1;
140         $lesson1->section = 0;
141         $lesson1->visible = true;
142         $lesson1->groupmode = 0;
143         $lesson1->groupingid = 0;
144         $lesson1->introfiles = [];
145         $lesson1->mediafiles = [];
147         $lesson2->coursemodule = $lesson2->cmid;
148         $lesson2->introformat = 1;
149         $lesson2->section = 0;
150         $lesson2->visible = true;
151         $lesson2->groupmode = 0;
152         $lesson2->groupingid = 0;
153         $lesson2->introfiles = [];
154         $lesson2->mediafiles = [];
156         foreach ($expectedfields as $field) {
157             $expected1[$field] = $lesson1->{$field};
158             $expected2[$field] = $lesson2->{$field};
159         }
161         $expectedlessons = array($expected2, $expected1);
163         // Call the external function passing course ids.
164         $result = mod_lesson_external::get_lessons_by_courses(array($course2->id, $this->course->id));
165         $result = external_api::clean_returnvalue($returndescription, $result);
167         $this->assertEquals($expectedlessons, $result['lessons']);
168         $this->assertCount(0, $result['warnings']);
170         // Call the external function without passing course id.
171         $result = mod_lesson_external::get_lessons_by_courses();
172         $result = external_api::clean_returnvalue($returndescription, $result);
173         $this->assertEquals($expectedlessons, $result['lessons']);
174         $this->assertCount(0, $result['warnings']);
176         // Unenrol user from second course and alter expected lessons.
177         $enrol->unenrol_user($instance2, $this->student->id);
178         array_shift($expectedlessons);
180         // Call the external function without passing course id.
181         $result = mod_lesson_external::get_lessons_by_courses();
182         $result = external_api::clean_returnvalue($returndescription, $result);
183         $this->assertEquals($expectedlessons, $result['lessons']);
185         // Call for the second course we unenrolled the user from, expected warning.
186         $result = mod_lesson_external::get_lessons_by_courses(array($course2->id));
187         $this->assertCount(1, $result['warnings']);
188         $this->assertEquals('1', $result['warnings'][0]['warningcode']);
189         $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
191         // Now, try as a teacher for getting all the additional fields.
192         self::setUser($this->teacher);
194         $additionalfields = array('password', 'dependency', 'conditions', 'activitylink', 'available', 'deadline',
195                                     'timemodified', 'completionendreached', 'completiontimespent');
197         foreach ($additionalfields as $field) {
198             $expectedlessons[0][$field] = $lesson1->{$field};
199         }
201         $result = mod_lesson_external::get_lessons_by_courses();
202         $result = external_api::clean_returnvalue($returndescription, $result);
203         $this->assertEquals($expectedlessons, $result['lessons']);
205         // Admin also should get all the information.
206         self::setAdminUser();
208         $result = mod_lesson_external::get_lessons_by_courses(array($this->course->id));
209         $result = external_api::clean_returnvalue($returndescription, $result);
210         $this->assertEquals($expectedlessons, $result['lessons']);
212         // Now, add a restriction.
213         $this->setUser($this->student);
214         $DB->set_field('lesson', 'usepassword', 1, array('id' => $lesson1->id));
215         $DB->set_field('lesson', 'password', 'abc', array('id' => $lesson1->id));
217         $lessons = mod_lesson_external::get_lessons_by_courses(array($this->course->id));
218         $lessons = external_api::clean_returnvalue(mod_lesson_external::get_lessons_by_courses_returns(), $lessons);
219         $this->assertFalse(isset($lessons['lessons'][0]['intro']));
220     }
222     /**
223      * Test the validate_attempt function.
224      */
225     public function test_validate_attempt() {
226         global $DB;
228         $this->setUser($this->student);
229         // Test deadline.
230         $oldtime = time() - DAYSECS;
231         $DB->set_field('lesson', 'deadline', $oldtime, array('id' => $this->lesson->id));
233         $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
234         $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
235         $this->assertEquals('lessonclosed', key($validation));
236         $this->assertCount(1, $validation);
238         // Test not available yet.
239         $futuretime = time() + DAYSECS;
240         $DB->set_field('lesson', 'deadline', 0, array('id' => $this->lesson->id));
241         $DB->set_field('lesson', 'available', $futuretime, array('id' => $this->lesson->id));
243         $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
244         $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
245         $this->assertEquals('lessonopen', key($validation));
246         $this->assertCount(1, $validation);
248         // Test password.
249         $DB->set_field('lesson', 'deadline', 0, array('id' => $this->lesson->id));
250         $DB->set_field('lesson', 'available', 0, array('id' => $this->lesson->id));
251         $DB->set_field('lesson', 'usepassword', 1, array('id' => $this->lesson->id));
252         $DB->set_field('lesson', 'password', 'abc', array('id' => $this->lesson->id));
254         $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
255         $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
256         $this->assertEquals('passwordprotectedlesson', key($validation));
257         $this->assertCount(1, $validation);
259         $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
260         $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => 'abc'], true);
261         $this->assertCount(0, $validation);
263         // Dependencies.
264         $record = new stdClass();
265         $record->course = $this->course->id;
266         $lesson2 = self::getDataGenerator()->create_module('lesson', $record);
267         $DB->set_field('lesson', 'usepassword', 0, array('id' => $this->lesson->id));
268         $DB->set_field('lesson', 'password', '', array('id' => $this->lesson->id));
269         $DB->set_field('lesson', 'dependency', $lesson->id, array('id' => $this->lesson->id));
271         $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
272         $lesson->conditions = serialize((object) ['completed' => true, 'timespent' => 0, 'gradebetterthan' => 0]);
273         $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
274         $this->assertEquals('completethefollowingconditions', key($validation));
275         $this->assertCount(1, $validation);
277         // Lesson withou pages.
278         $lesson = new lesson($lesson2);
279         $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
280         $this->assertEquals('lessonnotready2', key($validation));
281         $this->assertCount(1, $validation);
283         // Test retakes.
284         $DB->set_field('lesson', 'dependency', 0, array('id' => $this->lesson->id));
285         $DB->set_field('lesson', 'retake', 0, array('id' => $this->lesson->id));
286         $record = [
287             'lessonid' => $this->lesson->id,
288             'userid' => $this->student->id,
289             'grade' => 100,
290             'late' => 0,
291             'completed' => 1,
292         ];
293         $DB->insert_record('lesson_grades', (object) $record);
294         $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id)));
295         $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true);
296         $this->assertEquals('noretake', key($validation));
297         $this->assertCount(1, $validation);
298     }
300     /**
301      * Test the get_lesson_access_information function.
302      */
303     public function test_get_lesson_access_information() {
304         global $DB;
306         $this->setUser($this->student);
307         // Add previous attempt.
308         $record = [
309             'lessonid' => $this->lesson->id,
310             'userid' => $this->student->id,
311             'grade' => 100,
312             'late' => 0,
313             'completed' => 1,
314         ];
315         $DB->insert_record('lesson_grades', (object) $record);
317         $result = mod_lesson_external::get_lesson_access_information($this->lesson->id);
318         $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_access_information_returns(), $result);
319         $this->assertFalse($result['canmanage']);
320         $this->assertFalse($result['cangrade']);
321         $this->assertFalse($result['canviewreports']);
323         $this->assertFalse($result['leftduringtimedsession']);
324         $this->assertEquals(1, $result['reviewmode']);
325         $this->assertEquals(1, $result['attemptscount']);
326         $this->assertEquals(0, $result['lastpageseen']);
327         $this->assertEquals($this->page2->id, $result['firstpageid']);
328         $this->assertCount(1, $result['preventaccessreasons']);
329         $this->assertEquals('noretake', $result['preventaccessreasons'][0]['reason']);
330         $this->assertEquals(null, $result['preventaccessreasons'][0]['data']);
331         $this->assertEquals(get_string('noretake', 'lesson'), $result['preventaccessreasons'][0]['message']);
333         // Now check permissions as admin.
334         $this->setAdminUser();
335         $result = mod_lesson_external::get_lesson_access_information($this->lesson->id);
336         $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_access_information_returns(), $result);
337         $this->assertTrue($result['canmanage']);
338         $this->assertTrue($result['cangrade']);
339         $this->assertTrue($result['canviewreports']);
340     }