mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 22:08:20 +01:00
1167 lines
43 KiB
PHP
1167 lines
43 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/>.
|
|
|
|
/**
|
|
* Testing the H5P API.
|
|
*
|
|
* @package core_h5p
|
|
* @category test
|
|
* @copyright 2020 Sara Arjona <sara@moodle.com>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
declare(strict_types = 1);
|
|
|
|
namespace core_h5p;
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
/**
|
|
* Test class covering the H5P API.
|
|
*
|
|
* @package core_h5p
|
|
* @copyright 2020 Sara Arjona <sara@moodle.com>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
* @coversDefaultClass \core_h5p\api
|
|
*/
|
|
class api_test extends \advanced_testcase {
|
|
|
|
/**
|
|
* Test the behaviour of delete_library().
|
|
*
|
|
* @dataProvider delete_library_provider
|
|
* @param string $libraryname Machine name of the library to delete.
|
|
* @param int $expectedh5p Total of H5P contents expected after deleting the library.
|
|
* @param int $expectedlibraries Total of H5P libraries expected after deleting the library.
|
|
* @param int $expectedcontents Total of H5P content_libraries expected after deleting the library.
|
|
* @param int $expecteddependencies Total of H5P library dependencies expected after deleting the library.
|
|
*/
|
|
public function test_delete_library(string $libraryname, int $expectedh5p, int $expectedlibraries,
|
|
int $expectedcontents, int $expecteddependencies): void {
|
|
global $DB;
|
|
|
|
$this->setRunTestInSeparateProcess(true);
|
|
$this->resetAfterTest();
|
|
|
|
// Generate h5p related data.
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
|
|
$generator->generate_h5p_data();
|
|
$generator->create_library_record('H5P.TestingLibrary', 'TestingLibrary', 1, 0);
|
|
|
|
// Check the current content in H5P tables is the expected.
|
|
$counth5p = $DB->count_records('h5p');
|
|
$counth5plibraries = $DB->count_records('h5p_libraries');
|
|
$counth5pcontents = $DB->count_records('h5p_contents_libraries');
|
|
$counth5pdependencies = $DB->count_records('h5p_library_dependencies');
|
|
|
|
$this->assertSame(1, $counth5p);
|
|
$this->assertSame(7, $counth5plibraries);
|
|
$this->assertSame(5, $counth5pcontents);
|
|
$this->assertSame(7, $counth5pdependencies);
|
|
|
|
// Delete this library.
|
|
$factory = new factory();
|
|
$library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname]);
|
|
if ($library) {
|
|
api::delete_library($factory, $library);
|
|
}
|
|
|
|
// Check the expected libraries and content have been removed.
|
|
$counth5p = $DB->count_records('h5p');
|
|
$counth5plibraries = $DB->count_records('h5p_libraries');
|
|
$counth5pcontents = $DB->count_records('h5p_contents_libraries');
|
|
$counth5pdependencies = $DB->count_records('h5p_library_dependencies');
|
|
|
|
$this->assertSame($expectedh5p, $counth5p);
|
|
$this->assertSame($expectedlibraries, $counth5plibraries);
|
|
$this->assertSame($expectedcontents, $counth5pcontents);
|
|
$this->assertSame($expecteddependencies, $counth5pdependencies);
|
|
}
|
|
|
|
/**
|
|
* Data provider for test_delete_library().
|
|
*
|
|
* @return array
|
|
*/
|
|
public function delete_library_provider(): array {
|
|
return [
|
|
'Delete MainLibrary' => [
|
|
'MainLibrary',
|
|
0,
|
|
6,
|
|
0,
|
|
4,
|
|
],
|
|
'Delete Library1' => [
|
|
'Library1',
|
|
0,
|
|
5,
|
|
0,
|
|
1,
|
|
],
|
|
'Delete Library2' => [
|
|
'Library2',
|
|
0,
|
|
4,
|
|
0,
|
|
1,
|
|
],
|
|
'Delete Library3' => [
|
|
'Library3',
|
|
0,
|
|
4,
|
|
0,
|
|
0,
|
|
],
|
|
'Delete Library4' => [
|
|
'Library4',
|
|
0,
|
|
4,
|
|
0,
|
|
1,
|
|
],
|
|
'Delete Library5' => [
|
|
'Library5',
|
|
0,
|
|
3,
|
|
0,
|
|
0,
|
|
],
|
|
'Delete a library without dependencies' => [
|
|
'H5P.TestingLibrary',
|
|
1,
|
|
6,
|
|
5,
|
|
7,
|
|
],
|
|
'Delete unexisting library' => [
|
|
'LibraryX',
|
|
1,
|
|
7,
|
|
5,
|
|
7,
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of get_dependent_libraries().
|
|
*
|
|
* @dataProvider get_dependent_libraries_provider
|
|
* @param string $libraryname Machine name of the library to delete.
|
|
* @param int $expectedvalue Total of H5P required libraries expected.
|
|
*/
|
|
public function test_get_dependent_libraries(string $libraryname, int $expectedvalue): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Generate h5p related data.
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
|
|
$generator->generate_h5p_data();
|
|
$generator->create_library_record('H5P.TestingLibrary', 'TestingLibrary', 1, 0);
|
|
|
|
// Get required libraries.
|
|
$library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname], 'id');
|
|
if ($library) {
|
|
$libraries = api::get_dependent_libraries((int)$library->id);
|
|
} else {
|
|
$libraries = [];
|
|
}
|
|
|
|
$this->assertCount($expectedvalue, $libraries);
|
|
}
|
|
|
|
/**
|
|
* Data provider for test_get_dependent_libraries().
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_dependent_libraries_provider(): array {
|
|
return [
|
|
'Main library of a content' => [
|
|
'MainLibrary',
|
|
0,
|
|
],
|
|
'Library1' => [
|
|
'Library1',
|
|
1,
|
|
],
|
|
'Library2' => [
|
|
'Library2',
|
|
2,
|
|
],
|
|
'Library without dependencies' => [
|
|
'H5P.TestingLibrary',
|
|
0,
|
|
],
|
|
'Unexisting library' => [
|
|
'LibraryX',
|
|
0,
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of get_library().
|
|
*
|
|
* @dataProvider get_library_provider
|
|
* @param string $libraryname Machine name of the library to delete.
|
|
* @param bool $emptyexpected Wether the expected result is empty or not.
|
|
*/
|
|
public function test_get_library(string $libraryname, bool $emptyexpected): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Generate h5p related data.
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
|
|
$generator->generate_h5p_data();
|
|
$generator->create_library_record('H5P.TestingLibrary', 'TestingLibrary', 1, 0);
|
|
|
|
// Get the library identifier.
|
|
$library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname], 'id');
|
|
if ($library) {
|
|
$result = api::get_library((int)$library->id);
|
|
} else {
|
|
$result = null;
|
|
}
|
|
|
|
if ($emptyexpected) {
|
|
$this->assertEmpty($result);
|
|
} else {
|
|
$this->assertEquals($library->id, $result->id);
|
|
$this->assertEquals($libraryname, $result->machinename);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Data provider for test_get_library().
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_library_provider(): array {
|
|
return [
|
|
'Main library of a content' => [
|
|
'MainLibrary',
|
|
false,
|
|
],
|
|
'Library1' => [
|
|
'Library1',
|
|
false,
|
|
],
|
|
'Library without dependencies' => [
|
|
'H5P.TestingLibrary',
|
|
false,
|
|
],
|
|
'Unexisting library' => [
|
|
'LibraryX',
|
|
true,
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of get_content_from_pluginfile_url().
|
|
*/
|
|
public function test_get_content_from_pluginfile_url(): void {
|
|
$this->setRunTestInSeparateProcess(true);
|
|
$this->resetAfterTest();
|
|
$factory = new factory();
|
|
|
|
// Create the H5P data.
|
|
$filename = 'find-the-words.h5p';
|
|
$path = __DIR__ . '/fixtures/' . $filename;
|
|
$fakefile = helper::create_fake_stored_file_from_path($path);
|
|
$config = (object)[
|
|
'frame' => 1,
|
|
'export' => 1,
|
|
'embed' => 0,
|
|
'copyright' => 0,
|
|
];
|
|
|
|
// Get URL for this H5P content file.
|
|
$syscontext = \context_system::instance();
|
|
$url = \moodle_url::make_pluginfile_url(
|
|
$syscontext->id,
|
|
\core_h5p\file_storage::COMPONENT,
|
|
'unittest',
|
|
$fakefile->get_itemid(),
|
|
'/',
|
|
$filename
|
|
);
|
|
|
|
// Scenario 1: Get the H5P for this URL and check there isn't any existing H5P (because it hasn't been saved).
|
|
list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out());
|
|
$this->assertEquals($fakefile->get_pathnamehash(), $newfile->get_pathnamehash());
|
|
$this->assertEquals($fakefile->get_contenthash(), $newfile->get_contenthash());
|
|
$this->assertFalse($h5p);
|
|
|
|
// Scenario 2: Save the H5P and check now the H5P is exactly the same as the original one.
|
|
$h5pid = helper::save_h5p($factory, $fakefile, $config);
|
|
list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out());
|
|
|
|
$this->assertEquals($h5pid, $h5p->id);
|
|
$this->assertEquals($fakefile->get_pathnamehash(), $h5p->pathnamehash);
|
|
$this->assertEquals($fakefile->get_contenthash(), $h5p->contenthash);
|
|
|
|
// Scenario 3: Get the H5P for an unexisting H5P file.
|
|
$url = \moodle_url::make_pluginfile_url(
|
|
$syscontext->id,
|
|
\core_h5p\file_storage::COMPONENT,
|
|
'unittest',
|
|
$fakefile->get_itemid(),
|
|
'/',
|
|
'unexisting.h5p'
|
|
);
|
|
list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out());
|
|
$this->assertFalse($newfile);
|
|
$this->assertFalse($h5p);
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of get_original_content_from_pluginfile_url().
|
|
*
|
|
* @covers ::get_original_content_from_pluginfile_url
|
|
*/
|
|
public function test_get_original_content_from_pluginfile_url(): void {
|
|
$this->setRunTestInSeparateProcess(true);
|
|
$this->resetAfterTest();
|
|
$this->setAdminUser();
|
|
|
|
$factory = new factory();
|
|
$syscontext = \context_system::instance();
|
|
|
|
// Create the original file.
|
|
$filename = 'greeting-card-887.h5p';
|
|
$path = __DIR__ . '/fixtures/' . $filename;
|
|
$originalfile = helper::create_fake_stored_file_from_path($path);
|
|
$originalfilerecord = [
|
|
'contextid' => $originalfile->get_contextid(),
|
|
'component' => $originalfile->get_component(),
|
|
'filearea' => $originalfile->get_filearea(),
|
|
'itemid' => $originalfile->get_itemid(),
|
|
'filepath' => $originalfile->get_filepath(),
|
|
'filename' => $originalfile->get_filename(),
|
|
];
|
|
|
|
$config = (object)[
|
|
'frame' => 1,
|
|
'export' => 1,
|
|
'embed' => 0,
|
|
'copyright' => 0,
|
|
];
|
|
|
|
$originalurl = \moodle_url::make_pluginfile_url(
|
|
$originalfile->get_contextid(),
|
|
$originalfile->get_component(),
|
|
$originalfile->get_filearea(),
|
|
$originalfile->get_itemid(),
|
|
$originalfile->get_filepath(),
|
|
$originalfile->get_filename()
|
|
);
|
|
|
|
// Create a reference to the original file.
|
|
$reffilerecord = [
|
|
'contextid' => $syscontext->id,
|
|
'component' => 'core',
|
|
'filearea' => 'phpunit',
|
|
'itemid' => 0,
|
|
'filepath' => '/',
|
|
'filename' => $filename
|
|
];
|
|
|
|
$fs = get_file_storage();
|
|
$ref = $fs->pack_reference($originalfilerecord);
|
|
$repos = \repository::get_instances(['type' => 'user']);
|
|
$userrepository = reset($repos);
|
|
$referencedfile = $fs->create_file_from_reference($reffilerecord, $userrepository->id, $ref);
|
|
$this->assertEquals($referencedfile->get_contenthash(), $originalfile->get_contenthash());
|
|
|
|
$referencedurl = \moodle_url::make_pluginfile_url(
|
|
$syscontext->id,
|
|
'core',
|
|
'phpunit',
|
|
0,
|
|
'/',
|
|
$filename
|
|
);
|
|
|
|
// Scenario 1: Original file (without any reference).
|
|
$originalh5pid = helper::save_h5p($factory, $originalfile, $config);
|
|
list($source, $h5p, $file) = api::get_original_content_from_pluginfile_url($originalurl->out());
|
|
$this->assertEquals($originalfile->get_pathnamehash(), $source->get_pathnamehash());
|
|
$this->assertEquals($originalfile->get_contenthash(), $source->get_contenthash());
|
|
$this->assertEquals($originalh5pid, $h5p->id);
|
|
$this->assertFalse($file);
|
|
|
|
// Scenario 2: Referenced file (alias to originalfile).
|
|
list($source, $h5p, $file) = api::get_original_content_from_pluginfile_url($referencedurl->out());
|
|
$this->assertEquals($originalfile->get_pathnamehash(), $source->get_pathnamehash());
|
|
$this->assertEquals($originalfile->get_contenthash(), $source->get_contenthash());
|
|
$this->assertEquals($originalfile->get_contenthash(), $source->get_contenthash());
|
|
$this->assertEquals($originalh5pid, $h5p->id);
|
|
$this->assertEquals($referencedfile->get_pathnamehash(), $file->get_pathnamehash());
|
|
$this->assertEquals($referencedfile->get_contenthash(), $file->get_contenthash());
|
|
$this->assertEquals($referencedfile->get_contenthash(), $file->get_contenthash());
|
|
|
|
// Scenario 3: Unexisting file.
|
|
$unexistingurl = \moodle_url::make_pluginfile_url(
|
|
$syscontext->id,
|
|
'core',
|
|
'phpunit',
|
|
0,
|
|
'/',
|
|
'unexisting.h5p'
|
|
);
|
|
list($source, $h5p, $file) = api::get_original_content_from_pluginfile_url($unexistingurl->out());
|
|
$this->assertFalse($source);
|
|
$this->assertFalse($h5p);
|
|
$this->assertFalse($file);
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of can_edit_content().
|
|
*
|
|
* @covers ::can_edit_content
|
|
* @dataProvider can_edit_content_provider
|
|
*
|
|
* @param string $currentuser User who will call the method.
|
|
* @param string $fileauthor Author of the file to check.
|
|
* @param string $filecomponent Component of the file to check.
|
|
* @param bool $expected Expected result after calling the can_edit_content method.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function test_can_edit_content(string $currentuser, string $fileauthor, string $filecomponent, bool $expected): void {
|
|
global $USER;
|
|
|
|
$this->setRunTestInSeparateProcess(true);
|
|
$this->resetAfterTest();
|
|
|
|
// Create course.
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$context = \context_course::instance($course->id);
|
|
|
|
// Create some users.
|
|
$this->setAdminUser();
|
|
$teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
|
|
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
|
|
$users = [
|
|
'admin' => $USER,
|
|
'teacher' => $teacher,
|
|
'student' => $student,
|
|
];
|
|
|
|
// Set current user.
|
|
if ($currentuser !== 'admin') {
|
|
$this->setUser($users[$currentuser]);
|
|
}
|
|
|
|
// Create the file.
|
|
$filename = 'greeting-card-887.h5p';
|
|
$path = __DIR__ . '/fixtures/' . $filename;
|
|
if ($filecomponent === 'contentbank') {
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank');
|
|
$contents = $generator->generate_contentbank_data(
|
|
'contenttype_h5p',
|
|
1,
|
|
(int)$users[$fileauthor]->id,
|
|
$context,
|
|
true,
|
|
$path
|
|
);
|
|
$content = array_shift($contents);
|
|
$file = $content->get_file();
|
|
} else {
|
|
$filerecord = [
|
|
'contextid' => $context->id,
|
|
'component' => $filecomponent,
|
|
'filearea' => 'unittest',
|
|
'itemid' => rand(),
|
|
'filepath' => '/',
|
|
'filename' => basename($path),
|
|
'userid' => $users[$fileauthor]->id,
|
|
];
|
|
$fs = get_file_storage();
|
|
$file = $fs->create_file_from_pathname($filerecord, $path);
|
|
}
|
|
|
|
// Check if the currentuser can edit the file.
|
|
$result = api::can_edit_content($file);
|
|
$this->assertEquals($expected, $result);
|
|
}
|
|
|
|
/**
|
|
* Data provider for test_can_edit_content().
|
|
*
|
|
* @return array
|
|
*/
|
|
public function can_edit_content_provider(): array {
|
|
return [
|
|
// Component = user.
|
|
'user: Admin user is author' => [
|
|
'currentuser' => 'admin',
|
|
'fileauthor' => 'admin',
|
|
'filecomponent' => 'user',
|
|
'expected' => true,
|
|
],
|
|
'user: Admin user, teacher is author' => [
|
|
'currentuser' => 'admin',
|
|
'fileauthor' => 'teacher',
|
|
'filecomponent' => 'user',
|
|
'expected' => false,
|
|
],
|
|
'user: Teacher user, teacher is author' => [
|
|
'currentuser' => 'teacher',
|
|
'fileauthor' => 'teacher',
|
|
'filecomponent' => 'user',
|
|
'expected' => true,
|
|
],
|
|
'user: Teacher user, admin is author' => [
|
|
'currentuser' => 'teacher',
|
|
'fileauthor' => 'admin',
|
|
'filecomponent' => 'user',
|
|
'expected' => false,
|
|
],
|
|
'user: Student user, student is author' => [
|
|
'currentuser' => 'student',
|
|
'fileauthor' => 'student',
|
|
'filecomponent' => 'user',
|
|
'expected' => true,
|
|
],
|
|
'user: Student user, teacher is author' => [
|
|
'currentuser' => 'student',
|
|
'fileauthor' => 'teacher',
|
|
'filecomponent' => 'user',
|
|
'expected' => false,
|
|
],
|
|
|
|
// Component = mod_h5pactivity.
|
|
'mod_h5pactivity: Admin user is author' => [
|
|
'currentuser' => 'admin',
|
|
'fileauthor' => 'admin',
|
|
'filecomponent' => 'mod_h5pactivity',
|
|
'expected' => true,
|
|
],
|
|
'mod_h5pactivity: Admin user, teacher is author' => [
|
|
'currentuser' => 'admin',
|
|
'fileauthor' => 'teacher',
|
|
'filecomponent' => 'mod_h5pactivity',
|
|
'expected' => true,
|
|
],
|
|
'mod_h5pactivity: Teacher user, teacher is author' => [
|
|
'currentuser' => 'teacher',
|
|
'fileauthor' => 'teacher',
|
|
'filecomponent' => 'mod_h5pactivity',
|
|
'expected' => true,
|
|
],
|
|
'mod_h5pactivity: Teacher user, admin is author' => [
|
|
'currentuser' => 'teacher',
|
|
'fileauthor' => 'admin',
|
|
'filecomponent' => 'mod_h5pactivity',
|
|
'expected' => true,
|
|
],
|
|
'mod_h5pactivity: Student user, student is author' => [
|
|
'currentuser' => 'student',
|
|
'fileauthor' => 'student',
|
|
'filecomponent' => 'mod_h5pactivity',
|
|
'expected' => false,
|
|
],
|
|
'mod_h5pactivity: Student user, teacher is author' => [
|
|
'currentuser' => 'student',
|
|
'fileauthor' => 'teacher',
|
|
'filecomponent' => 'mod_h5pactivity',
|
|
'expected' => false,
|
|
],
|
|
|
|
// Component = mod_forum.
|
|
'mod_forum: Admin user is author' => [
|
|
'currentuser' => 'admin',
|
|
'fileauthor' => 'admin',
|
|
'filecomponent' => 'mod_forum',
|
|
'expected' => false,
|
|
],
|
|
'mod_forum: Admin user, teacher is author' => [
|
|
'currentuser' => 'admin',
|
|
'fileauthor' => 'teacher',
|
|
'filecomponent' => 'mod_forum',
|
|
'expected' => false,
|
|
],
|
|
|
|
// Component = contentbank.
|
|
'contentbank: Admin user is author' => [
|
|
'currentuser' => 'admin',
|
|
'fileauthor' => 'admin',
|
|
'filecomponent' => 'contentbank',
|
|
'expected' => true,
|
|
],
|
|
'contentbank: Admin user, teacher is author' => [
|
|
'currentuser' => 'admin',
|
|
'fileauthor' => 'teacher',
|
|
'filecomponent' => 'contentbank',
|
|
'expected' => true,
|
|
],
|
|
'contentbank: Teacher user, teacher is author' => [
|
|
'currentuser' => 'teacher',
|
|
'fileauthor' => 'teacher',
|
|
'filecomponent' => 'contentbank',
|
|
'expected' => true,
|
|
],
|
|
'contentbank: Teacher user, admin is author' => [
|
|
'currentuser' => 'teacher',
|
|
'fileauthor' => 'admin',
|
|
'filecomponent' => 'contentbank',
|
|
'expected' => false,
|
|
],
|
|
'contentbank: Student user, student is author' => [
|
|
'currentuser' => 'student',
|
|
'fileauthor' => 'student',
|
|
'filecomponent' => 'contentbank',
|
|
'expected' => false,
|
|
],
|
|
'contentbank: Student user, teacher is author' => [
|
|
'currentuser' => 'student',
|
|
'fileauthor' => 'teacher',
|
|
'filecomponent' => 'contentbank',
|
|
'expected' => false,
|
|
],
|
|
|
|
// Unexisting components.
|
|
'Unexisting component' => [
|
|
'currentuser' => 'admin',
|
|
'fileauthor' => 'admin',
|
|
'filecomponent' => 'unexisting_component',
|
|
'expected' => false,
|
|
],
|
|
'Unexisting module activity' => [
|
|
'currentuser' => 'admin',
|
|
'fileauthor' => 'admin',
|
|
'filecomponent' => 'mod_unexisting',
|
|
'expected' => false,
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of create_content_from_pluginfile_url().
|
|
*/
|
|
public function test_create_content_from_pluginfile_url(): void {
|
|
global $DB;
|
|
|
|
$this->setRunTestInSeparateProcess(true);
|
|
$this->resetAfterTest();
|
|
$factory = new factory();
|
|
|
|
// Create the H5P data.
|
|
$filename = 'find-the-words.h5p';
|
|
$path = __DIR__ . '/fixtures/' . $filename;
|
|
$fakefile = helper::create_fake_stored_file_from_path($path);
|
|
$config = (object)[
|
|
'frame' => 1,
|
|
'export' => 1,
|
|
'embed' => 0,
|
|
'copyright' => 0,
|
|
];
|
|
|
|
// Get URL for this H5P content file.
|
|
$syscontext = \context_system::instance();
|
|
$url = \moodle_url::make_pluginfile_url(
|
|
$syscontext->id,
|
|
\core_h5p\file_storage::COMPONENT,
|
|
'unittest',
|
|
$fakefile->get_itemid(),
|
|
'/',
|
|
$filename
|
|
);
|
|
|
|
// Scenario 1: Create the H5P from this URL and check the content is exactly the same as the fake file.
|
|
$messages = new \stdClass();
|
|
list($newfile, $h5pid) = api::create_content_from_pluginfile_url($url->out(), $config, $factory, $messages);
|
|
$this->assertNotFalse($h5pid);
|
|
$h5p = $DB->get_record('h5p', ['id' => $h5pid]);
|
|
$this->assertEquals($fakefile->get_pathnamehash(), $h5p->pathnamehash);
|
|
$this->assertEquals($fakefile->get_contenthash(), $h5p->contenthash);
|
|
$this->assertTrue(empty($messages->error));
|
|
$this->assertTrue(empty($messages->info));
|
|
|
|
// Scenario 2: Create the H5P for an unexisting H5P file.
|
|
$url = \moodle_url::make_pluginfile_url(
|
|
$syscontext->id,
|
|
\core_h5p\file_storage::COMPONENT,
|
|
'unittest',
|
|
$fakefile->get_itemid(),
|
|
'/',
|
|
'unexisting.h5p'
|
|
);
|
|
list($newfile, $h5p) = api::create_content_from_pluginfile_url($url->out(), $config, $factory, $messages);
|
|
$this->assertFalse($newfile);
|
|
$this->assertFalse($h5p);
|
|
$this->assertTrue(empty($messages->error));
|
|
$this->assertTrue(empty($messages->info));
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of delete_content_from_pluginfile_url().
|
|
*/
|
|
public function test_delete_content_from_pluginfile_url(): void {
|
|
global $DB;
|
|
|
|
$this->setRunTestInSeparateProcess(true);
|
|
$this->resetAfterTest();
|
|
$factory = new factory();
|
|
|
|
// Create the H5P data.
|
|
$filename = 'find-the-words.h5p';
|
|
$path = __DIR__ . '/fixtures/' . $filename;
|
|
$fakefile = helper::create_fake_stored_file_from_path($path);
|
|
$config = (object)[
|
|
'frame' => 1,
|
|
'export' => 1,
|
|
'embed' => 0,
|
|
'copyright' => 0,
|
|
];
|
|
|
|
// Get URL for this H5P content file.
|
|
$syscontext = \context_system::instance();
|
|
$url = \moodle_url::make_pluginfile_url(
|
|
$syscontext->id,
|
|
\core_h5p\file_storage::COMPONENT,
|
|
'unittest',
|
|
$fakefile->get_itemid(),
|
|
'/',
|
|
$filename
|
|
);
|
|
|
|
// Scenario 1: Try to remove the H5P content for an undeployed file.
|
|
list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out());
|
|
$this->assertEquals(0, $DB->count_records('h5p'));
|
|
api::delete_content_from_pluginfile_url($url->out(), $factory);
|
|
$this->assertEquals(0, $DB->count_records('h5p'));
|
|
|
|
// Scenario 2: Deploy an H5P from this URL, check it's created, remove it and check it has been removed as expected.
|
|
$this->assertEquals(0, $DB->count_records('h5p'));
|
|
|
|
$messages = new \stdClass();
|
|
list($newfile, $h5pid) = api::create_content_from_pluginfile_url($url->out(), $config, $factory, $messages);
|
|
$this->assertEquals(1, $DB->count_records('h5p'));
|
|
|
|
api::delete_content_from_pluginfile_url($url->out(), $factory);
|
|
$this->assertEquals(0, $DB->count_records('h5p'));
|
|
|
|
// Scenario 3: Try to remove the H5P for an unexisting H5P URL.
|
|
$url = \moodle_url::make_pluginfile_url(
|
|
$syscontext->id,
|
|
\core_h5p\file_storage::COMPONENT,
|
|
'unittest',
|
|
$fakefile->get_itemid(),
|
|
'/',
|
|
'unexisting.h5p'
|
|
);
|
|
$this->assertEquals(0, $DB->count_records('h5p'));
|
|
api::delete_content_from_pluginfile_url($url->out(), $factory);
|
|
$this->assertEquals(0, $DB->count_records('h5p'));
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of get_export_info_from_context_id().
|
|
*/
|
|
public function test_get_export_info_from_context_id(): void {
|
|
global $DB;
|
|
|
|
$this->setRunTestInSeparateProcess(true);
|
|
$this->resetAfterTest();
|
|
$factory = new factory();
|
|
|
|
// Create the H5P data.
|
|
$filename = 'find-the-words.h5p';
|
|
$syscontext = \context_system::instance();
|
|
|
|
// Test scenario 1: H5P exists and deployed.
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
|
|
$fakeexportfile = $generator->create_export_file($filename,
|
|
$syscontext->id,
|
|
\core_h5p\file_storage::COMPONENT,
|
|
\core_h5p\file_storage::EXPORT_FILEAREA);
|
|
|
|
$exportfile = api::get_export_info_from_context_id($syscontext->id,
|
|
$factory,
|
|
\core_h5p\file_storage::COMPONENT,
|
|
\core_h5p\file_storage::EXPORT_FILEAREA);
|
|
$this->assertEquals($fakeexportfile['filename'], $exportfile['filename']);
|
|
$this->assertEquals($fakeexportfile['filepath'], $exportfile['filepath']);
|
|
$this->assertEquals($fakeexportfile['filesize'], $exportfile['filesize']);
|
|
$this->assertEquals($fakeexportfile['timemodified'], $exportfile['timemodified']);
|
|
$this->assertEquals($fakeexportfile['fileurl'], $exportfile['fileurl']);
|
|
|
|
// Test scenario 2: H5P exist, deployed but the content has changed.
|
|
// We need to change the contenthash to simulate the H5P file was changed.
|
|
$h5pfile = $DB->get_record('h5p', []);
|
|
$h5pfile->contenthash = sha1('testedit');
|
|
$DB->update_record('h5p', $h5pfile);
|
|
$exportfile = api::get_export_info_from_context_id($syscontext->id,
|
|
$factory,
|
|
\core_h5p\file_storage::COMPONENT,
|
|
\core_h5p\file_storage::EXPORT_FILEAREA);
|
|
$this->assertNull($exportfile);
|
|
|
|
// Tests scenario 3: H5P is not deployed.
|
|
// We need to delete the H5P record to simulate the H5P was not deployed.
|
|
$DB->delete_records('h5p', ['id' => $h5pfile->id]);
|
|
$exportfile = api::get_export_info_from_context_id($syscontext->id,
|
|
$factory,
|
|
\core_h5p\file_storage::COMPONENT,
|
|
\core_h5p\file_storage::EXPORT_FILEAREA);
|
|
$this->assertNull($exportfile);
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of set_library_enabled().
|
|
*
|
|
* @covers ::set_library_enabled
|
|
* @dataProvider set_library_enabled_provider
|
|
*
|
|
* @param string $libraryname Library name to enable/disable.
|
|
* @param string $action Action to be done with the library. Supported values: enable, disable.
|
|
* @param int $expected Expected value for the enabled library field. -1 will be passed if the library doesn't exist.
|
|
*/
|
|
public function test_set_library_enabled(string $libraryname, string $action, int $expected): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Create libraries.
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
|
|
$generator->generate_h5p_data();
|
|
|
|
// Check by default the library is enabled.
|
|
$library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname]);
|
|
if ($expected >= 0) {
|
|
$this->assertEquals(1, $library->enabled);
|
|
$libraryid = (int) $library->id;
|
|
} else {
|
|
// Unexisting library. Set libraryid to some unexisting id.
|
|
$libraryid = -1;
|
|
$this->expectException('dml_missing_record_exception');
|
|
}
|
|
|
|
\core_h5p\api::set_library_enabled($libraryid, ($action == 'enable'));
|
|
|
|
// Check the value of the "enabled" field after calling enable/disable method.
|
|
$libraries = $DB->get_records('h5p_libraries');
|
|
foreach ($libraries as $libraryid => $library) {
|
|
if ($library->machinename == $libraryname) {
|
|
$this->assertEquals($expected, $library->enabled);
|
|
} else {
|
|
// Check that only $libraryname has been enabled/disabled.
|
|
$this->assertEquals(1, $library->enabled);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Data provider for test_set_library_enabled().
|
|
*
|
|
* @return array
|
|
*/
|
|
public function set_library_enabled_provider(): array {
|
|
return [
|
|
'Disable existing library' => [
|
|
'libraryname' => 'MainLibrary',
|
|
'action' => 'disable',
|
|
'expected' => 0,
|
|
],
|
|
'Enable existing library' => [
|
|
'libraryname' => 'MainLibrary',
|
|
'action' => 'enable',
|
|
'expected' => 1,
|
|
],
|
|
'Disable existing library (not main)' => [
|
|
'libraryname' => 'Library1',
|
|
'action' => 'disable',
|
|
'expected' => 0,
|
|
],
|
|
'Enable existing library (not main)' => [
|
|
'libraryname' => 'Library1',
|
|
'action' => 'enable',
|
|
'expected' => 1,
|
|
],
|
|
'Disable existing library (not runnable)' => [
|
|
'libraryname' => 'Library3',
|
|
'action' => 'disable',
|
|
'expected' => 1, // Not runnable libraries can't be disabled.
|
|
],
|
|
'Enable existing library (not runnable)' => [
|
|
'libraryname' => 'Library3',
|
|
'action' => 'enable',
|
|
'expected' => 1,
|
|
],
|
|
'Enable unexisting library' => [
|
|
'libraryname' => 'Unexisting library',
|
|
'action' => 'enable',
|
|
'expected' => -1,
|
|
],
|
|
'Disable unexisting library' => [
|
|
'libraryname' => 'Unexisting library',
|
|
'action' => 'disable',
|
|
'expected' => -1,
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of is_library_enabled().
|
|
*
|
|
* @covers ::is_library_enabled
|
|
* @dataProvider is_library_enabled_provider
|
|
*
|
|
* @param string $libraryname Library name to check.
|
|
* @param bool $expected Expected result after calling the method.
|
|
* @param bool $exception Exception expected or not.
|
|
* @param bool $useid Whether to use id for calling is_library_enabled method.
|
|
* @param bool $uselibraryname Whether to use libraryname for calling is_library_enabled method.
|
|
*/
|
|
public function test_is_library_enabled(string $libraryname, bool $expected, bool $exception = false,
|
|
bool $useid = false, bool $uselibraryname = true): void {
|
|
global $DB;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Create the following libraries:
|
|
// - H5P.Lib1: 1 version enabled, 1 version disabled.
|
|
// - H5P.Lib2: 2 versions enabled.
|
|
// - H5P.Lib3: 2 versions disabled.
|
|
// - H5P.Lib4: 1 version disabled.
|
|
// - H5P.Lib5: 1 version enabled.
|
|
$generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
|
|
$libraries = [
|
|
'H5P.Lib1.1' => $generator->create_library_record('H5P.Lib1', 'Lib1', 1, 1, 0, '', null, null, null, false),
|
|
'H5P.Lib1.2' => $generator->create_library_record('H5P.Lib1', 'Lib1', 1, 2),
|
|
'H5P.Lib2.1' => $generator->create_library_record('H5P.Lib2', 'Lib2', 2, 1),
|
|
'H5P.Lib2.2' => $generator->create_library_record('H5P.Lib2', 'Lib2', 2, 2),
|
|
'H5P.Lib3.1' => $generator->create_library_record('H5P.Lib3', 'Lib3', 3, 1, 0, '', null, null, null, false),
|
|
'H5P.Lib3.2' => $generator->create_library_record('H5P.Lib3', 'Lib3', 3, 2, 0, '', null, null, null, false),
|
|
'H5P.Lib4.1' => $generator->create_library_record('H5P.Lib4', 'Lib4', 4, 1, 0, '', null, null, null, false),
|
|
'H5P.Lib5.1' => $generator->create_library_record('H5P.Lib5', 'Lib5', 5, 1),
|
|
];
|
|
|
|
$countenabledlibraries = $DB->count_records('h5p_libraries', ['enabled' => 1]);
|
|
$this->assertEquals(4, $countenabledlibraries);
|
|
|
|
if ($useid) {
|
|
$librarydata = ['id' => $libraries[$libraryname]->id];
|
|
} else if ($uselibraryname) {
|
|
$librarydata = ['machinename' => $libraryname];
|
|
} else {
|
|
$librarydata = ['invalid' => true];
|
|
}
|
|
|
|
if ($exception) {
|
|
$this->expectException(\moodle_exception::class);
|
|
}
|
|
|
|
$result = api::is_library_enabled((object) $librarydata);
|
|
$this->assertEquals($expected, $result);
|
|
}
|
|
|
|
/**
|
|
* Data provider for test_is_library_enabled().
|
|
*
|
|
* @return array
|
|
*/
|
|
public function is_library_enabled_provider(): array {
|
|
return [
|
|
'Library with 2 versions, one of them disabled' => [
|
|
'libraryname' => 'H5P.Lib1',
|
|
'expected' => false,
|
|
],
|
|
'Library with 2 versions, all enabled' => [
|
|
'libraryname' => 'H5P.Lib2',
|
|
'expected' => true,
|
|
],
|
|
'Library with 2 versions, all disabled' => [
|
|
'libraryname' => 'H5P.Lib3',
|
|
'expected' => false,
|
|
],
|
|
'Library with only one version, disabled' => [
|
|
'libraryname' => 'H5P.Lib4',
|
|
'expected' => false,
|
|
],
|
|
'Library with only one version, enabled' => [
|
|
'libraryname' => 'H5P.Lib5',
|
|
'expected' => true,
|
|
],
|
|
'Library with 2 versions, one of them disabled (using id) - 1.1 (disabled)' => [
|
|
'libraryname' => 'H5P.Lib1.1',
|
|
'expected' => false,
|
|
'exception' => false,
|
|
'useid' => true,
|
|
],
|
|
'Library with 2 versions, one of them disabled (using id) - 1.2 (enabled)' => [
|
|
'libraryname' => 'H5P.Lib1.2',
|
|
'expected' => true,
|
|
'exception' => false,
|
|
'useid' => true,
|
|
],
|
|
'Library with 2 versions, all enabled (using id) - 2.1' => [
|
|
'libraryname' => 'H5P.Lib2.1',
|
|
'expected' => true,
|
|
'exception' => false,
|
|
'useid' => true,
|
|
],
|
|
'Library with 2 versions, all enabled (using id) - 2.2' => [
|
|
'libraryname' => 'H5P.Lib2.2',
|
|
'expected' => true,
|
|
'exception' => false,
|
|
'useid' => true,
|
|
],
|
|
'Library with 2 versions, all disabled (using id) - 3.1' => [
|
|
'libraryname' => 'H5P.Lib3.1',
|
|
'expected' => false,
|
|
'exception' => false,
|
|
'useid' => true,
|
|
],
|
|
'Library with 2 versions, all disabled (using id) - 3.2' => [
|
|
'libraryname' => 'H5P.Lib3.2',
|
|
'expected' => false,
|
|
'exception' => false,
|
|
'useid' => true,
|
|
],
|
|
'Library with only one version, disabled (using id)' => [
|
|
'libraryname' => 'H5P.Lib4.1',
|
|
'expected' => false,
|
|
'exception' => false,
|
|
'useid' => true,
|
|
],
|
|
'Library with only one version, enabled (using id)' => [
|
|
'libraryname' => 'H5P.Lib5.1',
|
|
'expected' => true,
|
|
'exception' => false,
|
|
'useid' => true,
|
|
],
|
|
'Unexisting library' => [
|
|
'libraryname' => 'H5P.Unexisting',
|
|
'expected' => true,
|
|
],
|
|
'Missing required parameters' => [
|
|
'libraryname' => 'H5P.Unexisting',
|
|
'expected' => false,
|
|
'exception' => true,
|
|
'useid' => false,
|
|
'uselibraryname' => false,
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test the behaviour of is_valid_package().
|
|
* @runInSeparateProcess
|
|
*
|
|
* @covers ::is_valid_package
|
|
* @dataProvider is_valid_package_provider
|
|
*
|
|
* @param string $filename The H5P content to validate.
|
|
* @param bool $expected Expected result after calling the method.
|
|
* @param bool $isadmin Whether the user calling the method will be admin or not.
|
|
* @param bool $onlyupdatelibs Whether new libraries can be installed or only the existing ones can be updated.
|
|
* @param bool $skipcontent Should the content be skipped (so only the libraries will be saved)?
|
|
*/
|
|
public function test_is_valid_package(string $filename, bool $expected, bool $isadmin = false, bool $onlyupdatelibs = false,
|
|
bool $skipcontent = false): void {
|
|
global $USER;
|
|
|
|
$this->resetAfterTest();
|
|
|
|
if ($isadmin) {
|
|
$this->setAdminUser();
|
|
$user = $USER;
|
|
} else {
|
|
// Create a user.
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$this->setUser($user);
|
|
}
|
|
|
|
// Prepare the file.
|
|
$path = __DIR__ . $filename;
|
|
$file = helper::create_fake_stored_file_from_path($path, (int)$user->id);
|
|
|
|
// Check if the H5P content is valid or not.
|
|
$result = api::is_valid_package($file, $onlyupdatelibs, $skipcontent);
|
|
$this->assertEquals($expected, $result);
|
|
}
|
|
|
|
/**
|
|
* Data provider for test_is_valid_package().
|
|
*
|
|
* @return array
|
|
*/
|
|
public function is_valid_package_provider(): array {
|
|
return [
|
|
'Valid H5P file (as admin)' => [
|
|
'filename' => '/fixtures/greeting-card-887.h5p',
|
|
'expected' => true,
|
|
'isadmin' => true,
|
|
],
|
|
'Valid H5P file (as user) without library update and checking content' => [
|
|
'filename' => '/fixtures/greeting-card-887.h5p',
|
|
'expected' => false, // Libraries are missing and user hasn't the right permissions to upload them.
|
|
'isadmin' => false,
|
|
'onlyupdatelibs' => false,
|
|
'skipcontent' => false,
|
|
],
|
|
'Valid H5P file (as user) with library update and checking content' => [
|
|
'filename' => '/fixtures/greeting-card-887.h5p',
|
|
'expected' => false, // Libraries are missing and user hasn't the right permissions to upload them.
|
|
'isadmin' => false,
|
|
'onlyupdatelibs' => true,
|
|
'skipcontent' => false,
|
|
],
|
|
'Valid H5P file (as user) without library update and skipping content' => [
|
|
'filename' => '/fixtures/greeting-card-887.h5p',
|
|
'expected' => true, // Content check is skipped so the package will be considered valid.
|
|
'isadmin' => false,
|
|
'onlyupdatelibs' => false,
|
|
'skipcontent' => true,
|
|
],
|
|
'Valid H5P file (as user) with library update and skipping content' => [
|
|
'filename' => '/fixtures/greeting-card-887.h5p',
|
|
'expected' => true, // Content check is skipped so the package will be considered valid.
|
|
'isadmin' => false,
|
|
'onlyupdatelibs' => true,
|
|
'skipcontent' => true,
|
|
],
|
|
'Invalid H5P file (as admin)' => [
|
|
'filename' => '/fixtures/h5ptest.zip',
|
|
'expected' => false,
|
|
'isadmin' => true,
|
|
],
|
|
'Invalid H5P file (as user)' => [
|
|
'filename' => '/fixtures/h5ptest.zip',
|
|
'expected' => false,
|
|
'isadmin' => false,
|
|
],
|
|
'Invalid H5P file (as user) skipping content' => [
|
|
'filename' => '/fixtures/h5ptest.zip',
|
|
'expected' => true, // Content check is skipped so the package will be considered valid.
|
|
'isadmin' => false,
|
|
'onlyupdatelibs' => false,
|
|
'skipcontent' => true,
|
|
],
|
|
];
|
|
}
|
|
}
|