589 lines
21 KiB
PHP
589 lines
21 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/>.
|
|
|
|
/**
|
|
* This file contains unit test related to xAPI library.
|
|
*
|
|
* @package core_xapi
|
|
* @copyright 2020 Ferran Recio
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
namespace core_xapi\local;
|
|
|
|
use core_xapi\local\statement\item;
|
|
use core_xapi\local\statement\item_actor;
|
|
use core_xapi\local\statement\item_object;
|
|
use core_xapi\local\statement\item_activity;
|
|
use core_xapi\local\statement\item_verb;
|
|
use core_xapi\local\statement\item_agent;
|
|
use core_xapi\local\statement\item_group;
|
|
use core_xapi\local\statement\item_result;
|
|
use core_xapi\local\statement\item_attachment;
|
|
use core_xapi\local\statement\item_context;
|
|
use core_xapi\iri;
|
|
use core_xapi\xapi_exception;
|
|
use advanced_testcase;
|
|
use stdClass;
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
/**
|
|
* Contains test cases for testing statement class.
|
|
*
|
|
* @package core_xapi
|
|
* @since Moodle 3.9
|
|
* @copyright 2020 Ferran Recio
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
class statement_testcase extends advanced_testcase {
|
|
|
|
/**
|
|
* Returns a valid item for a specific attribute.
|
|
*
|
|
* @param string $itemname statement item name
|
|
* @return item the resulting item
|
|
*/
|
|
private function get_valid_item(string $itemname): item {
|
|
global $USER, $CFG;
|
|
switch ($itemname) {
|
|
case 'attachments':
|
|
case 'attachment':
|
|
$data = (object) [
|
|
'usageType' => iri::generate('example', 'attachment'),
|
|
'display' => (object) [
|
|
'en-US' => 'Example',
|
|
],
|
|
'description' => (object) [
|
|
'en-US' => 'Description example',
|
|
],
|
|
"contentType" => "image/jpg",
|
|
"length" => 1234,
|
|
"sha2" => "b94c0f1cffb77475c6f1899111a0181efe1d6177"
|
|
];
|
|
return item_attachment::create_from_data($data);
|
|
case 'authority':
|
|
$data = (object) [
|
|
'objectType' => 'Agent',
|
|
'account' => (object) [
|
|
'homePage' => $CFG->wwwroot,
|
|
'name' => $USER->id,
|
|
],
|
|
];
|
|
return item_agent::create_from_data($data);
|
|
}
|
|
// For now, the rest of the optional properties have no validation
|
|
// so we create a standard stdClass for all of them.
|
|
$data = (object)[
|
|
'some' => 'data',
|
|
];
|
|
$classname = 'core_xapi\local\statement\item_'.$itemname;
|
|
if (class_exists($classname)) {
|
|
$item = $classname::create_from_data($data);
|
|
} else {
|
|
$item = item::create_from_data($data);
|
|
}
|
|
return $item;
|
|
}
|
|
|
|
/**
|
|
* Test statement creation.
|
|
*
|
|
* @dataProvider create_provider
|
|
* @param bool $useagent if use agent as actor (or group if false)
|
|
* @param array $extras extra item elements
|
|
* @param array $extravalues extra string values
|
|
*/
|
|
public function test_create(bool $useagent, array $extras, array $extravalues) {
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Create one course with a group.
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
|
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
|
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id));
|
|
|
|
$this->setUser($user);
|
|
|
|
// Our statement.
|
|
$statement = new statement();
|
|
|
|
// Populate statement.
|
|
if ($useagent) {
|
|
$statement->set_actor(item_agent::create_from_user($user));
|
|
} else {
|
|
$statement->set_actor(item_group::create_from_group($group));
|
|
}
|
|
$statement->set_verb(item_verb::create_from_id('cook'));
|
|
$statement->set_object(item_activity::create_from_id('paella'));
|
|
|
|
foreach ($extras as $extra) {
|
|
$method = 'set_'.$extra;
|
|
$item = $this->get_valid_item($extra);
|
|
$statement->$method($item);
|
|
}
|
|
|
|
// For now extra values have no validation.
|
|
foreach ($extravalues as $extra) {
|
|
$method = 'set_'.$extra;
|
|
$statement->$method('Example');
|
|
}
|
|
|
|
// Check resulting statement.
|
|
if ($useagent) {
|
|
$stuser = $statement->get_user();
|
|
$this->assertEquals($user->id, $stuser->id);
|
|
$stusers = $statement->get_all_users();
|
|
$this->assertCount(1, $stusers);
|
|
} else {
|
|
$stgroup = $statement->get_group();
|
|
$this->assertEquals($group->id, $stgroup->id);
|
|
$stusers = $statement->get_all_users();
|
|
$this->assertCount(1, $stusers);
|
|
$stuser = array_shift($stusers);
|
|
$this->assertEquals($user->id, $stuser->id);
|
|
}
|
|
$this->assertEquals('cook', $statement->get_verb_id());
|
|
$this->assertEquals('paella', $statement->get_activity_id());
|
|
|
|
// Check resulting json (only first node structure, internal structure
|
|
// depends on every item json_encode test).
|
|
$data = json_decode(json_encode($statement));
|
|
$this->assertNotEmpty($data->actor);
|
|
$this->assertNotEmpty($data->verb);
|
|
$this->assertNotEmpty($data->object);
|
|
$allextras = ['context', 'result', 'timestamp', 'stored', 'authority', 'version', 'attachments'];
|
|
$alldefined = array_merge($extras, $extravalues);
|
|
foreach ($allextras as $extra) {
|
|
if (in_array($extra, $alldefined)) {
|
|
$this->assertObjectHasAttribute($extra, $data);
|
|
$this->assertNotEmpty($data->$extra);
|
|
} else {
|
|
$this->assertObjectNotHasAttribute($extra, $data);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Data provider for the test_create and test_create_from_data tests.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function create_provider() : array {
|
|
return [
|
|
'Agent statement with no extras' => [
|
|
true, [], []
|
|
],
|
|
'Agent statement with context' => [
|
|
true, ['context'], []
|
|
],
|
|
'Agent statement with result' => [
|
|
true, ['result'], []
|
|
],
|
|
'Agent statement with timestamp' => [
|
|
true, [], ['timestamp']
|
|
],
|
|
'Agent statement with stored' => [
|
|
true, [], ['stored']
|
|
],
|
|
'Agent statement with authority' => [
|
|
true, ['authority'], []
|
|
],
|
|
'Agent statement with version' => [
|
|
true, [], ['version']
|
|
],
|
|
'Group statement with no extras' => [
|
|
false, [], []
|
|
],
|
|
'Group statement with context' => [
|
|
false, ['context'], []
|
|
],
|
|
'Group statement with result' => [
|
|
false, ['result'], []
|
|
],
|
|
'Group statement with timestamp' => [
|
|
false, [], ['timestamp']
|
|
],
|
|
'Group statement with stored' => [
|
|
false, [], ['stored']
|
|
],
|
|
'Group statement with authority' => [
|
|
false, ['authority'], []
|
|
],
|
|
'Group statement with version' => [
|
|
false, [], ['version']
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test statement creation from xAPI statement data.
|
|
*
|
|
* @dataProvider create_provider
|
|
* @param bool $useagent if use agent as actor (or group if false)
|
|
* @param array $extras extra item elements
|
|
* @param array $extravalues extra string values
|
|
*/
|
|
public function test_create_from_data(bool $useagent, array $extras, array $extravalues) {
|
|
$this->resetAfterTest();
|
|
|
|
// Create one course with a group.
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
|
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
|
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id));
|
|
|
|
$this->setUser($user);
|
|
|
|
// Populate data.
|
|
if ($useagent) {
|
|
$actor = item_agent::create_from_user($user);
|
|
} else {
|
|
$actor = item_group::create_from_group($group);
|
|
}
|
|
$verb = item_verb::create_from_id('cook');
|
|
$object = item_activity::create_from_id('paella');
|
|
|
|
$data = (object) [
|
|
'actor' => $actor->get_data(),
|
|
'verb' => $verb->get_data(),
|
|
'object' => $object->get_data(),
|
|
];
|
|
|
|
foreach ($extras as $extra) {
|
|
$item = $this->get_valid_item($extra);
|
|
$data->$extra = $item->get_data();
|
|
}
|
|
|
|
// For now extra values have no validation.
|
|
foreach ($extravalues as $extra) {
|
|
$data->$extra = 'Example';
|
|
}
|
|
|
|
$statement = statement::create_from_data($data);
|
|
|
|
// Check resulting statement.
|
|
if ($useagent) {
|
|
$stuser = $statement->get_user();
|
|
$this->assertEquals($user->id, $stuser->id);
|
|
$stusers = $statement->get_all_users();
|
|
$this->assertCount(1, $stusers);
|
|
} else {
|
|
$stgroup = $statement->get_group();
|
|
$this->assertEquals($group->id, $stgroup->id);
|
|
$stusers = $statement->get_all_users();
|
|
$this->assertCount(1, $stusers);
|
|
$stuser = array_shift($stusers);
|
|
$this->assertEquals($user->id, $stuser->id);
|
|
}
|
|
$this->assertEquals('cook', $statement->get_verb_id());
|
|
$this->assertEquals('paella', $statement->get_activity_id());
|
|
|
|
// Check resulting json (only first node structure, internal structure
|
|
// depends on every item json_encode test).
|
|
$data = json_decode(json_encode($statement));
|
|
$this->assertNotEmpty($data->actor);
|
|
$this->assertNotEmpty($data->verb);
|
|
$this->assertNotEmpty($data->object);
|
|
$allextras = ['context', 'result', 'timestamp', 'stored', 'authority', 'version', 'attachments'];
|
|
$alldefined = array_merge($extras, $extravalues);
|
|
foreach ($allextras as $extra) {
|
|
if (in_array($extra, $alldefined)) {
|
|
$this->assertObjectHasAttribute($extra, $data);
|
|
$this->assertNotEmpty($data->object);
|
|
} else {
|
|
$this->assertObjectNotHasAttribute($extra, $data);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test adding attachments to statement.
|
|
*
|
|
*/
|
|
public function test_add_attachment() {
|
|
|
|
// Our statement.
|
|
$statement = new statement();
|
|
|
|
$attachments = $statement->get_attachments();
|
|
$this->assertNull($attachments);
|
|
|
|
$item = $this->get_valid_item('attachment');
|
|
$itemdata = $item->get_data();
|
|
$statement->add_attachment($item);
|
|
|
|
$attachments = $statement->get_attachments();
|
|
$this->assertNotNull($attachments);
|
|
$this->assertCount(1, $attachments);
|
|
|
|
$attachment = current($attachments);
|
|
$attachmentdata = $attachment->get_data();
|
|
$this->assertEquals($itemdata->usageType, $attachmentdata->usageType);
|
|
$this->assertEquals($itemdata->length, $attachmentdata->length);
|
|
|
|
// Check resulting json.
|
|
$statementdata = json_decode(json_encode($statement));
|
|
$this->assertObjectHasAttribute('attachments', $statementdata);
|
|
$this->assertNotEmpty($statementdata->attachments);
|
|
$this->assertCount(1, $statementdata->attachments);
|
|
}
|
|
|
|
/**
|
|
* Test adding attachments to statement.
|
|
*
|
|
*/
|
|
public function test_add_attachment_from_data() {
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$this->setUser($user);
|
|
|
|
$actor = item_agent::create_from_user($user);
|
|
$verb = item_verb::create_from_id('cook');
|
|
$object = item_activity::create_from_id('paella');
|
|
|
|
$data = (object) [
|
|
'actor' => $actor->get_data(),
|
|
'verb' => $verb->get_data(),
|
|
'object' => $object->get_data(),
|
|
];
|
|
|
|
$item = $this->get_valid_item('attachment');
|
|
$itemdata = $item->get_data();
|
|
$data->attachments = [$itemdata];
|
|
|
|
$statement = statement::create_from_data($data);
|
|
|
|
$attachments = $statement->get_attachments();
|
|
$this->assertNotNull($attachments);
|
|
$this->assertCount(1, $attachments);
|
|
|
|
$attachment = current($attachments);
|
|
$attachmentdata = $attachment->get_data();
|
|
$this->assertEquals($itemdata->usageType, $attachmentdata->usageType);
|
|
$this->assertEquals($itemdata->length, $attachmentdata->length);
|
|
|
|
$statementdata = json_decode(json_encode($statement));
|
|
$this->assertObjectHasAttribute('attachments', $statementdata);
|
|
$this->assertNotEmpty($statementdata->attachments);
|
|
$this->assertCount(1, $statementdata->attachments);
|
|
|
|
// Now try to send an invalid attachments.
|
|
$this->expectException(xapi_exception::class);
|
|
$data->attachments = 'Invalid data';
|
|
$statement = statement::create_from_data($data);
|
|
}
|
|
|
|
/**
|
|
* Test all getters into a not set statement.
|
|
*
|
|
* @dataProvider invalid_gets_provider
|
|
* @param string $method the method to test
|
|
* @param bool $exception if an exception is expected
|
|
*/
|
|
public function test_invalid_gets(string $method, bool $exception) {
|
|
$statement = new statement();
|
|
if ($exception) {
|
|
$this->expectException(xapi_exception::class);
|
|
}
|
|
$result = $statement->$method();
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
/**
|
|
* Data provider for the text_invalid_gets.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function invalid_gets_provider() : array {
|
|
return [
|
|
'Method get_user on empty statement' => ['get_user', true],
|
|
'Method get_all_users on empty statement' => ['get_all_users', true],
|
|
'Method get_group on empty statement' => ['get_group', true],
|
|
'Method get_verb_id on empty statement' => ['get_verb_id', true],
|
|
'Method get_activity_id on empty statement' => ['get_activity_id', true],
|
|
'Method get_actor on empty statement' => ['get_actor', false],
|
|
'Method get_verb on empty statement' => ['get_verb', false],
|
|
'Method get_object on empty statement' => ['get_object', false],
|
|
'Method get_context on empty statement' => ['get_context', false],
|
|
'Method get_result on empty statement' => ['get_result', false],
|
|
'Method get_timestamp on empty statement' => ['get_timestamp', false],
|
|
'Method get_stored on empty statement' => ['get_stored', false],
|
|
'Method get_authority on empty statement' => ['get_authority', false],
|
|
'Method get_version on empty statement' => ['get_version', false],
|
|
'Method get_attachments on empty statement' => ['get_attachments', false],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Try to get a user from a group statement.
|
|
*/
|
|
public function test_invalid_get_user() {
|
|
|
|
$this->resetAfterTest();
|
|
|
|
// Create one course with a group.
|
|
$course = $this->getDataGenerator()->create_course();
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
|
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
|
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id));
|
|
|
|
// Our statement.
|
|
$statement = new statement();
|
|
|
|
// Populate statement.
|
|
$statement->set_actor(item_group::create_from_group($group));
|
|
$statement->set_verb(item_verb::create_from_id('cook'));
|
|
$statement->set_object(item_activity::create_from_id('paella'));
|
|
|
|
$this->expectException(xapi_exception::class);
|
|
$statement->get_user();
|
|
}
|
|
|
|
/**
|
|
* Try to get a group from an agent statement.
|
|
*/
|
|
public function test_invalid_get_group() {
|
|
$this->resetAfterTest();
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
|
|
// Our statement.
|
|
$statement = new statement();
|
|
|
|
// Populate statement.
|
|
$statement->set_actor(item_agent::create_from_user($user));
|
|
$statement->set_verb(item_verb::create_from_id('cook'));
|
|
$statement->set_object(item_activity::create_from_id('paella'));
|
|
|
|
$this->expectException(xapi_exception::class);
|
|
$statement->get_group();
|
|
}
|
|
|
|
/**
|
|
* Try to get activity Id from a statement with agent object.
|
|
*/
|
|
public function test_invalid_get_activity_id() {
|
|
$this->resetAfterTest();
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
|
|
// Our statement.
|
|
$statement = new statement();
|
|
|
|
// Populate statement with and agent object.
|
|
$statement->set_actor(item_agent::create_from_user($user));
|
|
$statement->set_verb(item_verb::create_from_id('cook'));
|
|
$statement->set_object(item_agent::create_from_user($user));
|
|
|
|
$this->expectException(xapi_exception::class);
|
|
$statement->get_activity_id();
|
|
}
|
|
|
|
/**
|
|
* Test for invalid structures.
|
|
*
|
|
* @dataProvider invalid_data_provider
|
|
* @param bool $useuser if use user into statement
|
|
* @param bool $userverb if use verb into statement
|
|
* @param bool $useobject if use object into statement
|
|
*/
|
|
public function test_invalid_data(bool $useuser, bool $userverb, bool $useobject): void {
|
|
|
|
$data = new stdClass();
|
|
|
|
if ($useuser) {
|
|
$this->resetAfterTest();
|
|
$user = $this->getDataGenerator()->create_user();
|
|
$data->actor = item_agent::create_from_user($user);
|
|
}
|
|
if ($userverb) {
|
|
$data->verb = item_verb::create_from_id('cook');
|
|
}
|
|
if ($useobject) {
|
|
$data->object = item_activity::create_from_id('paella');
|
|
}
|
|
|
|
$this->expectException(xapi_exception::class);
|
|
$statement = statement::create_from_data($data);
|
|
}
|
|
|
|
/**
|
|
* Data provider for the test_invalid_data tests.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function invalid_data_provider() : array {
|
|
return [
|
|
'No actor, no verb, no object' => [false, false, false],
|
|
'No actor, verb, no object' => [false, true, false],
|
|
'No actor, no verb, object' => [false, false, true],
|
|
'No actor, verb, object' => [false, true, true],
|
|
'Actor, no verb, no object' => [true, false, false],
|
|
'Actor, verb, no object' => [true, true, false],
|
|
'Actor, no verb, object' => [true, false, true],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Test minify statement.
|
|
*/
|
|
public function test_minify() {
|
|
|
|
$this->resetAfterTest();
|
|
|
|
$user = $this->getDataGenerator()->create_user();
|
|
|
|
$this->setUser($user);
|
|
|
|
// Our statement.
|
|
$statement = new statement();
|
|
|
|
// Populate statement.
|
|
$statement->set_actor(item_agent::create_from_user($user));
|
|
$statement->set_verb(item_verb::create_from_id('cook'));
|
|
$statement->set_object(item_activity::create_from_id('paella'));
|
|
$statement->set_result($this->get_valid_item('result'));
|
|
$statement->set_context($this->get_valid_item('context'));
|
|
$statement->set_authority($this->get_valid_item('authority'));
|
|
$statement->add_attachment($this->get_valid_item('attachment'));
|
|
$statement->set_version('Example');
|
|
$statement->set_timestamp('Example');
|
|
$statement->set_stored('Example');
|
|
|
|
$min = $statement->minify();
|
|
|
|
// Check calculated fields.
|
|
$this->assertCount(6, $min);
|
|
$this->assertArrayNotHasKey('actor', $min);
|
|
$this->assertArrayHasKey('verb', $min);
|
|
$this->assertArrayHasKey('object', $min);
|
|
$this->assertArrayHasKey('context', $min);
|
|
$this->assertArrayHasKey('result', $min);
|
|
$this->assertArrayNotHasKey('timestamp', $min);
|
|
$this->assertArrayNotHasKey('stored', $min);
|
|
$this->assertArrayHasKey('authority', $min);
|
|
$this->assertArrayNotHasKey('version', $min);
|
|
$this->assertArrayHasKey('attachments', $min);
|
|
}
|
|
}
|