Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]
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; 18 19 use advanced_testcase; 20 use coding_exception; 21 use dml_missing_record_exception; 22 use lang_string; 23 use xmldb_table; 24 25 /** 26 * Persistent testcase. 27 * 28 * @package core 29 * @copyright 2015 Frédéric Massart - FMCorz.net 30 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 31 * @covers \core\persistent 32 */ 33 class persistent_test extends advanced_testcase { 34 35 public function setUp(): void { 36 $this->make_persistent_table(); 37 $this->make_second_persistent_table(); 38 $this->resetAfterTest(); 39 } 40 41 /** 42 * Make the table for the persistent. 43 */ 44 protected function make_persistent_table() { 45 global $DB; 46 $dbman = $DB->get_manager(); 47 48 $table = new xmldb_table(core_testable_persistent::TABLE); 49 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 50 $table->add_field('shortname', XMLDB_TYPE_CHAR, '100', null, null, null, null); 51 $table->add_field('idnumber', XMLDB_TYPE_CHAR, '100', null, null, null, null); 52 $table->add_field('description', XMLDB_TYPE_TEXT, null, null, null, null, null); 53 $table->add_field('descriptionformat', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, '0'); 54 $table->add_field('parentid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 55 $table->add_field('path', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); 56 $table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 57 $table->add_field('scaleid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); 58 $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 59 $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 60 $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 61 62 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 63 64 if ($dbman->table_exists($table)) { 65 $dbman->drop_table($table); 66 } 67 68 $dbman->create_table($table); 69 } 70 71 /** 72 * Make the second table for the persistent. 73 */ 74 protected function make_second_persistent_table() { 75 global $DB; 76 $dbman = $DB->get_manager(); 77 78 $table = new xmldb_table(core_testable_second_persistent::TABLE); 79 $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); 80 $table->add_field('someint', XMLDB_TYPE_INTEGER, '10', null, null, null, null); 81 $table->add_field('intnull', XMLDB_TYPE_INTEGER, '10', null, null, null, null); 82 $table->add_field('somefloat', XMLDB_TYPE_FLOAT, '10,5', null, null, null, null); 83 $table->add_field('sometext', XMLDB_TYPE_TEXT, null, null, null, null, null); 84 $table->add_field('someraw', XMLDB_TYPE_CHAR, '100', null, null, null, null); 85 $table->add_field('booltrue', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0'); 86 $table->add_field('boolfalse', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0'); 87 $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 88 $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); 89 $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); 90 91 $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); 92 93 if ($dbman->table_exists($table)) { 94 $dbman->drop_table($table); 95 } 96 97 $dbman->create_table($table); 98 } 99 100 public function test_properties_definition() { 101 $expected = array( 102 'shortname' => array( 103 'type' => PARAM_TEXT, 104 'default' => '', 105 'null' => NULL_NOT_ALLOWED 106 ), 107 'idnumber' => array( 108 'type' => PARAM_TEXT, 109 'null' => NULL_NOT_ALLOWED 110 ), 111 'description' => array( 112 'type' => PARAM_TEXT, 113 'default' => '', 114 'null' => NULL_NOT_ALLOWED 115 ), 116 'descriptionformat' => array( 117 'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN), 118 'type' => PARAM_INT, 119 'default' => FORMAT_HTML, 120 'null' => NULL_NOT_ALLOWED 121 ), 122 'parentid' => array( 123 'type' => PARAM_INT, 124 'default' => 0, 125 'null' => NULL_NOT_ALLOWED 126 ), 127 'path' => array( 128 'type' => PARAM_RAW, 129 'default' => '', 130 'null' => NULL_NOT_ALLOWED 131 ), 132 'sortorder' => array( 133 'type' => PARAM_INT, 134 'message' => new lang_string('invalidrequest', 'error'), 135 'null' => NULL_NOT_ALLOWED 136 ), 137 'scaleid' => array( 138 'default' => null, 139 'type' => PARAM_INT, 140 'null' => NULL_ALLOWED 141 ), 142 'id' => array( 143 'default' => 0, 144 'type' => PARAM_INT, 145 'null' => NULL_NOT_ALLOWED 146 ), 147 'timecreated' => array( 148 'default' => 0, 149 'type' => PARAM_INT, 150 'null' => NULL_NOT_ALLOWED 151 ), 152 'timemodified' => array( 153 'default' => 0, 154 'type' => PARAM_INT, 155 'null' => NULL_NOT_ALLOWED 156 ), 157 'usermodified' => array( 158 'default' => 0, 159 'type' => PARAM_INT, 160 'null' => NULL_NOT_ALLOWED 161 ), 162 ); 163 $this->assertEquals($expected, core_testable_persistent::properties_definition()); 164 } 165 166 /** 167 * Test filtering record properties returns only those defined by the persistent 168 */ 169 public function test_properties_filter(): void { 170 $result = core_testable_persistent::properties_filter((object) [ 171 'idnumber' => '123', 172 'sortorder' => 1, 173 'invalidparam' => 'abc', 174 ]); 175 176 // We should get back all data except invalid param. 177 $this->assertEquals([ 178 'idnumber' => '123', 179 'sortorder' => 1, 180 ], $result); 181 } 182 183 /** 184 * Test creating persistent instance by specifying record ID in constructor 185 */ 186 public function test_constructor() : void { 187 $persistent = (new core_testable_persistent(0, (object) [ 188 'idnumber' => '123', 189 'sortorder' => 1, 190 ]))->create(); 191 192 // Now create a new instance, passing the original instance ID in the constructor. 193 $another = new core_testable_persistent($persistent->get('id')); 194 $this->assertEquals($another->to_record(), $persistent->to_record()); 195 } 196 197 /** 198 * Test creating persistent instance by specifying non-existing record ID in constructor throws appropriate exception 199 */ 200 public function test_constructor_invalid(): void { 201 $this->expectException(dml_missing_record_exception::class); 202 $this->expectExceptionMessage('Can\'t find data record in database table phpunit_persistent.'); 203 new core_testable_persistent(42); 204 } 205 206 public function test_to_record() { 207 $p = new core_testable_persistent(); 208 $expected = (object) array( 209 'shortname' => '', 210 'idnumber' => null, 211 'description' => '', 212 'descriptionformat' => FORMAT_HTML, 213 'parentid' => 0, 214 'path' => '', 215 'sortorder' => null, 216 'id' => 0, 217 'timecreated' => 0, 218 'timemodified' => 0, 219 'usermodified' => 0, 220 'scaleid' => null, 221 ); 222 $this->assertEquals($expected, $p->to_record()); 223 } 224 225 public function test_from_record() { 226 $p = new core_testable_persistent(); 227 $data = (object) array( 228 'shortname' => 'ddd', 229 'idnumber' => 'abc', 230 'description' => 'xyz', 231 'descriptionformat' => FORMAT_PLAIN, 232 'parentid' => 999, 233 'path' => '/a/b/c', 234 'sortorder' => 12, 235 'id' => 1, 236 'timecreated' => 2, 237 'timemodified' => 3, 238 'usermodified' => 4, 239 'scaleid' => null, 240 ); 241 $p->from_record($data); 242 $this->assertEquals($data, $p->to_record()); 243 } 244 245 public function test_from_record_invalid_param() { 246 $p = new core_testable_persistent(); 247 $data = (object) array( 248 'shortname' => 'ddd', 249 'idnumber' => 'abc', 250 'description' => 'xyz', 251 'descriptionformat' => FORMAT_PLAIN, 252 'parentid' => 999, 253 'path' => '/a/b/c', 254 'sortorder' => 12, 255 'id' => 1, 256 'timecreated' => 2, 257 'timemodified' => 3, 258 'usermodified' => 4, 259 'scaleid' => null, 260 'invalidparam' => 'abc' 261 ); 262 263 $p->from_record($data); 264 265 // Previous call should succeed, assert we get back all data except invalid param. 266 unset($data->invalidparam); 267 $this->assertEquals($data, $p->to_record()); 268 } 269 270 public function test_validate() { 271 $data = (object) array( 272 'idnumber' => 'abc', 273 'sortorder' => 0 274 ); 275 $p = new core_testable_persistent(0, $data); 276 $this->assertFalse(isset($p->beforevalidate)); 277 $this->assertTrue($p->validate()); 278 $this->assertTrue(isset($p->beforevalidate)); 279 $this->assertTrue($p->is_valid()); 280 $this->assertEquals(array(), $p->get_errors()); 281 $p->set('descriptionformat', -100); 282 283 $expected = array( 284 'descriptionformat' => new lang_string('invaliddata', 'error'), 285 ); 286 $this->assertEquals($expected, $p->validate()); 287 $this->assertFalse($p->is_valid()); 288 $this->assertEquals($expected, $p->get_errors()); 289 } 290 291 public function test_validation_required() { 292 $data = (object) array( 293 'idnumber' => 'abc' 294 ); 295 $p = new core_testable_persistent(0, $data); 296 $expected = array( 297 'sortorder' => new lang_string('requiredelement', 'form'), 298 ); 299 $this->assertFalse($p->is_valid()); 300 $this->assertEquals($expected, $p->get_errors()); 301 } 302 303 public function test_validation_custom() { 304 $data = (object) array( 305 'idnumber' => 'abc', 306 'sortorder' => 10, 307 ); 308 $p = new core_testable_persistent(0, $data); 309 $expected = array( 310 'sortorder' => new lang_string('invalidkey', 'error'), 311 ); 312 $this->assertFalse($p->is_valid()); 313 $this->assertEquals($expected, $p->get_errors()); 314 } 315 316 public function test_validation_custom_message() { 317 $data = (object) array( 318 'idnumber' => 'abc', 319 'sortorder' => 'abc', 320 ); 321 $p = new core_testable_persistent(0, $data); 322 $expected = array( 323 'sortorder' => new lang_string('invalidrequest', 'error'), 324 ); 325 $this->assertFalse($p->is_valid()); 326 $this->assertEquals($expected, $p->get_errors()); 327 } 328 329 public function test_validation_choices() { 330 $data = (object) array( 331 'idnumber' => 'abc', 332 'sortorder' => 0, 333 'descriptionformat' => -100 334 ); 335 $p = new core_testable_persistent(0, $data); 336 $expected = array( 337 'descriptionformat' => new lang_string('invaliddata', 'error'), 338 ); 339 $this->assertFalse($p->is_valid()); 340 $this->assertEquals($expected, $p->get_errors()); 341 } 342 343 public function test_validation_type() { 344 $data = (object) array( 345 'idnumber' => 'abc', 346 'sortorder' => 'NaN' 347 ); 348 $p = new core_testable_persistent(0, $data); 349 $this->assertFalse($p->is_valid()); 350 $this->assertArrayHasKey('sortorder', $p->get_errors()); 351 } 352 353 public function test_validation_null() { 354 $data = (object) array( 355 'idnumber' => null, 356 'sortorder' => 0, 357 'scaleid' => 'bad!' 358 ); 359 $p = new core_testable_persistent(0, $data); 360 $this->assertFalse($p->is_valid()); 361 $this->assertArrayHasKey('idnumber', $p->get_errors()); 362 $this->assertArrayHasKey('scaleid', $p->get_errors()); 363 $p->set('idnumber', 'abc'); 364 $this->assertFalse($p->is_valid()); 365 $this->assertArrayNotHasKey('idnumber', $p->get_errors()); 366 $this->assertArrayHasKey('scaleid', $p->get_errors()); 367 $p->set('scaleid', null); 368 $this->assertTrue($p->is_valid()); 369 $this->assertArrayNotHasKey('scaleid', $p->get_errors()); 370 } 371 372 public function test_create() { 373 global $DB; 374 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc')); 375 $this->assertFalse(isset($p->beforecreate)); 376 $this->assertFalse(isset($p->aftercreate)); 377 $p->create(); 378 $record = $DB->get_record(core_testable_persistent::TABLE, array('id' => $p->get('id')), '*', MUST_EXIST); 379 $expected = $p->to_record(); 380 $this->assertTrue(isset($p->beforecreate)); 381 $this->assertTrue(isset($p->aftercreate)); 382 $this->assertEquals($expected->sortorder, $record->sortorder); 383 $this->assertEquals($expected->idnumber, $record->idnumber); 384 $this->assertEquals($expected->id, $record->id); 385 $this->assertTrue($p->is_valid()); // Should always be valid after a create. 386 } 387 388 public function test_update() { 389 global $DB; 390 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc')); 391 $p->create(); 392 $id = $p->get('id'); 393 $p->set('sortorder', 456); 394 $p->from_record((object) array('idnumber' => 'def')); 395 $this->assertFalse(isset($p->beforeupdate)); 396 $this->assertFalse(isset($p->afterupdate)); 397 $p->update(); 398 399 $expected = $p->to_record(); 400 $record = $DB->get_record(core_testable_persistent::TABLE, array('id' => $p->get('id')), '*', MUST_EXIST); 401 $this->assertTrue(isset($p->beforeupdate)); 402 $this->assertTrue(isset($p->afterupdate)); 403 $this->assertEquals($id, $record->id); 404 $this->assertEquals(456, $record->sortorder); 405 $this->assertEquals('def', $record->idnumber); 406 $this->assertTrue($p->is_valid()); // Should always be valid after an update. 407 } 408 409 /** 410 * Test set_many prior to updating the persistent 411 */ 412 public function test_set_many_update(): void { 413 global $DB; 414 415 $persistent = (new core_testable_persistent(0, (object) [ 416 'idnumber' => 'test', 417 'sortorder' => 2 418 ]))->create(); 419 420 // Set multiple properties, and update. 421 $persistent->set_many([ 422 'idnumber' => 'test2', 423 'sortorder' => 1, 424 ])->update(); 425 426 // Confirm our persistent was updated. 427 $record = $DB->get_record(core_testable_persistent::TABLE, ['id' => $persistent->get('id')], '*', MUST_EXIST); 428 $this->assertEquals('test2', $record->idnumber); 429 $this->assertEquals(1, $record->sortorder); 430 } 431 432 public function test_save() { 433 global $DB; 434 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc')); 435 $this->assertFalse(isset($p->beforecreate)); 436 $this->assertFalse(isset($p->aftercreate)); 437 $this->assertFalse(isset($p->beforeupdate)); 438 $this->assertFalse(isset($p->beforeupdate)); 439 $p->save(); 440 $record = $DB->get_record(core_testable_persistent::TABLE, array('id' => $p->get('id')), '*', MUST_EXIST); 441 $expected = $p->to_record(); 442 $this->assertTrue(isset($p->beforecreate)); 443 $this->assertTrue(isset($p->aftercreate)); 444 $this->assertFalse(isset($p->beforeupdate)); 445 $this->assertFalse(isset($p->beforeupdate)); 446 $this->assertEquals($expected->sortorder, $record->sortorder); 447 $this->assertEquals($expected->idnumber, $record->idnumber); 448 $this->assertEquals($expected->id, $record->id); 449 $this->assertTrue($p->is_valid()); // Should always be valid after a save/create. 450 451 $p->set('idnumber', 'abcd'); 452 $p->save(); 453 $record = $DB->get_record(core_testable_persistent::TABLE, array('id' => $p->get('id')), '*', MUST_EXIST); 454 $expected = $p->to_record(); 455 $this->assertTrue(isset($p->beforeupdate)); 456 $this->assertTrue(isset($p->beforeupdate)); 457 $this->assertEquals($expected->sortorder, $record->sortorder); 458 $this->assertEquals($expected->idnumber, $record->idnumber); 459 $this->assertEquals($expected->id, $record->id); 460 $this->assertTrue($p->is_valid()); // Should always be valid after a save/update. 461 } 462 463 /** 464 * Test set_many prior to saving the persistent 465 */ 466 public function test_set_many_save(): void { 467 global $DB; 468 469 $persistent = (new core_testable_persistent(0, (object) [ 470 'idnumber' => 'test', 471 'sortorder' => 2 472 ])); 473 474 // Set multiple properties, and save. 475 $persistent->set_many([ 476 'idnumber' => 'test2', 477 'sortorder' => 1, 478 ])->save(); 479 480 // Confirm our persistent was saved. 481 $record = $DB->get_record(core_testable_persistent::TABLE, ['id' => $persistent->get('id')], '*', MUST_EXIST); 482 $this->assertEquals('test2', $record->idnumber); 483 $this->assertEquals(1, $record->sortorder); 484 } 485 486 /** 487 * Test set_many with empty array should not modify the persistent 488 */ 489 public function test_set_many_empty(): void { 490 global $DB; 491 492 $persistent = (new core_testable_persistent(0, (object) [ 493 'idnumber' => 'test', 494 'sortorder' => 2 495 ]))->create(); 496 497 // Set empty properties, and update. 498 $persistent->set_many([])->update(); 499 500 // Confirm our persistent was not updated. 501 $record = $DB->get_record(core_testable_persistent::TABLE, ['id' => $persistent->get('id')], '*', MUST_EXIST); 502 $this->assertEquals('test', $record->idnumber); 503 $this->assertEquals(2, $record->sortorder); 504 } 505 506 /** 507 * Test set with invalid property 508 */ 509 public function test_set_invalid_property(): void { 510 $persistent = (new core_testable_persistent(0, (object) [ 511 'idnumber' => 'test', 512 'sortorder' => 2 513 ])); 514 515 $this->expectException(coding_exception::class); 516 $this->expectExceptionMessage('Unexpected property \'invalid\' requested'); 517 $persistent->set('invalid', 'stuff'); 518 } 519 520 /** 521 * Test set_many with invalid property 522 */ 523 public function test_set_many_invalid_property(): void { 524 $persistent = (new core_testable_persistent(0, (object) [ 525 'idnumber' => 'test', 526 'sortorder' => 2 527 ])); 528 529 $this->expectException(coding_exception::class); 530 $this->expectExceptionMessage('Unexpected property \'invalid\' requested'); 531 $persistent->set_many(['invalid' => 'stuff']); 532 } 533 534 public function test_read() { 535 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc')); 536 $p->create(); 537 unset($p->beforevalidate); 538 unset($p->beforecreate); 539 unset($p->aftercreate); 540 541 $p2 = new core_testable_persistent($p->get('id')); 542 $this->assertEquals($p, $p2); 543 544 $p3 = new core_testable_persistent(); 545 $p3->set('id', $p->get('id')); 546 $p3->read(); 547 $this->assertEquals($p, $p3); 548 } 549 550 public function test_delete() { 551 global $DB; 552 553 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc')); 554 $p->create(); 555 $this->assertNotEquals(0, $p->get('id')); 556 $this->assertTrue($DB->record_exists_select(core_testable_persistent::TABLE, 'id = ?', array($p->get('id')))); 557 $this->assertFalse(isset($p->beforedelete)); 558 $this->assertFalse(isset($p->afterdelete)); 559 560 $p->delete(); 561 $this->assertFalse($DB->record_exists_select(core_testable_persistent::TABLE, 'id = ?', array($p->get('id')))); 562 $this->assertEquals(0, $p->get('id')); 563 $this->assertEquals(true, $p->beforedelete); 564 $this->assertEquals(true, $p->afterdelete); 565 } 566 567 public function test_has_property() { 568 $this->assertFalse(core_testable_persistent::has_property('unknown')); 569 $this->assertTrue(core_testable_persistent::has_property('idnumber')); 570 } 571 572 public function test_custom_setter_getter() { 573 global $DB; 574 575 $path = array(1, 2, 3); 576 $json = json_encode($path); 577 578 $p = new core_testable_persistent(0, (object) array('sortorder' => 0, 'idnumber' => 'abc')); 579 $p->set('path', $path); 580 $this->assertEquals($path, $p->get('path')); 581 $this->assertEquals($json, $p->to_record()->path); 582 583 $p->create(); 584 $record = $DB->get_record(core_testable_persistent::TABLE, array('id' => $p->get('id')), 'id, path', MUST_EXIST); 585 $this->assertEquals($json, $record->path); 586 } 587 588 /** 589 * Test get_record method for creating persistent instance 590 */ 591 public function test_get_record(): void { 592 $persistent = (new core_testable_persistent(0, (object) [ 593 'idnumber' => '123', 594 'sortorder' => 1, 595 ]))->create(); 596 597 $another = core_testable_persistent::get_record(['id' => $persistent->get('id')]); 598 599 // Assert we got back a persistent instance, and it matches original. 600 $this->assertInstanceOf(core_testable_persistent::class, $another); 601 $this->assertEquals($another->to_record(), $persistent->to_record()); 602 } 603 604 /** 605 * Test get_record method for creating persistent instance, ignoring a non-existing record 606 */ 607 public function test_get_record_ignore_missing(): void { 608 $persistent = core_testable_persistent::get_record(['id' => 42]); 609 $this->assertFalse($persistent); 610 } 611 612 /** 613 * Test get_record method for creating persistent instance, throws appropriate exception for non-existing record 614 */ 615 public function test_get_record_must_exist(): void { 616 $this->expectException(dml_missing_record_exception::class); 617 $this->expectExceptionMessage('Can\'t find data record in database table phpunit_persistent.'); 618 core_testable_persistent::get_record(['id' => 42], MUST_EXIST); 619 } 620 621 public function test_record_exists() { 622 global $DB; 623 $this->assertFalse($DB->record_exists(core_testable_persistent::TABLE, array('idnumber' => 'abc'))); 624 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc')); 625 $p->create(); 626 $id = $p->get('id'); 627 $this->assertTrue(core_testable_persistent::record_exists($id)); 628 $this->assertTrue($DB->record_exists(core_testable_persistent::TABLE, array('idnumber' => 'abc'))); 629 $p->delete(); 630 $this->assertFalse(core_testable_persistent::record_exists($id)); 631 } 632 633 public function test_get_sql_fields() { 634 $expected = '' . 635 'c.id AS prefix_id, ' . 636 'c.shortname AS prefix_shortname, ' . 637 'c.idnumber AS prefix_idnumber, ' . 638 'c.description AS prefix_description, ' . 639 'c.descriptionformat AS prefix_descriptionformat, ' . 640 'c.parentid AS prefix_parentid, ' . 641 'c.path AS prefix_path, ' . 642 'c.sortorder AS prefix_sortorder, ' . 643 'c.scaleid AS prefix_scaleid, ' . 644 'c.timecreated AS prefix_timecreated, ' . 645 'c.timemodified AS prefix_timemodified, ' . 646 'c.usermodified AS prefix_usermodified'; 647 $this->assertEquals($expected, core_testable_persistent::get_sql_fields('c', 'prefix_')); 648 } 649 650 public function test_get_sql_fields_too_long() { 651 $this->expectException(coding_exception::class); 652 $this->expectExceptionMessageMatches('/The alias .+ exceeds 30 characters/'); 653 core_testable_persistent::get_sql_fields('c'); 654 } 655 656 public function test_get(): void { 657 $data = [ 658 'someint' => 123, 659 'intnull' => null, 660 'somefloat' => 33.44, 661 'sometext' => 'Hello', 662 'someraw' => '/dev/hello', 663 'booltrue' => true, 664 'boolfalse' => false, 665 ]; 666 $p = new core_testable_second_persistent(0, (object)$data); 667 $p->create(); 668 669 $this->assertSame($data['intnull'], $p->get('intnull')); 670 $this->assertSame($data['someint'], $p->get('someint')); 671 $this->assertIsFloat($p->get('somefloat')); // Avoid === comparisons on floats, verify type and value separated. 672 $this->assertEqualsWithDelta($data['somefloat'], $p->get('somefloat'), 0.00001); 673 $this->assertSame($data['sometext'], $p->get('sometext')); 674 $this->assertSame($data['someraw'], $p->get('someraw')); 675 $this->assertSame($data['booltrue'], $p->get('booltrue')); 676 $this->assertSame($data['boolfalse'], $p->get('boolfalse')); 677 678 // Ensure that types are correct after reloading data from database. 679 $p->read(); 680 681 $this->assertSame($data['someint'], $p->get('someint')); 682 $this->assertSame($data['intnull'], $p->get('intnull')); 683 $this->assertIsFloat($p->get('somefloat')); // Avoid === comparisons on floats, verify type and value separated. 684 $this->assertEqualsWithDelta($data['somefloat'], $p->get('somefloat'), 0.00001); 685 $this->assertSame($data['sometext'], $p->get('sometext')); 686 $this->assertSame($data['someraw'], $p->get('someraw')); 687 $this->assertSame($data['booltrue'], $p->get('booltrue')); 688 $this->assertSame($data['boolfalse'], $p->get('boolfalse')); 689 } 690 } 691 692 /** 693 * Example persistent class. 694 * 695 * @package core 696 * @copyright 2015 Frédéric Massart - FMCorz.net 697 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 698 */ 699 class core_testable_persistent extends persistent { 700 701 const TABLE = 'phpunit_persistent'; 702 703 /** @var bool before validate status. */ 704 public ?bool $beforevalidate; 705 706 /** @var bool before create status. */ 707 public ?bool $beforecreate; 708 709 /** @var bool before update status. */ 710 public ?bool $beforeupdate; 711 712 /** @var bool before delete status. */ 713 public ?bool $beforedelete; 714 715 /** @var bool after create status. */ 716 public ?bool $aftercreate; 717 718 /** @var bool after update status. */ 719 public ?bool $afterupdate; 720 721 /** @var bool after delete status. */ 722 public ?bool $afterdelete; 723 724 protected static function define_properties() { 725 return array( 726 'shortname' => array( 727 'type' => PARAM_TEXT, 728 'default' => '' 729 ), 730 'idnumber' => array( 731 'type' => PARAM_TEXT, 732 ), 733 'description' => array( 734 'type' => PARAM_TEXT, 735 'default' => '' 736 ), 737 'descriptionformat' => array( 738 'choices' => array(FORMAT_HTML, FORMAT_MOODLE, FORMAT_PLAIN, FORMAT_MARKDOWN), 739 'type' => PARAM_INT, 740 'default' => FORMAT_HTML 741 ), 742 'parentid' => array( 743 'type' => PARAM_INT, 744 'default' => 0 745 ), 746 'path' => array( 747 'type' => PARAM_RAW, 748 'default' => '' 749 ), 750 'sortorder' => array( 751 'type' => PARAM_INT, 752 'message' => new lang_string('invalidrequest', 'error') 753 ), 754 'scaleid' => array( 755 'type' => PARAM_INT, 756 'default' => null, 757 'null' => NULL_ALLOWED 758 ) 759 ); 760 } 761 762 protected function before_validate() { 763 $this->beforevalidate = true; 764 } 765 766 protected function before_create() { 767 $this->beforecreate = true; 768 } 769 770 protected function before_update() { 771 $this->beforeupdate = true; 772 } 773 774 protected function before_delete() { 775 $this->beforedelete = true; 776 } 777 778 protected function after_create() { 779 $this->aftercreate = true; 780 } 781 782 protected function after_update($result) { 783 $this->afterupdate = true; 784 } 785 786 protected function after_delete($result) { 787 $this->afterdelete = true; 788 } 789 790 protected function get_path() { 791 $value = $this->raw_get('path'); 792 if (!empty($value)) { 793 $value = json_decode($value); 794 } 795 return $value; 796 } 797 798 protected function set_path($value) { 799 if (!empty($value)) { 800 $value = json_encode($value); 801 } 802 $this->raw_set('path', $value); 803 } 804 805 protected function validate_sortorder($value) { 806 if ($value == 10) { 807 return new lang_string('invalidkey', 'error'); 808 } 809 return true; 810 } 811 812 } 813 814 /** 815 * Example persistent class to test types. 816 * 817 * @package core 818 * @copyright 2021 David Matamoros <davidmc@moodle.com> 819 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 820 */ 821 class core_testable_second_persistent extends persistent { 822 823 /** Table name for the persistent. */ 824 const TABLE = 'phpunit_second_persistent'; 825 826 /** 827 * Return the list of properties. 828 * 829 * @return array 830 */ 831 protected static function define_properties(): array { 832 return [ 833 'someint' => [ 834 'type' => PARAM_INT, 835 ], 836 'intnull' => [ 837 'type' => PARAM_INT, 838 'null' => NULL_ALLOWED, 839 'default' => null, 840 ], 841 'somefloat' => [ 842 'type' => PARAM_FLOAT, 843 ], 844 'sometext' => [ 845 'type' => PARAM_TEXT, 846 'default' => '' 847 ], 848 'someraw' => [ 849 'type' => PARAM_RAW, 850 'default' => '' 851 ], 852 'booltrue' => [ 853 'type' => PARAM_BOOL, 854 ], 855 'boolfalse' => [ 856 'type' => PARAM_BOOL, 857 ] 858 ]; 859 } 860 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body