Proyectos de Subversion Moodle

Rev

| 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_external;
18
 
19
/**
20
 * Unit tests for core_external\util.
21
 *
22
 * @package     core_external
23
 * @category    test
24
 * @copyright   2022 Andrew Lyons <andrew@nicols.co.uk>
25
 * @license     http://www.gnu.org/copyleft/gpl.html GNU Public License
26
 * @covers      \core_external\util
27
 */
28
class util_test extends \advanced_testcase {
29
    /** @var \moodle_database The database connection */
30
    protected $db;
31
 
32
    /**
33
     * Store the global DB for restore between tests.
34
     */
35
    public function setUp(): void {
36
        global $DB;
37
 
38
        $this->db = $DB;
39
        external_settings::reset();
40
    }
41
 
42
    /**
43
     * A helper to include the legacy external functions.
44
     */
45
    protected function include_legacy_functions(): void {
46
        global $CFG;
47
 
48
        $this->assertTrue(
49
            $this->isInIsolation(),
50
            'Inclusion of the legacy test functions requires the test to be run in isolation.',
51
        );
52
 
53
        // Note: This is retained for testing of the old functions.
54
        require_once("{$CFG->libdir}/externallib.php");
55
    }
56
 
57
    /**
58
     * Reset the global DB between tests.
59
     */
60
    public function tearDown(): void {
61
        global $DB;
62
        if ($this->db !== null) {
63
            $DB = $this->db;
64
        }
65
        external_settings::reset();
66
    }
67
 
68
    /**
69
     * Validate courses, but still return courses even if they fail validation.
70
     *
71
     * @covers \core_external\util::validate_courses
72
     */
73
    public function test_validate_courses_keepfails(): void {
74
        $this->resetAfterTest(true);
75
 
76
        $c1 = $this->getDataGenerator()->create_course();
77
        $c2 = $this->getDataGenerator()->create_course();
78
        $c3 = $this->getDataGenerator()->create_course();
79
        $u1 = $this->getDataGenerator()->create_user();
80
        $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
81
        $courseids = [$c1->id, $c2->id, $c3->id];
82
 
83
        $this->setUser($u1);
84
        [$courses, $warnings] = util::validate_courses($courseids, [], false, true);
85
        $this->assertCount(2, $warnings);
86
        $this->assertEquals($c2->id, $warnings[0]['itemid']);
87
        $this->assertEquals($c3->id, $warnings[1]['itemid']);
88
        $this->assertCount(3, $courses);
89
        $this->assertTrue($courses[$c1->id]->contextvalidated);
90
        $this->assertFalse($courses[$c2->id]->contextvalidated);
91
        $this->assertFalse($courses[$c3->id]->contextvalidated);
92
    }
93
 
94
    /**
95
     * Validate courses can re-use an array of prefetched courses.
96
     *
97
     * @covers \core_external\util::validate_courses
98
     */
99
    public function test_validate_courses_prefetch(): void {
100
        $this->resetAfterTest(true);
101
 
102
        $c1 = $this->getDataGenerator()->create_course();
103
        $c2 = $this->getDataGenerator()->create_course();
104
        $c3 = $this->getDataGenerator()->create_course();
105
        $c4 = $this->getDataGenerator()->create_course();
106
        $u1 = $this->getDataGenerator()->create_user();
107
        $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
108
        $this->getDataGenerator()->enrol_user($u1->id, $c2->id);
109
 
110
        $courseids = [$c1->id, $c2->id, $c3->id];
111
        $courses = [$c2->id => $c2, $c3->id => $c3, $c4->id => $c4];
112
 
113
        $this->setUser($u1);
114
        [$courses, $warnings] = util::validate_courses($courseids, $courses);
115
        $this->assertCount(2, $courses);
116
        $this->assertCount(1, $warnings);
117
        $this->assertArrayHasKey($c1->id, $courses);
118
        $this->assertSame($c2, $courses[$c2->id]);
119
        $this->assertArrayNotHasKey($c3->id, $courses);
120
        // The extra course passed is not returned.
121
        $this->assertArrayNotHasKey($c4->id, $courses);
122
    }
123
 
124
    /**
125
     * Test the Validate courses standard functionality.
126
     *
127
     * @covers \core_external\util::validate_courses
128
     */
129
    public function test_validate_courses(): void {
130
        $this->resetAfterTest(true);
131
 
132
        $c1 = $this->getDataGenerator()->create_course();
133
        $c2 = $this->getDataGenerator()->create_course();
134
        $c3 = $this->getDataGenerator()->create_course();
135
        $u1 = $this->getDataGenerator()->create_user();
136
        $this->getDataGenerator()->enrol_user($u1->id, $c1->id);
137
        $courseids = [$c1->id, $c2->id, $c3->id];
138
 
139
        $this->setAdminUser();
140
        [$courses, $warnings] = util::validate_courses($courseids);
141
        $this->assertEmpty($warnings);
142
        $this->assertCount(3, $courses);
143
        $this->assertArrayHasKey($c1->id, $courses);
144
        $this->assertArrayHasKey($c2->id, $courses);
145
        $this->assertArrayHasKey($c3->id, $courses);
146
        $this->assertEquals($c1->id, $courses[$c1->id]->id);
147
        $this->assertEquals($c2->id, $courses[$c2->id]->id);
148
        $this->assertEquals($c3->id, $courses[$c3->id]->id);
149
 
150
        $this->setUser($u1);
151
        [$courses, $warnings] = util::validate_courses($courseids);
152
        $this->assertCount(2, $warnings);
153
        $this->assertEquals($c2->id, $warnings[0]['itemid']);
154
        $this->assertEquals($c3->id, $warnings[1]['itemid']);
155
        $this->assertCount(1, $courses);
156
        $this->assertArrayHasKey($c1->id, $courses);
157
        $this->assertArrayNotHasKey($c2->id, $courses);
158
        $this->assertArrayNotHasKey($c3->id, $courses);
159
        $this->assertEquals($c1->id, $courses[$c1->id]->id);
160
    }
161
 
162
    /**
163
     * Text util::get_area_files
164
     *
165
     * @covers \core_external\util::get_area_files
166
     */
167
    public function test_get_area_files(): void {
168
        global $CFG, $DB;
169
 
170
        $this->db = $DB;
171
        $DB = $this->getMockBuilder('moodle_database')->getMock();
172
 
173
        $content = base64_encode("Let us create a nice simple file.");
174
        $timemodified = 102030405;
175
        $itemid = 42;
176
        $filesize = strlen($content);
177
 
178
        $DB->method('get_records_sql')->willReturn([
179
            (object) [
180
                'filename'      => 'example.txt',
181
                'filepath'      => '/',
182
                'mimetype'      => 'text/plain',
183
                'filesize'      => $filesize,
184
                'timemodified'  => $timemodified,
185
                'itemid'        => $itemid,
186
                'pathnamehash'  => sha1('/example.txt'),
187
            ],
188
        ]);
189
 
190
        $component = 'mod_foo';
191
        $filearea = 'area';
192
        $context = 12345;
193
 
194
        $expectedfiles = [[
195
            'filename' => 'example.txt',
196
            'filepath' => '/',
197
            'fileurl' => "{$CFG->wwwroot}/webservice/pluginfile.php/{$context}/{$component}/{$filearea}/{$itemid}/example.txt",
198
            'timemodified' => $timemodified,
199
            'filesize' => $filesize,
200
            'mimetype' => 'text/plain',
201
            'isexternalfile' => false,
202
            'icon' => 'f/text',
203
        ],
204
        ];
205
        // Get all the files for the area.
206
        $files = util::get_area_files($context, $component, $filearea, false);
207
        $this->assertEquals($expectedfiles, $files);
208
 
209
        $DB->method('get_in_or_equal')->willReturn([
210
            '= :mock1',
211
            ['mock1' => $itemid],
212
        ]);
213
 
214
        // Get just the file indicated by $itemid.
215
        $files = util::get_area_files($context, $component, $filearea, $itemid);
216
        $this->assertEquals($expectedfiles, $files);
217
    }
218
 
219
    /**
220
     * Test default time for user created tokens.
221
     *
222
     * @covers \core_external\util::generate_token_for_current_user
223
     */
224
    public function test_user_created_tokens_duration(): void {
225
        global $CFG, $DB;
226
        $this->resetAfterTest(true);
227
 
228
        $CFG->enablewebservices = 1;
229
        $CFG->enablemobilewebservice = 1;
230
        $user1 = $this->getDataGenerator()->create_user();
231
        $user2 = $this->getDataGenerator()->create_user();
232
        $service = $DB->get_record('external_services', ['shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE, 'enabled' => 1]);
233
 
234
        $this->setUser($user1);
235
        $timenow = time();
236
        $token = util::generate_token_for_current_user($service);
237
        $this->assertGreaterThanOrEqual($timenow + $CFG->tokenduration, $token->validuntil);
238
 
239
        // Change token default time.
240
        $this->setUser($user2);
241
        set_config('tokenduration', DAYSECS);
242
        $token = util::generate_token_for_current_user($service);
243
        $timenow = time();
244
        $this->assertLessThanOrEqual($timenow + DAYSECS, $token->validuntil);
245
    }
246
 
247
 
248
    /**
249
     * Test the format_text function.
250
     *
251
     * @covers \core_external\util::format_text
252
     * @runInSeparateProcess
253
     */
254
    public function test_format_text(): void {
255
        $this->include_legacy_functions();
256
        $settings = external_settings::get_instance();
257
 
258
        $settings->set_raw(true);
259
        $settings->set_filter(false);
260
        $context = \context_system::instance();
261
 
262
        $test = '$$ \pi $$';
263
        $testformat = FORMAT_MARKDOWN;
264
        $correct = [$test, $testformat];
265
        $this->assertSame($correct, util::format_text($test, $testformat, $context, 'core', '', 0));
266
 
267
        // Function external_format_text should work with context id or context instance.
268
        $this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0), $correct);
269
        $this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0), $correct);
270
 
271
        $settings->set_raw(false);
272
        $settings->set_filter(true);
273
 
274
        $test = '$$ \pi $$';
275
        $testformat = FORMAT_MARKDOWN;
276
        $correct = ['<span class="filter_mathjaxloader_equation"><p><span class="nolink">$$ \pi $$</span></p>
277
</span>', FORMAT_HTML,
278
        ];
279
        $this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0), $correct);
280
 
281
        // Function external_format_text should work with context id or context instance.
282
        $this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0), $correct);
283
        $this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0), $correct);
284
 
285
        // Filters can be opted out from by the developer.
286
        $test = '$$ \pi $$';
287
        $testformat = FORMAT_MARKDOWN;
288
        $correct = ['<p>$$ \pi $$</p>
289
', FORMAT_HTML,
290
        ];
291
        $this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, ['filter' => false]), $correct);
292
 
293
        // Function external_format_text should work with context id or context instance.
294
        $this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, ['filter' => false]), $correct);
295
        $this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, ['filter' => false]), $correct);
296
 
297
        $test = '<p><a id="test"></a><a href="#test">Text</a></p>';
298
        $testformat = FORMAT_HTML;
299
        $correct = [$test, FORMAT_HTML];
300
        $options = ['allowid' => true];
301
        $this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
302
        // Function external_format_text should work with context id or context instance.
303
        $this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, $options), $correct);
304
        $this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
305
 
306
        $test = '<p><a id="test"></a><a href="#test">Text</a></p>';
307
        $testformat = FORMAT_HTML;
308
        $correct = ['<p><a></a><a href="#test">Text</a></p>', FORMAT_HTML];
309
        $options = new \stdClass();
310
        $options->allowid = false;
311
        $this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
312
 
313
        // Function external_format_text should work with context id or context instance.
314
        $this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, $options), $correct);
315
        $this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
316
 
317
        $test = '<p><a id="test"></a><a href="#test">Text</a></p>' . "\n" . 'Newline';
318
        $testformat = FORMAT_MOODLE;
319
        $correct = ['<p><a id="test"></a><a href="#test">Text</a></p> Newline', FORMAT_HTML];
320
        $options = new \stdClass();
321
        $options->newlines = false;
322
        $this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
323
 
324
        // Function external_format_text should work with context id or context instance.
325
        $this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, $options), $correct);
326
        $this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
327
 
328
        $test = '<p><a id="test"></a><a href="#test">Text</a></p>';
329
        $testformat = FORMAT_MOODLE;
330
        $correct = ['<div class="text_to_html">' . $test . '</div>', FORMAT_HTML];
331
        $options = new \stdClass();
332
        $options->para = true;
333
        $this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
334
 
335
        // Function external_format_text should work with context id or context instance.
336
        $this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, $options), $correct);
337
        $this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
338
 
339
        $test = '<p><a id="test"></a><a href="#test">Text</a></p>';
340
        $testformat = FORMAT_MOODLE;
341
        $correct = [$test, FORMAT_HTML];
342
        $options = new \stdClass();
343
        $options->context = $context;
344
        $this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
345
 
346
        // Function external_format_text should work with context id or context instance.
347
        $this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, $options), $correct);
348
        $this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
349
    }
350
    /**
351
     * Teset the format_string function.
352
     *
353
     * @covers \core_external\util::format_string
354
     * @runInSeparateProcess
355
     */
356
    public function test_external_format_string(): void {
357
        $this->resetAfterTest();
358
        $this->include_legacy_functions();
359
        $settings = external_settings::get_instance();
360
 
361
        // Enable multilang filter to on content and heading.
362
        filter_set_global_state('multilang', TEXTFILTER_ON);
363
        filter_set_applies_to_strings('multilang', 1);
364
        $filtermanager = \filter_manager::instance();
365
        $filtermanager->reset_caches();
366
 
367
        $settings->set_raw(true);
368
        $settings->set_filter(true);
369
        $context = \context_system::instance();
370
 
371
        $test = '<span lang="en" class="multilang">EN</span><span lang="fr" class="multilang">FR</span> ';
372
        $test .= '<script>hi</script> <h3>there</h3>!';
373
        $correct = $test;
374
        $this->assertSame($correct, util::format_string($test, $context));
375
 
376
        // Function external_format_string should work with context id or context instance.
377
        $this->assertSame($correct, external_format_string($test, $context));
378
        $this->assertSame($correct, external_format_string($test, $context->id));
379
 
380
        $settings->set_raw(false);
381
        $settings->set_filter(false);
382
 
383
        $test = '<span lang="en" class="multilang">EN</span><span lang="fr" class="multilang">FR</span> ';
384
        $test .= '<script>hi</script> <h3>there</h3>?';
385
        $correct = 'ENFR hi there?';
386
        $this->assertSame($correct, util::format_string($test, $context));
387
 
388
        // Function external_format_string should work with context id or context instance.
389
        $this->assertSame($correct, external_format_string($test, $context));
390
        $this->assertSame($correct, external_format_string($test, $context->id));
391
 
392
        $settings->set_filter(true);
393
 
394
        $test = '<span lang="en" class="multilang">EN</span><span lang="fr" class="multilang">FR</span> ';
395
        $test .= '<script>hi</script> <h3>there</h3>@';
396
        $correct = 'EN hi there@';
397
        $this->assertSame($correct, util::format_string($test, $context));
398
 
399
        // Function external_format_string should work with context id or context instance.
400
        $this->assertSame($correct, external_format_string($test, $context));
401
        $this->assertSame($correct, external_format_string($test, $context->id));
402
 
403
        // Filters can be opted out.
404
        $test = '<span lang="en" class="multilang">EN</span><span lang="fr" class="multilang">FR</span> ';
405
        $test .= '<script>hi</script> <h3>there</h3>%';
406
        $correct = 'ENFR hi there%';
407
        $this->assertSame($correct, util::format_string($test, $context, false, ['filter' => false]));
408
 
409
        // Function external_format_string should work with context id or context instance.
410
        $this->assertSame($correct, external_format_string($test, $context->id, false, ['filter' => false]));
411
        $this->assertSame($correct, external_format_string($test, $context, false, ['filter' => false]));
412
 
413
        $this->assertSame("& < > \" '", format_string("& < > \" '", true, ['escape' => false]));
414
    }
415
}