. /** * Lesson module external functions tests * * @package mod_lesson * @category external * @copyright 2017 Juan Leyva * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 3.3 */ defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/webservice/tests/helpers.php'); require_once($CFG->dirroot . '/mod/lesson/locallib.php'); /** * Silly class to access mod_lesson_external internal methods. * * @package mod_lesson * @copyright 2017 Juan Leyva * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 3.3 */ class testable_mod_lesson_external extends mod_lesson_external { /** * Validates a new attempt. * * @param lesson $lesson lesson instance * @param array $params request parameters * @param boolean $return whether to return the errors or throw exceptions * @return [array the errors (if return set to true) * @since Moodle 3.3 */ public static function validate_attempt(lesson $lesson, $params, $return = false) { return parent::validate_attempt($lesson, $params, $return); } } /** * Lesson module external functions tests * * @package mod_lesson * @category external * @copyright 2017 Juan Leyva * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 3.3 */ class mod_lesson_external_testcase extends externallib_advanced_testcase { /** * Set up for every test */ public function setUp() { global $DB; $this->resetAfterTest(); $this->setAdminUser(); // Setup test data. $this->course = $this->getDataGenerator()->create_course(); $this->lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $this->course->id)); $lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); $this->page1 = $lessongenerator->create_content($this->lesson); $this->page2 = $lessongenerator->create_question_truefalse($this->lesson); $this->context = context_module::instance($this->lesson->cmid); $this->cm = get_coursemodule_from_instance('lesson', $this->lesson->id); // Create users. $this->student = self::getDataGenerator()->create_user(); $this->teacher = self::getDataGenerator()->create_user(); // Users enrolments. $this->studentrole = $DB->get_record('role', array('shortname' => 'student')); $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual'); $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual'); } /** * Test test_mod_lesson_get_lessons_by_courses */ public function test_mod_lesson_get_lessons_by_courses() { global $DB; // Create additional course. $course2 = self::getDataGenerator()->create_course(); // Second lesson. $record = new stdClass(); $record->course = $course2->id; $lesson2 = self::getDataGenerator()->create_module('lesson', $record); // Execute real Moodle enrolment as we'll call unenrol() method on the instance later. $enrol = enrol_get_plugin('manual'); $enrolinstances = enrol_get_instances($course2->id, true); foreach ($enrolinstances as $courseenrolinstance) { if ($courseenrolinstance->enrol == "manual") { $instance2 = $courseenrolinstance; break; } } $enrol->enrol_user($instance2, $this->student->id, $this->studentrole->id); self::setUser($this->student); $returndescription = mod_lesson_external::get_lessons_by_courses_returns(); // Create what we expect to be returned when querying the two courses. // First for the student user. $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles', 'practice', 'modattempts', 'usepassword', 'grade', 'custom', 'ongoing', 'usemaxgrade', 'maxanswers', 'maxattempts', 'review', 'nextpagedefault', 'feedback', 'minquestions', 'maxpages', 'timelimit', 'retake', 'mediafile', 'mediafiles', 'mediaheight', 'mediawidth', 'mediaclose', 'slideshow', 'width', 'height', 'bgcolor', 'displayleft', 'displayleftif', 'progressbar', 'allowofflineattempts'); // Add expected coursemodule and data. $lesson1 = $this->lesson; $lesson1->coursemodule = $lesson1->cmid; $lesson1->introformat = 1; $lesson1->introfiles = []; $lesson1->mediafiles = []; $lesson2->coursemodule = $lesson2->cmid; $lesson2->introformat = 1; $lesson2->introfiles = []; $lesson2->mediafiles = []; $booltypes = array('practice', 'modattempts', 'usepassword', 'custom', 'ongoing', 'review', 'feedback', 'retake', 'slideshow', 'displayleft', 'progressbar', 'allowofflineattempts'); foreach ($expectedfields as $field) { if (in_array($field, $booltypes)) { $lesson1->{$field} = (bool) $lesson1->{$field}; $lesson2->{$field} = (bool) $lesson2->{$field}; } $expected1[$field] = $lesson1->{$field}; $expected2[$field] = $lesson2->{$field}; } $expectedlessons = array($expected2, $expected1); // Call the external function passing course ids. $result = mod_lesson_external::get_lessons_by_courses(array($course2->id, $this->course->id)); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedlessons, $result['lessons']); $this->assertCount(0, $result['warnings']); // Call the external function without passing course id. $result = mod_lesson_external::get_lessons_by_courses(); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedlessons, $result['lessons']); $this->assertCount(0, $result['warnings']); // Unenrol user from second course and alter expected lessons. $enrol->unenrol_user($instance2, $this->student->id); array_shift($expectedlessons); // Call the external function without passing course id. $result = mod_lesson_external::get_lessons_by_courses(); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedlessons, $result['lessons']); // Call for the second course we unenrolled the user from, expected warning. $result = mod_lesson_external::get_lessons_by_courses(array($course2->id)); $this->assertCount(1, $result['warnings']); $this->assertEquals('1', $result['warnings'][0]['warningcode']); $this->assertEquals($course2->id, $result['warnings'][0]['itemid']); // Now, try as a teacher for getting all the additional fields. self::setUser($this->teacher); $additionalfields = array('password', 'dependency', 'conditions', 'activitylink', 'available', 'deadline', 'timemodified', 'completionendreached', 'completiontimespent'); foreach ($additionalfields as $field) { $expectedlessons[0][$field] = $lesson1->{$field}; } $result = mod_lesson_external::get_lessons_by_courses(); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedlessons, $result['lessons']); // Admin also should get all the information. self::setAdminUser(); $result = mod_lesson_external::get_lessons_by_courses(array($this->course->id)); $result = external_api::clean_returnvalue($returndescription, $result); $this->assertEquals($expectedlessons, $result['lessons']); // Now, add a restriction. $this->setUser($this->student); $DB->set_field('lesson', 'usepassword', 1, array('id' => $lesson1->id)); $DB->set_field('lesson', 'password', 'abc', array('id' => $lesson1->id)); $lessons = mod_lesson_external::get_lessons_by_courses(array($this->course->id)); $lessons = external_api::clean_returnvalue(mod_lesson_external::get_lessons_by_courses_returns(), $lessons); $this->assertFalse(isset($lessons['lessons'][0]['intro'])); } /** * Test the validate_attempt function. */ public function test_validate_attempt() { global $DB; $this->setUser($this->student); // Test deadline. $oldtime = time() - DAYSECS; $DB->set_field('lesson', 'deadline', $oldtime, array('id' => $this->lesson->id)); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('lessonclosed', key($validation)); $this->assertCount(1, $validation); // Test not available yet. $futuretime = time() + DAYSECS; $DB->set_field('lesson', 'deadline', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'available', $futuretime, array('id' => $this->lesson->id)); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('lessonopen', key($validation)); $this->assertCount(1, $validation); // Test password. $DB->set_field('lesson', 'deadline', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'available', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'usepassword', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'password', 'abc', array('id' => $this->lesson->id)); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('passwordprotectedlesson', key($validation)); $this->assertCount(1, $validation); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => 'abc'], true); $this->assertCount(0, $validation); // Dependencies. $record = new stdClass(); $record->course = $this->course->id; $lesson2 = self::getDataGenerator()->create_module('lesson', $record); $DB->set_field('lesson', 'usepassword', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'password', '', array('id' => $this->lesson->id)); $DB->set_field('lesson', 'dependency', $lesson->id, array('id' => $this->lesson->id)); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $lesson->conditions = serialize((object) ['completed' => true, 'timespent' => 0, 'gradebetterthan' => 0]); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('completethefollowingconditions', key($validation)); $this->assertCount(1, $validation); // Lesson withou pages. $lesson = new lesson($lesson2); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('lessonnotready2', key($validation)); $this->assertCount(1, $validation); // Test retakes. $DB->set_field('lesson', 'dependency', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'retake', 0, array('id' => $this->lesson->id)); $record = [ 'lessonid' => $this->lesson->id, 'userid' => $this->student->id, 'grade' => 100, 'late' => 0, 'completed' => 1, ]; $DB->insert_record('lesson_grades', (object) $record); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('noretake', key($validation)); $this->assertCount(1, $validation); // Test time limit restriction. $timenow = time(); // Create a timer for the current user. $timer1 = new stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 0; $timer1->starttime = $timenow - DAYSECS; $timer1->lessontime = $timenow; $timer1->id = $DB->insert_record("lesson_timer", $timer1); // Out of time. $DB->set_field('lesson', 'timelimit', HOURSECS, array('id' => $this->lesson->id)); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => '', 'pageid' => 1], true); $this->assertEquals('eolstudentoutoftime', key($validation)); $this->assertCount(1, $validation); } /** * Test the get_lesson_access_information function. */ public function test_get_lesson_access_information() { global $DB; $this->setUser($this->student); // Add previous attempt. $record = [ 'lessonid' => $this->lesson->id, 'userid' => $this->student->id, 'grade' => 100, 'late' => 0, 'completed' => 1, ]; $DB->insert_record('lesson_grades', (object) $record); $result = mod_lesson_external::get_lesson_access_information($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_access_information_returns(), $result); $this->assertFalse($result['canmanage']); $this->assertFalse($result['cangrade']); $this->assertFalse($result['canviewreports']); $this->assertFalse($result['leftduringtimedsession']); $this->assertEquals(1, $result['reviewmode']); $this->assertEquals(1, $result['attemptscount']); $this->assertEquals(0, $result['lastpageseen']); $this->assertEquals($this->page2->id, $result['firstpageid']); $this->assertCount(1, $result['preventaccessreasons']); $this->assertEquals('noretake', $result['preventaccessreasons'][0]['reason']); $this->assertEquals(null, $result['preventaccessreasons'][0]['data']); $this->assertEquals(get_string('noretake', 'lesson'), $result['preventaccessreasons'][0]['message']); // Now check permissions as admin. $this->setAdminUser(); $result = mod_lesson_external::get_lesson_access_information($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_access_information_returns(), $result); $this->assertTrue($result['canmanage']); $this->assertTrue($result['cangrade']); $this->assertTrue($result['canviewreports']); } /** * Test test_view_lesson invalid id. */ public function test_view_lesson_invalid_id() { $this->expectException('moodle_exception'); mod_lesson_external::view_lesson(0); } /** * Test test_view_lesson user not enrolled. */ public function test_view_lesson_user_not_enrolled() { // Test not-enrolled user. $usernotenrolled = self::getDataGenerator()->create_user(); $this->setUser($usernotenrolled); $this->expectException('moodle_exception'); mod_lesson_external::view_lesson($this->lesson->id); } /** * Test test_view_lesson user student. */ public function test_view_lesson_user_student() { // Test user with full capabilities. $this->setUser($this->student); // Trigger and capture the event. $sink = $this->redirectEvents(); $result = mod_lesson_external::view_lesson($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::view_lesson_returns(), $result); $this->assertTrue($result['status']); $events = $sink->get_events(); $this->assertCount(1, $events); $event = array_shift($events); // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_lesson\event\course_module_viewed', $event); $this->assertEquals($this->context, $event->get_context()); $moodlelesson = new \moodle_url('/mod/lesson/view.php', array('id' => $this->cm->id)); $this->assertEquals($moodlelesson, $event->get_url()); $this->assertEventContextNotUsed($event); $this->assertNotEmpty($event->get_name()); } /** * Test test_view_lesson user missing capabilities. */ public function test_view_lesson_user_missing_capabilities() { // Test user with no capabilities. // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles. assign_capability('mod/lesson:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id); // Empty all the caches that may be affected by this change. accesslib_clear_all_caches_for_unit_testing(); course_modinfo::clear_instance_cache(); $this->setUser($this->student); $this->expectException('moodle_exception'); mod_lesson_external::view_lesson($this->lesson->id); } /** * Test for get_questions_attempts */ public function test_get_questions_attempts() { global $DB; $this->setUser($this->student); $attemptnumber = 1; // Test lesson without page attempts. $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber); $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['attempts']); // Create a fake attempt for the first possible answer. $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id'); $answerid = reset($p2answers)->id; $newpageattempt = [ 'lessonid' => $this->lesson->id, 'pageid' => $this->page2->id, 'userid' => $this->student->id, 'answerid' => $answerid, 'retry' => $attemptnumber, 'correct' => 1, 'useranswer' => '1', 'timeseen' => time(), ]; $DB->insert_record('lesson_attempts', (object) $newpageattempt); $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber); $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(1, $result['attempts']); $newpageattempt['id'] = $result['attempts'][0]['id']; $this->assertEquals($newpageattempt, $result['attempts'][0]); // Test filtering. Only correct. $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, true); $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(1, $result['attempts']); // Test filtering. Only correct only for page 2. $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, true, $this->page2->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(1, $result['attempts']); // Teacher retrieve student page attempts. $this->setUser($this->teacher); $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, false, null, $this->student->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(1, $result['attempts']); // Test exception. $this->setUser($this->student); $this->expectException('moodle_exception'); $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, false, null, $this->teacher->id); } /** * Test get user grade. */ public function test_get_user_grade() { global $DB; // Add grades for the user. $newgrade = [ 'lessonid' => $this->lesson->id, 'userid' => $this->student->id, 'grade' => 50, 'late' => 0, 'completed' => time(), ]; $DB->insert_record('lesson_grades', (object) $newgrade); $newgrade = [ 'lessonid' => $this->lesson->id, 'userid' => $this->student->id, 'grade' => 100, 'late' => 0, 'completed' => time(), ]; $DB->insert_record('lesson_grades', (object) $newgrade); $this->setUser($this->student); // Test lesson without multiple attemps. The first result must be returned. $result = mod_lesson_external::get_user_grade($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertEquals(50, $result['grade']); $this->assertEquals('50.00', $result['formattedgrade']); // With retakes. By default average. $DB->set_field('lesson', 'retake', 1, array('id' => $this->lesson->id)); $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->student->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertEquals(75, $result['grade']); $this->assertEquals('75.00', $result['formattedgrade']); // With retakes. With max grade setting. $DB->set_field('lesson', 'usemaxgrade', 1, array('id' => $this->lesson->id)); $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->student->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertEquals(100, $result['grade']); $this->assertEquals('100.00', $result['formattedgrade']); // Test as teacher we get the same result. $this->setUser($this->teacher); $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->student->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertEquals(100, $result['grade']); $this->assertEquals('100.00', $result['formattedgrade']); // Test exception. As student try to retrieve grades from teacher. $this->setUser($this->student); $this->expectException('moodle_exception'); $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->teacher->id); } /** * Test get_user_attempt_grade */ public function test_get_user_attempt_grade() { global $DB; // Create a fake attempt for the first possible answer. $attemptnumber = 1; $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id'); $answerid = reset($p2answers)->id; $newpageattempt = [ 'lessonid' => $this->lesson->id, 'pageid' => $this->page2->id, 'userid' => $this->student->id, 'answerid' => $answerid, 'retry' => $attemptnumber, 'correct' => 1, 'useranswer' => '1', 'timeseen' => time(), ]; $DB->insert_record('lesson_attempts', (object) $newpageattempt); // Test first without custom scoring. All questions receive the same value if correctly responsed. $DB->set_field('lesson', 'custom', 0, array('id' => $this->lesson->id)); $this->setUser($this->student); $result = mod_lesson_external::get_user_attempt_grade($this->lesson->id, $attemptnumber, $this->student->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_grade_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertEquals(1, $result['grade']['nquestions']); $this->assertEquals(1, $result['grade']['attempts']); $this->assertEquals(1, $result['grade']['total']); $this->assertEquals(1, $result['grade']['earned']); $this->assertEquals(100, $result['grade']['grade']); $this->assertEquals(0, $result['grade']['nmanual']); $this->assertEquals(0, $result['grade']['manualpoints']); // With custom scoring, in this case, we don't retrieve any values since we are using questions without particular score. $DB->set_field('lesson', 'custom', 1, array('id' => $this->lesson->id)); $result = mod_lesson_external::get_user_attempt_grade($this->lesson->id, $attemptnumber, $this->student->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_grade_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertEquals(1, $result['grade']['nquestions']); $this->assertEquals(1, $result['grade']['attempts']); $this->assertEquals(0, $result['grade']['total']); $this->assertEquals(0, $result['grade']['earned']); $this->assertEquals(0, $result['grade']['grade']); $this->assertEquals(0, $result['grade']['nmanual']); $this->assertEquals(0, $result['grade']['manualpoints']); } /** * Test get_content_pages_viewed */ public function test_get_content_pages_viewed() { global $DB; // Create another content pages. $lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); $page3 = $lessongenerator->create_content($this->lesson); $branch1 = new stdClass; $branch1->lessonid = $this->lesson->id; $branch1->userid = $this->student->id; $branch1->pageid = $this->page1->id; $branch1->retry = 1; $branch1->flag = 0; $branch1->timeseen = time(); $branch1->nextpageid = $page3->id; $branch1->id = $DB->insert_record("lesson_branch", $branch1); $branch2 = new stdClass; $branch2->lessonid = $this->lesson->id; $branch2->userid = $this->student->id; $branch2->pageid = $page3->id; $branch2->retry = 1; $branch2->flag = 0; $branch2->timeseen = time() + 1; $branch2->nextpageid = 0; $branch2->id = $DB->insert_record("lesson_branch", $branch2); // Test first attempt. $result = mod_lesson_external::get_content_pages_viewed($this->lesson->id, 1, $this->student->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_content_pages_viewed_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(2, $result['pages']); foreach ($result['pages'] as $page) { if ($page['id'] == $branch1->id) { $this->assertEquals($branch1, (object) $page); } else { $this->assertEquals($branch2, (object) $page); } } // Attempt without pages viewed. $result = mod_lesson_external::get_content_pages_viewed($this->lesson->id, 3, $this->student->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_content_pages_viewed_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['pages']); } /** * Test get_user_timers */ public function test_get_user_timers() { global $DB; // Create a couple of timers for the current user. $timer1 = new stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 1; $timer1->starttime = time() - WEEKSECS; $timer1->lessontime = time(); $timer1->timemodifiedoffline = time(); $timer1->id = $DB->insert_record("lesson_timer", $timer1); $timer2 = new stdClass; $timer2->lessonid = $this->lesson->id; $timer2->userid = $this->student->id; $timer2->completed = 0; $timer2->starttime = time() - DAYSECS; $timer2->lessontime = time() + 1; $timer2->timemodifiedoffline = time() + 1; $timer2->id = $DB->insert_record("lesson_timer", $timer2); // Test retrieve timers. $result = mod_lesson_external::get_user_timers($this->lesson->id, $this->student->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_user_timers_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(2, $result['timers']); foreach ($result['timers'] as $timer) { if ($timer['id'] == $timer1->id) { $this->assertEquals($timer1, (object) $timer); } else { $this->assertEquals($timer2, (object) $timer); } } } /** * Test for get_pages */ public function test_get_pages() { global $DB; $this->setAdminUser(); // Create another content page. $lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); $page3 = $lessongenerator->create_content($this->lesson); $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id'); // Add files everywhere. $fs = get_file_storage(); $filerecord = array( 'contextid' => $this->context->id, 'component' => 'mod_lesson', 'filearea' => 'page_contents', 'itemid' => $this->page1->id, 'filepath' => '/', 'filename' => 'file.txt', 'sortorder' => 1 ); $fs->create_file_from_string($filerecord, 'Test resource file'); $filerecord['itemid'] = $page3->id; $fs->create_file_from_string($filerecord, 'Test resource file'); foreach ($p2answers as $answer) { $filerecord['filearea'] = 'page_answers'; $filerecord['itemid'] = $answer->id; $fs->create_file_from_string($filerecord, 'Test resource file'); $filerecord['filearea'] = 'page_responses'; $fs->create_file_from_string($filerecord, 'Test resource file'); } $result = mod_lesson_external::get_pages($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_pages_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(3, $result['pages']); // Check pages and values. foreach ($result['pages'] as $page) { if ($page['page']['id'] == $this->page2->id) { $this->assertEquals(2 * count($page['answerids']), $page['filescount']); $this->assertEquals('Lesson TF question 2', $page['page']['title']); } else { // Content page, no answers. $this->assertCount(0, $page['answerids']); $this->assertEquals(1, $page['filescount']); } } // Now, as student without pages menu. $this->setUser($this->student); $DB->set_field('lesson', 'displayleft', 0, array('id' => $this->lesson->id)); $result = mod_lesson_external::get_pages($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_pages_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(3, $result['pages']); foreach ($result['pages'] as $page) { $this->assertArrayNotHasKey('title', $page['page']); } } /** * Test launch_attempt. Time restrictions already tested in test_validate_attempt. */ public function test_launch_attempt() { global $DB, $SESSION; // Test time limit restriction. $timenow = time(); // Create a timer for the current user. $timer1 = new stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 0; $timer1->starttime = $timenow; $timer1->lessontime = $timenow; $timer1->id = $DB->insert_record("lesson_timer", $timer1); $DB->set_field('lesson', 'timelimit', 30, array('id' => $this->lesson->id)); unset($SESSION->lesson_messages); $result = mod_lesson_external::launch_attempt($this->lesson->id, '', 1); $result = external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(2, $result['messages']); $messages = []; foreach ($result['messages'] as $message) { $messages[] = $message['type']; } sort($messages); $this->assertEquals(['center', 'notifyproblem'], $messages); } /** * Test launch_attempt not finished forcing review mode. */ public function test_launch_attempt_not_finished_in_review_mode() { global $DB, $SESSION; // Create a timer for the current user. $timenow = time(); $timer1 = new stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 0; $timer1->starttime = $timenow; $timer1->lessontime = $timenow; $timer1->id = $DB->insert_record("lesson_timer", $timer1); unset($SESSION->lesson_messages); $this->setUser($this->teacher); $result = mod_lesson_external::launch_attempt($this->lesson->id, '', 1, true); $result = external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result); // Everything ok as teacher. $this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['messages']); // Should fails as student. $this->setUser($this->student); // Now, try to review this attempt. We should not be able because is a non-finished attempt. $this->expectException('moodle_exception'); mod_lesson_external::launch_attempt($this->lesson->id, '', 1, true); } /** * Test launch_attempt just finished forcing review mode. */ public function test_launch_attempt_just_finished_in_review_mode() { global $DB, $SESSION, $USER; // Create a timer for the current user. $timenow = time(); $timer1 = new stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 1; $timer1->starttime = $timenow; $timer1->lessontime = $timenow; $timer1->id = $DB->insert_record("lesson_timer", $timer1); // Create attempt. $newpageattempt = [ 'lessonid' => $this->lesson->id, 'pageid' => $this->page2->id, 'userid' => $this->student->id, 'answerid' => 0, 'retry' => 0, // First attempt is always 0. 'correct' => 1, 'useranswer' => '1', 'timeseen' => time(), ]; $DB->insert_record('lesson_attempts', (object) $newpageattempt); // Create grade. $record = [ 'lessonid' => $this->lesson->id, 'userid' => $this->student->id, 'grade' => 100, 'late' => 0, 'completed' => 1, ]; $DB->insert_record('lesson_grades', (object) $record); unset($SESSION->lesson_messages); $this->setUser($this->student); $result = mod_lesson_external::launch_attempt($this->lesson->id, '', $this->page2->id, true); $result = external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result); // Everything ok as student. $this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['messages']); } /** * Test launch_attempt not just finished forcing review mode. */ public function test_launch_attempt_not_just_finished_in_review_mode() { global $DB, $CFG, $SESSION; // Create a timer for the current user. $timenow = time(); $timer1 = new stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 1; $timer1->starttime = $timenow - DAYSECS; $timer1->lessontime = $timenow - $CFG->sessiontimeout - HOURSECS; $timer1->id = $DB->insert_record("lesson_timer", $timer1); unset($SESSION->lesson_messages); // Everything ok as teacher. $this->setUser($this->teacher); $result = mod_lesson_external::launch_attempt($this->lesson->id, '', 1, true); $result = external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['messages']); // Fail as student. $this->setUser($this->student); $this->expectException('moodle_exception'); mod_lesson_external::launch_attempt($this->lesson->id, '', 1, true); } /* * Test get_page_data */ public function test_get_page_data() { global $DB; // Test a content page first (page1). $result = mod_lesson_external::get_page_data($this->lesson->id, $this->page1->id, '', false, true); $result = external_api::clean_returnvalue(mod_lesson_external::get_page_data_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['answers']); // No answers, auto-generated content page. $this->assertEmpty($result['ongoingscore']); $this->assertEmpty($result['progress']); $this->assertEquals($this->page1->id, $result['newpageid']); // No answers, so is pointing to the itself. $this->assertEquals($this->page1->id, $result['page']['id']); $this->assertEquals(0, $result['page']['nextpageid']); // Is the last page. $this->assertEquals('Content', $result['page']['typestring']); $this->assertEquals($this->page2->id, $result['page']['prevpageid']); // Previous page. // Check contents. $this->assertTrue(strpos($result['pagecontent'], $this->page1->title) !== false); $this->assertTrue(strpos($result['pagecontent'], $this->page1->contents) !== false); // Check menu availability. $this->assertFalse($result['displaymenu']); // Check now a page with answers (true / false) and with menu available. $DB->set_field('lesson', 'displayleft', 1, array('id' => $this->lesson->id)); $result = mod_lesson_external::get_page_data($this->lesson->id, $this->page2->id, '', false, true); $result = external_api::clean_returnvalue(mod_lesson_external::get_page_data_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(2, $result['answers']); // One for true, one for false. // Check menu availability. $this->assertTrue($result['displaymenu']); // Check contents. $this->assertTrue(strpos($result['pagecontent'], $this->page2->contents) !== false); $this->assertEquals(0, $result['page']['prevpageid']); // Previous page. $this->assertEquals($this->page1->id, $result['page']['nextpageid']); // Next page. } /** * Test get_page_data as student */ public function test_get_page_data_student() { // Now check using a normal student account. $this->setUser($this->student); // First we need to launch the lesson so the timer is on. mod_lesson_external::launch_attempt($this->lesson->id); $result = mod_lesson_external::get_page_data($this->lesson->id, $this->page2->id, '', false, true); $result = external_api::clean_returnvalue(mod_lesson_external::get_page_data_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(2, $result['answers']); // One for true, one for false. // Check contents. $this->assertTrue(strpos($result['pagecontent'], $this->page2->contents) !== false); // Check we don't see answer information. $this->assertArrayNotHasKey('jumpto', $result['answers'][0]); $this->assertArrayNotHasKey('score', $result['answers'][0]); $this->assertArrayNotHasKey('jumpto', $result['answers'][1]); $this->assertArrayNotHasKey('score', $result['answers'][1]); } /** * Test get_page_data without launching attempt. */ public function test_get_page_data_without_launch() { // Now check using a normal student account. $this->setUser($this->student); $this->expectException('moodle_exception'); $result = mod_lesson_external::get_page_data($this->lesson->id, $this->page2->id, '', false, true); } /** * Creates an attempt for the given userwith a correct or incorrect answer and optionally finishes it. * * @param stdClass $user Create an attempt for this user * @param boolean $correct If the answer should be correct * @param boolean $finished If we should finish the attempt * @return array the result of the attempt creation or finalisation */ protected function create_attempt($user, $correct = true, $finished = false) { global $DB; $this->setUser($user); // First we need to launch the lesson so the timer is on. mod_lesson_external::launch_attempt($this->lesson->id); $DB->set_field('lesson', 'feedback', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'progressbar', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'custom', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'maxattempts', 3, array('id' => $this->lesson->id)); $answercorrect = 0; $answerincorrect = 0; $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id'); foreach ($p2answers as $answer) { if ($answer->jumpto == 0) { $answerincorrect = $answer->id; } else { $answercorrect = $answer->id; } } $data = array( array( 'name' => 'answerid', 'value' => $correct ? $answercorrect : $answerincorrect, ), array( 'name' => '_qf__lesson_display_answer_form_truefalse', 'value' => 1, ) ); $result = mod_lesson_external::process_page($this->lesson->id, $this->page2->id, $data); $result = external_api::clean_returnvalue(mod_lesson_external::process_page_returns(), $result); if ($finished) { $result = mod_lesson_external::finish_attempt($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::finish_attempt_returns(), $result); } return $result; } /** * Test process_page */ public function test_process_page() { global $DB; // Attempt first with incorrect response. $result = $this->create_attempt($this->student, false, false); $this->assertEquals($this->page2->id, $result['newpageid']); // Same page, since the answer was incorrect. $this->assertFalse($result['correctanswer']); // Incorrect answer. $this->assertEquals(50, $result['progress']); // Attempt with correct response. $result = $this->create_attempt($this->student, true, false); $this->assertEquals($this->page1->id, $result['newpageid']); // Next page, the answer was correct. $this->assertTrue($result['correctanswer']); // Correct response. $this->assertFalse($result['maxattemptsreached']); // Still one attempt. $this->assertEquals(50, $result['progress']); } /** * Test finish attempt not doing anything. */ public function test_finish_attempt_not_doing_anything() { $this->setUser($this->student); // First we need to launch the lesson so the timer is on. mod_lesson_external::launch_attempt($this->lesson->id); $result = mod_lesson_external::finish_attempt($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::finish_attempt_returns(), $result); $this->assertCount(0, $result['warnings']); $returneddata = []; foreach ($result['data'] as $data) { $returneddata[$data['name']] = $data['value']; } $this->assertEquals(1, $returneddata['gradelesson']); // Graded lesson. $this->assertEquals(1, $returneddata['welldone']); // Finished correctly (even without grades). $gradeinfo = json_decode($returneddata['gradeinfo']); $expectedgradeinfo = (object) [ 'nquestions' => 0, 'attempts' => 0, 'total' => 0, 'earned' => 0, 'grade' => 0, 'nmanual' => 0, 'manualpoints' => 0, ]; } /** * Test finish attempt with correct answer. */ public function test_finish_attempt_with_correct_answer() { // Create a finished attempt. $result = $this->create_attempt($this->student, true, true); $this->assertCount(0, $result['warnings']); $returneddata = []; foreach ($result['data'] as $data) { $returneddata[$data['name']] = $data['value']; } $this->assertEquals(1, $returneddata['gradelesson']); // Graded lesson. $this->assertEquals(1, $returneddata['numberofpagesviewed']); $this->assertEquals(1, $returneddata['numberofcorrectanswers']); $gradeinfo = json_decode($returneddata['gradeinfo']); $expectedgradeinfo = (object) [ 'nquestions' => 1, 'attempts' => 1, 'total' => 1, 'earned' => 1, 'grade' => 100, 'nmanual' => 0, 'manualpoints' => 0, ]; } /** * Test get_attempts_overview */ public function test_get_attempts_overview() { global $DB; // Create a finished attempt with incorrect answer. $this->setCurrentTimeStart(); $this->create_attempt($this->student, false, true); $this->setAdminUser(); $result = mod_lesson_external::get_attempts_overview($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result); // One attempt, 0 for grade (incorrect response) in overal statistics. $this->assertEquals(1, $result['data']['numofattempts']); $this->assertEquals(0, $result['data']['avescore']); $this->assertEquals(0, $result['data']['highscore']); $this->assertEquals(0, $result['data']['lowscore']); // Check one student, finished attempt, 0 for grade. $this->assertCount(1, $result['data']['students']); $this->assertEquals($this->student->id, $result['data']['students'][0]['id']); $this->assertEquals(0, $result['data']['students'][0]['bestgrade']); $this->assertCount(1, $result['data']['students'][0]['attempts']); $this->assertEquals(1, $result['data']['students'][0]['attempts'][0]['end']); $this->assertEquals(0, $result['data']['students'][0]['attempts'][0]['grade']); $this->assertTimeCurrent($result['data']['students'][0]['attempts'][0]['timestart']); $this->assertTimeCurrent($result['data']['students'][0]['attempts'][0]['timeend']); // Add a new attempt (same user). sleep(1); // Allow first retake. $DB->set_field('lesson', 'retake', 1, array('id' => $this->lesson->id)); // Create a finished attempt with correct answer. $this->setCurrentTimeStart(); $this->create_attempt($this->student, true, true); $this->setAdminUser(); $result = mod_lesson_external::get_attempts_overview($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result); // Two attempts with maximum grade. $this->assertEquals(2, $result['data']['numofattempts']); $this->assertEquals(50.00, format_float($result['data']['avescore'], 2)); $this->assertEquals(100, $result['data']['highscore']); $this->assertEquals(0, $result['data']['lowscore']); // Check one student, finished two attempts, 100 for final grade. $this->assertCount(1, $result['data']['students']); $this->assertEquals($this->student->id, $result['data']['students'][0]['id']); $this->assertEquals(100, $result['data']['students'][0]['bestgrade']); $this->assertCount(2, $result['data']['students'][0]['attempts']); foreach ($result['data']['students'][0]['attempts'] as $attempt) { if ($attempt['try'] == 0) { // First attempt, 0 for grade. $this->assertEquals(0, $attempt['grade']); } else { $this->assertEquals(100, $attempt['grade']); } } // Now, add other user failed attempt. $student2 = self::getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($student2->id, $this->course->id, $this->studentrole->id, 'manual'); $this->create_attempt($student2, false, true); // Now check we have two students and the statistics changed. $this->setAdminUser(); $result = mod_lesson_external::get_attempts_overview($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result); // Total of 3 attempts with maximum grade. $this->assertEquals(3, $result['data']['numofattempts']); $this->assertEquals(33.33, format_float($result['data']['avescore'], 2)); $this->assertEquals(100, $result['data']['highscore']); $this->assertEquals(0, $result['data']['lowscore']); // Check students. $this->assertCount(2, $result['data']['students']); } /** * Test get_attempts_overview when there aren't attempts. */ public function test_get_attempts_overview_no_attempts() { $this->setAdminUser(); $result = mod_lesson_external::get_attempts_overview($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertArrayNotHasKey('data', $result); } /** * Test get_user_attempt */ public function test_get_user_attempt() { global $DB; // Create a finished and unfinished attempt with incorrect answer. $this->setCurrentTimeStart(); $this->create_attempt($this->student, true, true); $DB->set_field('lesson', 'retake', 1, array('id' => $this->lesson->id)); sleep(1); $this->create_attempt($this->student, false, false); $this->setAdminUser(); // Test first attempt finished. $result = mod_lesson_external::get_user_attempt($this->lesson->id, $this->student->id, 0); $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_returns(), $result); $this->assertCount(2, $result['answerpages']); // 2 pages in the lesson. $this->assertCount(2, $result['answerpages'][0]['answerdata']['answers']); // 2 possible answers in true/false. $this->assertEquals(100, $result['userstats']['grade']); // Correct answer. $this->assertEquals(1, $result['userstats']['gradeinfo']['total']); // Total correct answers. $this->assertEquals(100, $result['userstats']['gradeinfo']['grade']); // Correct answer. // Check page object contains the lesson pages answered. $pagesanswered = array(); foreach ($result['answerpages'] as $answerp) { $pagesanswered[] = $answerp['page']['id']; } sort($pagesanswered); $this->assertEquals(array($this->page1->id, $this->page2->id), $pagesanswered); // Test second attempt unfinished. $result = mod_lesson_external::get_user_attempt($this->lesson->id, $this->student->id, 1); $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_returns(), $result); $this->assertCount(2, $result['answerpages']); // 2 pages in the lesson. $this->assertCount(2, $result['answerpages'][0]['answerdata']['answers']); // 2 possible answers in true/false. $this->assertArrayNotHasKey('gradeinfo', $result['userstats']); // No grade info since it not finished. // Check as student I can get this information for only me. $this->setUser($this->student); // Test first attempt finished. $result = mod_lesson_external::get_user_attempt($this->lesson->id, $this->student->id, 0); $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_returns(), $result); $this->assertCount(2, $result['answerpages']); // 2 pages in the lesson. $this->assertCount(2, $result['answerpages'][0]['answerdata']['answers']); // 2 possible answers in true/false. $this->assertEquals(100, $result['userstats']['grade']); // Correct answer. $this->assertEquals(1, $result['userstats']['gradeinfo']['total']); // Total correct answers. $this->assertEquals(100, $result['userstats']['gradeinfo']['grade']); // Correct answer. $this->expectException('moodle_exception'); $result = mod_lesson_external::get_user_attempt($this->lesson->id, $this->teacher->id, 0); } /** * Test get_pages_possible_jumps */ public function test_get_pages_possible_jumps() { $this->setAdminUser(); $result = mod_lesson_external::get_pages_possible_jumps($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_pages_possible_jumps_returns(), $result); $this->assertCount(0, $result['warnings']); $this->assertCount(3, $result['jumps']); // 3 jumps, 2 from the question page and 1 from the content. foreach ($result['jumps'] as $jump) { if ($jump['answerid'] != 0) { // Check only pages with answers. if ($jump['jumpto'] == 0) { $this->assertEquals($jump['pageid'], $jump['calculatedjump']); // 0 means to jump to current page. } else { // Question is configured to jump to next page if correct. $this->assertEquals($this->page1->id, $jump['calculatedjump']); } } } } /** * Test get_pages_possible_jumps when offline attemps are disabled for a normal user */ public function test_get_pages_possible_jumps_with_offlineattemps_disabled() { $this->setUser($this->student->id); $result = mod_lesson_external::get_pages_possible_jumps($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_pages_possible_jumps_returns(), $result); $this->assertCount(0, $result['jumps']); } /** * Test get_pages_possible_jumps when offline attemps are enabled for a normal user */ public function test_get_pages_possible_jumps_with_offlineattemps_enabled() { global $DB; $DB->set_field('lesson', 'allowofflineattempts', 1, array('id' => $this->lesson->id)); $this->setUser($this->student->id); $result = mod_lesson_external::get_pages_possible_jumps($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_pages_possible_jumps_returns(), $result); $this->assertCount(3, $result['jumps']); } /* * Test get_lesson user student. */ public function test_get_lesson_user_student() { // Test user with full capabilities. $this->setUser($this->student); // Lesson not using password. $result = mod_lesson_external::get_lesson($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); $this->assertCount(36, $result['lesson']); // Expect most of the fields. $this->assertFalse(isset($result['password'])); } /** * Test get_lesson user student with missing password. */ public function test_get_lesson_user_student_with_missing_password() { global $DB; // Test user with full capabilities. $this->setUser($this->student); $DB->set_field('lesson', 'usepassword', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'password', 'abc', array('id' => $this->lesson->id)); // Lesson not using password. $result = mod_lesson_external::get_lesson($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); $this->assertCount(6, $result['lesson']); // Expect just this few fields. $this->assertFalse(isset($result['intro'])); } /** * Test get_lesson user student with correct password. */ public function test_get_lesson_user_student_with_correct_password() { global $DB; // Test user with full capabilities. $this->setUser($this->student); $password = 'abc'; $DB->set_field('lesson', 'usepassword', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'password', $password, array('id' => $this->lesson->id)); // Lesson not using password. $result = mod_lesson_external::get_lesson($this->lesson->id, $password); $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); $this->assertCount(36, $result['lesson']); $this->assertFalse(isset($result['intro'])); } /** * Test get_lesson teacher. */ public function test_get_lesson_teacher() { global $DB; // Test user with full capabilities. $this->setUser($this->teacher); $password = 'abc'; $DB->set_field('lesson', 'usepassword', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'password', $password, array('id' => $this->lesson->id)); // Lesson not passing a valid password (but we are teachers, we should see all the info). $result = mod_lesson_external::get_lesson($this->lesson->id); $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); $this->assertCount(45, $result['lesson']); // Expect all the fields. $this->assertEquals($result['lesson']['password'], $password); } }