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
declare(strict_types=1);
18
 
19
namespace core_reportbuilder\local\helpers;
20
 
21
use core_reportbuilder_generator;
22
use core_reportbuilder\local\entities\user;
1441 ariadna 23
use core_reportbuilder\local\filters\{boolean_select, date, select, text};
24
use core_reportbuilder\local\report\{column, filter};
25
use core_reportbuilder\tests\core_reportbuilder_testcase;
1 efrain 26
use core_user\reportbuilder\datasource\users;
27
 
28
/**
29
 * Unit tests for user profile fields helper
30
 *
31
 * @package     core_reportbuilder
32
 * @covers      \core_reportbuilder\local\helpers\user_profile_fields
33
 * @copyright   2021 David Matamoros <davidmc@moodle.com>
34
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35
 */
1441 ariadna 36
final class user_profile_fields_test extends core_reportbuilder_testcase {
1 efrain 37
 
38
    /**
39
     * Generate custom profile fields, one of each type
40
     *
41
     * @return user_profile_fields
42
     */
43
    private function generate_userprofilefields(): user_profile_fields {
44
        $this->getDataGenerator()->create_custom_profile_field([
1441 ariadna 45
            'shortname' => 'checkbox', 'name' => 'Checkbox field', 'datatype' => 'checkbox', 'defaultdata' => 1]);
1 efrain 46
 
1441 ariadna 47
        // This field is available only to admins.
1 efrain 48
        $this->getDataGenerator()->create_custom_profile_field([
1441 ariadna 49
            'shortname' => 'datetime', 'name' => 'Date field', 'datatype' => 'datetime', 'param2' => 2022, 'param3' => 0,
50
                'defaultdata' => 0, 'visible' => PROFILE_VISIBLE_NONE]);
1 efrain 51
 
52
        $this->getDataGenerator()->create_custom_profile_field([
1441 ariadna 53
            'shortname' => 'menu', 'name' => 'Menu field', 'datatype' => 'menu', 'param1' => "Cat\nDog\nFish",
54
                'defaultdata' => 'Cat']);
1 efrain 55
 
56
        $this->getDataGenerator()->create_custom_profile_field([
57
            'shortname' => 'Social', 'name' => 'msn', 'datatype' => 'social', 'param1' => 'msn']);
58
 
59
        $this->getDataGenerator()->create_custom_profile_field([
1441 ariadna 60
            'shortname' => 'text', 'name' => 'Text field', 'datatype' => 'text', 'defaultdata' => 'default']);
1 efrain 61
 
62
        $this->getDataGenerator()->create_custom_profile_field([
1441 ariadna 63
            'shortname' => 'textarea', 'name' => 'Textarea field', 'datatype' => 'textarea', 'defaultdata' => 'Default']);
1 efrain 64
 
65
        $userentity = new user();
66
        $useralias = $userentity->get_table_alias('user');
67
 
68
        // Create an instance of the userprofilefield helper.
1441 ariadna 69
        return new user_profile_fields("{$useralias}.id", $userentity->get_entity_name());
1 efrain 70
    }
71
 
72
    /**
73
     * Test for get_columns
74
     */
75
    public function test_get_columns(): void {
76
        $this->resetAfterTest();
1441 ariadna 77
        $this->setAdminUser();
1 efrain 78
 
1441 ariadna 79
        // Get pre-existing user profile fields.
1 efrain 80
        $userentity = new user();
1441 ariadna 81
        $initialcolumns = (new user_profile_fields(
82
            $userentity->get_table_alias('user') . '.id',
83
            $userentity->get_entity_name(),
84
        ))->get_columns();
1 efrain 85
 
1441 ariadna 86
        // Create a field which will duplicate one of the subsequently generated fields (case-insensitive shortname).
87
        $this->getDataGenerator()->create_custom_profile_field([
88
            'shortname' => 'CHECKBOX',
89
            'name' => 'Duplicate checkbox field',
90
            'datatype' => 'checkbox',
91
        ]);
1 efrain 92
 
93
        // Add new custom profile fields.
94
        $userprofilefields = $this->generate_userprofilefields();
95
 
1441 ariadna 96
        // Ensure pre-existing fields are ignored in subsequent assertions.
97
        $columns = array_slice($userprofilefields->get_columns(), count($initialcolumns));
98
        $this->assertCount(6, $columns);
1 efrain 99
        $this->assertContainsOnlyInstancesOf(column::class, $columns);
100
 
1441 ariadna 101
        // Column titles.
102
        $this->assertEquals([
1 efrain 103
            'Checkbox field',
104
            'Date field',
105
            'Menu field',
106
            'MSN ID',
107
            'Text field',
108
            'Textarea field',
1441 ariadna 109
        ], array_map(
110
            fn(column $column): string => $column->get_title(),
111
            $columns,
112
        ));
1 efrain 113
 
1441 ariadna 114
        // Column types.
115
        $this->assertEquals([
1 efrain 116
            column::TYPE_BOOLEAN,
117
            column::TYPE_TIMESTAMP,
118
            column::TYPE_TEXT,
119
            column::TYPE_TEXT,
120
            column::TYPE_TEXT,
121
            column::TYPE_LONGTEXT,
1441 ariadna 122
        ], array_map(
123
            fn(column $column): int => $column->get_type(),
124
            $columns,
125
        ));
126
 
127
        // Column sortable.
128
        $this->assertEquals([
129
            true,
130
            true,
131
            true,
132
            true,
133
            true,
134
            true,
135
        ], array_map(
136
            fn(column $column): bool => $column->get_is_sortable(),
137
            $columns,
138
        ));
139
 
140
        // Column available.
141
        $this->assertEquals([
142
            true,
143
            true,
144
            true,
145
            true,
146
            true,
147
            true,
148
        ], array_map(
149
            fn(column $column): bool => $column->get_is_available(),
150
            $columns,
151
        ));
152
 
153
        // Column available, for non-privileged user.
154
        $this->setUser(null);
155
        $this->assertEquals([
156
            true,
157
            false,
158
            true,
159
            true,
160
            true,
161
            true,
162
        ], array_map(
163
            fn(column $column): bool => $column->get_is_available(),
164
            array_slice($userprofilefields->get_columns(), count($initialcolumns)),
165
        ));
1 efrain 166
    }
167
 
168
    /**
1441 ariadna 169
     * Test that joins added to the profile fields helper are present in its columns/filters
1 efrain 170
     */
171
    public function test_add_join(): void {
172
        $this->resetAfterTest();
173
 
174
        $userprofilefields = $this->generate_userprofilefields();
175
 
1441 ariadna 176
        // We always join on the user info data table.
177
        $columnjoins = $userprofilefields->get_columns()[0]->get_joins();
178
        $this->assertCount(1, $columnjoins);
179
        $this->assertStringStartsWith('LEFT JOIN {user_info_data}', $columnjoins[0]);
180
 
181
        $filterjoins = $userprofilefields->get_filters()[0]->get_joins();
182
        $this->assertCount(1, $filterjoins);
183
        $this->assertStringStartsWith('LEFT JOIN {user_info_data}', $filterjoins[0]);
184
 
185
        // Add additional join.
1 efrain 186
        $userprofilefields->add_join('JOIN {test} t ON t.id = id');
187
 
1441 ariadna 188
        $columnjoins = $userprofilefields->get_columns()[0]->get_joins();
189
        $this->assertCount(2, $columnjoins);
190
        $this->assertEquals('JOIN {test} t ON t.id = id', $columnjoins[0]);
191
        $this->assertStringStartsWith('LEFT JOIN {user_info_data}', $columnjoins[1]);
1 efrain 192
 
1441 ariadna 193
        $filterjoins = $userprofilefields->get_filters()[0]->get_joins();
194
        $this->assertCount(2, $filterjoins);
195
        $this->assertEquals('JOIN {test} t ON t.id = id', $filterjoins[0]);
196
        $this->assertStringStartsWith('LEFT JOIN {user_info_data}', $filterjoins[1]);
1 efrain 197
    }
198
 
199
    /**
200
     * Test for get_filters
201
     */
202
    public function test_get_filters(): void {
203
        $this->resetAfterTest();
1441 ariadna 204
        $this->setAdminUser();
1 efrain 205
 
1441 ariadna 206
        // Get pre-existing user profile fields.
1 efrain 207
        $userentity = new user();
1441 ariadna 208
        $initialfilters = (new user_profile_fields(
209
            $userentity->get_table_alias('user') . '.id',
210
            $userentity->get_entity_name(),
211
        ))->get_filters();
1 efrain 212
 
1441 ariadna 213
        // Create a field which will duplicate one of the subsequently generated fields (case-insensitive shortname).
214
        $this->getDataGenerator()->create_custom_profile_field([
215
            'shortname' => 'CHECKBOX',
216
            'name' => 'Duplicate checkbox field',
217
            'datatype' => 'checkbox',
218
        ]);
1 efrain 219
 
220
        // Add new custom profile fields.
221
        $userprofilefields = $this->generate_userprofilefields();
222
 
1441 ariadna 223
        // Ensure pre-existing fields are ignored in subsequent assertions.
224
        $filters = array_slice($userprofilefields->get_filters(), count($initialfilters));
225
        $this->assertCount(6, $filters);
1 efrain 226
        $this->assertContainsOnlyInstancesOf(filter::class, $filters);
227
 
1441 ariadna 228
        // Filter headers.
229
        $this->assertEquals([
1 efrain 230
            'Checkbox field',
231
            'Date field',
232
            'Menu field',
233
            'MSN ID',
234
            'Text field',
235
            'Textarea field',
1441 ariadna 236
        ], array_map(
237
            fn(filter $filter): string => $filter->get_header(),
238
            $filters,
239
        ));
240
 
241
        // Filter types.
242
        $this->assertEquals([
243
            boolean_select::class,
244
            date::class,
245
            select::class,
246
            text::class,
247
            text::class,
248
            text::class,
249
        ], array_map(
250
            fn(filter $filter) => $filter->get_filter_class(),
251
            $filters,
252
        ));
253
 
254
        // Filter available.
255
        $this->assertEquals([
256
            true,
257
            true,
258
            true,
259
            true,
260
            true,
261
            true,
262
        ], array_map(
263
            fn(filter $filter): bool => $filter->get_is_available(),
264
            $filters,
265
        ));
266
 
267
        // Filter available, for non-privileged user.
268
        $this->setUser(null);
269
        $this->assertEquals([
270
            true,
271
            false,
272
            true,
273
            true,
274
            true,
275
            true,
276
        ], array_map(
277
            fn(filter $filter): bool => $filter->get_is_available(),
278
            array_slice($userprofilefields->get_filters(), count($initialfilters)),
279
        ));
1 efrain 280
    }
281
 
282
    /**
283
     * Test that adding user profile field columns to a report returns expected values
284
     */
285
    public function test_custom_report_content(): void {
286
        $this->resetAfterTest();
1441 ariadna 287
        $this->setAdminUser();
1 efrain 288
 
289
        // Create test subject with user profile fields content.
1441 ariadna 290
        $this->generate_userprofilefields();
291
        $this->getDataGenerator()->create_user([
1 efrain 292
            'firstname' => 'Zebedee',
1441 ariadna 293
            'profile_field_checkbox' => 0,
1 efrain 294
            'profile_field_datetime' => '2021-12-09',
1441 ariadna 295
            'profile_field_menu' => 'Dog',
1 efrain 296
            'profile_field_Social' => 12345,
297
            'profile_field_text' => 'Hello',
298
            'profile_field_textarea' => 'Goodbye',
299
        ]);
300
 
301
        /** @var core_reportbuilder_generator $generator */
302
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
303
        $report = $generator->create_report(['name' => 'Users', 'source' => users::class, 'default' => 0]);
304
 
305
        // Add user profile field columns to the report.
1441 ariadna 306
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:firstname', 'sortenabled' => 1]);
1 efrain 307
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_checkbox']);
308
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_datetime']);
309
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_menu']);
310
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_social']);
311
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_text']);
312
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:profilefield_textarea']);
313
 
314
        $content = $this->get_custom_report_content($report->get('id'));
315
        $this->assertEquals([
316
            [
1441 ariadna 317
                'Admin',
318
                'Yes',
319
                'Not set',
320
                'Cat',
321
                '',
322
                'default',
323
                format_text('Default', options: ['overflowdiv' => true]),
1 efrain 324
            ], [
1441 ariadna 325
                'Zebedee',
326
                'No',
327
                '9 December 2021',
328
                'Dog',
329
                '12345',
330
                'Hello',
331
                format_text('Goodbye', options: ['overflowdiv' => true]),
1 efrain 332
            ],
1441 ariadna 333
        ], array_map('array_values', $content));
1 efrain 334
    }
335
 
336
    /**
337
     * Data provider for {@see test_custom_report_filter}
338
     *
339
     * @return array[]
340
     */
1441 ariadna 341
    public static function custom_report_filter_provider(): array {
1 efrain 342
        return [
343
            'Filter by checkbox profile field' => ['user:profilefield_checkbox', [
1441 ariadna 344
                'user:profilefield_checkbox_operator' => boolean_select::NOT_CHECKED,
345
            ], 'testuser'],
346
            'Filter by checkbox profile field (default)' => ['user:profilefield_checkbox', [
1 efrain 347
                'user:profilefield_checkbox_operator' => boolean_select::CHECKED,
348
            ], 'admin'],
349
            'Filter by datetime profile field' => ['user:profilefield_datetime', [
350
                'user:profilefield_datetime_operator' => date::DATE_RANGE,
351
                'user:profilefield_datetime_from' => 1622502000,
352
            ], 'testuser'],
1441 ariadna 353
            'Filter by datetime profile field (no match)' => ['user:profilefield_datetime', [
354
                'user:profilefield_datetime_operator' => date::DATE_RANGE,
355
                'user:profilefield_datetime_from' => 1672531200,
356
            ]],
1 efrain 357
            'Filter by menu profile field' => ['user:profilefield_menu', [
358
                'user:profilefield_menu_operator' => select::EQUAL_TO,
359
                'user:profilefield_menu_value' => 'Dog',
360
            ], 'testuser'],
1441 ariadna 361
            'Filter by menu profile field (default)' => ['user:profilefield_menu', [
362
                'user:profilefield_menu_operator' => select::EQUAL_TO,
363
                'user:profilefield_menu_value' => 'Cat',
1 efrain 364
            ], 'admin'],
1441 ariadna 365
            'Filter by menu profile field (no match)' => ['user:profilefield_menu', [
366
                'user:profilefield_menu_operator' => select::EQUAL_TO,
367
                'user:profilefield_menu_value' => 'Fish',
368
            ]],
1 efrain 369
            'Filter by social profile field' => ['user:profilefield_social', [
370
                'user:profilefield_social_operator' => text::IS_EQUAL_TO,
371
                'user:profilefield_social_value' => '12345',
372
            ], 'testuser'],
1441 ariadna 373
            'Filter by social profile field (no match)' => ['user:profilefield_social', [
374
                'user:profilefield_social_operator' => text::IS_EQUAL_TO,
375
                'user:profilefield_social_value' => '54321',
376
            ]],
1 efrain 377
            'Filter by text profile field' => ['user:profilefield_text', [
378
                'user:profilefield_text_operator' => text::IS_EQUAL_TO,
379
                'user:profilefield_text_value' => 'Hello',
380
            ], 'testuser'],
1441 ariadna 381
            'Filter by text profile field (default)' => ['user:profilefield_text', [
382
                'user:profilefield_text_operator' => text::IS_EQUAL_TO,
383
                'user:profilefield_text_value' => 'default',
1 efrain 384
            ], 'admin'],
1441 ariadna 385
            'Filter by text profile field (no match)' => ['user:profilefield_text', [
386
                'user:profilefield_text_operator' => text::IS_EQUAL_TO,
387
                'user:profilefield_text_value' => 'hola',
388
            ]],
1 efrain 389
            'Filter by textarea profile field' => ['user:profilefield_textarea', [
390
                'user:profilefield_textarea_operator' => text::IS_EQUAL_TO,
391
                'user:profilefield_textarea_value' => 'Goodbye',
392
            ], 'testuser'],
1441 ariadna 393
            'Filter by textarea profile field (default)' => ['user:profilefield_textarea', [
394
                'user:profilefield_textarea_operator' => text::IS_EQUAL_TO,
395
                'user:profilefield_textarea_value' => 'Default',
1 efrain 396
            ], 'admin'],
1441 ariadna 397
            'Filter by textarea profile field (no match)' => ['user:profilefield_textarea', [
398
                'user:profilefield_textarea_operator' => text::IS_EMPTY,
399
                'user:profilefield_textarea_value' => 'Adios',
400
            ]],
1 efrain 401
        ];
402
    }
403
 
404
    /**
405
     * Test filtering report by custom profile fields
406
     *
407
     * @param string $filtername
408
     * @param array $filtervalues
1441 ariadna 409
     * @param string|null $expectmatch
1 efrain 410
     *
411
     * @dataProvider custom_report_filter_provider
412
     */
1441 ariadna 413
    public function test_custom_report_filter(string $filtername, array $filtervalues, ?string $expectmatch = null): void {
1 efrain 414
        $this->resetAfterTest();
1441 ariadna 415
        $this->setAdminUser();
1 efrain 416
 
417
        // Create test subject with user profile fields content.
1441 ariadna 418
        $this->generate_userprofilefields();
419
        $this->getDataGenerator()->create_user([
1 efrain 420
            'username' => 'testuser',
1441 ariadna 421
            'profile_field_checkbox' => 0,
1 efrain 422
            'profile_field_datetime' => '2021-12-09',
423
            'profile_field_menu' => 'Dog',
424
            'profile_field_Social' => '12345',
425
            'profile_field_text' => 'Hello',
426
            'profile_field_textarea' => 'Goodbye',
427
        ]);
428
 
429
        /** @var core_reportbuilder_generator $generator */
430
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
431
 
432
        // Create report containing single column, and given filter.
433
        $report = $generator->create_report(['name' => 'Users', 'source' => users::class, 'default' => 0]);
434
        $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:username']);
435
 
436
        // Add filter, set it's values.
437
        $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]);
438
        $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues);
439
 
1441 ariadna 440
        if ($expectmatch !== null) {
441
            $this->assertCount(1, $content);
442
            $this->assertEquals($expectmatch, reset($content[0]));
443
        } else {
444
            $this->assertEmpty($content);
445
        }
1 efrain 446
    }
447
 
448
    /**
449
     * Stress test user datasource using profile fields
450
     *
451
     * In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
452
     */
453
    public function test_stress_datasource(): void {
454
        if (!PHPUNIT_LONGTEST) {
455
            $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
456
        }
457
 
458
        $this->resetAfterTest();
459
 
1441 ariadna 460
        $this->generate_userprofilefields();
461
        $this->getDataGenerator()->create_user([
1 efrain 462
            'profile_field_checkbox' => true,
463
            'profile_field_datetime' => '2021-12-09',
464
            'profile_field_menu' => 'Dog',
465
            'profile_field_Social' => '12345',
466
            'profile_field_text' => 'Hello',
467
            'profile_field_textarea' => 'Goodbye',
468
        ]);
469
 
470
        $this->datasource_stress_test_columns(users::class);
471
        $this->datasource_stress_test_columns_aggregation(users::class);
472
        $this->datasource_stress_test_conditions(users::class, 'user:idnumber');
473
    }
474
}