5168a57baa4a9aefa9af3ca56a81507c0a605174
[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     }
342     /**
343      * Test test_view_lesson invalid id.
344      */
345     public function test_view_lesson_invalid_id() {
346         $this->setExpectedException('moodle_exception');
347         mod_lesson_external::view_lesson(0);
348     }
350     /**
351      * Test test_view_lesson user not enrolled.
352      */
353     public function test_view_lesson_user_not_enrolled() {
354         // Test not-enrolled user.
355         $usernotenrolled = self::getDataGenerator()->create_user();
356         $this->setUser($usernotenrolled);
357         $this->setExpectedException('moodle_exception');
358         mod_lesson_external::view_lesson($this->lesson->id);
359     }
361     /**
362      * Test test_view_lesson user student.
363      */
364     public function test_view_lesson_user_student() {
365         // Test user with full capabilities.
366         $this->setUser($this->student);
368         // Trigger and capture the event.
369         $sink = $this->redirectEvents();
371         $result = mod_lesson_external::view_lesson($this->lesson->id);
372         $result = external_api::clean_returnvalue(mod_lesson_external::view_lesson_returns(), $result);
373         $this->assertTrue($result['status']);
375         $events = $sink->get_events();
376         $this->assertCount(1, $events);
377         $event = array_shift($events);
379         // Checking that the event contains the expected values.
380         $this->assertInstanceOf('\mod_lesson\event\course_module_viewed', $event);
381         $this->assertEquals($this->context, $event->get_context());
382         $moodlelesson = new \moodle_url('/mod/lesson/view.php', array('id' => $this->cm->id));
383         $this->assertEquals($moodlelesson, $event->get_url());
384         $this->assertEventContextNotUsed($event);
385         $this->assertNotEmpty($event->get_name());
386     }
388     /**
389      * Test test_view_lesson user missing capabilities.
390      */
391     public function test_view_lesson_user_missing_capabilities() {
392         // Test user with no capabilities.
393         // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
394         assign_capability('mod/lesson:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
395         // Empty all the caches that may be affected  by this change.
396         accesslib_clear_all_caches_for_unit_testing();
397         course_modinfo::clear_instance_cache();
399         $this->setUser($this->student);
400         $this->setExpectedException('moodle_exception');
401         mod_lesson_external::view_lesson($this->lesson->id);
402     }
404     /**
405      * Test for get_questions_attempts
406      */
407     public function test_get_questions_attempts() {
408         global $DB;
410         $this->setUser($this->student);
411         $attemptnumber = 1;
413         // Test lesson without page attempts.
414         $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber);
415         $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
416         $this->assertCount(0, $result['warnings']);
417         $this->assertCount(0, $result['attempts']);
419         // Create a fake attempt for the first possible answer.
420         $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id');
421         $answerid = reset($p2answers)->id;
423         $newpageattempt = [
424             'lessonid' => $this->lesson->id,
425             'pageid' => $this->page2->id,
426             'userid' => $this->student->id,
427             'answerid' => $answerid,
428             'retry' => $attemptnumber,
429             'correct' => 1,
430             'useranswer' => '1',
431             'timeseen' => time(),
432         ];
433         $DB->insert_record('lesson_attempts', (object) $newpageattempt);
435         $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber);
436         $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
437         $this->assertCount(0, $result['warnings']);
438         $this->assertCount(1, $result['attempts']);
440         $newpageattempt['id'] = $result['attempts'][0]['id'];
441         $this->assertEquals($newpageattempt, $result['attempts'][0]);
443         // Test filtering. Only correct.
444         $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, true);
445         $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
446         $this->assertCount(0, $result['warnings']);
447         $this->assertCount(1, $result['attempts']);
449         // Test filtering. Only correct only for page 2.
450         $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, true, $this->page2->id);
451         $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
452         $this->assertCount(0, $result['warnings']);
453         $this->assertCount(1, $result['attempts']);
455         // Teacher retrieve student page attempts.
456         $this->setUser($this->teacher);
457         $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, false, null, $this->student->id);
458         $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
459         $this->assertCount(0, $result['warnings']);
460         $this->assertCount(1, $result['attempts']);
462         // Test exception.
463         $this->setUser($this->student);
464         $this->expectException('moodle_exception');
465         $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, false, null, $this->teacher->id);
466     }
468     /**
469      * Test get user grade.
470      */
471     public function test_get_user_grade() {
472         global $DB;
474         // Add grades for the user.
475         $newgrade = [
476             'lessonid' => $this->lesson->id,
477             'userid' => $this->student->id,
478             'grade' => 50,
479             'late' => 0,
480             'completed' => time(),
481         ];
482         $DB->insert_record('lesson_grades', (object) $newgrade);
484         $newgrade = [
485             'lessonid' => $this->lesson->id,
486             'userid' => $this->student->id,
487             'grade' => 100,
488             'late' => 0,
489             'completed' => time(),
490         ];
491         $DB->insert_record('lesson_grades', (object) $newgrade);
493         $this->setUser($this->student);
495         // Test lesson without multiple attemps. The first result must be returned.
496         $result = mod_lesson_external::get_user_grade($this->lesson->id);
497         $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
498         $this->assertCount(0, $result['warnings']);
499         $this->assertEquals(50, $result['grade']);
500         $this->assertEquals('50.00', $result['formattedgrade']);
502         // With retakes. By default average.
503         $DB->set_field('lesson', 'retake', 1, array('id' => $this->lesson->id));
504         $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->student->id);
505         $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
506         $this->assertCount(0, $result['warnings']);
507         $this->assertEquals(75, $result['grade']);
508         $this->assertEquals('75.00', $result['formattedgrade']);
510         // With retakes. With max grade setting.
511         $DB->set_field('lesson', 'usemaxgrade', 1, array('id' => $this->lesson->id));
512         $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->student->id);
513         $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
514         $this->assertCount(0, $result['warnings']);
515         $this->assertEquals(100, $result['grade']);
516         $this->assertEquals('100.00', $result['formattedgrade']);
518         // Test as teacher we get the same result.
519         $this->setUser($this->teacher);
520         $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->student->id);
521         $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
522         $this->assertCount(0, $result['warnings']);
523         $this->assertEquals(100, $result['grade']);
524         $this->assertEquals('100.00', $result['formattedgrade']);
526         // Test exception. As student try to retrieve grades from teacher.
527         $this->setUser($this->student);
528         $this->expectException('moodle_exception');
529         $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->teacher->id);
530     }
532     /**
533      * Test get_user_attempt_grade
534      */
535     public function test_get_user_attempt_grade() {
536         global $DB;
538         // Create a fake attempt for the first possible answer.
539         $attemptnumber = 1;
540         $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id');
541         $answerid = reset($p2answers)->id;
543         $newpageattempt = [
544             'lessonid' => $this->lesson->id,
545             'pageid' => $this->page2->id,
546             'userid' => $this->student->id,
547             'answerid' => $answerid,
548             'retry' => $attemptnumber,
549             'correct' => 1,
550             'useranswer' => '1',
551             'timeseen' => time(),
552         ];
553         $DB->insert_record('lesson_attempts', (object) $newpageattempt);
555         // Test first without custom scoring. All questions receive the same value if correctly responsed.
556         $DB->set_field('lesson', 'custom', 0, array('id' => $this->lesson->id));
557         $this->setUser($this->student);
558         $result = mod_lesson_external::get_user_attempt_grade($this->lesson->id, $attemptnumber, $this->student->id);
559         $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_grade_returns(), $result);
560         $this->assertCount(0, $result['warnings']);
561         $this->assertEquals(1, $result['nquestions']);
562         $this->assertEquals(1, $result['attempts']);
563         $this->assertEquals(1, $result['total']);
564         $this->assertEquals(1, $result['earned']);
565         $this->assertEquals(100, $result['grade']);
566         $this->assertEquals(0, $result['nmanual']);
567         $this->assertEquals(0, $result['manualpoints']);
569         // With custom scoring, in this case, we don't retrieve any values since we are using questions without particular score.
570         $DB->set_field('lesson', 'custom', 1, array('id' => $this->lesson->id));
571         $result = mod_lesson_external::get_user_attempt_grade($this->lesson->id, $attemptnumber, $this->student->id);
572         $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_grade_returns(), $result);
573         $this->assertCount(0, $result['warnings']);
574         $this->assertEquals(1, $result['nquestions']);
575         $this->assertEquals(1, $result['attempts']);
576         $this->assertEquals(0, $result['total']);
577         $this->assertEquals(0, $result['earned']);
578         $this->assertEquals(0, $result['grade']);
579         $this->assertEquals(0, $result['nmanual']);
580         $this->assertEquals(0, $result['manualpoints']);
581     }
583     /**
584      * Test get_content_pages_viewed
585      */
586     public function test_get_content_pages_viewed() {
587         global $DB;
589         // Create another content pages.
590         $lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
591         $page3 = $lessongenerator->create_content($this->lesson);
593         $branch1 = new stdClass;
594         $branch1->lessonid = $this->lesson->id;
595         $branch1->userid = $this->student->id;
596         $branch1->pageid = $this->page1->id;
597         $branch1->retry = 1;
598         $branch1->flag = 0;
599         $branch1->timeseen = time();
600         $branch1->nextpageid = $page3->id;
601         $branch1->id = $DB->insert_record("lesson_branch", $branch1);
603         $branch2 = new stdClass;
604         $branch2->lessonid = $this->lesson->id;
605         $branch2->userid = $this->student->id;
606         $branch2->pageid = $page3->id;
607         $branch2->retry = 1;
608         $branch2->flag = 0;
609         $branch2->timeseen = time() + 1;
610         $branch2->nextpageid = 0;
611         $branch2->id = $DB->insert_record("lesson_branch", $branch2);
613         // Test first attempt.
614         $result = mod_lesson_external::get_content_pages_viewed($this->lesson->id, 1, $this->student->id);
615         $result = external_api::clean_returnvalue(mod_lesson_external::get_content_pages_viewed_returns(), $result);
616         $this->assertCount(0, $result['warnings']);
617         $this->assertCount(2, $result['pages']);
618         foreach ($result['pages'] as $page) {
619             if ($page['id'] == $branch1->id) {
620                 $this->assertEquals($branch1, (object) $page);
621             } else {
622                 $this->assertEquals($branch2, (object) $page);
623             }
624         }
626         // Attempt without pages viewed.
627         $result = mod_lesson_external::get_content_pages_viewed($this->lesson->id, 3, $this->student->id);
628         $result = external_api::clean_returnvalue(mod_lesson_external::get_content_pages_viewed_returns(), $result);
629         $this->assertCount(0, $result['warnings']);
630         $this->assertCount(0, $result['pages']);
631     }