Differences Between: [Versions 310 and 311] [Versions 39 and 311]
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_backup; 18 19 use backup; 20 use backup_controller; 21 use backup_nested_element; 22 use backup_optigroup; 23 use backup_plan; 24 use backup_plugin_element; 25 use backup_step; 26 use backup_step_exception; 27 use backup_subplugin_element; 28 use base_step; 29 use base_step_exception; 30 use restore_path_element; 31 use restore_plugin; 32 use restore_step_exception; 33 use restore_subplugin; 34 35 defined('MOODLE_INTERNAL') || die(); 36 37 require_once (__DIR__.'/fixtures/plan_fixtures.php'); 38 39 /** 40 * @package core_backup 41 * @category test 42 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} 43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 44 */ 45 class step_test extends \advanced_testcase { 46 47 protected $moduleid; // course_modules id used for testing 48 protected $sectionid; // course_sections id used for testing 49 protected $courseid; // course id used for testing 50 protected $userid; // user record used for testing 51 52 protected function setUp(): void { 53 global $DB, $CFG; 54 parent::setUp(); 55 56 $this->resetAfterTest(true); 57 58 $course = $this->getDataGenerator()->create_course(); 59 $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id), array('section'=>3)); 60 $coursemodule = $DB->get_record('course_modules', array('id'=>$page->cmid)); 61 62 $this->moduleid = $coursemodule->id; 63 $this->sectionid = $DB->get_field("course_sections", 'id', array("section"=>$coursemodule->section, "course"=>$course->id)); 64 $this->courseid = $coursemodule->course; 65 $this->userid = 2; // admin 66 67 // Disable all loggers 68 $CFG->backup_error_log_logger_level = backup::LOG_NONE; 69 $CFG->backup_file_logger_level = backup::LOG_NONE; 70 $CFG->backup_database_logger_level = backup::LOG_NONE; 71 $CFG->backup_file_logger_level_extra = backup::LOG_NONE; 72 } 73 74 /** 75 * test base_step class 76 */ 77 function test_base_step() { 78 79 $bp = new \mock_base_plan('planname'); // We need one plan 80 $bt = new \mock_base_task('taskname', $bp); // We need one task 81 // Instantiate 82 $bs = new \mock_base_step('stepname', $bt); 83 $this->assertTrue($bs instanceof base_step); 84 $this->assertEquals($bs->get_name(), 'stepname'); 85 } 86 87 /** 88 * test backup_step class 89 */ 90 function test_backup_step() { 91 92 // We need one (non interactive) controller for instatiating plan 93 $bc = new backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE, 94 backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid); 95 // We need one plan 96 $bp = new backup_plan($bc); 97 // We need one task 98 $bt = new \mock_backup_task('taskname', $bp); 99 // Instantiate step 100 $bs = new \mock_backup_step('stepname', $bt); 101 $this->assertTrue($bs instanceof backup_step); 102 $this->assertEquals($bs->get_name(), 'stepname'); 103 104 $bc->destroy(); 105 } 106 107 /** 108 * test restore_step class, decrypt method 109 */ 110 public function test_restore_step_decrypt() { 111 112 $this->resetAfterTest(true); 113 114 if (!function_exists('openssl_encrypt')) { 115 $this->markTestSkipped('OpenSSL extension is not loaded.'); 116 117 } else if (!function_exists('hash_hmac')) { 118 $this->markTestSkipped('Hash extension is not loaded.'); 119 120 } else if (!in_array(backup::CIPHER, openssl_get_cipher_methods())) { 121 $this->markTestSkipped('Expected cipher not available: ' . backup::CIPHER); 122 } 123 124 $bt = new \mock_restore_task_basepath('taskname'); 125 $bs = new \mock_restore_structure_step('steptest', null, $bt); 126 $this->assertTrue(method_exists($bs, 'decrypt')); 127 128 // Let's prepare a string for being decrypted. 129 $secret = 'This is a secret message that nobody else will be able to read but me 💩 '; 130 $key = hash('md5', 'Moodle rocks and this is not secure key, who cares, it is a test'); 131 $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(backup::CIPHER)); 132 $message = $iv . openssl_encrypt($secret, backup::CIPHER, $key, OPENSSL_RAW_DATA, $iv); 133 $hmac = hash_hmac('sha256', $message, $key, true); 134 $crypt = base64_encode($hmac . $message); 135 136 // Running it without a key configured, returns null. 137 $this->assertNull($bs->decrypt($crypt)); 138 139 // Store the key into config. 140 set_config('backup_encryptkey', base64_encode($key), 'backup'); 141 142 // Verify decrypt works and returns original. 143 $this->assertSame($secret, $bs->decrypt($crypt)); 144 145 // Finally, test the integrity failure detection is working. 146 // (this can be caused by changed hmac, key or message, in 147 // this case we are just forcing it via changed hmac). 148 $hmac = md5($message); 149 $crypt = base64_encode($hmac . $message); 150 $this->assertNull($bs->decrypt($crypt)); 151 } 152 153 /** 154 * test backup_structure_step class 155 */ 156 function test_backup_structure_step() { 157 global $CFG; 158 159 $file = $CFG->tempdir . '/test/test_backup_structure_step.txt'; 160 // Remove the test dir and any content 161 @remove_dir(dirname($file)); 162 // Recreate test dir 163 if (!check_dir_exists(dirname($file), true, true)) { 164 throw new \moodle_exception('error_creating_temp_dir', 'error', dirname($file)); 165 } 166 167 // We need one (non interactive) controller for instatiating plan 168 $bc = new backup_controller(backup::TYPE_1ACTIVITY, $this->moduleid, backup::FORMAT_MOODLE, 169 backup::INTERACTIVE_NO, backup::MODE_GENERAL, $this->userid); 170 // We need one plan 171 $bp = new backup_plan($bc); 172 // We need one task with mocked basepath 173 $bt = new \mock_backup_task_basepath('taskname'); 174 $bp->add_task($bt); 175 // Instantiate backup_structure_step (and add it to task) 176 $bs = new \mock_backup_structure_step('steptest', basename($file), $bt); 177 // Execute backup_structure_step 178 $bs->execute(); 179 180 // Test file has been created 181 $this->assertTrue(file_exists($file)); 182 183 // Some simple tests with contents 184 $contents = file_get_contents($file); 185 $this->assertTrue(strpos($contents, '<?xml version="1.0"') !== false); 186 $this->assertTrue(strpos($contents, '<test id="1">') !== false); 187 $this->assertTrue(strpos($contents, '<field1>value1</field1>') !== false); 188 $this->assertTrue(strpos($contents, '<field2>value2</field2>') !== false); 189 $this->assertTrue(strpos($contents, '</test>') !== false); 190 191 $bc->destroy(); 192 193 unlink($file); // delete file 194 195 // Remove the test dir and any content 196 @remove_dir(dirname($file)); 197 } 198 199 200 /** 201 * Verify the add_plugin_structure() backup method behavior and created structures. 202 */ 203 public function test_backup_structure_step_add_plugin_structure() { 204 // Create mocked task, step and element. 205 $bt = new \mock_backup_task_basepath('taskname'); 206 $bs = new \mock_backup_structure_step('steptest', null, $bt); 207 $el = new backup_nested_element('question', array('id'), array('one', 'two', 'qtype')); 208 // Wrong plugintype. 209 try { 210 $bs->add_plugin_structure('fakeplugin', $el, true); 211 $this->assertTrue(false, 'base_step_exception expected'); 212 } catch (\Exception $e) { 213 $this->assertTrue($e instanceof backup_step_exception); 214 $this->assertEquals('incorrect_plugin_type', $e->errorcode); 215 } 216 // Correct plugintype qtype call (@ 'question' level). 217 $bs->add_plugin_structure('qtype', $el, false); 218 $ch = $el->get_children(); 219 $this->assertEquals(1, count($ch)); 220 $og = reset($ch); 221 $this->assertTrue($og instanceof backup_optigroup); 222 $ch = $og->get_children(); 223 $this->assertTrue(array_key_exists('optigroup_qtype_calculatedsimple_question', $ch)); 224 $this->assertTrue($ch['optigroup_qtype_calculatedsimple_question'] instanceof backup_plugin_element); 225 } 226 227 /** 228 * Verify the add_subplugin_structure() backup method behavior and created structures. 229 */ 230 public function test_backup_structure_step_add_subplugin_structure() { 231 // Create mocked task, step and element. 232 $bt = new \mock_backup_task_basepath('taskname'); 233 $bs = new \mock_backup_structure_step('steptest', null, $bt); 234 $el = new backup_nested_element('workshop', array('id'), array('one', 'two', 'qtype')); 235 // Wrong plugin type. 236 try { 237 $bs->add_subplugin_structure('fakesubplugin', $el, true, 'fakeplugintype', 'fakepluginname'); 238 $this->assertTrue(false, 'base_step_exception expected'); 239 } catch (\Exception $e) { 240 $this->assertTrue($e instanceof backup_step_exception); 241 $this->assertEquals('incorrect_plugin_type', $e->errorcode); 242 } 243 // Wrong plugin type. 244 try { 245 $bs->add_subplugin_structure('fakesubplugin', $el, true, 'mod', 'fakepluginname'); 246 $this->assertTrue(false, 'base_step_exception expected'); 247 } catch (\Exception $e) { 248 $this->assertTrue($e instanceof backup_step_exception); 249 $this->assertEquals('incorrect_plugin_name', $e->errorcode); 250 } 251 // Wrong plugin not having subplugins. 252 try { 253 $bs->add_subplugin_structure('fakesubplugin', $el, true, 'mod', 'page'); 254 $this->assertTrue(false, 'base_step_exception expected'); 255 } catch (\Exception $e) { 256 $this->assertTrue($e instanceof backup_step_exception); 257 $this->assertEquals('plugin_missing_subplugins_configuration', $e->errorcode); 258 } 259 // Wrong BC (defaulting to mod and modulename) use not having subplugins. 260 try { 261 $bt->set_modulename('page'); 262 $bs->add_subplugin_structure('fakesubplugin', $el, true); 263 $this->assertTrue(false, 'base_step_exception expected'); 264 } catch (\Exception $e) { 265 $this->assertTrue($e instanceof backup_step_exception); 266 $this->assertEquals('plugin_missing_subplugins_configuration', $e->errorcode); 267 } 268 // Wrong subplugin type. 269 try { 270 $bs->add_subplugin_structure('fakesubplugin', $el, true, 'mod', 'workshop'); 271 $this->assertTrue(false, 'base_step_exception expected'); 272 } catch (\Exception $e) { 273 $this->assertTrue($e instanceof backup_step_exception); 274 $this->assertEquals('incorrect_subplugin_type', $e->errorcode); 275 } 276 // Wrong BC subplugin type. 277 try { 278 $bt->set_modulename('workshop'); 279 $bs->add_subplugin_structure('fakesubplugin', $el, true); 280 $this->assertTrue(false, 'base_step_exception expected'); 281 } catch (\Exception $e) { 282 $this->assertTrue($e instanceof backup_step_exception); 283 $this->assertEquals('incorrect_subplugin_type', $e->errorcode); 284 } 285 // Correct call to workshopform subplugin (@ 'workshop' level). 286 $bs->add_subplugin_structure('workshopform', $el, true, 'mod', 'workshop'); 287 $ch = $el->get_children(); 288 $this->assertEquals(1, count($ch)); 289 $og = reset($ch); 290 $this->assertTrue($og instanceof backup_optigroup); 291 $ch = $og->get_children(); 292 $this->assertTrue(array_key_exists('optigroup_workshopform_accumulative_workshop', $ch)); 293 $this->assertTrue($ch['optigroup_workshopform_accumulative_workshop'] instanceof backup_subplugin_element); 294 295 // Correct BC call to workshopform subplugin (@ 'assessment' level). 296 $el = new backup_nested_element('assessment', array('id'), array('one', 'two', 'qtype')); 297 $bt->set_modulename('workshop'); 298 $bs->add_subplugin_structure('workshopform', $el, true); 299 $ch = $el->get_children(); 300 $this->assertEquals(1, count($ch)); 301 $og = reset($ch); 302 $this->assertTrue($og instanceof backup_optigroup); 303 $ch = $og->get_children(); 304 $this->assertTrue(array_key_exists('optigroup_workshopform_accumulative_assessment', $ch)); 305 $this->assertTrue($ch['optigroup_workshopform_accumulative_assessment'] instanceof backup_subplugin_element); 306 307 // TODO: Add some test covering a non-mod subplugin once we have some implemented in core. 308 } 309 310 /** 311 * Verify the add_plugin_structure() restore method behavior and created structures. 312 */ 313 public function test_restore_structure_step_add_plugin_structure() { 314 // Create mocked task, step and element. 315 $bt = new \mock_restore_task_basepath('taskname'); 316 $bs = new \mock_restore_structure_step('steptest', null, $bt); 317 $el = new restore_path_element('question', '/some/path/to/question'); 318 // Wrong plugintype. 319 try { 320 $bs->add_plugin_structure('fakeplugin', $el); 321 $this->assertTrue(false, 'base_step_exception expected'); 322 } catch (\Exception $e) { 323 $this->assertTrue($e instanceof restore_step_exception); 324 $this->assertEquals('incorrect_plugin_type', $e->errorcode); 325 } 326 // Correct plugintype qtype call (@ 'question' level). 327 $bs->add_plugin_structure('qtype', $el); 328 $patheles = $bs->get_pathelements(); 329 // Verify some well-known qtype plugin restore_path_elements have been added. 330 $keys = array( 331 '/some/path/to/question/plugin_qtype_calculated_question/answers/answer', 332 '/some/path/to/question/plugin_qtype_calculated_question/dataset_definitions/dataset_definition', 333 '/some/path/to/question/plugin_qtype_calculated_question/calculated_options/calculated_option', 334 '/some/path/to/question/plugin_qtype_essay_question/essay', 335 '/some/path/to/question/plugin_qtype_random_question', 336 '/some/path/to/question/plugin_qtype_truefalse_question/answers/answer'); 337 foreach ($keys as $key) { 338 // Verify the element exists. 339 $this->assertArrayHasKey($key, $patheles); 340 // Verify the element is a restore_path_element. 341 $this->assertTrue($patheles[$key] instanceof restore_path_element); 342 // Check it has a processing object. 343 $po = $patheles[$key]->get_processing_object(); 344 $this->assertTrue($po instanceof restore_plugin); 345 } 346 } 347 348 /** 349 * Verify the add_subplugin_structure() restore method behavior and created structures. 350 */ 351 public function test_restore_structure_step_add_subplugin_structure() { 352 // Create mocked task, step and element. 353 $bt = new \mock_restore_task_basepath('taskname'); 354 $bs = new \mock_restore_structure_step('steptest', null, $bt); 355 $el = new restore_path_element('workshop', '/path/to/workshop'); 356 // Wrong plugin type. 357 try { 358 $bs->add_subplugin_structure('fakesubplugin', $el, 'fakeplugintype', 'fakepluginname'); 359 $this->assertTrue(false, 'base_step_exception expected'); 360 } catch (\Exception $e) { 361 $this->assertTrue($e instanceof restore_step_exception); 362 $this->assertEquals('incorrect_plugin_type', $e->errorcode); 363 } 364 // Wrong plugin type. 365 try { 366 $bs->add_subplugin_structure('fakesubplugin', $el, 'mod', 'fakepluginname'); 367 $this->assertTrue(false, 'base_step_exception expected'); 368 } catch (\Exception $e) { 369 $this->assertTrue($e instanceof restore_step_exception); 370 $this->assertEquals('incorrect_plugin_name', $e->errorcode); 371 } 372 // Wrong plugin not having subplugins. 373 try { 374 $bs->add_subplugin_structure('fakesubplugin', $el, 'mod', 'page'); 375 $this->assertTrue(false, 'base_step_exception expected'); 376 } catch (\Exception $e) { 377 $this->assertTrue($e instanceof restore_step_exception); 378 $this->assertEquals('plugin_missing_subplugins_configuration', $e->errorcode); 379 } 380 // Wrong BC (defaulting to mod and modulename) use not having subplugins. 381 try { 382 $bt->set_modulename('page'); 383 $bs->add_subplugin_structure('fakesubplugin', $el); 384 $this->assertTrue(false, 'base_step_exception expected'); 385 } catch (\Exception $e) { 386 $this->assertTrue($e instanceof restore_step_exception); 387 $this->assertEquals('plugin_missing_subplugins_configuration', $e->errorcode); 388 } 389 // Wrong subplugin type. 390 try { 391 $bs->add_subplugin_structure('fakesubplugin', $el, 'mod', 'workshop'); 392 $this->assertTrue(false, 'base_step_exception expected'); 393 } catch (\Exception $e) { 394 $this->assertTrue($e instanceof restore_step_exception); 395 $this->assertEquals('incorrect_subplugin_type', $e->errorcode); 396 } 397 // Wrong BC subplugin type. 398 try { 399 $bt->set_modulename('workshop'); 400 $bs->add_subplugin_structure('fakesubplugin', $el); 401 $this->assertTrue(false, 'base_step_exception expected'); 402 } catch (\Exception $e) { 403 $this->assertTrue($e instanceof restore_step_exception); 404 $this->assertEquals('incorrect_subplugin_type', $e->errorcode); 405 } 406 // Correct call to workshopform subplugin (@ 'workshop' level). 407 $bt = new \mock_restore_task_basepath('taskname'); 408 $bs = new \mock_restore_structure_step('steptest', null, $bt); 409 $el = new restore_path_element('workshop', '/path/to/workshop'); 410 $bs->add_subplugin_structure('workshopform', $el, 'mod', 'workshop'); 411 $patheles = $bs->get_pathelements(); 412 // Verify some well-known workshopform subplugin restore_path_elements have been added. 413 $keys = array( 414 '/path/to/workshop/subplugin_workshopform_accumulative_workshop/workshopform_accumulative_dimension', 415 '/path/to/workshop/subplugin_workshopform_comments_workshop/workshopform_comments_dimension', 416 '/path/to/workshop/subplugin_workshopform_numerrors_workshop/workshopform_numerrors_map', 417 '/path/to/workshop/subplugin_workshopform_rubric_workshop/workshopform_rubric_config'); 418 foreach ($keys as $key) { 419 // Verify the element exists. 420 $this->assertArrayHasKey($key, $patheles); 421 // Verify the element is a restore_path_element. 422 $this->assertTrue($patheles[$key] instanceof restore_path_element); 423 // Check it has a processing object. 424 $po = $patheles[$key]->get_processing_object(); 425 $this->assertTrue($po instanceof restore_subplugin); 426 } 427 428 // Correct BC call to workshopform subplugin (@ 'assessment' level). 429 $bt = new \mock_restore_task_basepath('taskname'); 430 $bs = new \mock_restore_structure_step('steptest', null, $bt); 431 $el = new restore_path_element('assessment', '/a/assessment'); 432 $bt->set_modulename('workshop'); 433 $bs->add_subplugin_structure('workshopform', $el); 434 $patheles = $bs->get_pathelements(); 435 // Verify some well-known workshopform subplugin restore_path_elements have been added. 436 $keys = array( 437 '/a/assessment/subplugin_workshopform_accumulative_assessment/workshopform_accumulative_grade', 438 '/a/assessment/subplugin_workshopform_comments_assessment/workshopform_comments_grade', 439 '/a/assessment/subplugin_workshopform_numerrors_assessment/workshopform_numerrors_grade', 440 '/a/assessment/subplugin_workshopform_rubric_assessment/workshopform_rubric_grade'); 441 foreach ($keys as $key) { 442 // Verify the element exists. 443 $this->assertArrayHasKey($key, $patheles); 444 // Verify the element is a restore_path_element. 445 $this->assertTrue($patheles[$key] instanceof restore_path_element); 446 // Check it has a processing object. 447 $po = $patheles[$key]->get_processing_object(); 448 $this->assertTrue($po instanceof restore_subplugin); 449 } 450 451 // TODO: Add some test covering a non-mod subplugin once we have some implemented in core. 452 } 453 454 /** 455 * wrong base_step class tests 456 */ 457 function test_base_step_wrong() { 458 459 // Try to pass one wrong task 460 try { 461 $bt = new \mock_base_step('teststep', new \stdClass()); 462 $this->assertTrue(false, 'base_step_exception expected'); 463 } catch (\Exception $e) { 464 $this->assertTrue($e instanceof base_step_exception); 465 $this->assertEquals($e->errorcode, 'wrong_base_task_specified'); 466 } 467 } 468 469 /** 470 * wrong backup_step class tests 471 */ 472 function test_backup_test_wrong() { 473 474 // Try to pass one wrong task 475 try { 476 $bt = new \mock_backup_step('teststep', new \stdClass()); 477 $this->assertTrue(false, 'backup_step_exception expected'); 478 } catch (\Exception $e) { 479 $this->assertTrue($e instanceof backup_step_exception); 480 $this->assertEquals($e->errorcode, 'wrong_backup_task_specified'); 481 } 482 } 483 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body