Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 
11 efrain 100
    public function test_properties_definition(): void {
1 efrain 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
 
11 efrain 206
    public function test_to_record(): void {
1 efrain 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
 
11 efrain 225
    public function test_from_record(): void {
1 efrain 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
 
11 efrain 245
    public function test_from_record_invalid_param(): void {
1 efrain 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
 
11 efrain 270
    public function test_validate(): void {
1 efrain 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
 
11 efrain 291
    public function test_validation_required(): void {
1 efrain 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
 
11 efrain 303
    public function test_validation_custom(): void {
1 efrain 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
 
11 efrain 316
    public function test_validation_custom_message(): void {
1 efrain 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
 
11 efrain 329
    public function test_validation_choices(): void {
1 efrain 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
 
11 efrain 343
    public function test_validation_type(): void {
1 efrain 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
 
11 efrain 353
    public function test_validation_null(): void {
1 efrain 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
 
11 efrain 372
    public function test_create(): void {
1 efrain 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
 
11 efrain 388
    public function test_update(): void {
1 efrain 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
 
11 efrain 432
    public function test_save(): void {
1 efrain 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
 
11 efrain 534
    public function test_read(): void {
1 efrain 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
 
11 efrain 550
    public function test_delete(): void {
1 efrain 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
 
11 efrain 567
    public function test_has_property(): void {
1 efrain 568
        $this->assertFalse(core_testable_persistent::has_property('unknown'));
569
        $this->assertTrue(core_testable_persistent::has_property('idnumber'));
570
    }
571
 
11 efrain 572
    public function test_custom_setter_getter(): void {
1 efrain 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
 
11 efrain 621
    public function test_record_exists(): void {
1 efrain 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
 
11 efrain 633
    public function test_get_sql_fields(): void {
1 efrain 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
 
11 efrain 650
    public function test_get_sql_fields_too_long(): void {
1 efrain 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
}