MDL-69542 enrol_lti: add new entity classes for storing launch data

This commit is contained in:
Jake Dallimore 2022-01-24 16:22:51 +08:00
parent 23894ddd98
commit 7a1f433043
18 changed files with 3977 additions and 0 deletions

View File

@ -0,0 +1,178 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* The ags_info class, instances of which represent grade service information for a resource_link or context.
*
* For information about Assignment and Grade Services 2.0, see https://www.imsglobal.org/spec/lti-ags/v2p0/.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class ags_info {
/** @var string Scope for lineitem management, used when a platform allows the tool to create lineitems.*/
private const SCOPES_LINEITEM_MANAGE = 'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem';
/** @var string Scope for lineitem reads, used when a tool only grants read access to line items.*/
private const SCOPES_LINEITEM_READONLY = 'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly';
/** @var string Scope for reading results.*/
private const SCOPES_RESULT_READONLY = 'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly';
/** @var string Scope for posting scores.*/
private const SCOPES_SCORES_POST = 'https://purl.imsglobal.org/spec/lti-ags/scope/score';
/** @var \moodle_url The service URL used to get/put lineitems*/
private $lineitemsurl;
/** @var \moodle_url|null The lineitemurl, which is only present when a single lineitem is supported.*/
private $lineitemurl;
/** @var array The array of supported lineitem-related scopes for this service instance.*/
private $lineitemscopes = [];
/** @var string|null The supported result scope for this service instance.*/
private $resultscope = null;
/** @var string|null The supported score scope for this service instance.*/
private $scorescope = null;
/**
* The ags_info constructor.
*
* @param \moodle_url $lineitemsurl The service URL used to get/put lineitems.
* @param \moodle_url|null $lineitemurl The lineitemurl, which is only present when a single lineitem is supported.
* @param array $scopes The array of supported scopes for this service instance.
*/
private function __construct(\moodle_url $lineitemsurl, ?\moodle_url $lineitemurl, array $scopes) {
$this->lineitemsurl = $lineitemsurl;
$this->lineitemurl = $lineitemurl;
$this->validate_scopes($scopes);
}
/**
* Factory method to create a new ags_info instance.
*
* @param \moodle_url $lineitemsurl The service URL used to get/put lineitems.
* @param \moodle_url|null $lineitemurl The lineitemurl, which is only present when a single lineitem is supported.
* @param array $scopes The array of supported scopes for this service instance.
* @return ags_info the object instance.
*/
public static function create(\moodle_url $lineitemsurl, ?\moodle_url $lineitemurl = null,
array $scopes = []): ags_info {
return new self($lineitemsurl, $lineitemurl, $scopes);
}
/**
* Check the supplied scopes for validity and set instance vars if appropriate.
*
* @param array $scopes the array of string scopes to check.
* @throws \coding_exception if any of the scopes is invalid.
*/
private function validate_scopes(array $scopes): void {
$validscopes = [
self::SCOPES_LINEITEM_READONLY,
self::SCOPES_LINEITEM_MANAGE,
self::SCOPES_RESULT_READONLY,
self::SCOPES_SCORES_POST
];
foreach ($scopes as $scope) {
if (!is_string($scope)) {
throw new \coding_exception('Scope must be a string value');
}
$key = array_search($scope, $validscopes);
if ($key === false) {
throw new \coding_exception("Scope '{$scope}' is invalid.");
}
if ($key == 0) {
$this->lineitemscopes[] = self::SCOPES_LINEITEM_READONLY;
} else if ($key == 1) {
$this->lineitemscopes[] = self::SCOPES_LINEITEM_MANAGE;
} else if ($key == 2) {
$this->resultscope = self::SCOPES_RESULT_READONLY;
} else if ($key == 3) {
$this->scorescope = self::SCOPES_SCORES_POST;
}
}
}
/**
* Get the url for querying line items.
*
* @return \moodle_url the url.
*/
public function get_lineitemsurl(): \moodle_url {
return $this->lineitemsurl;
}
/**
* Get the single line item url, in cases where only one line item exists.
*
* @return \moodle_url|null the url, or null if not present.
*/
public function get_lineitemurl(): ?\moodle_url {
return $this->lineitemurl;
}
/**
* Get the authorization scope for lineitems.
*
* @return array|null the scopes, if present, else null.
*/
public function get_lineitemscope(): ?array {
return !empty($this->lineitemscopes) ? $this->lineitemscopes : null;
}
/**
* Get the authorization scope for results.
*
* @return string|null the scope, if present, else null.
*/
public function get_resultscope(): ?string {
return $this->resultscope;
}
/**
* Get the authorization scope for scores.
*
* @return string|null the scope, if present, else null.
*/
public function get_scorescope(): ?string {
return $this->scorescope;
}
/**
* Get all supported scopes for this service.
*
* @return string[] the array of supported scopes.
*/
public function get_scopes(): array {
$scopes = [];
foreach ($this->lineitemscopes as $lineitemscope) {
$scopes[] = $lineitemscope;
}
if (!empty($this->resultscope)) {
$scopes[] = $this->resultscope;
}
if (!empty($this->scorescope)) {
$scopes[] = $this->scorescope;
}
return $scopes;
}
}

View File

@ -0,0 +1,186 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Class application_registration.
*
* This class represents an LTI Advantage Application Registration.
* Each registered application may contain one or more deployments of the Moodle tool.
* This registration provides the security contract for all tool deployments belonging to the registration.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class application_registration {
/** @var int|null the if of this registration instance, or null if it hasn't been stored yet. */
private $id;
/** @var string the name of the application being registered. */
private $name;
/** @var \moodle_url the issuer identifying the platform, as provided by the platform. */
private $platformid;
/** @var string the client id as provided by the platform. */
private $clientid;
/** @var \moodle_url the authentication request URL, as provided by the platform. */
private $authenticationrequesturl;
/** @var \moodle_url the certificate URL, as provided by the platform. */
private $jwksurl;
/** @var \moodle_url the access token URL, as provided by the platform. */
private $accesstokenurl;
/**
* The application_registration constructor.
*
* @param string $name the descriptor for this application registration.
* @param \moodle_url $platformid the URL of application
* @param string $clientid unique id for the client on the application
* @param \moodle_url $authenticationrequesturl URL to send OIDC Auth requests to.
* @param \moodle_url $jwksurl URL to use to get public keys from the application.
* @param \moodle_url $accesstokenurl URL to use to get an access token from the application, used in service calls.
* @param int|null $id the id of the object instance, if being created from an existing store item.
*/
private function __construct(string $name, \moodle_url $platformid, string $clientid,
\moodle_url $authenticationrequesturl, \moodle_url $jwksurl, \moodle_url $accesstokenurl, int $id = null) {
if (empty($name)) {
throw new \coding_exception("Invalid 'name' arg. Cannot be an empty string.");
}
if (empty($clientid)) {
throw new \coding_exception("Invalid 'clientid' arg. Cannot be an empty string.");
}
$this->name = $name;
$this->platformid = $platformid;
$this->clientid = $clientid;
$this->authenticationrequesturl = $authenticationrequesturl;
$this->jwksurl = $jwksurl;
$this->accesstokenurl = $accesstokenurl;
$this->id = $id;
}
/**
* Factory method to create a new instance of an application repository
*
* @param string $name the descriptor for this application registration.
* @param \moodle_url $platformid the URL of application
* @param string $clientid unique id for the client on the application
* @param \moodle_url $authenticationrequesturl URL to send OIDC Auth requests to.
* @param \moodle_url $jwksurl URL to use to get public keys from the application.
* @param \moodle_url $accesstokenurl URL to use to get an access token from the application, used in service calls.
* @param int|null $id the id of the object instance, if being created from an existing store item.
* @return application_registration the application_registration instance.
*/
public static function create(string $name, \moodle_url $platformid, string $clientid,
\moodle_url $authenticationrequesturl, \moodle_url $jwksurl, \moodle_url $accesstokenurl,
int $id = null): application_registration {
return new self($name, $platformid, $clientid, $authenticationrequesturl, $jwksurl, $accesstokenurl, $id);
}
/**
* Get the integer id of this object instance.
*
* Will return null if the instance has not yet been stored.
*
* @return null|int the id, if set, otherwise null.
*/
public function get_id(): ?int {
return $this->id;
}
/**
* Get the name of the application being registered.
*
* @return string the name.
*/
public function get_name(): string {
return $this->name;
}
/**
* Get the platform id.
*
* @return \moodle_url the platformid/issuer URL.
*/
public function get_platformid(): \moodle_url {
return $this->platformid;
}
/**
* Get the client id.
*
* @return string the client id.
*/
public function get_clientid(): string {
return $this->clientid;
}
/**
* Get the authentication request URL.
*
* @return \moodle_url the authentication request URL.
*/
public function get_authenticationrequesturl(): \moodle_url {
return $this->authenticationrequesturl;
}
/**
* Get the JWKS URL.
*
* @return \moodle_url the JWKS URL.
*/
public function get_jwksurl(): \moodle_url {
return $this->jwksurl;
}
/**
* Get the access token URL.
*
* @return \moodle_url the access token URL.
*/
public function get_accesstokenurl(): \moodle_url {
return $this->accesstokenurl;
}
/**
* Add a tool deployment to this registration.
*
* @param string $name human readable name for the deployment.
* @param string $deploymentid the unique id of the tool deployment in the platform.
* @return deployment the new deployment.
* @throws \coding_exception if trying to add a deployment to an instance without an id assigned.
*/
public function add_tool_deployment(string $name, string $deploymentid): deployment {
if (empty($this->get_id())) {
throw new \coding_exception("Can't add deployment to a resource_link that hasn't first been saved.");
}
return deployment::create(
$this->get_id(),
$deploymentid,
$name
);
}
}

View File

@ -0,0 +1,179 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Class context, instances of which represent a context in the platform.
*
* See: http://www.imsglobal.org/spec/lti/v1p3/#context-type-vocabulary for supported context types.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */
class context {
// The following full contexts are per the spec:
// http://www.imsglobal.org/spec/lti/v1p3/#context-type-vocabulary.
/** @var string course template context */
private const CONTEXT_TYPE_COURSE_TEMPLATE = 'http://purl.imsglobal.org/vocab/lis/v2/course#CourseTemplate';
/** @var string course offering context */
private const CONTEXT_TYPE_COURSE_OFFERING = 'http://purl.imsglobal.org/vocab/lis/v2/course#CourseOffering';
/** @var string course section context */
private const CONTEXT_TYPE_COURSE_SECTION = 'http://purl.imsglobal.org/vocab/lis/v2/course#CourseSection';
/** @var string group context */
private const CONTEXT_TYPE_GROUP = 'http://purl.imsglobal.org/vocab/lis/v2/course#Group';
// The following simple names are deprecated but are still supported in 1.3 for backwards compatibility.
// http://www.imsglobal.org/spec/lti/v1p3/#context-type-vocabulary.
/** @var string deprecated simple course template context */
private const LEGACY_CONTEXT_TYPE_COURSE_TEMPLATE = 'CourseTemplate';
/** @var string deprecated simple course offering context */
private const LEGACY_CONTEXT_TYPE_COURSE_OFFERING = 'CourseOffering';
/** @var string deprecated simple course section context */
private const LEGACY_CONTEXT_TYPE_COURSE_SECTION = 'CourseSection';
/** @var string deprecated simple group context */
private const LEGACY_CONTEXT_TYPE_GROUP = 'Group';
/** @var int the local id of the deployment instance to which this context belongs. */
private $deploymentid;
/** @var string the contextid as supplied by the platform. */
private $contextid;
/** @var int|null the local id of this object instance, which can be null if the object hasn't been stored before */
private $id;
/** @var string[] the array of context types */
private $types;
/**
* Private constructor.
*
* @param int $deploymentid the local id of the deployment instance to which this context belongs.
* @param string $contextid the context id string, as provided by the platform during launch.
* @param array $types an array of string context types, as provided by the platform during launch.
* @param int|null $id local id of this object instance, nullable for new objects.
*/
private function __construct(int $deploymentid, string $contextid, array $types, ?int $id) {
if (!is_null($id) && $id <= 0) {
throw new \coding_exception('id must be a positive int');
}
$this->deploymentid = $deploymentid;
$this->contextid = $contextid;
$this->set_types($types); // Handles type validation.
$this->id = $id;
}
/**
* Factory method for creating a context instance.
*
* @param int $deploymentid the local id of the deployment instance to which this context belongs.
* @param string $contextid the context id string, as provided by the platform during launch.
* @param array $types an array of string context types, as provided by the platform during launch.
* @param int|null $id local id of this object instance, nullable for new objects.
* @return context the context instance.
*/
public static function create(int $deploymentid, string $contextid, array $types, int $id = null): context {
return new self($deploymentid, $contextid, $types, $id);
}
/**
* Check whether a context is valid or not, checking also deprecated but supported legacy context names.
*
* @param string $type context type to check.
* @param bool $includelegacy whether to check the legacy simple context names too.
* @return bool true if the type is valid, false otherwise.
*/
private function is_valid_type(string $type, bool $includelegacy = false): bool {
// Check LTI Advantage types.
$valid = in_array($type, [
self::CONTEXT_TYPE_COURSE_TEMPLATE,
self::CONTEXT_TYPE_COURSE_OFFERING,
self::CONTEXT_TYPE_COURSE_SECTION,
self::CONTEXT_TYPE_GROUP
]);
// Check legacy short names.
if ($includelegacy) {
$valid = $valid || in_array($type, [
self::LEGACY_CONTEXT_TYPE_COURSE_TEMPLATE,
self::LEGACY_CONTEXT_TYPE_COURSE_OFFERING,
self::LEGACY_CONTEXT_TYPE_COURSE_SECTION,
self::LEGACY_CONTEXT_TYPE_GROUP
]);
}
return $valid;
}
/**
* Get the object instance id.
*
* @return int|null the id, or null if the object doesn't yet have one assigned.
*/
public function get_id(): ?int {
return $this->id;
}
/**
* Return the platform contextid string.
*
* @return string the id of the context in the platform.
*/
public function get_contextid(): string {
return $this->contextid;
}
/**
* Get the id of the local deployment instance to which this context instance belongs.
*
* @return int the id of the local deployment instance to which this context instance belongs.
*/
public function get_deploymentid(): int {
return $this->deploymentid;
}
/**
* Get the context types this context instance represents.
*
* @return string[] the array of context types this context instance represents.
*/
public function get_types(): array {
return $this->types;
}
/**
* Set the list of types this context instance represents.
*
* @param array $types the array of string types.
* @throws \coding_exception if any of the supplied types are invalid.
*/
public function set_types(array $types): void {
foreach ($types as $type) {
if (!$this->is_valid_type($type, true)) {
throw new \coding_exception("Cannot set invalid context type '{$type}'.");
}
}
$this->types = $types;
}
}

View File

@ -0,0 +1,178 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Class deployment.
*
* This class represents an LTI Advantage Tool Deployment (http://www.imsglobal.org/spec/lti/v1p3/#tool-deployment).
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class deployment {
/** @var int|null the id of this object instance, or null if it has not been saved yet. */
private $id;
/** @var string the name of this deployment. */
private $deploymentname;
/** @var string The platform-issued deployment id. */
private $deploymentid;
/** @var int the local ID of the application registration to which this deployment belongs. */
private $registrationid;
/** @var string|null the legacy consumer key, if the deployment instance is migrated from a legacy consumer. */
private $legacyconsumerkey;
/**
* The private deployment constructor.
*
* @param string $deploymentname the name of this deployment.
* @param string $deploymentid the platform-issued deployment id.
* @param int $registrationid the local ID of the application registration.
* @param int|null $id the id of this object instance, or null if it is a new instance which has not yet been saved.
* @param string|null $legacyconsumerkey the 1.1 consumer key associated with this deployment, used for upgrades.
*/
private function __construct(string $deploymentname, string $deploymentid, int $registrationid, ?int $id = null,
?string $legacyconsumerkey = null) {
if (!is_null($id) && $id <= 0) {
throw new \coding_exception('id must be a positive int');
}
if (empty($deploymentname)) {
throw new \coding_exception("Invalid 'deploymentname' arg. Cannot be an empty string.");
}
if (empty($deploymentid)) {
throw new \coding_exception("Invalid 'deploymentid' arg. Cannot be an empty string.");
}
$this->deploymentname = $deploymentname;
$this->deploymentid = $deploymentid;
$this->registrationid = $registrationid;
$this->id = $id;
$this->legacyconsumerkey = $legacyconsumerkey;
}
/**
* Factory method to create a new instance of a deployment.
*
* @param int $registrationid the local ID of the application registration.
* @param string $deploymentid the platform-issued deployment id.
* @param string $deploymentname the name of this deployment.
* @param int|null $id optional local id of this object instance, omitted for new deployment objects.
* @param string|null $legacyconsumerkey the 1.1 consumer key associated with this deployment, used for upgrades.
* @return deployment the deployment instance.
*/
public static function create(int $registrationid, string $deploymentid, string $deploymentname,
?int $id = null, ?string $legacyconsumerkey = null): deployment {
return new self($deploymentname, $deploymentid, $registrationid, $id, $legacyconsumerkey);
}
/**
* Return the object id.
*
* @return int|null the id.
*/
public function get_id(): ?int {
return $this->id;
}
/**
* Return the short name of this tool deployment.
*
* @return string the short name.
*/
public function get_deploymentname(): string {
return $this->deploymentname;
}
/**
* Get the deployment id string.
*
* @return string deploymentid
*/
public function get_deploymentid(): string {
return $this->deploymentid;
}
/**
* Get the id of the application_registration.
*
* @return int the id of the application_registration instance to which this deployment belongs.
*/
public function get_registrationid(): int {
return $this->registrationid;
}
/**
* Get the legacy consumer key to which this deployment instance is mapped.
*
* @return string|null the legacy consumer key, if set, else null.
*/
public function get_legacy_consumer_key(): ?string {
return $this->legacyconsumerkey;
}
/**
* Factory method to add a platform-specific context to the deployment.
*
* @param string $contextid the contextid, as supplied by the platform during launch.
* @param array $types the context types the context represents, as supplied by the platform during launch.
* @return context the context instance.
* @throws \coding_exception if the context could not be created.
*/
public function add_context(string $contextid, array $types): context {
if (!$this->get_id()) {
throw new \coding_exception('Can\'t add context to a deployment that hasn\'t first been saved');
}
return context::create($this->get_id(), $contextid, $types);
}
/**
* Factory method to create a resource link from this deployment instance.
*
* @param string $resourcelinkid the platform-issued string id of the resource link.
* @param int $resourceid the local published resource to which this link points.
* @param int|null $contextid the platform context instance in which the resource link resides, if available.
* @return resource_link the resource_link instance.
* @throws \coding_exception if the resource_link can't be created.
*/
public function add_resource_link(string $resourcelinkid, int $resourceid,
int $contextid = null): resource_link {
if (!$this->get_id()) {
throw new \coding_exception('Can\'t add resource_link to a deployment that hasn\'t first been saved');
}
return resource_link::create($resourcelinkid, $this->get_id(), $resourceid, $contextid);
}
/**
* Set the legacy consumer key for this instance, indicating that the deployment has been migrated from a consumer.
*
* @param string $key the legacy consumer key.
* @throws \coding_exception if the key is invalid.
*/
public function set_legacy_consumer_key(string $key): void {
if (strlen($key) > 255) {
throw new \coding_exception('Legacy consumer key too long. Cannot exceed 255 chars.');
}
$this->legacyconsumerkey = $key;
}
}

View File

@ -0,0 +1,191 @@
<?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 enrol_lti\local\ltiadvantage\entity;
use enrol_lti\local\ltiadvantage\repository\legacy_consumer_repository;
/**
* The migration_claim class, instances of which represent information passed in an 'lti1p1' migration claim.
*
* Provides validation and data retrieval for the claim.
*
* See https://www.imsglobal.org/spec/lti/v1p3/migr#lti-1-1-migration-claim
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class migration_claim {
/** @var string the LTI 1.1 consumer key */
private $consumerkey;
/** @var string the LTI 1.1 user identifier.
* This is only included in the claim if it differs to the value included in the LTI 1.3 'sub' claim.
* I.e. https://www.imsglobal.org/spec/security/v1p0#id-token
*/
private $userid = null;
/** @var string the LTI 1.1 context identifier.
* This is only included in the claim if it differs to the 'id' property of the LTI 1.3 'context' claim.
* I.e. https://purl.imsglobal.org/spec/lti/claim/context#id.
*/
private $contextid = null;
/** @var string the LTI 1.1 consumer instance GUID.
* This is only included in the claim if it differs to the 'guid' property of the LTI 1.3 'tool_platform' claim.
* I.e. https://purl.imsglobal.org/spec/lti/claim/tool_platform#guid.
*/
private $toolconsumerinstanceguid = null;
/** @var string the LTI 1.1 resource link identifier.
* This is only included in the claim if it differs to the 'id' property of the LTI 1.3 'resource_link' claim.
* I.e. https://purl.imsglobal.org/spec/lti/claim/resource_link#id.
*/
private $resourcelinkid = null;
/** @var legacy_consumer_repository repository instance for querying consumer secrets when verifying signature. */
private $legacyconsumerrepo;
/**
* The migration_claim constructor.
*
* @param array $claim the array of claim data, as received in a resource link launch.
* @param string $deploymentid the deployment id included in the launch.
* @param string $platform the platform included in the launch.
* @param string $clientid the client id included in the launch.
* @param string $exp the exp included in the launch.
* @param string $nonce the nonce included in the launch.
* @param legacy_consumer_repository $legacyconsumerrepo a legacy consumer repository instance.
* @throws \coding_exception if the claim data is invalid.
*/
public function __construct(array $claim, string $deploymentid, string $platform, string $clientid, string $exp,
string $nonce, legacy_consumer_repository $legacyconsumerrepo) {
// The oauth_consumer_key property MUST be sent.
// See: https://www.imsglobal.org/spec/lti/v1p3/migr#oauth_consumer_key.
if (empty($claim['oauth_consumer_key'])) {
throw new \coding_exception("Missing 'oauth_consumer_key' property in lti1p1 migration claim.");
}
// The oauth_consumer_key_sign property MAY be sent.
// For user migration to take place, however, this is deemed a required property.
// See: https://www.imsglobal.org/spec/lti/v1p3/migr#oauth_consumer_key_sign.
if (empty($claim['oauth_consumer_key_sign'])) {
throw new \coding_exception("Missing 'oauth_consumer_key_sign' property in lti1p1 migration claim.");
}
$this->legacyconsumerrepo = $legacyconsumerrepo;
if (!$this->verify_signature($claim['oauth_consumer_key'], $claim['oauth_consumer_key_sign'], $deploymentid,
$platform, $clientid, $exp, $nonce, $legacyconsumerrepo)) {
throw new \coding_exception("Invalid 'oauth_consumer_key_sign' signature in lti1p1 claim.");
}
$this->consumerkey = $claim['oauth_consumer_key'];
$this->userid = $claim['user_id'] ?? null;
$this->contextid = $claim['context_id'] ?? null;
$this->toolconsumerinstanceguid = $claim['tool_consumer_instance_guid'] ?? null;
$this->resourcelinkid = $claim['resource_link_id'] ?? null;
}
/**
* Verify the claim signature by recalculating it using the launch data and locally stored consumer secret.
*
* @param string $consumerkey the LTI 1.1 consumer key.
* @param string $signature a signature of the LTI 1.1 consumer key and associated launch data.
* @param string $deploymentid the deployment id included in the launch.
* @param string $platform the platform included in the launch.
* @param string $clientid the client id included in the launch.
* @param string $exp the exp included in the launch.
* @param string $nonce the nonce included in the launch.
* @return bool true if the signature was verified, false otherwise.
*/
private function verify_signature(string $consumerkey, string $signature, string $deploymentid, string $platform,
string $clientid, string $exp, string $nonce): bool {
$base = [
$consumerkey,
$deploymentid,
$platform,
$clientid,
$exp,
$nonce
];
$basestring = implode('&', $base);
// Legacy enrol_lti code permits tools to share a consumer key but use different secrets. This results in
// potentially many secrets per mapped tool consumer. As such, when generating the migration claim it's
// impossible to know which secret the platform will use to sign the consumer key. The consumer key in the
// migration claim is thus verified by trying all known secrets for the consumer, until either a match is found
// or no signatures match.
$consumersecrets = $this->legacyconsumerrepo->get_consumer_secrets($consumerkey);
foreach ($consumersecrets as $consumersecret) {
$calculatedsignature = base64_encode(hash_hmac('sha256', $basestring, $consumersecret));
if ($signature === $calculatedsignature) {
return true;
}
}
return false;
}
/**
* Return the consumer key stored in the claim.
*
* @return string the consumer key included in the claim.
*/
public function get_consumer_key(): string {
return $this->consumerkey;
}
/**
* Return the LTI 1.1 user id stored in the claim.
*
* @return string|null the user id, or null if not provided in the claim.
*/
public function get_user_id(): ?string {
return $this->userid;
}
/**
* Return the LTI 1.1 context id stored in the claim.
*
* @return string|null the context id, or null if not provided in the claim.
*/
public function get_context_id(): ?string {
return $this->contextid;
}
/**
* Return the LTI 1.1 tool consumer instance GUID stored in the claim.
*
* @return string|null the tool consumer instance GUID, or null if not provided in the claim.
*/
public function get_tool_consumer_instance_guid(): ?string {
return $this->toolconsumerinstanceguid;
}
/**
* Return the LTI 1.1 resource link id stored in the claim.
*
* @return string|null the resource link id, or null if not provided in the claim.
*/
public function get_resource_link_id(): ?string {
return $this->resourcelinkid;
}
}

View File

@ -0,0 +1,131 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Class nrps_info, instances of which represent a names and roles provisioning service for a resource.
*
* For information about Names and Role Provisioning Services 2.0, see http://www.imsglobal.org/spec/lti-nrps/v2p0.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */
class nrps_info {
/** @var \moodle_url the memberships URL for the service. */
private $contextmembershipsurl;
/** @var float[] the array of supported service versions. */
private $serviceversions;
// Service versions are specified by the platform during launch.
// See http://www.imsglobal.org/spec/lti-nrps/v2p0#lti-1-3-integration.
/** @var string version 1.0 */
private const SERVICE_VERSION_1 = '1.0';
/** @var string version 2.0 */
private const SERVICE_VERSION_2 = '2.0';
// Scope that must be requested as part of making a service call.
// See: http://www.imsglobal.org/spec/lti-nrps/v2p0#lti-1-3-integration.
/** @var string the scope to request to make service calls. */
private $servicescope = 'https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly';
/**
* The private nrps_info constructor.
*
* @param \moodle_url $contextmembershipsurl the memberships URL.
* @param string[] $serviceversions the supported service versions.
*/
private function __construct(\moodle_url $contextmembershipsurl, array $serviceversions = [self::SERVICE_VERSION_2]) {
$this->contextmembershipsurl = $contextmembershipsurl;
$this->set_service_versions($serviceversions);
}
/**
* Factory method to create a new nrps_info instance.
*
* @param \moodle_url $contextmembershipsurl the memberships URL.
* @param string[] $serviceversions the supported service versions.
* @return nrps_info the object instance.
*/
public static function create(\moodle_url $contextmembershipsurl,
array $serviceversions = [self::SERVICE_VERSION_2]): nrps_info {
return new self($contextmembershipsurl, $serviceversions);
}
/**
* Check whether the supplied service version is valid or not.
*
* @param string $serviceversion the service version to check.
* @return bool true if valid, false otherwise.
*/
private function is_valid_service_version(string $serviceversion): bool {
$validversions = [
self::SERVICE_VERSION_1,
self::SERVICE_VERSION_2
];
return in_array($serviceversion, $validversions);
}
/**
* Tries to set the supported service versions for this instance.
*
* @param array $serviceversions the service versions to set.
* @throws \coding_exception if any of the supplied versions are not valid.
*/
private function set_service_versions(array $serviceversions): void {
if (empty($serviceversions)) {
throw new \coding_exception('Service versions array cannot be empty');
}
$serviceversions = array_unique($serviceversions);
foreach ($serviceversions as $serviceversion) {
if (!$this->is_valid_service_version($serviceversion)) {
throw new \coding_exception("Invalid Names and Roles service version '{$serviceversion}'");
}
}
$this->serviceversions = $serviceversions;
}
/**
* Get the service URL for this grade service instance.
*
* @return \moodle_url the service URL.
*/
public function get_context_memberships_url(): \moodle_url {
return $this->contextmembershipsurl;
}
/**
* Get the supported service versions for this grade service instance.
*
* @return string[] the array of supported service versions.
*/
public function get_service_versions(): array {
return $this->serviceversions;
}
/**
* Get the nrps service scope.
*
* @return string the service scope.
*/
public function get_service_scope(): string {
return $this->servicescope;
}
}

View File

@ -0,0 +1,69 @@
<?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 enrol_lti\local\ltiadvantage\entity;
use moodle_url;
/**
* Class registration_url, representing a single dynamic registration URL.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class registration_url extends moodle_url {
/** @var string the address of the registration URL. */
protected $address;
/** @var string the random token used to secure this registration URL. */
protected $token;
/** @var int Unix time at which this registration URL is no longer valid. */
protected $expirytime;
/**
* Constructor.
*
* @param int $expirytime the unix time after which the URL is deemed invalid.
* @param string|null $token the unique token securing requests to the URL.
* @throws \coding_exception if the token or expiry time is invalid.
*/
public function __construct(int $expirytime, string $token = null) {
global $CFG;
if ($expirytime < 0) {
throw new \coding_exception('Invalid registration_url expiry time. Must be greater than or equal to 0.');
}
$this->address = $CFG->wwwroot . '/enrol/lti/register.php';
$this->expirytime = $expirytime;
if (is_null($token)) {
$bytes = random_bytes(30);
$token = bin2hex($bytes);
}
$this->token = $token;
parent::__construct($this->address, ['token' => $this->token], null);
}
/**
* Get the expiry time of this registration_url instance.
*
* @return int the unix time of the expiry.
*/
public function get_expiry_time(): int {
return $this->expirytime;
}
}

View File

@ -0,0 +1,231 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Class resource_link.
*
* This class represents an LTI Advantage Resource Link (http://www.imsglobal.org/spec/lti/v1p3/#resource-link).
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class resource_link {
/** @var int|null the id of this object, or null if the object hasn't been stored yet. */
private $id;
/** @var string resourcelinkid the id of the resource link as supplied by the platform. */
private $resourcelinkid;
/** @var int $deploymentid the local id of the deployment instance to which this resource link belongs. */
private $deploymentid;
/** @var int|null $contextid the id of local context object representing the platform context, or null. */
private $contextid;
/** @var int The id of the local published resource this resource_link points to. */
private $resourceid;
/** @var ags_info|null the grade service for this resource_link, null if not applicable/not provided. */
private $gradeservice;
/** @var nrps_info|null the names and roles service for this resource_link, null if not applicable/not provided. */
private $namesrolesservice;
/**
* The private resource_link constructor.
*
* @param string $resourcelinkid the id of the resource link as supplied by the platform.
* @param int $deploymentid the local id of the deployment instance to which this resource link belongs.
* @param int $resourceid the id of the local resource to which this link refers.
* @param int|null $contextid the id local context object representing the context within the platform.
* @param int|null $id the local id of this resource_link object.
* @throws \coding_exception if the instance is unable to be created.
*/
private function __construct(string $resourcelinkid, int $deploymentid, int $resourceid, ?int $contextid = null,
int $id = null) {
if (empty($resourcelinkid)) {
throw new \coding_exception('Error: resourcelinkid cannot be an empty string');
}
$this->resourcelinkid = $resourcelinkid;
$this->deploymentid = $deploymentid;
$this->resourceid = $resourceid;
$this->contextid = $contextid;
$this->id = $id;
$this->gradeservice = null;
$this->namesrolesservice = null;
}
/**
* Factory method to create an instance.
*
* @param string $resourcelinkid the resourcelinkid, as provided by the platform.
* @param int $deploymentid the local id of the deployment to which this resource link belongs.
* @param int $resourceid the id of the local resource this resource_link refers to.
* @param int|null $contextid the id of the local context object representing the platform context.
* @param int|null $id the local id of the resource link instance.
* @return resource_link the newly created instance.
*/
public static function create(string $resourcelinkid, int $deploymentid, int $resourceid, ?int $contextid = null,
int $id = null): resource_link {
return new self($resourcelinkid, $deploymentid, $resourceid, $contextid, $id);
}
/**
* Return the id of this object instance.
*
* @return null|int the id or null if the object has not yet been stored.
*/
public function get_id(): ?int {
return $this->id;
}
/**
* Get the resourcelinkid as provided by the platform.
*
* @return string the resourcelinkid.
*/
public function get_resourcelinkid(): string {
return $this->resourcelinkid;
}
/**
* Return the id of the deployment to which this resource link belongs.
*
* This id is the local id of the deployment instance, not the deploymentid provided by the platform.
*
* @return int the deployment id.
*/
public function get_deploymentid(): int {
return $this->deploymentid;
}
/**
* Get the local id of the published resource to which this resource link refers.
*
* @return int the id of the published resource.
*/
public function get_resourceid(): int {
return $this->resourceid;
}
/**
* Return the id of the context object holding information about where this resource link resides.
*
* @return int|null the id or null if not present.
*/
public function get_contextid(): ?int {
return $this->contextid;
}
/**
* Link this resource_link instance with a context.
*
* @param int $contextid the local id of the context instance containing information about the platform context.
*/
public function set_contextid(int $contextid): void {
if ($contextid <= 0) {
throw new \coding_exception('Context id must be a positive int');
}
$this->contextid = $contextid;
}
/**
* Set which local published resource this resource link refers to.
*
* @param int $resourceid the published resource id.
*/
public function set_resourceid(int $resourceid): void {
if ($resourceid <= 0) {
throw new \coding_exception('Resource id must be a positive int');
}
$this->resourceid = $resourceid;
}
/**
* Add grade service information to this resource_link instance.
*
* @param \moodle_url $lineitemsurl the service URL for get/put of line items.
* @param \moodle_url|null $lineitemurl the service URL if only a single line item is present in the platform.
* @param string[] $scopes the string array of grade service scopes which may be used by the service.
*/
public function add_grade_service(\moodle_url $lineitemsurl, ?\moodle_url $lineitemurl = null, array $scopes = []) {
$this->gradeservice = ags_info::create($lineitemsurl, $lineitemurl, $scopes);
}
/**
* Get the grade service attached to this resource_link instance, or null if there isn't one associated.
*
* @return ags_info|null the grade service object instance, or null if not found.
*/
public function get_grade_service(): ?ags_info {
return $this->gradeservice;
}
/**
* Add names and roles service information to this resource_link instance.
*
* @param \moodle_url $contextmembershipurl the service URL for memberships.
* @param string[] $serviceversions the string array of supported service versions.
*/
public function add_names_and_roles_service(\moodle_url $contextmembershipurl, array $serviceversions): void {
$this->namesrolesservice = nrps_info::create($contextmembershipurl, $serviceversions);
}
/**
* Get the names and roles service attached to this resource_link instance, or null if there isn't one associated.
*
* @return nrps_info|null the names and roles service object instance, or null if not found.
*/
public function get_names_and_roles_service(): ?nrps_info {
return $this->namesrolesservice;
}
/**
* Factory method to create a user from this resource_link instance.
*
* This is useful for associating the user with the resource link and resource I.e. the user was created when
* launching a specific resource link.
*
* @param int $userid the id of the Moodle user record.
* @param string $sourceid the id of the user on the platform.
* @param string $lang the user's lang code.
* @param string $city the user's city.
* @param string $country the user's country.
* @param string $institution the user's institution.
* @param string $timezone the user's timezone.
* @param int|null $maildisplay the user's maildisplay, which can be omitted to use sensible defaults.
* @return user the user instance.
* @throws \coding_exception if trying to add a user to an as-yet-unsaved resource_link instance.
*/
public function add_user(int $userid, string $sourceid, string $lang,
string $city, string $country, string $institution, string $timezone,
?int $maildisplay = null): user {
if (empty($this->get_id())) {
throw new \coding_exception('Can\'t add user to a resource_link that hasn\'t first been saved');
}
return user::create_from_resource_link($this->get_id(), $this->get_resourceid(), $userid,
$this->get_deploymentid(), $sourceid, $lang, $timezone, $city, $country,
$institution, $maildisplay);
}
}

View File

@ -0,0 +1,420 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Class user, instances of which represent a specific lti user in the tool.
*
* A user is always associated with a resource, as lti users cannot exist without a tool-published-resource. A user will
* always come from either:
* - a resource link launch or
* - a membership sync
* Both of which required a published resource.
*
* Additionally, a user may be associated with a given resource_link instance, to signify that the user originated from
* that resource_link. If a user is not associated with a resource_link, such as when creating a user during a member
* sync, that param is nullable. This can be achieved via the factory method user::create_from_resource_link() or set
* after instantiation via the user::set_resource_link_id() method.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */
class user {
/** @var int the id of the published resource to which this user belongs. */
private $resourceid;
/** @var int the local id of the deployment instance to which this user belongs. */
private $deploymentid;
/** @var string the id of the user in the platform site. */
private $sourceid;
/** @var int|null the id of this user instance, or null if not stored yet. */
private $id;
/** @var int|null the id of the user in the tool site, or null if the instance hasn't been stored yet. */
private $localid;
/** @var string city of the user. */
private $city;
/** @var string country of the user. */
private $country;
/** @var string institution of the user.*/
private $institution;
/** @var string timezone of the user. */
private $timezone;
/** @var int maildisplay of the user. */
private $maildisplay;
/** @var string language code of the user. */
private $lang;
/** @var float the user's last grade value. */
private $lastgrade;
/** @var int|null the user's last access unix timestamp, or null if they have not accessed the resource yet.*/
private $lastaccess;
/** @var int|null the id of the resource_link instance, or null if the user doesn't originate from one. */
private $resourcelinkid;
/**
* Private constructor.
*
* @param int $resourceid the id of the published resource to which this user belongs.
* @param int $userid the id of the Moodle user to which this LTI user relates.
* @param int $deploymentid the local id of the deployment instance to which this user belongs.
* @param string $sourceid the id of the user in the platform site.
* @param string $lang the user's language code.
* @param string $city the user's city.
* @param string $country the user's country.
* @param string $institution the user's institution.
* @param string $timezone the user's timezone.
* @param int|null $maildisplay the user's maildisplay, or null to select defaults.
* @param float|null $lastgrade the user's last grade value.
* @param int|null $lastaccess the user's last access time, or null if they haven't accessed the resource.
* @param int|null $resourcelinkid the id of the resource link to link to the user, or null if not applicable.
* @param int|null $id the id of this object instance, or null if it's a not-yet-persisted object.
* @throws \coding_exception
*/
private function __construct(int $resourceid, int $userid, int $deploymentid, string $sourceid,
string $lang, string $city, string $country,
string $institution, string $timezone, ?int $maildisplay, ?float $lastgrade, ?int $lastaccess,
?int $resourcelinkid = null, ?int $id = null) {
global $CFG;
$this->resourceid = $resourceid;
$this->localid = $userid;
$this->deploymentid = $deploymentid;
if (empty($sourceid)) {
throw new \coding_exception('Invalid sourceid value. Cannot be an empty string.');
}
$this->sourceid = $sourceid;
$this->set_lang($lang);
$this->set_city($city);
$this->set_country($country);
$this->set_institution($institution);
$this->set_timezone($timezone);
$maildisplay = $maildisplay ?? ($CFG->defaultpreference_maildisplay ?? 2);
$this->set_maildisplay($maildisplay);
$this->lastgrade = $lastgrade ?? 0.0;
$this->lastaccess = $lastaccess;
$this->resourcelinkid = $resourcelinkid;
$this->id = $id;
}
/**
* Factory method for creating a user instance associated with a given resource_link instance.
*
* @param int $resourcelinkid the local id of the resource link instance to link to the user.
* @param int $resourceid the id of the published resource to which this user belongs.
* @param int $userid the id of the Moodle user to which this LTI user relates.
* @param int $deploymentid the local id of the deployment instance to which this user belongs.
* @param string $sourceid the id of the user in the platform site.
* @param string $lang the user's language code.
* @param string $timezone the user's timezone.
* @param string $city the user's city.
* @param string $country the user's country.
* @param string $institution the user's institution.
* @param int|null $maildisplay the user's maildisplay, or null to select defaults.
* @return user the user instance.
*/
public static function create_from_resource_link(int $resourcelinkid, int $resourceid, int $userid,
int $deploymentid, string $sourceid, string $lang, string $timezone,
string $city = '', string $country = '', string $institution = '',
?int $maildisplay = null): user {
return new self($resourceid, $userid, $deploymentid, $sourceid, $lang, $city,
$country, $institution, $timezone, $maildisplay, null, null, $resourcelinkid);
}
/**
* Factory method for creating a user.
*
* @param int $resourceid the id of the published resource to which this user belongs.
* @param int $userid the id of the Moodle user to which this LTI user relates.
* @param int $deploymentid the local id of the deployment instance to which this user belongs.
* @param string $sourceid the id of the user in the platform site.
* @param string $lang the user's language code.
* @param string $timezone the user's timezone.
* @param string $city the user's city.
* @param string $country the user's country.
* @param string $institution the user's institution.
* @param int|null $maildisplay the user's maildisplay, or null to select defaults.
* @param float|null $lastgrade the user's last grade value.
* @param int|null $lastaccess the user's last access time, or null if they haven't accessed the resource.
* @param int|null $resourcelinkid the local id of the resource link instance associated with the user.
* @param int|null $id the id of this lti user instance, or null if it's a not-yet-persisted object.
* @return user the user instance.
*/
public static function create(int $resourceid, int $userid, int $deploymentid, string $sourceid,
string $lang, string $timezone, string $city = '',
string $country = '', string $institution = '', ?int $maildisplay = null, ?float $lastgrade = null,
?int $lastaccess = null, ?int $resourcelinkid = null, int $id = null): user {
return new self($resourceid, $userid, $deploymentid, $sourceid, $lang, $city,
$country, $institution, $timezone, $maildisplay, $lastgrade, $lastaccess, $resourcelinkid, $id);
}
/**
* Get the id of this user instance.
*
* @return int|null the object id, or null if not yet persisted.
*/
public function get_id(): ?int {
return $this->id;
}
/**
* Get the id of the resource_link instance to which this user is associated.
*
* @return int|null the object id, or null if the user isn't associated with one.
*/
public function get_resourcelinkid(): ?int {
return $this->resourcelinkid;
}
/**
* Associate this user with the given resource_link instance, denoting that this user launched from the instance.
*
* @param int $resourcelinkid the id of the resource_link instance.
*/
public function set_resourcelinkid(int $resourcelinkid): void {
if ($resourcelinkid <= 0) {
throw new \coding_exception("Invalid resourcelinkid '$resourcelinkid' provided. Must be > 0.");
}
$this->resourcelinkid = $resourcelinkid;
}
/**
* Get the id of the published resource to which this user is associated.
*
* @return int the resource id.
*/
public function get_resourceid(): int {
return $this->resourceid;
}
/**
* Get the id of the deployment instance to which this user belongs.
*
* @return int id of the deployment instance.
*/
public function get_deploymentid(): int {
return $this->deploymentid;
}
/**
* Get the id of the user in the platform.
*
* @return string the source id.
*/
public function get_sourceid(): string {
return $this->sourceid;
}
/**
* Get the id of the user in the tool.
*
* @return int|null the id, or null if the object instance hasn't yet been persisted.
*/
public function get_localid(): ?int {
return $this->localid;
}
/**
* Get the user's city.
*
* @return string the city.
*/
public function get_city(): string {
return $this->city;
}
/**
* Set the user's city.
*
* @param string $city the city string.
*/
public function set_city(string $city): void {
$this->city = $city;
}
/**
* Get the user's country code.
*
* @return string the country code.
*/
public function get_country(): string {
return $this->country;
}
/**
* Set the user's country.
*
* @param string $countrycode the 2 digit country code representing the country, or '' to denote none.
*/
public function set_country(string $countrycode): void {
global $CFG;
require_once($CFG->libdir . '/moodlelib.php');
$validcountrycodes = array_merge([''], array_keys(get_string_manager()->get_list_of_countries(true)));
if (!in_array($countrycode, $validcountrycodes)) {
throw new \coding_exception("Invalid country code '$countrycode'.");
}
$this->country = $countrycode;
}
/**
* Get the instituation of the user.
*
* @return string the institution.
*/
public function get_institution(): string {
return $this->institution;
}
/**
* Set the user's institution.
*
* @param string $institution the name of the institution.
*/
public function set_institution(string $institution): void {
$this->institution = $institution;
}
/**
* Get the timezone of the user.
*
* @return string the user timezone.
*/
public function get_timezone(): string {
return $this->timezone;
}
/**
* Set the user's timezone, or set '99' to specify server timezone.
*
* @param string $timezone the timezone string, or '99' to use server timezone.
*/
public function set_timezone(string $timezone): void {
if (empty($timezone)) {
throw new \coding_exception('Invalid timezone value. Cannot be an empty string.');
}
$validtimezones = array_keys(\core_date::get_list_of_timezones(null, true));
if (!in_array($timezone, $validtimezones)) {
throw new \coding_exception("Invalid timezone '$timezone' provided.");
}
$this->timezone = $timezone;
}
/**
* Get the maildisplay of the user.
*
* @return int the maildisplay.
*/
public function get_maildisplay(): int {
return $this->maildisplay;
}
/**
* Set the user's mail display preference from a range of supported options.
*
* 0 - hide from non privileged users
* 1 - allow everyone to see
* 2 - allow only course participants to see
*
* @param int $maildisplay the maildisplay preference to set.
*/
public function set_maildisplay(int $maildisplay): void {
if (!in_array($maildisplay, range(0, 2))) {
throw new \coding_exception("Invalid maildisplay value '$maildisplay'. Must be in the range {0..2}.");
}
$this->maildisplay = $maildisplay;
}
/**
* Get the lang code of the user.
*
* @return string the user's language code.
*/
public function get_lang(): string {
return $this->lang;
}
/**
* Set the user's language.
*
* @param string $langcode the language code representing the user's language.
*/
public function set_lang(string $langcode): void {
if (empty($langcode)) {
throw new \coding_exception('Invalid lang value. Cannot be an empty string.');
}
$validlangcodes = array_keys(get_string_manager()->get_list_of_translations());
if (!in_array($langcode, $validlangcodes)) {
throw new \coding_exception("Invalid lang '$langcode' provided.");
}
$this->lang = $langcode;
}
/**
* Get the last grade value for this user.
*
* @return float the float grade.
*/
public function get_lastgrade(): float {
return $this->lastgrade;
}
/**
* Set the last grade for the user.
*
* @param float $lastgrade the last grade the user received.
*/
public function set_lastgrade(float $lastgrade): void {
global $CFG;
require_once($CFG->libdir . '/gradelib.php');
$this->lastgrade = grade_floatval($lastgrade);
}
/**
* Get the last access timestamp for this user.
*
* @return int|null the last access timestampt, or null if the user hasn't accessed the resource.
*/
public function get_lastaccess(): ?int {
return $this->lastaccess;
}
/**
* Set the last access time for the user.
*
* @param int $time unix timestamp representing the last time the user accessed the published resource.
* @throws \coding_exception if $time is not a positive int.
*/
public function set_lastaccess(int $time): void {
if ($time < 0) {
throw new \coding_exception('Cannot set negative access time');
}
$this->lastaccess = $time;
}
}

View File

@ -0,0 +1,276 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Tests for ags_info.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \enrol_lti\local\ltiadvantage\entity\ags_info
*/
class ags_info_test extends \advanced_testcase {
/**
* Test creation of the object instances.
* @dataProvider instantiation_data_provider
* @param array $args the arguments to the creation method.
* @param array $expectations various expectations for the test cases.
* @covers ::create
*/
public function test_creation(array $args, array $expectations) {
if (!$expectations['valid']) {
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
ags_info::create(...array_values($args));
} else {
$agsinfo = ags_info::create(...array_values($args));
$this->assertEquals($args['lineitemsurl'], $agsinfo->get_lineitemsurl());
$this->assertEquals($args['lineitemurl'], $agsinfo->get_lineitemurl());
$this->assertEquals($args['scopes'], $agsinfo->get_scopes());
$this->assertEquals($expectations['lineitemscope'], $agsinfo->get_lineitemscope());
$this->assertEquals($expectations['scorescope'], $agsinfo->get_scorescope());
$this->assertEquals($expectations['resultscope'], $agsinfo->get_resultscope());
}
}
/**
* Data provider for testing object instantiation.
* @return array the data for testing.
*/
public function instantiation_data_provider(): array {
return [
'Both lineitems and lineitem URL provided with full list of valid scopes' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/score'
]
],
'expectations' => [
'valid' => true,
'lineitemscope' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly'
],
'resultscope' => 'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
'scorescope' => 'https://purl.imsglobal.org/spec/lti-ags/scope/score'
]
],
'Both lineitems and lineitem URL provided with lineitem scopes only' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
]
],
'expectations' => [
'valid' => true,
'lineitemscope' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
],
'scorescope' => null,
'resultscope' => null
]
],
'Both lineitems and lineitem URL provided with score scope only' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/score'
]
],
'expectations' => [
'valid' => true,
'lineitemscope' => null,
'scorescope' => 'https://purl.imsglobal.org/spec/lti-ags/scope/score',
'resultscope' => null
]
],
'Both lineitems and lineitem URL provided with result scope only' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly'
]
],
'expectations' => [
'valid' => true,
'lineitemscope' => null,
'scorescope' => null,
'resultscope' => 'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly'
]
],
'Both lineitems and lineitem URL provided with no scopes' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
'scopes' => []
],
'expectations' => [
'valid' => true,
'lineitemscope' => null,
'scorescope' => null,
'resultscope' => null
]
],
'Just lineitems URL, no lineitem URL, with full list of valid scopes' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => null,
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/score'
]
],
'expectations' => [
'valid' => true,
'lineitemscope' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly'
],
'resultscope' => 'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
'scorescope' => 'https://purl.imsglobal.org/spec/lti-ags/scope/score'
]
],
'Just lineitems URL, no lineitem URL, with lineitems scopes only' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => null,
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
]
],
'expectations' => [
'valid' => true,
'lineitemscope' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
],
'scorescope' => null,
'resultscope' => null
]
],
'Just lineitems URL, no lineitem URL, with score scope only' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => null,
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/score'
]
],
'expectations' => [
'valid' => true,
'lineitemscope' => null,
'scorescope' => 'https://purl.imsglobal.org/spec/lti-ags/scope/score',
'resultscope' => null
]
],
'Just lineitems URL, no lineitem URL, with result scope only' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => null,
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly'
]
],
'expectations' => [
'valid' => true,
'lineitemscope' => null,
'scorescope' => null,
'resultscope' => 'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly'
]
],
'Just lineitems URL, no lineitem URL, with no scopes' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => null,
'scopes' => []
],
'expectations' => [
'valid' => true,
'lineitemscope' => null,
'scorescope' => null,
'resultscope' => null
]
],
'Both lineitems and lineitem URL provided with non-string scope' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/score',
12345
]
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => 'Scope must be a string value'
]
],
'Both lineitems and lineitem URL provided with invalid scopes' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
'scopes' => [
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
'https://purl.imsglobal.org/spec/lti-ags/scope/score',
'https://example.com/invalid/scope'
]
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Scope 'https://example.com/invalid/scope' is invalid."
]
],
'Both lineitems and lineitem URL provided with invalid scope types' => [
'args' => [
'lineitemsurl' => new \moodle_url('https://platform.example.org/10/lineitems'),
'lineitemurl' => new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
'scopes' => [
12
]
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Scope must be a string value"
]
],
];
}
}

View File

@ -0,0 +1,241 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Tests for application_registration.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \enrol_lti\local\ltiadvantage\entity\application_registration
*/
class application_registration_test extends \advanced_testcase {
/**
* Test the creation of an application_registration instance.
*
* @dataProvider creation_data_provider
* @param array $args the arguments to the creation method.
* @param array $expectations various expectations for the test cases.
* @covers ::create
*/
public function test_creation(array $args, array $expectations) {
if ($expectations['valid']) {
$reg = application_registration::create(...array_values($args));
$this->assertEquals($args['name'], $reg->get_name());
$this->assertEquals($args['platformid'], $reg->get_platformid());
$this->assertEquals($args['clientid'], $reg->get_clientid());
$this->assertEquals($args['authrequesturl'], $reg->get_authenticationrequesturl());
$this->assertEquals($args['jwksurl'], $reg->get_jwksurl());
$this->assertEquals($args['accesstokenurl'], $reg->get_accesstokenurl());
$expectedid = $args['id'] ?? null;
$this->assertEquals($expectedid, $reg->get_id());
} else {
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
application_registration::create(...array_values($args));
}
}
/**
* Data provider for testing the creation of application_registration instances.
*
* @return array the data for testing.
*/
public function creation_data_provider(): array {
return [
'Valid, only required args provided' => [
'args' => [
'name' => 'Platform X',
'platformid' => new \moodle_url('https://lms.example.com'),
'clientid' => 'client-id-12345',
'authrequesturl' => new \moodle_url('https://lms.example.com/auth'),
'jwksurl' => new \moodle_url('https://lms.example.com/jwks'),
'accesstokenurl' => new \moodle_url('https://lms.example.com/token'),
],
'expectations' => [
'valid' => true
]
],
'Valid, all args provided' => [
'args' => [
'name' => 'Platform X',
'platformid' => new \moodle_url('https://lms.example.com'),
'clientid' => 'client-id-12345',
'authrequesturl' => new \moodle_url('https://lms.example.com/auth'),
'jwksurl' => new \moodle_url('https://lms.example.com/jwks'),
'accesstokenurl' => new \moodle_url('https://lms.example.com/token'),
'id' => 24
],
'expectations' => [
'valid' => true
]
],
'Invalid, empty name provided' => [
'args' => [
'name' => '',
'platformid' => new \moodle_url('https://lms.example.com'),
'clientid' => 'client-id-12345',
'authrequesturl' => new \moodle_url('https://lms.example.com/auth'),
'jwksurl' => new \moodle_url('https://lms.example.com/jwks'),
'accesstokenurl' => new \moodle_url('https://lms.example.com/token'),
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid 'name' arg. Cannot be an empty string."
]
],
'Invalid, empty clientid provided' => [
'args' => [
'name' => 'Platform X',
'platformid' => new \moodle_url('https://lms.example.com'),
'clientid' => '',
'authrequesturl' => new \moodle_url('https://lms.example.com/auth'),
'jwksurl' => new \moodle_url('https://lms.example.com/jwks'),
'accesstokenurl' => new \moodle_url('https://lms.example.com/token'),
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid 'clientid' arg. Cannot be an empty string."
]
]
];
}
/**
* Test the factory method for creating a tool deployment associated with the registration instance.
*
* @dataProvider add_tool_deployment_data_provider
* @param array $args the arguments to the creation method.
* @param array $expectations various expectations for the test cases.
* @covers ::add_tool_deployment
*/
public function test_add_tool_deployment(array $args, array $expectations) {
if ($expectations['valid']) {
$reg = application_registration::create(...array_values($args['registration']));
$deployment = $reg->add_tool_deployment(...array_values($args['deployment']));
$this->assertInstanceOf(deployment::class, $deployment);
$this->assertEquals($args['deployment']['name'], $deployment->get_deploymentname());
$this->assertEquals($args['deployment']['deploymentid'], $deployment->get_deploymentid());
$this->assertEquals($reg->get_id(), $deployment->get_registrationid());
} else {
$reg = application_registration::create(...array_values($args['registration']));
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
$reg->add_tool_deployment(...array_values($args['deployment']));
}
}
/**
* Data provider for testing the add_tool_deployment method.
*
* @return array the array of test data.
*/
public function add_tool_deployment_data_provider(): array {
return [
'Valid, contains id on registration and valid deployment data provided' => [
'args' => [
'registration' => [
'name' => 'Platform X',
'platformid' => new \moodle_url('https://lms.example.com'),
'clientid' => 'client-id-12345',
'authrequesturl' => new \moodle_url('https://lms.example.com/auth'),
'jwksurl' => new \moodle_url('https://lms.example.com/jwks'),
'accesstokenurl' => new \moodle_url('https://lms.example.com/token'),
'id' => 24
],
'deployment' => [
'name' => 'Deployment at site level',
'deploymentid' => 'id-123abc'
]
],
'expectations' => [
'valid' => true,
]
],
'Invalid, missing id on registration' => [
'args' => [
'registration' => [
'name' => 'Platform X',
'platformid' => new \moodle_url('https://lms.example.com'),
'clientid' => 'client-id-12345',
'authrequesturl' => new \moodle_url('https://lms.example.com/auth'),
'jwksurl' => new \moodle_url('https://lms.example.com/jwks'),
'accesstokenurl' => new \moodle_url('https://lms.example.com/token'),
],
'deployment' => [
'name' => 'Deployment at site level',
'deploymentid' => 'id-123abc'
]
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Can't add deployment to a resource_link that hasn't first been saved."
]
],
'Invalid, id present on registration but empty deploymentname' => [
'args' => [
'registration' => [
'name' => 'Platform X',
'platformid' => new \moodle_url('https://lms.example.com'),
'clientid' => 'client-id-12345',
'authrequesturl' => new \moodle_url('https://lms.example.com/auth'),
'jwksurl' => new \moodle_url('https://lms.example.com/jwks'),
'accesstokenurl' => new \moodle_url('https://lms.example.com/token'),
'id' => 24
],
'deployment' => [
'name' => '',
'deploymentid' => 'id-123abc'
]
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid 'deploymentname' arg. Cannot be an empty string."
]
],
'Invalid, id present on registration but empty deploymentid' => [
'args' => [
'registration' => [
'name' => 'Platform X',
'platformid' => new \moodle_url('https://lms.example.com'),
'clientid' => 'client-id-12345',
'authrequesturl' => new \moodle_url('https://lms.example.com/auth'),
'jwksurl' => new \moodle_url('https://lms.example.com/jwks'),
'accesstokenurl' => new \moodle_url('https://lms.example.com/token'),
'id' => 24
],
'deployment' => [
'name' => 'Site deployment',
'deploymentid' => ''
]
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid 'deploymentid' arg. Cannot be an empty string."
]
]
];
}
}

View File

@ -0,0 +1,206 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Tests for context.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \enrol_lti\local\ltiadvantage\entity\context
*/
class context_test extends \advanced_testcase {
/**
* Test creation of the object instances.
*
* @dataProvider instantiation_data_provider
* @param array $args the arguments to the creation method.
* @param array $expectations various expectations for the test cases.
* @covers ::create
*/
public function test_creation(array $args, array $expectations) {
if (!$expectations['valid']) {
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
context::create(...array_values($args));
} else {
$context = context::create(...array_values($args));
$this->assertEquals($args['deploymentid'], $context->get_deploymentid());
$this->assertEquals($args['contextid'], $context->get_contextid());
$this->assertEquals($args['types'], $context->get_types());
$this->assertEquals($args['id'], $context->get_id());
}
}
/**
* Data provider for testing object instantiation.
* @return array[] the data for testing.
*/
public function instantiation_data_provider(): array {
return [
'Creation of a course section context' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'http://purl.imsglobal.org/vocab/lis/v2/course#CourseSection'
],
'id' => null
],
'expectations' => [
'valid' => true,
]
],
'Creation of a course offering context' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'http://purl.imsglobal.org/vocab/lis/v2/course#CourseOffering'
],
'id' => null
],
'expectations' => [
'valid' => true,
]
],
'Creation of a course template context' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'http://purl.imsglobal.org/vocab/lis/v2/course#CourseTemplate'
],
'id' => null
],
'expectations' => [
'valid' => true,
]
],
'Creation of a course group context' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'http://purl.imsglobal.org/vocab/lis/v2/course#Group'
],
'id' => null
],
'expectations' => [
'valid' => true,
]
],
'Creation of an invalid context' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'http://example.com/invalid/context'
],
'id' => null
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Cannot set invalid context type 'http://example.com/invalid/context'."
]
],
'Creation of a simple name context with type CourseTemplate' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'CourseTemplate'
],
'id' => null
],
'expectations' => [
'valid' => true,
]
],
'Creation of a simple name context with type CourseOffering' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'CourseOffering'
],
'id' => null
],
'expectations' => [
'valid' => true,
]
],
'Creation of a simple name context with type CourseSection' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'CourseSection'
],
'id' => null
],
'expectations' => [
'valid' => true,
]
],
'Creation of a simple name context with type Group' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'Group'
],
'id' => null
],
'expectations' => [
'valid' => true,
]
],
'Creation of a context with id' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'Group'
],
'id' => 24
],
'expectations' => [
'valid' => true,
]
],
'Creation of a context with invalid id' => [
'args' => [
'deploymentid' => 24,
'contextid' => 'context-123',
'types' => [
'Group'
],
'id' => 0
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "id must be a positive int"
]
],
];
}
}

View File

@ -0,0 +1,190 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Tests for deployment.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \enrol_lti\local\ltiadvantage\entity\deployment
*/
class deployment_test extends \advanced_testcase {
/**
* Test creation of the object instances.
*
* @dataProvider instantiation_data_provider
* @param array $args the arguments to the creation method.
* @param array $expectations various expectations for the test cases.
* @covers ::create
*/
public function test_creation(array $args, array $expectations) {
if (!$expectations['valid']) {
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
deployment::create(...array_values($args));
} else {
$deployment = deployment::create(...array_values($args));
$this->assertEquals($args['deploymentname'], $deployment->get_deploymentname());
$this->assertEquals($args['deploymentid'], $deployment->get_deploymentid());
$this->assertEquals($args['registrationid'], $deployment->get_registrationid());
$this->assertEquals($args['legacyconsumerkey'], $deployment->get_legacy_consumer_key());
$this->assertEquals($args['id'], $deployment->get_id());
}
}
/**
* Data provider for testing object instantiation.
* @return array the data for testing.
*/
public function instantiation_data_provider(): array {
return [
'Valid deployment creation, no id or legacy consumer key' => [
'args' => [
'registrationid' => 99,
'deploymentid' => 'deployment-abcde',
'deploymentname' => 'Global platform deployment',
'id' => null,
'legacyconsumerkey' => null,
],
'expectations' => [
'valid' => true,
]
],
'Valid deployment creation, with id, no legacy consumer key' => [
'args' => [
'registrationid' => 99,
'deploymentid' => 'deployment-abcde',
'deploymentname' => 'Global platform deployment',
'id' => 45,
'legacyconsumerkey' => null,
],
'expectations' => [
'valid' => true,
]
],
'Valid deployment creation, with id and legacy consumer key' => [
'args' => [
'registrationid' => 99,
'deploymentid' => 'deployment-abcde',
'deploymentname' => 'Global platform deployment',
'id' => 45,
'legacyconsumerkey' => '1bcb23dfff',
],
'expectations' => [
'valid' => true,
]
],
'Invalid deployment creation, invalid id' => [
'args' => [
'registrationid' => 99,
'deploymentid' => 'deployment-abcde',
'deploymentname' => 'Global platform deployment',
'id' => 0,
'legacyconsumerkey' => null,
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => 'id must be a positive int'
]
],
'Invalid deployment creation, empty deploymentname' => [
'args' => [
'registrationid' => 99,
'deploymentid' => 'deployment-abcde',
'deploymentname' => '',
'id' => null,
'legacyconsumerkey' => null,
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid 'deploymentname' arg. Cannot be an empty string."
]
],
'Invalid deployment creation, empty deploymentid' => [
'args' => [
'registrationid' => 99,
'deploymentid' => '',
'deploymentname' => 'Global platform deployment',
'id' => null,
'legacyconsumerkey' => null,
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid 'deploymentid' arg. Cannot be an empty string."
]
]
];
}
/**
* Test verifying that a context can only be created from a deployment that has an id.
*
* @covers ::add_context
*/
public function test_add_context() {
$deploymentwithid = deployment::create(123, 'deploymentid123', 'Global tool deployment', 55);
$context = $deploymentwithid->add_context('context-id-123', ['CourseSection']);
$this->assertInstanceOf(context::class, $context);
$this->assertEquals(55, $context->get_deploymentid());
$deploymentwithoutid = deployment::create(123, 'deploymentid123', 'Global tool deployment');
$this->expectException(\coding_exception::class);
$this->expectExceptionMessage("Can't add context to a deployment that hasn't first been saved");
$deploymentwithoutid->add_context('context-id-345', ['Group']);
}
/**
* Test verifying that a resource_link can only be created from a deployment that has an id.
*
* @covers ::add_resource_link
*/
public function test_add_resource_link() {
$deploymentwithid = deployment::create(123, 'deploymentid123', 'Global tool deployment', 55);
$resourcelink = $deploymentwithid->add_resource_link('res-link-id-123', 45);
$this->assertInstanceOf(resource_link::class, $resourcelink);
$this->assertEquals(55, $resourcelink->get_deploymentid());
$resourcelink2 = $deploymentwithid->add_resource_link('res-link-id-456', 45, 66);
$this->assertEquals(66, $resourcelink2->get_contextid());
$deploymentwithoutid = deployment::create(123, 'deploymentid123', 'Global tool deployment');
$this->expectException(\coding_exception::class);
$this->expectExceptionMessage("Can't add resource_link to a deployment that hasn't first been saved");
$deploymentwithoutid->add_resource_link('res-link-id-123', 45);
}
/**
* Test the setter set_legacy_consumer_key.
*
* @covers ::set_legacy_consumer_key
*/
public function test_set_legacy_consumer_key() {
$deployment = deployment::create(12, 'deploy-id-123', 'Global tool deployment');
$deployment->set_legacy_consumer_key(str_repeat('a', 255));
$this->assertEquals(str_repeat('a', 255), $deployment->get_legacy_consumer_key());
$this->expectException(\coding_exception::class);
$this->expectExceptionMessage('Legacy consumer key too long. Cannot exceed 255 chars.');
$deployment->set_legacy_consumer_key(str_repeat('a', 256));
}
}

View File

@ -0,0 +1,193 @@
<?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 enrol_lti\local\ltiadvantage\entity;
use enrol_lti\local\ltiadvantage\repository\legacy_consumer_repository;
/**
* Tests for migration_claim.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \enrol_lti\local\ltiadvantage\entity\migration_claim
*/
class migration_claim_test extends \advanced_testcase {
/**
* Setup run for each test case.
*/
protected function setUp(): void {
$this->resetAfterTest();
}
/**
* Returns a stub legacy_consumer_repository, allowing tests to verify claims using a predefined secret.
*/
protected function get_stub_legacy_consumer_repo() {
$mockedlegacyconsumerrepo = $this->createStub(legacy_consumer_repository::class);
$mockedlegacyconsumerrepo->method('get_consumer_secrets')
->willReturn(['consumer_secret']);
return $mockedlegacyconsumerrepo;
}
/**
* Test instantiation and getters of the migration_claim.
*
* @dataProvider migration_claim_provider
* @param array $migrationclaimdata the lti1p1 migration claim.
* @param string $deploymentid string id of the tool deployment.
* @param string $platform string url of the issuer.
* @param string $clientid string id of the client.
* @param string $exp expiry time.
* @param string $nonce nonce.
* @param legacy_consumer_repository $legacyconsumerrepo legacy consumer repo instance.
* @param array $expected array containing expectation data.
* @covers ::__construct
*/
public function test_migration_claim(array $migrationclaimdata, string $deploymentid, string $platform,
string $clientid, string $exp, string $nonce, legacy_consumer_repository $legacyconsumerrepo,
array $expected) {
if (!empty($expected['exception'])) {
$this->expectException($expected['exception']);
$this->expectExceptionMessage($expected['exceptionmessage']);
new migration_claim($migrationclaimdata, $deploymentid, $platform, $clientid, $exp, $nonce,
$legacyconsumerrepo);
} else {
$migrationclaim = new migration_claim($migrationclaimdata, $deploymentid, $platform, $clientid, $exp,
$nonce, $legacyconsumerrepo);
$this->assertInstanceOf(migration_claim::class, $migrationclaim);
$this->assertEquals($expected['user_id'], $migrationclaim->get_user_id());
$this->assertEquals($expected['context_id'], $migrationclaim->get_context_id());
$this->assertEquals($expected['tool_consumer_instance_guid'],
$migrationclaim->get_tool_consumer_instance_guid());
$this->assertEquals($expected['resource_link_id'], $migrationclaim->get_resource_link_id());
}
}
/**
* Data provider testing migration_claim instantiation.
*
* @return array[] the test case data.
*/
public function migration_claim_provider(): array {
// Note: See https://www.imsglobal.org/spec/lti/v1p3/migr#lti-1-1-migration-claim for details regarding the
// correct generation of oauth_consumer_key_sign signature.
return [
'Invalid - missing oauth_consumer_key' => [
'lti1p1migrationclaim' => [
'oauth_consumer_key' => '',
'oauth_consumer_key_sign' => 'abcd',
],
'deploymentid' => 'D12345',
'platform' => 'https://lms.example.org/',
'clientid' => 'a1b2c3d4',
'exp' => '1622612930',
'nonce' => 'j45j2j5nnjn24544',
new legacy_consumer_repository(),
'expected' => [
'exception' => \coding_exception::class,
'exceptionmessage' => "Missing 'oauth_consumer_key' property in lti1p1 migration claim."
]
],
'Invalid - missing oauth_consumer_key_sign' => [
'lti1p1migrationclaim' => [
'oauth_consumer_key' => 'CONSUMER_1',
'oauth_consumer_key_sign' => '',
],
'deploymentid' => 'D12345',
'platform' => 'https://lms.example.org/',
'clientid' => 'a1b2c3d4',
'exp' => '1622612930',
'nonce' => 'j45j2j5nnjn24544',
new legacy_consumer_repository(),
'expected' => [
'exception' => \coding_exception::class,
'exceptionmessage' => "Missing 'oauth_consumer_key_sign' property in lti1p1 migration claim."
]
],
'Invalid - incorrect oauth_consumer_key_sign' => [
'lti1p1migrationclaim' => [
'oauth_consumer_key' => 'CONSUMER_1',
'oauth_consumer_key_sign' => 'badsignature',
],
'deploymentid' => 'D12345',
'platform' => 'https://lms.example.org/',
'clientid' => 'a1b2c3d4',
'exp' => '1622612930',
'nonce' => 'j45j2j5nnjn24544',
new legacy_consumer_repository(),
'expected' => [
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid 'oauth_consumer_key_sign' signature in lti1p1 claim."
]
],
'Valid - signature valid, map properties not provided' => [
'lti1p1migrationclaim' => [
'oauth_consumer_key' => 'CONSUMER_1',
'oauth_consumer_key_sign' => base64_encode(
hash_hmac(
'sha256',
'CONSUMER_1&D12345&https://lms.example.org/&a1b2c3d4&1622612930&j45j2j5nnjn24544',
'consumer_secret'
)
),
],
'deploymentid' => 'D12345',
'platform' => 'https://lms.example.org/',
'clientid' => 'a1b2c3d4',
'exp' => '1622612930',
'nonce' => 'j45j2j5nnjn24544',
$this->get_stub_legacy_consumer_repo(),
'expected' => [
'user_id' => null,
'context_id' => null,
'tool_consumer_instance_guid' => null,
'resource_link_id' => null
]
],
'Valid - signature valid, map properties are provided' => [
'lti1p1migrationclaim' => [
'oauth_consumer_key' => 'CONSUMER_1',
'oauth_consumer_key_sign' => base64_encode(
hash_hmac(
'sha256',
'CONSUMER_1&D12345&https://lms.example.org/&a1b2c3d4&1622612930&j45j2j5nnjn24544',
'consumer_secret'
)
),
'user_id' => '24',
'context_id' => 'd345b',
'tool_consumer_instance_guid' => '12345-123',
'resource_link_id' => '4b6fa'
],
'deploymentid' => 'D12345',
'platform' => 'https://lms.example.org/',
'clientid' => 'a1b2c3d4',
'exp' => '1622612930',
'nonce' => 'j45j2j5nnjn24544',
$this->get_stub_legacy_consumer_repo(),
'expected' => [
'user_id' => '24',
'context_id' => 'd345b',
'tool_consumer_instance_guid' => '12345-123',
'resource_link_id' => '4b6fa'
]
]
];
}
}

View File

@ -0,0 +1,101 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Tests for nrps_info.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \enrol_lti\local\ltiadvantage\entity\nrps_info
*/
class nrps_info_test extends \advanced_testcase {
/**
* Test creation of the object instances.
*
* @dataProvider instantiation_data_provider
* @param array $args the arguments to the creation method.
* @param array $expectations various expectations for the test cases.
* @covers ::create
*/
public function test_create(array $args, array $expectations) {
if (!$expectations['valid']) {
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
nrps_info::create(...array_values($args));
} else {
$nrpsinfo = nrps_info::create(...array_values($args));
$this->assertEquals($args['contextmembershipsurl'], $nrpsinfo->get_context_memberships_url());
$this->assertEquals($expectations['serviceversions'], $nrpsinfo->get_service_versions());
$this->assertEquals('https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly',
$nrpsinfo->get_service_scope());
}
}
/**
* Data provider for testing object instantiation.
* @return array the data for testing.
*/
public function instantiation_data_provider(): array {
return [
'Valid creation' => [
'args' => [
'contextmembershipsurl' => new \moodle_url('https://lms.example.com/45/memberships'),
'serviceversions' => ['1.0', '2.0'],
],
'expectations' => [
'valid' => true,
'serviceversions' => ['1.0', '2.0']
]
],
'Missing service version' => [
'args' => [
'contextmembershipsurl' => new \moodle_url('https://lms.example.com/45/memberships'),
'serviceversions' => [],
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => 'Service versions array cannot be empty'
]
],
'Invalid service version' => [
'args' => [
'contextmembershipsurl' => new \moodle_url('https://lms.example.com/45/memberships'),
'serviceversions' => ['1.1'],
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid Names and Roles service version '1.1'"
]
],
'Duplicate service version' => [
'args' => [
'contextmembershipsurl' => new \moodle_url('https://lms.example.com/45/memberships'),
'serviceversions' => ['1.0', '1.0'],
],
'expectations' => [
'valid' => true,
'serviceversions' => ['1.0']
]
]
];
}
}

View File

@ -0,0 +1,101 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Tests for registration_url.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \enrol_lti\local\ltiadvantage\entity\registration_url
*/
class registration_url_test extends \advanced_testcase {
/**
* Test the creation and validation of a registration_url instance.
*
* @dataProvider registration_url_data_provider
* @param array $args the arguments to the constructor.
* @param array $expectations various expectations for the test cases.
* @covers ::__construct
*/
public function test_registration_url(array $args, array $expectations) {
if ($expectations['valid']) {
$regurl = new registration_url(...array_values($args));
$this->assertEquals($expectations['expirytime'], $regurl->get_expiry_time());
if (!empty($expectations['token'])) {
$this->assertEquals($expectations['token'], $regurl->param('token'));
} else {
$this->assertNotEmpty($regurl->param('token'));
}
} else {
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
new registration_url(...array_values($args));
}
}
/**
* Data provider used to test registration_url object creation.
*
* @return array the array of test data.
*/
public function registration_url_data_provider(): array {
return [
'Valid, required args only, expiry 0' => [
'args' => [
'expirytime' => 0
],
'expectations' => [
'valid' => true,
'expirytime' => 0,
]
],
'Valid, required args only, expiry positive' => [
'args' => [
'expirytime' => 50
],
'expectations' => [
'valid' => true,
'expirytime' => 50,
]
],
'Invalid, required args only, expiry negative' => [
'args' => [
'expirytime' => -70
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => 'Invalid registration_url expiry time. Must be greater than or equal to 0.'
]
],
'Valid, all args provided' => [
'args' => [
'expirytime' => 56,
'token' => 'token-abcde-12345'
],
'expectations' => [
'valid' => true,
'expirytime' => 56,
'token' => 'token-abcde-12345'
]
]
];
}
}

View File

@ -0,0 +1,199 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Tests for resource_link.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \enrol_lti\local\ltiadvantage\entity\resource_link
*/
class resource_link_test extends \advanced_testcase {
/**
* Test creation of the object instances.
*
* @dataProvider instantiation_data_provider
* @param array $args the arguments to the creation method.
* @param array $expectations various expectations for the test cases.
* @covers ::create
*/
public function test_create(array $args, array $expectations) {
if (!$expectations['valid']) {
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
resource_link::create(...array_values($args));
} else {
$reslink = resource_link::create(...array_values($args));
$this->assertEquals($args['resourcelinkid'], $reslink->get_resourcelinkid());
$this->assertEquals($args['resourceid'], $reslink->get_resourceid());
$this->assertEquals($args['deploymentid'], $reslink->get_deploymentid());
$this->assertEquals($args['contextid'], $reslink->get_contextid());
$this->assertEquals($args['id'], $reslink->get_id());
$this->assertEquals(null, $reslink->get_grade_service());
$this->assertEquals(null, $reslink->get_names_and_roles_service());
}
}
/**
* Data provider for testing object instantiation.
* @return array the data for testing.
*/
public function instantiation_data_provider(): array {
return [
'Valid creation, no context or id provided' => [
'args' => [
'resourcelinkid' => 'res-link-id-123',
'deploymentid' => 45,
'resourceid' => 24,
'contextid' => null,
'id' => null
],
'expectations' => [
'valid' => true
]
],
'Valid creation, context id provided, no id provided' => [
'args' => [
'resourcelinkid' => 'res-link-id-123',
'deploymentid' => 45,
'resourceid' => 24,
'contextid' => 777,
'id' => null
],
'expectations' => [
'valid' => true
]
],
'Valid creation, context id and id provided' => [
'args' => [
'resourcelinkid' => 'res-link-id-123',
'deploymentid' => 45,
'resourceid' => 24,
'contextid' => 777,
'id' => 33
],
'expectations' => [
'valid' => true
]
],
'Invlid creation, empty resource link id string' => [
'args' => [
'resourcelinkid' => '',
'deploymentid' => 45,
'resourceid' => 24,
'contextid' => null,
'id' => null
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => 'Error: resourcelinkid cannot be an empty string'
]
]
];
}
/**
* Test confirming that a grade service instance can be added to the object instance.
*
* @covers ::add_grade_service
*/
public function test_add_grade_service() {
$reslink = resource_link::create('res-link-id-123', 24, 44);
$this->assertNull($reslink->get_grade_service());
$reslink->add_grade_service(
new \moodle_url('https://platform.example.org/10/lineitems'),
new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
['https://purl.imsglobal.org/spec/lti-ags/scope/lineitem']
);
$gradeservice = $reslink->get_grade_service();
$this->assertInstanceOf(ags_info::class, $gradeservice);
$this->assertEquals(new \moodle_url('https://platform.example.org/10/lineitems'),
$gradeservice->get_lineitemsurl());
$this->assertEquals(new \moodle_url('https://platform.example.org/10/lineitems/4/lineitem'),
$gradeservice->get_lineitemurl());
$this->assertEquals(['https://purl.imsglobal.org/spec/lti-ags/scope/lineitem'], $gradeservice->get_scopes());
}
/**
* Test confirming that a names and roles service instance can be added to the object instance.
*
* @covers ::add_names_and_roles_service
*/
public function test_add_names_and_roles_service() {
$reslink = resource_link::create('res-link-id-123', 24, 44);
$this->assertNull($reslink->get_names_and_roles_service());
$reslink->add_names_and_roles_service(new \moodle_url('https://lms.example.com/10/memberships'), ['2.0']);
$nrps = $reslink->get_names_and_roles_service();
$this->assertInstanceOf(nrps_info::class, $nrps);
$this->assertEquals(new \moodle_url('https://lms.example.com/10/memberships'),
$nrps->get_context_memberships_url());
$this->assertEquals(['2.0'], $nrps->get_service_versions());
}
/**
* Verify that a user can be created from a resource link that has an id.
*
* @covers ::add_user
*/
public function test_add_user() {
$reslinkwithid = resource_link::create('res-link-id-123', 24, 44, 66, 33);
$user = $reslinkwithid->add_user(2, 'platform-user-id-123', 'en', 'Sydney', 'AU', 'Test university', '99');
$this->assertInstanceOf(user::class, $user);
$this->assertEquals(33, $user->get_resourcelinkid());
$reslinkwithoutid = resource_link::create('res-link-id-123', 24, 44);
$this->expectException(\coding_exception::class);
$this->expectExceptionMessage("Can't add user to a resource_link that hasn't first been saved");
$reslinkwithoutid->add_user(2, 'platform-user-id-123', 'en', 'Sydney', 'Australia', 'Test university', '99');
}
/**
* Test confirming that the resourceid can be changed on the object.
*
* @covers ::set_resourceid
*/
public function test_set_resource_id() {
$reslink = resource_link::create('res-link-id-123', 24, 44);
$this->assertEquals(44, $reslink->get_resourceid());
$reslink->set_resourceid(333);
$this->assertEquals(333, $reslink->get_resourceid());
$this->expectException(\coding_exception::class);
$this->expectExceptionMessage('Resource id must be a positive int');
$reslink->set_resourceid(0);
}
/**
* Test confirming that the contextid can be changed on the object.
*
* @covers ::set_contextid
*/
public function test_set_context_id() {
$reslink = resource_link::create('res-link-id-123', 24, 44);
$this->assertEquals(null, $reslink->get_contextid());
$reslink->set_contextid(333);
$this->assertEquals(333, $reslink->get_contextid());
$this->expectException(\coding_exception::class);
$this->expectExceptionMessage('Context id must be a positive int');
$reslink->set_contextid(0);
}
}

View File

@ -0,0 +1,707 @@
<?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 enrol_lti\local\ltiadvantage\entity;
/**
* Tests for user.
*
* @package enrol_lti
* @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \enrol_lti\local\ltiadvantage\entity\user
*/
class user_test extends \advanced_testcase {
/**
* Test creation of a user instance using the factory method.
*
* @dataProvider create_data_provider
* @param array $args the arguments to the creation method.
* @param array $expectations various expectations for the test cases.
* @covers ::create
*/
public function test_create(array $args, array $expectations) {
if ($expectations['valid']) {
$user = user::create(...array_values($args));
$this->assertInstanceOf(user::class, $user);
$this->assertEquals($expectations['id'], $user->get_id());
$this->assertEquals($expectations['localid'], $user->get_localid());
$this->assertEquals($expectations['resourcelinkid'], $user->get_resourcelinkid());
$this->assertEquals($expectations['resourceid'], $user->get_resourceid());
$this->assertEquals($expectations['deploymentid'], $user->get_deploymentid());
$this->assertEquals($expectations['sourceid'], $user->get_sourceid());
$this->assertEquals($expectations['lang'], $user->get_lang());
$this->assertEquals($expectations['timezone'], $user->get_timezone());
$this->assertEquals($expectations['city'], $user->get_city());
$this->assertEquals($expectations['country'], $user->get_country());
$this->assertEquals($expectations['institution'], $user->get_institution());
$this->assertEquals($expectations['maildisplay'], $user->get_maildisplay());
$this->assertEquals($expectations['lastgrade'], $user->get_lastgrade());
$this->assertEquals($expectations['lastaccess'], $user->get_lastaccess());
} else {
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
user::create(...array_values($args));
}
}
/**
* Data provider for testing the user::create() method.
*
* @return array the data for testing.
*/
public function create_data_provider(): array {
return [
'Valid create, only required args provided' => [
'args' => [
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99'
],
'expectations' => [
'valid' => true,
'resourceid' => 22,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => '',
'country' => '',
'institution' => '',
'maildisplay' => 2,
'lastgrade' => 0.0,
'lastaccess' => null,
'id' => null,
'localid' => 2,
'resourcelinkid' => null,
]
],
'Valid create, all args provided explicitly' => [
'args' => [
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => 'Melbourne',
'country' => 'AU',
'institution' => 'My institution',
'maildisplay' => 1,
'lastgrade' => 50.55,
'lastaccess' => 14567888,
'resourcelinkid' => 44,
'id' => 22
],
'expectations' => [
'valid' => true,
'resourceid' => 22,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => 'Melbourne',
'country' => 'AU',
'institution' => 'My institution',
'maildisplay' => 1,
'lastgrade' => 50.55,
'lastaccess' => 14567888,
'resourcelinkid' => 44,
'localid' => 2,
'id' => 22,
]
],
'Valid create, optional args explicitly nulled for default values' => [
'args' => [
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => 'Melbourne',
'country' => 'AU',
'institution' => 'My institution',
'maildisplay' => null,
'lastgrade' => null,
'lastaccess' => null,
'resourcelinkid' => null,
'id' => null
],
'expectations' => [
'valid' => true,
'resourceid' => 22,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => 'Melbourne',
'country' => 'AU',
'institution' => 'My institution',
'maildisplay' => 2,
'lastgrade' => 0.0,
'lastaccess' => null,
'resourcelinkid' => null,
'localid' => 2,
'id' => null
]
],
'Invalid create, lang with bad value (fr not installed)' => [
'args' => [
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'fr',
'timezone' => '99',
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid lang 'fr' provided."
]
],
'Invalid create, timezone with bad value' => [
'args' => [
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => 'NOT/FOUND',
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid timezone 'NOT/FOUND' provided."
]
],
'Invalid create, explicitly provided country with bad value' => [
'args' => [
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => '',
'country' => 'FFF',
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid country code 'FFF'."
]
],
'Invalid create, explicit maildisplay with bad value' => [
'args' => [
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => '',
'country' => '',
'institution' => '',
'maildisplay' => 3,
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid maildisplay value '3'. Must be in the range {0..2}."
]
],
];
}
/**
* Test creation of a user instance from a resource link.
*
* @dataProvider create_from_resource_link_data_provider
* @param array $args the arguments to the creation method.
* @param array $expectations various expectations for the test cases.
* @covers ::create_from_resource_link
*/
public function test_create_from_resource_link(array $args, array $expectations) {
if ($expectations['valid']) {
$user = user::create_from_resource_link(...array_values($args));
$this->assertInstanceOf(user::class, $user);
$this->assertEquals($expectations['id'], $user->get_id());
$this->assertEquals($expectations['localid'], $user->get_localid());
$this->assertEquals($expectations['resourcelinkid'], $user->get_resourcelinkid());
$this->assertEquals($expectations['resourceid'], $user->get_resourceid());
$this->assertEquals($expectations['deploymentid'], $user->get_deploymentid());
$this->assertEquals($expectations['sourceid'], $user->get_sourceid());
$this->assertEquals($expectations['lang'], $user->get_lang());
$this->assertEquals($expectations['city'], $user->get_city());
$this->assertEquals($expectations['country'], $user->get_country());
$this->assertEquals($expectations['institution'], $user->get_institution());
$this->assertEquals($expectations['timezone'], $user->get_timezone());
$this->assertEquals($expectations['maildisplay'], $user->get_maildisplay());
$this->assertEquals($expectations['lastgrade'], $user->get_lastgrade());
$this->assertEquals($expectations['lastaccess'], $user->get_lastaccess());
} else {
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
user::create_from_resource_link(...array_values($args));
}
}
/**
* Data provider used in testing the user::create_from_resource_link() method.
*
* @return array the data for testing.
*/
public function create_from_resource_link_data_provider(): array {
return [
'Valid creation, all args provided explicitly' => [
'args' => [
'resourcelinkid' => 11,
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => 'Melbourne',
'country' => 'AU',
'institution' => 'platform',
'maildisplay' => 1
],
'expectations' => [
'valid' => true,
'id' => null,
'localid' => 2,
'resourcelinkid' => 11,
'resourceid' => 22,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => 'Melbourne',
'country' => 'AU',
'institution' => 'platform',
'maildisplay' => 1,
'lastgrade' => 0.0,
'lastaccess' => null
]
],
'Valid creation, only required args provided, explicit values' => [
'args' => [
'resourcelinkid' => 11,
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => 'UTC'
],
'expectations' => [
'valid' => true,
'id' => null,
'localid' => 2,
'resourcelinkid' => 11,
'resourceid' => 22,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => 'UTC',
'city' => '',
'country' => '',
'institution' => '',
'maildisplay' => 2,
'lastgrade' => 0.0,
'lastaccess' => null
]
],
'Invalid creation, only required args provided, empty sourceid' => [
'args' => [
'resourcelinkid' => 11,
'resourceid' => 22,
'user' => 2,
'deploymentid' => 33,
'sourceid' => '',
'lang' => 'en',
'timezone' => 'UTC'
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => 'Invalid sourceid value. Cannot be an empty string.'
]
],
'Invalid creation, only required args provided, empty lang' => [
'args' => [
'resourcelinkid' => 11,
'resourceid' => 22,
'user' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => '',
'timezone' => 'UTC'
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => 'Invalid lang value. Cannot be an empty string.'
]
],
'Invalid creation, only required args provided, empty timezone' => [
'args' => [
'resourcelinkid' => 11,
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => ''
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => 'Invalid timezone value. Cannot be an empty string.'
]
],
'Invalid creation, only required args provided, invalid lang (fr not installed)' => [
'args' => [
'resourcelinkid' => 11,
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'fr',
'timezone' => 'UTC'
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid lang 'fr' provided."
]
],
'Invalid creation, only required args provided, invalid timezone' => [
'args' => [
'resourcelinkid' => 11,
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => 'NOT/FOUND'
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid timezone 'NOT/FOUND' provided."
]
],
'Invalid creation, all args provided explicitly, invalid country' => [
'args' => [
'resourcelinkid' => 11,
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => 'Melbourne',
'country' => 'FFF',
'institution' => 'platform',
'maildisplay' => 1
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid country code 'FFF'."
]
],
'Invalid creation, all args provided explicitly, invalid maildisplay' => [
'args' => [
'resourcelinkid' => 11,
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => '99',
'city' => 'Melbourne',
'country' => 'AU',
'institution' => 'platform',
'maildisplay' => 4
],
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid maildisplay value '4'. Must be in the range {0..2}."
]
],
];
}
/**
* Helper to create a simple, working user for testing.
*
* @return user a user instance.
*/
protected function create_test_user(): user {
$args = [
'resourcelinkid' => 11,
'resourceid' => 22,
'userid' => 2,
'deploymentid' => 33,
'sourceid' => 'user-id-123',
'lang' => 'en',
'timezone' => 'UTC'
];
return user::create_from_resource_link(...array_values($args));
}
/**
* Test the behaviour of the user setters and getters.
*
* @dataProvider setters_getters_data_provider
* @param string $methodname the name of the setter
* @param mixed $arg the argument to the setter
* @param array $expectations the array of expectations
* @covers ::__construct
*/
public function test_setters_and_getters(string $methodname, $arg, array $expectations) {
$user = $this->create_test_user();
$setter = 'set_'.$methodname;
$getter = 'get_'.$methodname;
if ($expectations['valid']) {
$user->$setter($arg);
if (isset($expectations['expectedvalue'])) {
$this->assertEquals($expectations['expectedvalue'], $user->$getter());
} else {
$this->assertEquals($arg, $user->$getter());
}
} else {
$this->expectException($expectations['exception']);
$this->expectExceptionMessage($expectations['exceptionmessage']);
$user->$setter($arg);
}
}
/**
* Data provider for testing the user object setters.
*
* @return array the array of test data.
*/
public function setters_getters_data_provider(): array {
return [
'Testing set_resourcelinkid with valid id' => [
'methodname' => 'resourcelinkid',
'arg' => 8,
'expectations' => [
'valid' => true,
]
],
'Testing set_resourcelinkid with invalid id' => [
'methodname' => 'resourcelinkid',
'arg' => -1,
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid resourcelinkid '-1' provided. Must be > 0."
]
],
'Testing set_city with a non-empty string' => [
'methodname' => 'city',
'arg' => 'Melbourne',
'expectations' => [
'valid' => true,
]
],
'Testing set_city with an empty string' => [
'methodname' => 'city',
'arg' => '',
'expectations' => [
'valid' => true,
]
],
'Testing set_country with a valid country code' => [
'methodname' => 'country',
'arg' => 'AU',
'expectations' => [
'valid' => true,
]
],
'Testing set_country with an empty string' => [
'methodname' => 'country',
'arg' => '',
'expectations' => [
'valid' => true,
]
],
'Testing set_country with an invalid country code' => [
'methodname' => 'country',
'arg' => 'FFF',
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid country code 'FFF'."
]
],
'Testing set_institution with a non-empty string' => [
'methodname' => 'institution',
'arg' => 'Some institution',
'expectations' => [
'valid' => true,
]
],
'Testing set_institution with an empty string' => [
'methodname' => 'institution',
'arg' => '',
'expectations' => [
'valid' => true,
]
],
'Testing set_timezone with a valid real timezone' => [
'methodname' => 'timezone',
'arg' => 'Pacific/Wallis',
'expectations' => [
'valid' => true,
]
],
'Testing set_timezone with a valid server timezone value' => [
'methodname' => 'timezone',
'arg' => '99',
'expectations' => [
'valid' => true,
]
],
'Testing set_timezone with an invalid timezone value' => [
'methodname' => 'timezone',
'arg' => 'NOT/FOUND',
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid timezone 'NOT/FOUND' provided."
]
],
'Testing set_maildisplay with a valid int: 0' => [
'methodname' => 'maildisplay',
'arg' => '0',
'expectations' => [
'valid' => true,
]
],
'Testing set_maildisplay with a valid int: 1' => [
'methodname' => 'maildisplay',
'arg' => '1',
'expectations' => [
'valid' => true,
]
],
'Testing set_maildisplay with a valid int: 2' => [
'methodname' => 'maildisplay',
'arg' => '1',
'expectations' => [
'valid' => true,
]
],
'Testing set_maildisplay with invalid int' => [
'methodname' => 'maildisplay',
'arg' => '-1',
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid maildisplay value '-1'. Must be in the range {0..2}."
]
],
'Testing set_maildisplay with invalid int' => [
'methodname' => 'maildisplay',
'arg' => '3',
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid maildisplay value '3'. Must be in the range {0..2}."
]
],
'Testing set_lang with valid lang code' => [
'methodname' => 'lang',
'arg' => 'en',
'expectations' => [
'valid' => true,
]
],
'Testing set_lang with an empty string' => [
'methodname' => 'lang',
'arg' => '',
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => 'Invalid lang value. Cannot be an empty string.'
]
],
'Testing set_lang with an empty string' => [
'methodname' => 'lang',
'arg' => 'ff',
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => "Invalid lang 'ff' provided."
]
],
'Testing set_lastgrade with valid grade' => [
'methodname' => 'lastgrade',
'arg' => 0.0,
'expectations' => [
'valid' => true
]
],
'Testing set_lastgrade with valid non zero grade' => [
'methodname' => 'lastgrade',
'arg' => 150.0,
'expectations' => [
'valid' => true
]
],
'Testing set_lastgrade with valid non zero long decimal grade' => [
'methodname' => 'lastgrade',
'arg' => 150.777779,
'expectations' => [
'valid' => true,
'expectedvalue' => 150.77778
]
],
'Testing set_lastaccess with valid time' => [
'methodname' => 'lastaccess',
'arg' => 4,
'expectations' => [
'valid' => true
]
],
'Testing set_lastaccess with invalid time' => [
'methodname' => 'lastaccess',
'arg' => -1,
'expectations' => [
'valid' => false,
'exception' => \coding_exception::class,
'exceptionmessage' => 'Cannot set negative access time'
]
],
];
}
}