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