2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Persistent class tests.
21 * @copyright 2015 Frédéric Massart - FMCorz.net
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') ||
die();
29 * Persistent testcase.
32 * @copyright 2015 Frédéric Massart - FMCorz.net
33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 class core_persistent_testcase
extends advanced_testcase
{
37 public function setUp() {
38 $this->make_persistent_table();
39 $this->resetAfterTest();
43 * Make the table for the persistent.
45 protected function make_persistent_table() {
47 $dbman = $DB->get_manager();
49 $table = new xmldb_table(core_testable_persistent
::TABLE
);
50 $table->add_field('id', XMLDB_TYPE_INTEGER
, '10', null, XMLDB_NOTNULL
, XMLDB_SEQUENCE
, null);
51 $table->add_field('shortname', XMLDB_TYPE_CHAR
, '100', null, null, null, null);
52 $table->add_field('idnumber', XMLDB_TYPE_CHAR
, '100', null, null, null, null);
53 $table->add_field('description', XMLDB_TYPE_TEXT
, null, null, null, null, null);
54 $table->add_field('descriptionformat', XMLDB_TYPE_INTEGER
, '4', null, XMLDB_NOTNULL
, null, '0');
55 $table->add_field('parentid', XMLDB_TYPE_INTEGER
, '10', null, XMLDB_NOTNULL
, null, '0');
56 $table->add_field('path', XMLDB_TYPE_CHAR
, '255', null, XMLDB_NOTNULL
, null, null);
57 $table->add_field('sortorder', XMLDB_TYPE_INTEGER
, '10', null, XMLDB_NOTNULL
, null, null);
58 $table->add_field('scaleid', XMLDB_TYPE_INTEGER
, '10', null, null, null, null);
59 $table->add_field('timecreated', XMLDB_TYPE_INTEGER
, '10', null, XMLDB_NOTNULL
, null, null);
60 $table->add_field('timemodified', XMLDB_TYPE_INTEGER
, '10', null, XMLDB_NOTNULL
, null, null);
61 $table->add_field('usermodified', XMLDB_TYPE_INTEGER
, '10', null, XMLDB_NOTNULL
, null, '0');
63 $table->add_key('primary', XMLDB_KEY_PRIMARY
, array('id'));
65 if ($dbman->table_exists($table)) {
66 $dbman->drop_table($table);
69 $dbman->create_table($table);
72 public function test_properties_definition() {
77 'null' => NULL_NOT_ALLOWED
81 'null' => NULL_NOT_ALLOWED
83 'description' => array(
86 'null' => NULL_NOT_ALLOWED
88 'descriptionformat' => array(
89 'choices' => array(FORMAT_HTML
, FORMAT_MOODLE
, FORMAT_PLAIN
, FORMAT_MARKDOWN
),
91 'default' => FORMAT_HTML
,
92 'null' => NULL_NOT_ALLOWED
97 'null' => NULL_NOT_ALLOWED
102 'null' => NULL_NOT_ALLOWED
104 'sortorder' => array(
106 'message' => new lang_string('invalidrequest', 'error'),
107 'null' => NULL_NOT_ALLOWED
112 'null' => NULL_ALLOWED
117 'null' => NULL_NOT_ALLOWED
119 'timecreated' => array(
122 'null' => NULL_NOT_ALLOWED
124 'timemodified' => array(
127 'null' => NULL_NOT_ALLOWED
129 'usermodified' => array(
132 'null' => NULL_NOT_ALLOWED
135 $this->assertEquals($expected, core_testable_persistent
::properties_definition());
138 public function test_to_record() {
139 $p = new core_testable_persistent();
140 $expected = (object) array(
144 'descriptionformat' => FORMAT_HTML
,
154 $this->assertEquals($expected, $p->to_record());
157 public function test_from_record() {
158 $p = new core_testable_persistent();
159 $data = (object) array(
160 'shortname' => 'ddd',
162 'description' => 'xyz',
163 'descriptionformat' => FORMAT_PLAIN
,
173 $p->from_record($data);
174 $this->assertEquals($data, $p->to_record());
178 * @expectedException coding_exception
180 public function test_from_record_invalid_param() {
181 $p = new core_testable_persistent();
182 $data = (object) array(
183 'invalidparam' => 'abc'
186 $p->from_record($data);
189 public function test_validate() {
190 $data = (object) array(
194 $p = new core_testable_persistent(0, $data);
195 $this->assertFalse(isset($p->beforevalidate
));
196 $this->assertTrue($p->validate());
197 $this->assertTrue(isset($p->beforevalidate
));
198 $this->assertTrue($p->is_valid());
199 $this->assertEquals(array(), $p->get_errors());
200 $p->set('descriptionformat', -100);
203 'descriptionformat' => new lang_string('invaliddata', 'error'),
205 $this->assertEquals($expected, $p->validate());
206 $this->assertFalse($p->is_valid());
207 $this->assertEquals($expected, $p->get_errors());
210 public function test_validation_required() {
211 $data = (object) array(
214 $p = new core_testable_persistent(0, $data);
216 'sortorder' => new lang_string('requiredelement', 'form'),
218 $this->assertFalse($p->is_valid());
219 $this->assertEquals($expected, $p->get_errors());
222 public function test_validation_custom() {
223 $data = (object) array(
227 $p = new core_testable_persistent(0, $data);
229 'sortorder' => new lang_string('invalidkey', 'error'),
231 $this->assertFalse($p->is_valid());
232 $this->assertEquals($expected, $p->get_errors());
235 public function test_validation_custom_message() {
236 $data = (object) array(
238 'sortorder' => 'abc',
240 $p = new core_testable_persistent(0, $data);
242 'sortorder' => new lang_string('invalidrequest', 'error'),
244 $this->assertFalse($p->is_valid());
245 $this->assertEquals($expected, $p->get_errors());
248 public function test_validation_choices() {
249 $data = (object) array(
252 'descriptionformat' => -100
254 $p = new core_testable_persistent(0, $data);
256 'descriptionformat' => new lang_string('invaliddata', 'error'),
258 $this->assertFalse($p->is_valid());
259 $this->assertEquals($expected, $p->get_errors());
262 public function test_validation_type() {
263 $data = (object) array(
267 $p = new core_testable_persistent(0, $data);
268 $this->assertFalse($p->is_valid());
269 $this->assertArrayHasKey('sortorder', $p->get_errors());
272 public function test_validation_null() {
273 $data = (object) array(
278 $p = new core_testable_persistent(0, $data);
279 $this->assertFalse($p->is_valid());
280 $this->assertArrayHasKey('idnumber', $p->get_errors());
281 $this->assertArrayHasKey('scaleid', $p->get_errors());
282 $p->set('idnumber', 'abc');
283 $this->assertFalse($p->is_valid());
284 $this->assertArrayNotHasKey('idnumber', $p->get_errors());
285 $this->assertArrayHasKey('scaleid', $p->get_errors());
286 $p->set('scaleid', null);
287 $this->assertTrue($p->is_valid());
288 $this->assertArrayNotHasKey('scaleid', $p->get_errors());
291 public function test_create() {
293 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc'));
294 $this->assertFalse(isset($p->beforecreate
));
295 $this->assertFalse(isset($p->aftercreate
));
297 $record = $DB->get_record(core_testable_persistent
::TABLE
, array('id' => $p->get('id')), '*', MUST_EXIST
);
298 $expected = $p->to_record();
299 $this->assertTrue(isset($p->beforecreate
));
300 $this->assertTrue(isset($p->aftercreate
));
301 $this->assertEquals($expected->sortorder
, $record->sortorder
);
302 $this->assertEquals($expected->idnumber
, $record->idnumber
);
303 $this->assertEquals($expected->id
, $record->id
);
304 $this->assertTrue($p->is_valid()); // Should always be valid after a create.
307 public function test_update() {
309 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc'));
312 $p->set('sortorder', 456);
313 $p->from_record((object) array('idnumber' => 'def'));
314 $this->assertFalse(isset($p->beforeupdate
));
315 $this->assertFalse(isset($p->afterupdate
));
318 $expected = $p->to_record();
319 $record = $DB->get_record(core_testable_persistent
::TABLE
, array('id' => $p->get('id')), '*', MUST_EXIST
);
320 $this->assertTrue(isset($p->beforeupdate
));
321 $this->assertTrue(isset($p->afterupdate
));
322 $this->assertEquals($id, $record->id
);
323 $this->assertEquals(456, $record->sortorder
);
324 $this->assertEquals('def', $record->idnumber
);
325 $this->assertTrue($p->is_valid()); // Should always be valid after an update.
328 public function test_save() {
330 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc'));
331 $this->assertFalse(isset($p->beforecreate
));
332 $this->assertFalse(isset($p->aftercreate
));
333 $this->assertFalse(isset($p->beforeupdate
));
334 $this->assertFalse(isset($p->beforeupdate
));
336 $record = $DB->get_record(core_testable_persistent
::TABLE
, array('id' => $p->get('id')), '*', MUST_EXIST
);
337 $expected = $p->to_record();
338 $this->assertTrue(isset($p->beforecreate
));
339 $this->assertTrue(isset($p->aftercreate
));
340 $this->assertFalse(isset($p->beforeupdate
));
341 $this->assertFalse(isset($p->beforeupdate
));
342 $this->assertEquals($expected->sortorder
, $record->sortorder
);
343 $this->assertEquals($expected->idnumber
, $record->idnumber
);
344 $this->assertEquals($expected->id
, $record->id
);
345 $this->assertTrue($p->is_valid()); // Should always be valid after a save/create.
347 $p->set('idnumber', 'abcd');
349 $record = $DB->get_record(core_testable_persistent
::TABLE
, array('id' => $p->get('id')), '*', MUST_EXIST
);
350 $expected = $p->to_record();
351 $this->assertTrue(isset($p->beforeupdate
));
352 $this->assertTrue(isset($p->beforeupdate
));
353 $this->assertEquals($expected->sortorder
, $record->sortorder
);
354 $this->assertEquals($expected->idnumber
, $record->idnumber
);
355 $this->assertEquals($expected->id
, $record->id
);
356 $this->assertTrue($p->is_valid()); // Should always be valid after a save/update.
359 public function test_read() {
360 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc'));
362 unset($p->beforevalidate
);
363 unset($p->beforecreate
);
364 unset($p->aftercreate
);
366 $p2 = new core_testable_persistent($p->get('id'));
367 $this->assertEquals($p, $p2);
369 $p3 = new core_testable_persistent();
370 $p3->set('id', $p->get('id'));
372 $this->assertEquals($p, $p3);
375 public function test_delete() {
378 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc'));
380 $this->assertNotEquals(0, $p->get('id'));
381 $this->assertTrue($DB->record_exists_select(core_testable_persistent
::TABLE
, 'id = ?', array($p->get('id'))));
382 $this->assertFalse(isset($p->beforedelete
));
383 $this->assertFalse(isset($p->afterdelete
));
386 $this->assertFalse($DB->record_exists_select(core_testable_persistent
::TABLE
, 'id = ?', array($p->get('id'))));
387 $this->assertEquals(0, $p->get('id'));
388 $this->assertEquals(true, $p->beforedelete
);
389 $this->assertEquals(true, $p->afterdelete
);
392 public function test_has_property() {
393 $this->assertFalse(core_testable_persistent
::has_property('unknown'));
394 $this->assertTrue(core_testable_persistent
::has_property('idnumber'));
397 public function test_custom_setter_getter() {
400 $path = array(1, 2, 3);
401 $json = json_encode($path);
403 $p = new core_testable_persistent(0, (object) array('sortorder' => 0, 'idnumber' => 'abc'));
404 $p->set('path', $path);
405 $this->assertEquals($path, $p->get('path'));
406 $this->assertEquals($json, $p->to_record()->path
);
409 $record = $DB->get_record(core_testable_persistent
::TABLE
, array('id' => $p->get('id')), 'id, path', MUST_EXIST
);
410 $this->assertEquals($json, $record->path
);
413 public function test_record_exists() {
415 $this->assertFalse($DB->record_exists(core_testable_persistent
::TABLE
, array('idnumber' => 'abc')));
416 $p = new core_testable_persistent(0, (object) array('sortorder' => 123, 'idnumber' => 'abc'));
419 $this->assertTrue(core_testable_persistent
::record_exists($id));
420 $this->assertTrue($DB->record_exists(core_testable_persistent
::TABLE
, array('idnumber' => 'abc')));
422 $this->assertFalse(core_testable_persistent
::record_exists($id));
425 public function test_get_sql_fields() {
427 'c.id AS prefix_id, ' .
428 'c.shortname AS prefix_shortname, ' .
429 'c.idnumber AS prefix_idnumber, ' .
430 'c.description AS prefix_description, ' .
431 'c.descriptionformat AS prefix_descriptionformat, ' .
432 'c.parentid AS prefix_parentid, ' .
433 'c.path AS prefix_path, ' .
434 'c.sortorder AS prefix_sortorder, ' .
435 'c.scaleid AS prefix_scaleid, ' .
436 'c.timecreated AS prefix_timecreated, ' .
437 'c.timemodified AS prefix_timemodified, ' .
438 'c.usermodified AS prefix_usermodified';
439 $this->assertEquals($expected, core_testable_persistent
::get_sql_fields('c', 'prefix_'));
443 * @expectedException coding_exception
444 * @expectedExceptionMessageRegExp /The alias .+ exceeds 30 characters/
446 public function test_get_sql_fields_too_long() {
447 core_testable_persistent
::get_sql_fields('c');
452 * Example persistent class.
455 * @copyright 2015 Frédéric Massart - FMCorz.net
456 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
458 class core_testable_persistent
extends \core\persistent
{
460 const TABLE
= 'phpunit_persistent';
462 protected static function define_properties() {
464 'shortname' => array(
465 'type' => PARAM_TEXT
,
469 'type' => PARAM_TEXT
,
471 'description' => array(
472 'type' => PARAM_TEXT
,
475 'descriptionformat' => array(
476 'choices' => array(FORMAT_HTML
, FORMAT_MOODLE
, FORMAT_PLAIN
, FORMAT_MARKDOWN
),
478 'default' => FORMAT_HTML
488 'sortorder' => array(
490 'message' => new lang_string('invalidrequest', 'error')
495 'null' => NULL_ALLOWED
500 protected function before_validate() {
501 $this->beforevalidate
= true;
504 protected function before_create() {
505 $this->beforecreate
= true;
508 protected function before_update() {
509 $this->beforeupdate
= true;
512 protected function before_delete() {
513 $this->beforedelete
= true;
516 protected function after_create() {
517 $this->aftercreate
= true;
520 protected function after_update($result) {
521 $this->afterupdate
= true;
524 protected function after_delete($result) {
525 $this->afterdelete
= true;
528 protected function get_path() {
529 $value = $this->raw_get('path');
530 if (!empty($value)) {
531 $value = json_decode($value);
536 protected function set_path($value) {
537 if (!empty($value)) {
538 $value = json_encode($value);
540 $this->raw_set('path', $value);
543 protected function validate_sortorder($value) {
545 return new lang_string('invalidkey', 'error');