mirror of
https://github.com/moodle/moodle.git
synced 2025-02-23 03:18:07 +01:00
418 lines
17 KiB
PHP
418 lines
17 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
namespace core_external;
|
|
|
|
/**
|
|
* Unit tests for core_external\util.
|
|
*
|
|
* @package core_external
|
|
* @category test
|
|
* @copyright 2022 Andrew Lyons <andrew@nicols.co.uk>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
|
* @covers \core_external\util
|
|
*/
|
|
final class util_test extends \advanced_testcase {
|
|
/** @var \moodle_database The database connection */
|
|
protected $db;
|
|
|
|
/**
|
|
* Store the global DB for restore between tests.
|
|
*/
|
|
public function setUp(): void {
|
|
global $DB;
|
|
parent::setUp();
|
|
|
|
$this->db = $DB;
|
|
external_settings::reset();
|
|
}
|
|
|
|
/**
|
|
* A helper to include the legacy external functions.
|
|
*/
|
|
protected function include_legacy_functions(): void {
|
|
global $CFG;
|
|
|
|
$this->assertTrue(
|
|
$this->isInIsolation(),
|
|
'Inclusion of the legacy test functions requires the test to be run in isolation.',
|
|
);
|
|
|
|
// Note: This is retained for testing of the old functions.
|
|
require_once("{$CFG->libdir}/externallib.php");
|
|
}
|
|
|
|
/**
|
|
* Reset the global DB between tests.
|
|
*/
|
|
public function tearDown(): void {
|
|
global $DB;
|
|
if ($this->db !== null) {
|
|
$DB = $this->db;
|
|
}
|
|
external_settings::reset();
|
|
parent::tearDown();
|
|
}
|
|
|
|
/**
|
|
* Validate courses, but still return courses even if they fail validation.
|
|
*
|
|
* @covers \core_external\util::validate_courses
|
|
*/
|
|
public function test_validate_courses_keepfails(): void {
|
|
$this->resetAfterTest(true);
|
|
|
|
$c1 = $this->getDataGenerator()->create_course();
|
|
$c2 = $this->getDataGenerator()->create_course();
|
|
$c3 = $this->getDataGenerator()->create_course();
|
|
$u1 = $this->getDataGenerator()->create_user();
|
|
$this->getDataGenerator()->enrol_user($u1->id, $c1->id);
|
|
$courseids = [$c1->id, $c2->id, $c3->id];
|
|
|
|
$this->setUser($u1);
|
|
[$courses, $warnings] = util::validate_courses($courseids, [], false, true);
|
|
$this->assertCount(2, $warnings);
|
|
$this->assertEquals($c2->id, $warnings[0]['itemid']);
|
|
$this->assertEquals($c3->id, $warnings[1]['itemid']);
|
|
$this->assertCount(3, $courses);
|
|
$this->assertTrue($courses[$c1->id]->contextvalidated);
|
|
$this->assertFalse($courses[$c2->id]->contextvalidated);
|
|
$this->assertFalse($courses[$c3->id]->contextvalidated);
|
|
}
|
|
|
|
/**
|
|
* Validate courses can re-use an array of prefetched courses.
|
|
*
|
|
* @covers \core_external\util::validate_courses
|
|
*/
|
|
public function test_validate_courses_prefetch(): void {
|
|
$this->resetAfterTest(true);
|
|
|
|
$c1 = $this->getDataGenerator()->create_course();
|
|
$c2 = $this->getDataGenerator()->create_course();
|
|
$c3 = $this->getDataGenerator()->create_course();
|
|
$c4 = $this->getDataGenerator()->create_course();
|
|
$u1 = $this->getDataGenerator()->create_user();
|
|
$this->getDataGenerator()->enrol_user($u1->id, $c1->id);
|
|
$this->getDataGenerator()->enrol_user($u1->id, $c2->id);
|
|
|
|
$courseids = [$c1->id, $c2->id, $c3->id];
|
|
$courses = [$c2->id => $c2, $c3->id => $c3, $c4->id => $c4];
|
|
|
|
$this->setUser($u1);
|
|
[$courses, $warnings] = util::validate_courses($courseids, $courses);
|
|
$this->assertCount(2, $courses);
|
|
$this->assertCount(1, $warnings);
|
|
$this->assertArrayHasKey($c1->id, $courses);
|
|
$this->assertSame($c2, $courses[$c2->id]);
|
|
$this->assertArrayNotHasKey($c3->id, $courses);
|
|
// The extra course passed is not returned.
|
|
$this->assertArrayNotHasKey($c4->id, $courses);
|
|
}
|
|
|
|
/**
|
|
* Test the Validate courses standard functionality.
|
|
*
|
|
* @covers \core_external\util::validate_courses
|
|
*/
|
|
public function test_validate_courses(): void {
|
|
$this->resetAfterTest(true);
|
|
|
|
$c1 = $this->getDataGenerator()->create_course();
|
|
$c2 = $this->getDataGenerator()->create_course();
|
|
$c3 = $this->getDataGenerator()->create_course();
|
|
$u1 = $this->getDataGenerator()->create_user();
|
|
$this->getDataGenerator()->enrol_user($u1->id, $c1->id);
|
|
$courseids = [$c1->id, $c2->id, $c3->id];
|
|
|
|
$this->setAdminUser();
|
|
[$courses, $warnings] = util::validate_courses($courseids);
|
|
$this->assertEmpty($warnings);
|
|
$this->assertCount(3, $courses);
|
|
$this->assertArrayHasKey($c1->id, $courses);
|
|
$this->assertArrayHasKey($c2->id, $courses);
|
|
$this->assertArrayHasKey($c3->id, $courses);
|
|
$this->assertEquals($c1->id, $courses[$c1->id]->id);
|
|
$this->assertEquals($c2->id, $courses[$c2->id]->id);
|
|
$this->assertEquals($c3->id, $courses[$c3->id]->id);
|
|
|
|
$this->setUser($u1);
|
|
[$courses, $warnings] = util::validate_courses($courseids);
|
|
$this->assertCount(2, $warnings);
|
|
$this->assertEquals($c2->id, $warnings[0]['itemid']);
|
|
$this->assertEquals($c3->id, $warnings[1]['itemid']);
|
|
$this->assertCount(1, $courses);
|
|
$this->assertArrayHasKey($c1->id, $courses);
|
|
$this->assertArrayNotHasKey($c2->id, $courses);
|
|
$this->assertArrayNotHasKey($c3->id, $courses);
|
|
$this->assertEquals($c1->id, $courses[$c1->id]->id);
|
|
}
|
|
|
|
/**
|
|
* Text util::get_area_files
|
|
*
|
|
* @covers \core_external\util::get_area_files
|
|
*/
|
|
public function test_get_area_files(): void {
|
|
global $CFG, $DB;
|
|
|
|
$this->db = $DB;
|
|
$DB = $this->getMockBuilder('moodle_database')->getMock();
|
|
|
|
$content = base64_encode("Let us create a nice simple file.");
|
|
$timemodified = 102030405;
|
|
$itemid = 42;
|
|
$filesize = strlen($content);
|
|
|
|
$DB->method('get_records_sql')->willReturn([
|
|
(object) [
|
|
'filename' => 'example.txt',
|
|
'filepath' => '/',
|
|
'mimetype' => 'text/plain',
|
|
'filesize' => $filesize,
|
|
'timemodified' => $timemodified,
|
|
'itemid' => $itemid,
|
|
'pathnamehash' => sha1('/example.txt'),
|
|
],
|
|
]);
|
|
|
|
$component = 'mod_foo';
|
|
$filearea = 'area';
|
|
$context = 12345;
|
|
|
|
$expectedfiles = [[
|
|
'filename' => 'example.txt',
|
|
'filepath' => '/',
|
|
'fileurl' => "{$CFG->wwwroot}/webservice/pluginfile.php/{$context}/{$component}/{$filearea}/{$itemid}/example.txt",
|
|
'timemodified' => $timemodified,
|
|
'filesize' => $filesize,
|
|
'mimetype' => 'text/plain',
|
|
'isexternalfile' => false,
|
|
'icon' => 'f/text',
|
|
],
|
|
];
|
|
// Get all the files for the area.
|
|
$files = util::get_area_files($context, $component, $filearea, false);
|
|
$this->assertEquals($expectedfiles, $files);
|
|
|
|
$DB->method('get_in_or_equal')->willReturn([
|
|
'= :mock1',
|
|
['mock1' => $itemid],
|
|
]);
|
|
|
|
// Get just the file indicated by $itemid.
|
|
$files = util::get_area_files($context, $component, $filearea, $itemid);
|
|
$this->assertEquals($expectedfiles, $files);
|
|
}
|
|
|
|
/**
|
|
* Test default time for user created tokens.
|
|
*
|
|
* @covers \core_external\util::generate_token_for_current_user
|
|
*/
|
|
public function test_user_created_tokens_duration(): void {
|
|
global $CFG, $DB;
|
|
$this->resetAfterTest(true);
|
|
|
|
$CFG->enablewebservices = 1;
|
|
$CFG->enablemobilewebservice = 1;
|
|
$user1 = $this->getDataGenerator()->create_user();
|
|
$user2 = $this->getDataGenerator()->create_user();
|
|
$service = $DB->get_record('external_services', ['shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE, 'enabled' => 1]);
|
|
|
|
$this->setUser($user1);
|
|
$timenow = time();
|
|
$token = util::generate_token_for_current_user($service);
|
|
$this->assertGreaterThanOrEqual($timenow + $CFG->tokenduration, $token->validuntil);
|
|
|
|
// Change token default time.
|
|
$this->setUser($user2);
|
|
set_config('tokenduration', DAYSECS);
|
|
$token = util::generate_token_for_current_user($service);
|
|
$timenow = time();
|
|
$this->assertLessThanOrEqual($timenow + DAYSECS, $token->validuntil);
|
|
}
|
|
|
|
|
|
/**
|
|
* Test the format_text function.
|
|
*
|
|
* @covers \core_external\util::format_text
|
|
* @runInSeparateProcess
|
|
*/
|
|
public function test_format_text(): void {
|
|
$this->include_legacy_functions();
|
|
$settings = external_settings::get_instance();
|
|
|
|
$settings->set_raw(true);
|
|
$settings->set_filter(false);
|
|
$context = \context_system::instance();
|
|
|
|
$test = '$$ \pi $$';
|
|
$testformat = FORMAT_MARKDOWN;
|
|
$correct = [$test, $testformat];
|
|
$this->assertSame($correct, util::format_text($test, $testformat, $context, 'core', '', 0));
|
|
|
|
// Function external_format_text should work with context id or context instance.
|
|
$this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0), $correct);
|
|
$this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0), $correct);
|
|
|
|
$settings->set_raw(false);
|
|
$settings->set_filter(true);
|
|
|
|
$test = '$$ \pi $$';
|
|
$testformat = FORMAT_MARKDOWN;
|
|
$correct = ['<span class="filter_mathjaxloader_equation"><p><span class="nolink">$$ \pi $$</span></p>
|
|
</span>', FORMAT_HTML,
|
|
];
|
|
$this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0), $correct);
|
|
|
|
// Function external_format_text should work with context id or context instance.
|
|
$this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0), $correct);
|
|
$this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0), $correct);
|
|
|
|
// Filters can be opted out from by the developer.
|
|
$test = '$$ \pi $$';
|
|
$testformat = FORMAT_MARKDOWN;
|
|
$correct = ['<p>$$ \pi $$</p>
|
|
', FORMAT_HTML,
|
|
];
|
|
$this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, ['filter' => false]), $correct);
|
|
|
|
// Function external_format_text should work with context id or context instance.
|
|
$this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, ['filter' => false]), $correct);
|
|
$this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, ['filter' => false]), $correct);
|
|
|
|
$test = '<p><a id="test"></a><a href="#test">Text</a></p>';
|
|
$testformat = FORMAT_HTML;
|
|
$correct = [$test, FORMAT_HTML];
|
|
$options = ['allowid' => true];
|
|
$this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
|
|
// Function external_format_text should work with context id or context instance.
|
|
$this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, $options), $correct);
|
|
$this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
|
|
|
|
$test = '<p><a id="test"></a><a href="#test">Text</a></p>';
|
|
$testformat = FORMAT_HTML;
|
|
$correct = ['<p><a></a><a href="#test">Text</a></p>', FORMAT_HTML];
|
|
$options = new \stdClass();
|
|
$options->allowid = false;
|
|
$this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
|
|
|
|
// Function external_format_text should work with context id or context instance.
|
|
$this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, $options), $correct);
|
|
$this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
|
|
|
|
$test = '<p><a id="test"></a><a href="#test">Text</a></p>' . "\n" . 'Newline';
|
|
$testformat = FORMAT_MOODLE;
|
|
$correct = ['<p><a id="test"></a><a href="#test">Text</a></p> Newline', FORMAT_HTML];
|
|
$options = new \stdClass();
|
|
$options->newlines = false;
|
|
$this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
|
|
|
|
// Function external_format_text should work with context id or context instance.
|
|
$this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, $options), $correct);
|
|
$this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
|
|
|
|
$test = '<p><a id="test"></a><a href="#test">Text</a></p>';
|
|
$testformat = FORMAT_MOODLE;
|
|
$correct = ['<div class="text_to_html">' . $test . '</div>', FORMAT_HTML];
|
|
$options = new \stdClass();
|
|
$options->para = true;
|
|
$this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
|
|
|
|
// Function external_format_text should work with context id or context instance.
|
|
$this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, $options), $correct);
|
|
$this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
|
|
|
|
$test = '<p><a id="test"></a><a href="#test">Text</a></p>';
|
|
$testformat = FORMAT_MOODLE;
|
|
$correct = [$test, FORMAT_HTML];
|
|
$options = new \stdClass();
|
|
$options->context = $context;
|
|
$this->assertSame(util::format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
|
|
|
|
// Function external_format_text should work with context id or context instance.
|
|
$this->assertSame(external_format_text($test, $testformat, $context->id, 'core', '', 0, $options), $correct);
|
|
$this->assertSame(external_format_text($test, $testformat, $context, 'core', '', 0, $options), $correct);
|
|
}
|
|
/**
|
|
* Teset the format_string function.
|
|
*
|
|
* @covers \core_external\util::format_string
|
|
* @runInSeparateProcess
|
|
*/
|
|
public function test_external_format_string(): void {
|
|
$this->resetAfterTest();
|
|
$this->include_legacy_functions();
|
|
$settings = external_settings::get_instance();
|
|
|
|
// Enable multilang filter to on content and heading.
|
|
filter_set_global_state('multilang', TEXTFILTER_ON);
|
|
filter_set_applies_to_strings('multilang', 1);
|
|
$filtermanager = \filter_manager::instance();
|
|
$filtermanager->reset_caches();
|
|
|
|
$settings->set_raw(true);
|
|
$settings->set_filter(true);
|
|
$context = \context_system::instance();
|
|
|
|
$test = '<span lang="en" class="multilang">EN</span><span lang="fr" class="multilang">FR</span> ';
|
|
$test .= '<script>hi</script> <h3>there</h3>!';
|
|
$correct = $test;
|
|
$this->assertSame($correct, util::format_string($test, $context));
|
|
|
|
// Function external_format_string should work with context id or context instance.
|
|
$this->assertSame($correct, external_format_string($test, $context));
|
|
$this->assertSame($correct, external_format_string($test, $context->id));
|
|
|
|
$settings->set_raw(false);
|
|
$settings->set_filter(false);
|
|
|
|
$test = '<span lang="en" class="multilang">EN</span><span lang="fr" class="multilang">FR</span> ';
|
|
$test .= '<script>hi</script> <h3>there</h3>?';
|
|
$correct = 'ENFR hi there?';
|
|
$this->assertSame($correct, util::format_string($test, $context));
|
|
|
|
// Function external_format_string should work with context id or context instance.
|
|
$this->assertSame($correct, external_format_string($test, $context));
|
|
$this->assertSame($correct, external_format_string($test, $context->id));
|
|
|
|
$settings->set_filter(true);
|
|
|
|
$test = '<span lang="en" class="multilang">EN</span><span lang="fr" class="multilang">FR</span> ';
|
|
$test .= '<script>hi</script> <h3>there</h3>@';
|
|
$correct = 'EN hi there@';
|
|
$this->assertSame($correct, util::format_string($test, $context));
|
|
|
|
// Function external_format_string should work with context id or context instance.
|
|
$this->assertSame($correct, external_format_string($test, $context));
|
|
$this->assertSame($correct, external_format_string($test, $context->id));
|
|
|
|
// Filters can be opted out.
|
|
$test = '<span lang="en" class="multilang">EN</span><span lang="fr" class="multilang">FR</span> ';
|
|
$test .= '<script>hi</script> <h3>there</h3>%';
|
|
$correct = 'ENFR hi there%';
|
|
$this->assertSame($correct, util::format_string($test, $context, false, ['filter' => false]));
|
|
|
|
// Function external_format_string should work with context id or context instance.
|
|
$this->assertSame($correct, external_format_string($test, $context->id, false, ['filter' => false]));
|
|
$this->assertSame($correct, external_format_string($test, $context, false, ['filter' => false]));
|
|
|
|
$this->assertSame("& < > \" '", format_string("& < > \" '", true, ['escape' => false]));
|
|
}
|
|
}
|