See Release Notes
Long Term Support Release
Differences Between: [Versions 400 and 401] [Versions 401 and 403]
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/>. 16 17 namespace core_question; 18 19 use core_question\local\bank\question_version_status; 20 use question_bank; 21 22 /** 23 * Question version unit tests. 24 * 25 * @package core_question 26 * @copyright 2021 Catalyst IT Australia Pty Ltd 27 * @author Guillermo Gomez Arias <guillermogomez@catalyst-au.net> 28 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 29 * @coversDefaultClass \question_bank 30 */ 31 class version_test extends \advanced_testcase { 32 33 /** 34 * @var \context_module module context. 35 */ 36 protected $context; 37 38 /** 39 * @var \stdClass course object. 40 */ 41 protected $course; 42 43 /** 44 * @var \component_generator_base question generator. 45 */ 46 protected $qgenerator; 47 48 /** 49 * @var \stdClass quiz object. 50 */ 51 protected $quiz; 52 53 /** 54 * Called before every test. 55 */ 56 protected function setUp(): void { 57 parent::setUp(); 58 self::setAdminUser(); 59 $this->resetAfterTest(); 60 61 $datagenerator = $this->getDataGenerator(); 62 $this->course = $datagenerator->create_course(); 63 $this->quiz = $datagenerator->create_module('quiz', ['course' => $this->course->id]); 64 $this->qgenerator = $datagenerator->get_plugin_generator('core_question'); 65 $this->context = \context_module::instance($this->quiz->cmid); 66 } 67 68 /** 69 * Test if creating a question a new version and bank entry records are created. 70 * 71 * @covers ::load_question 72 */ 73 public function test_make_question_create_version_and_bank_entry() { 74 global $DB; 75 76 $qcategory = $this->qgenerator->create_question_category(['contextid' => $this->context->id]); 77 $question = $this->qgenerator->create_question('shortanswer', null, ['category' => $qcategory->id]); 78 79 // Get the question object after creating a question. 80 $questiondefinition = question_bank::load_question($question->id); 81 82 // The version and bank entry in the object should be the same. 83 $sql = "SELECT qv.id AS versionid, qv.questionbankentryid 84 FROM {question} q 85 JOIN {question_versions} qv ON qv.questionid = q.id 86 JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid 87 WHERE q.id = ?"; 88 $questionversion = $DB->get_record_sql($sql, [$questiondefinition->id]); 89 $this->assertEquals($questionversion->versionid, $questiondefinition->versionid); 90 $this->assertEquals($questionversion->questionbankentryid, $questiondefinition->questionbankentryid); 91 92 // If a question is updated, a new version should be created. 93 $question = $this->qgenerator->update_question($question, null, ['name' => 'This is a new version']); 94 $newquestiondefinition = question_bank::load_question($question->id); 95 // The version should be 2. 96 $this->assertEquals('2', $newquestiondefinition->version); 97 98 // Both versions should be in same bank entry. 99 $this->assertEquals($questiondefinition->questionbankentryid, $newquestiondefinition->questionbankentryid); 100 } 101 102 /** 103 * Test if deleting a question the related version and bank entry records are deleted. 104 * 105 * @covers ::load_question 106 * @covers ::question_delete_question 107 */ 108 public function test_delete_question_delete_versions() { 109 global $DB; 110 111 $qcategory = $this->qgenerator->create_question_category(['contextid' => $this->context->id]); 112 $question = $this->qgenerator->create_question('shortanswer', null, ['category' => $qcategory->id]); 113 $questionfirstversionid = $question->id; 114 115 // Create a new version and try to remove it. 116 $question = $this->qgenerator->update_question($question, null, ['name' => 'This is a new version']); 117 118 // The new version and bank entry record should exist. 119 $sql = "SELECT q.id, qv.id AS versionid, qv.questionbankentryid 120 FROM {question} q 121 JOIN {question_versions} qv ON qv.questionid = q.id 122 JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid 123 WHERE q.id = ?"; 124 $questionobject = $DB->get_records_sql($sql, [$question->id]); 125 $this->assertCount(1, $questionobject); 126 127 // Try to delete new version. 128 question_delete_question($question->id); 129 130 // The version record should not exist. 131 $sql = "SELECT qv.* 132 FROM {question_versions} qv 133 WHERE qv.id = ?"; 134 $questionversion = $DB->get_record_sql($sql, [$questionobject[$question->id]->versionid]); 135 $this->assertFalse($questionversion); 136 137 // The bank entry record should exist because there is an older version. 138 $sql = "SELECT qbe.* 139 FROM {question_bank_entries} qbe 140 WHERE qbe.id = ?"; 141 $questionbankentry = $DB->get_records_sql($sql, [$questionobject[$question->id]->questionbankentryid]); 142 $this->assertCount(1, $questionbankentry); 143 144 // Now remove the first version. 145 question_delete_question($questionfirstversionid); 146 $sql = "SELECT qbe.* 147 FROM {question_bank_entries} qbe 148 WHERE qbe.id = ?"; 149 $questionbankentry = $DB->get_record_sql($sql, [$questionobject[$question->id]->questionbankentryid]); 150 // The bank entry record should not exist. 151 $this->assertFalse($questionbankentry); 152 } 153 154 /** 155 * Test if deleting a question will not break a quiz. 156 * 157 * @covers ::load_question 158 * @covers ::quiz_add_quiz_question 159 * @covers ::question_delete_question 160 */ 161 public function test_delete_question_in_use() { 162 global $DB; 163 164 $qcategory = $this->qgenerator->create_question_category(['contextid' => $this->context->id]); 165 $question = $this->qgenerator->create_question('shortanswer', null, ['category' => $qcategory->id]); 166 $questionfirstversionid = $question->id; 167 168 // Create a new version and try to remove it after adding it to a quiz. 169 $question = $this->qgenerator->update_question($question, null, ['name' => 'This is a new version']); 170 171 // Add it to the quiz. 172 quiz_add_quiz_question($question->id, $this->quiz); 173 174 // Try to delete new version. 175 question_delete_question($question->id); 176 // Try to delete old version. 177 question_delete_question($questionfirstversionid); 178 179 // The used question version should exist even after trying to remove it, but now hidden. 180 $questionversion2 = question_bank::load_question($question->id); 181 $this->assertEquals($question->id, $questionversion2->id); 182 $this->assertEquals(question_version_status::QUESTION_STATUS_HIDDEN, $questionversion2->status); 183 184 // The unused version should be completely gone. 185 $this->assertFalse($DB->record_exists('question', ['id' => $questionfirstversionid])); 186 } 187 188 /** 189 * Test if moving a category will not break a quiz. 190 * 191 * @covers ::load_question 192 * @covers ::quiz_add_quiz_question 193 */ 194 public function test_move_category_with_questions() { 195 global $DB; 196 197 $qcategory = $this->qgenerator->create_question_category(['contextid' => $this->context->id]); 198 $qcategorychild = $this->qgenerator->create_question_category(['contextid' => $this->context->id, 199 'parent' => $qcategory->id]); 200 $systemcontext = \context_system::instance(); 201 $qcategorysys = $this->qgenerator->create_question_category(['contextid' => $systemcontext->id]); 202 $question = $this->qgenerator->create_question('shortanswer', null, ['category' => $qcategorychild->id]); 203 $questiondefinition = question_bank::load_question($question->id); 204 205 // Add it to the quiz. 206 quiz_add_quiz_question($question->id, $this->quiz); 207 208 // Move the category to system context. 209 $contexts = new \core_question\local\bank\question_edit_contexts($systemcontext); 210 $qcobject = new \qbank_managecategories\question_category_object(null, 211 new \moodle_url('/question/bank/managecategories/category.php', ['courseid' => SITEID]), 212 $contexts->having_one_edit_tab_cap('categories'), 0, null, 0, 213 $contexts->having_cap('moodle/question:add')); 214 $qcobject->move_questions_and_delete_category($qcategorychild->id, $qcategorysys->id); 215 216 // The bank entry record should point to the new category in order to not break quizzes. 217 $sql = "SELECT qbe.questioncategoryid 218 FROM {question_bank_entries} qbe 219 WHERE qbe.id = ?"; 220 $questionbankentry = $DB->get_record_sql($sql, [$questiondefinition->questionbankentryid]); 221 $this->assertEquals($qcategorysys->id, $questionbankentry->questioncategoryid); 222 } 223 224 /** 225 * Test that all versions will have the same bank entry idnumber value. 226 * 227 * @covers ::load_question 228 */ 229 public function test_id_number_in_bank_entry() { 230 global $DB; 231 232 $qcategory = $this->qgenerator->create_question_category(['contextid' => $this->context->id]); 233 $question = $this->qgenerator->create_question('shortanswer', null, 234 [ 235 'category' => $qcategory->id, 236 'idnumber' => 'id1' 237 ]); 238 $questionid1 = $question->id; 239 240 // Create a new version and try to remove it after adding it to a quiz. 241 $question = $this->qgenerator->update_question($question, null, ['idnumber' => 'id2']); 242 $questionid2 = $question->id; 243 // Change the id number and get the question object. 244 $question = $this->qgenerator->update_question($question, null, ['idnumber' => 'id3']); 245 $questionid3 = $question->id; 246 247 // The new version and bank entry record should exist. 248 $questiondefinition = question_bank::load_question($question->id); 249 $sql = "SELECT q.id AS questionid, qv.id AS versionid, qbe.id AS questionbankentryid, qbe.idnumber 250 FROM {question_bank_entries} qbe 251 JOIN {question_versions} qv ON qv.questionbankentryid = qbe.id 252 JOIN {question} q ON q.id = qv.questionid 253 WHERE qbe.id = ?"; 254 $questionbankentry = $DB->get_records_sql($sql, [$questiondefinition->questionbankentryid]); 255 256 // We should have 3 versions and 1 question bank entry with the same idnumber. 257 $this->assertCount(3, $questionbankentry); 258 $this->assertEquals($questionbankentry[$questionid1]->idnumber, 'id3'); 259 $this->assertEquals($questionbankentry[$questionid2]->idnumber, 'id3'); 260 $this->assertEquals($questionbankentry[$questionid3]->idnumber, 'id3'); 261 } 262 263 /** 264 * Test that all the versions are available from the method. 265 * 266 * @covers ::get_all_versions_of_question 267 */ 268 public function test_get_all_versions_of_question() { 269 $qcategory = $this->qgenerator->create_question_category(['contextid' => $this->context->id]); 270 $question = $this->qgenerator->create_question('shortanswer', null, 271 [ 272 'category' => $qcategory->id, 273 'idnumber' => 'id1' 274 ]); 275 $questionid1 = $question->id; 276 277 // Create a new version. 278 $question = $this->qgenerator->update_question($question, null, ['idnumber' => 'id2']); 279 $questionid2 = $question->id; 280 // Change the id number and get the question object. 281 $question = $this->qgenerator->update_question($question, null, ['idnumber' => 'id3']); 282 $questionid3 = $question->id; 283 284 $questiondefinition = question_bank::get_all_versions_of_question($question->id); 285 286 // Test the versions are available. 287 $this->assertEquals(array_slice($questiondefinition, 0, 1)[0]->questionid, $questionid3); 288 $this->assertEquals(array_slice($questiondefinition, 1, 1)[0]->questionid, $questionid2); 289 $this->assertEquals(array_slice($questiondefinition, 2, 1)[0]->questionid, $questionid1); 290 } 291 292 /** 293 * Test that all the versions of questions are available from the method. 294 * 295 * @covers ::get_all_versions_of_questions 296 */ 297 public function test_get_all_versions_of_questions() { 298 global $DB; 299 300 $questionversions = []; 301 $qcategory = $this->qgenerator->create_question_category(['contextid' => $this->context->id]); 302 $question = $this->qgenerator->create_question('shortanswer', null, 303 [ 304 'category' => $qcategory->id, 305 'idnumber' => 'id1' 306 ]); 307 $questionversions[1] = $question->id; 308 309 // Create a new version. 310 $question = $this->qgenerator->update_question($question, null, ['idnumber' => 'id2']); 311 $questionversions[2] = $question->id; 312 // Change the id number and get the question object. 313 $question = $this->qgenerator->update_question($question, null, ['idnumber' => 'id3']); 314 $questionversions[3] = $question->id; 315 316 $questionbankentryid = $DB->get_record('question_versions', ['questionid' => $question->id], 'questionbankentryid'); 317 318 $questionversionsofquestions = question_bank::get_all_versions_of_questions([$question->id]); 319 $questionbankentryids = array_keys($questionversionsofquestions)[0]; 320 $this->assertEquals($questionbankentryid->questionbankentryid, $questionbankentryids); 321 $this->assertEquals($questionversions, $questionversionsofquestions[$questionbankentryids]); 322 } 323 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body