From fe3441d95f79e4be01ac5c0c24c8a265e3637367 Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Tue, 19 Oct 2021 21:01:52 +0100 Subject: [PATCH] MDL-72743 mod_quiz: verify user mapping prior to restoring attempts. --- .../backup/moodle2/restore_quiz_stepslib.php | 17 ++- .../question_attempts_missing_users.mbz | Bin 0 -> 12535 bytes mod/quiz/tests/restore_attempt_test.php | 101 ++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 mod/quiz/tests/fixtures/question_attempts_missing_users.mbz create mode 100644 mod/quiz/tests/restore_attempt_test.php diff --git a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php index 6bdb81376ff..3b5fc42ad03 100644 --- a/mod/quiz/backup/moodle2/restore_quiz_stepslib.php +++ b/mod/quiz/backup/moodle2/restore_quiz_stepslib.php @@ -40,6 +40,9 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st */ protected $sectioncreated = false; + /** @var stdClass|null $currentquizattempt Track the current quiz attempt being restored. */ + protected $currentquizattempt = null; + /** * @var bool when restoring old quizzes (2.8 or before) this records the * shufflequestionsoption quiz option which has moved to the quiz_sections table. @@ -453,7 +456,16 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st $data->quiz = $this->get_new_parentid('quiz'); $data->attempt = $data->attemptnum; - $data->userid = $this->get_mappingid('user', $data->userid); + // Get user mapping, return early if no mapping found for the quiz attempt. + $olduserid = $data->userid; + $data->userid = $this->get_mappingid('user', $olduserid, 0); + if ($data->userid === 0) { + $this->log('Mapped user ID not found for user ' . $olduserid . ', quiz ' . $this->get_new_parentid('quiz') . + ', attempt ' . $data->attempt . '. Skipping quiz attempt', backup::LOG_INFO); + + $this->currentquizattempt = null; + return; + } if (!empty($data->timecheckstate)) { $data->timecheckstate = $this->apply_date_offset($data->timecheckstate); @@ -488,6 +500,9 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st global $DB; $data = $this->currentquizattempt; + if ($data === null) { + return; + } $oldid = $data->id; $data->uniqueid = $newusageid; diff --git a/mod/quiz/tests/fixtures/question_attempts_missing_users.mbz b/mod/quiz/tests/fixtures/question_attempts_missing_users.mbz new file mode 100644 index 0000000000000000000000000000000000000000..2ddc2a66f593888eacec4c727508fd4ce86b2053 GIT binary patch literal 12535 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 h@!76+. + +/** + * Unit tests restoring quiz attempts + * + * @package mod_quiz + * @copyright 2021 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_quiz_restore_attempt_test extends advanced_testcase { + + /** + * Load required libraries + */ + public static function setUpBeforeClass(): void { + global $CFG; + + require_once("{$CFG->dirroot}/backup/util/includes/restore_includes.php"); + } + + /** + * Test restore dates. + */ + public function test_restore_question_attempts_missing_users(): void { + global $DB, $USER; + + $this->resetAfterTest(); + $this->setAdminUser(); + + // Contains a quiz with four attempts from different users. The users are as follows (user ID -> user): + // 3 -> User 01, 4 -> User 02, 5 -> User 03, 6 -> User 04. + // The user details for User 02 and User 03 have been removed from the backup file. + $testfixture = __DIR__ . '/fixtures/question_attempts_missing_users.mbz'; + + // Extract our test fixture, ready to be restored. + $backuptempdir = 'aaa'; + $backuppath = make_backup_temp_directory($backuptempdir); + get_file_packer('application/vnd.moodle.backup')->extract_to_pathname($testfixture, $backuppath); + + // Do the restore to new course with default settings. + $categoryid = $DB->get_field('course_categories', 'MIN(id)', []); + $courseid = restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid); + + $controller = new restore_controller($backuptempdir, $courseid, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id, + backup::TARGET_NEW_COURSE); + + $this->assertTrue($controller->execute_precheck()); + $controller->execute_plan(); + $controller->destroy(); + + // Grade restore also generates some debugging. + $this->assertDebuggingCalledCount(2); + + $restoredquiz = $DB->get_record('quiz', []); + + // Assert logs were added for User 02 and User 03, due to missing mapping lookup. + $loginfomessages = $DB->get_fieldset_select('backup_logs', 'message', 'backupid = ? AND loglevel = ?', [ + $controller->get_restoreid(), + backup::LOG_INFO, + ]); + + $this->assertContains("Mapped user ID not found for user 4, quiz {$restoredquiz->id}, attempt 1. Skipping quiz attempt", + $loginfomessages); + $this->assertContains("Mapped user ID not found for user 5, quiz {$restoredquiz->id}, attempt 1. Skipping quiz attempt", + $loginfomessages); + + // User 01 has supplied the wrong answer, assert dates match the backup file too. + $user01attempt = $DB->get_record('quiz_attempts', [ + 'quiz' => $restoredquiz->id, + 'userid' => core_user::get_user_by_username('user01')->id, + ]); + + $this->assertEquals(1634751274, $user01attempt->timestart); + $this->assertEquals(1634751290, $user01attempt->timefinish); + $this->assertEquals(0.0, (float) $user01attempt->sumgrades); + + // User 04 has supplied the correct answer, assert dates match the backup file too. + $user04attempt = $DB->get_record('quiz_attempts', [ + 'quiz' => $restoredquiz->id, + 'userid' => core_user::get_user_by_username('user04')->id, + ]); + + $this->assertEquals(1634751341, $user04attempt->timestart); + $this->assertEquals(1634751347, $user04attempt->timefinish); + $this->assertEquals(1.0, (float) $user04attempt->sumgrades); + } +}