moodle/lib/tests/moodlenet/activity_sender_test.php

315 lines
12 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core\moodlenet;
use context_course;
use core\http_client;
use core\oauth2\issuer;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Http\Message\ResponseInterface;
use ReflectionMethod;
use stdClass;
use testing_data_generator;
/**
* Unit tests for {@see activity_sender}.
*
* @coversDefaultClass \core\moodlenet\activity_sender
* @package core
* @copyright 2023 Huong Nguyen <huongnv13@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class activity_sender_test extends \advanced_testcase {
/** @var testing_data_generator Data generator. */
private testing_data_generator $generator;
/** @var stdClass Course object. */
private stdClass $course;
/** @var stdClass Activity object, */
private stdClass $moduleinstance;
/** @var context_course Course context instance. */
private context_course $coursecontext;
/** @var issuer $issuer Dummy issuer. */
private issuer $issuer;
/** @var MockObject $mockoauthclient Mock OAuth client. */
private MockObject $mockoauthclient;
public static function setUpBeforeClass(): void {
parent::setUpBeforeClass();
require_once(__DIR__ . '/helpers.php');
}
/**
* Set up function for tests.
*/
protected function setUp(): void {
parent::setUp();
$this->resetAfterTest();
// Get data generator.
$this->generator = $this->getDataGenerator();
// Create course.
$this->course = $this->generator->create_course();
$this->moduleinstance = $this->generator->create_module('assign', ['course' => $this->course->id]);
$this->coursecontext = context_course::instance($this->course->id);
// Create mock issuer.
$this->issuer = helpers::get_mock_issuer(1);
// Create mock builder for OAuth2 client.
$mockbuilder = $this->getMockBuilder('core\oauth2\client');
$mockbuilder->onlyMethods(['get_issuer', 'is_logged_in', 'get_accesstoken']);
$mockbuilder->setConstructorArgs([$this->issuer, '', '']);
// Get the OAuth2 client mock.
$this->mockoauthclient = $mockbuilder->getMock();
}
/**
* Test prepare_share_contents method.
*
* @covers ::prepare_share_contents
*/
public function test_prepare_share_contents(): void {
global $USER;
$this->setAdminUser();
$httpclient = new http_client();
$moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient);
// Set get_file method accessibility.
$method = new ReflectionMethod(activity_sender::class, 'prepare_share_contents');
$method->setAccessible(true);
// Test with invalid share format.
$this->expectException(\moodle_exception::class);
$this->expectExceptionMessage(get_string('moodlenet:invalidshareformat', 'error'));
$package = $method->invoke(new activity_sender(
$this->moduleinstance->cmid,
$USER->id,
$moodlenetclient,
$this->mockoauthclient,
random_int(5, 30)
));
// Test with valid share format and invalid course module.
$package = $method->invoke(new activity_sender(
random_int(5, 30),
$USER->id,
$moodlenetclient,
$this->mockoauthclient,
resource_sender::SHARE_FORMAT_BACKUP
));
$this->assertEmpty($package);
// Test with valid share format and valid course module.
$package = $method->invoke(new activity_sender(
$this->moduleinstance->cmid,
$USER->id,
$moodlenetclient,
$this->mockoauthclient,
resource_sender::SHARE_FORMAT_BACKUP
));
$this->assertNotEmpty($package);
// Confirm the expected stored_file object is returned.
$this->assertInstanceOf(\stored_file::class, $package);
}
/**
* Test get_resource_description method.
*
* @covers ::get_resource_description
*/
public function test_get_resource_description(): void {
global $USER;
$this->setAdminUser();
$activity = $this->generator->create_module('assign', [
'course' => $this->course->id,
'intro' => '<p>This is an example Moodle activity description.</p>
<p>&nbsp;</p>
<p>This is a formatted intro</p>
<p>&nbsp;</p>
<p>This thing has many lines.</p>
<p>&nbsp;</p>
<p>The last word of this sentence is in <strong>bold</strong></p>'
]);
$httpclient = new http_client();
$moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient);
// Set get_resource_description method accessibility.
$method = new ReflectionMethod(activity_sender::class, 'get_resource_description');
$method->setAccessible(true);
// Test the processed description.
$processeddescription = $method->invoke(new activity_sender(
$activity->cmid,
$USER->id,
$moodlenetclient,
$this->mockoauthclient,
resource_sender::SHARE_FORMAT_BACKUP
), $this->coursecontext);
$this->assertEquals('This is an example Moodle activity description.
 
This is a formatted intro
 
This thing has many lines.
 
The last word of this sentence is in bold', $processeddescription);
}
/**
* Test share_resource() method.
*
* @dataProvider share_resource_provider
* @covers ::share_resource
* @covers ::log_event
* @covers \core\moodlenet\moodlenet_client::create_resource_from_stored_file
* @covers \core\moodlenet\moodlenet_client::prepare_file_share_request_data
* @param ResponseInterface $httpresponse
* @param array $expected
*/
public function test_share_resource(ResponseInterface $httpresponse, array $expected): void {
global $CFG, $USER;
$this->setAdminUser();
// Enable the experimental flag.
$CFG->enablesharingtomoodlenet = true;
// Set OAuth 2 service in the outbound setting to the dummy issuer.
set_config('oauthservice', $this->issuer->get('id'), 'moodlenet');
// Generate access token for the mock.
$accesstoken = new stdClass();
$accesstoken->token = random_string(64);
// Get the OAuth2 client mock and set the return value for necessary methods.
$this->mockoauthclient->method('get_issuer')->will($this->returnValue($this->issuer));
$this->mockoauthclient->method('is_logged_in')->will($this->returnValue(true));
$this->mockoauthclient->method('get_accesstoken')->will($this->returnValue($accesstoken));
// Create Guzzle mock.
$mockguzzlehandler = new MockHandler([$httpresponse]);
$handlerstack = HandlerStack::create($mockguzzlehandler);
$httpclient = new http_client(['handler' => $handlerstack]);
// Create events sink.
$sink = $this->redirectEvents();
// Create activity sender.
$moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient);
$activitysender = new activity_sender(
$this->moduleinstance->cmid,
$USER->id,
$moodlenetclient,
$this->mockoauthclient,
resource_sender::SHARE_FORMAT_BACKUP
);
if (isset($expected['exception'])) {
$this->expectException(ClientException::class);
$this->expectExceptionMessage($expected['exception']);
}
// Call the API.
$result = $activitysender->share_resource();
// Verify the result.
$this->assertEquals($expected['response_code'], $result['responsecode']);
$this->assertEquals($expected['resource_url'], $result['drafturl']);
// Verify the events.
$events = $sink->get_events();
$event = reset($events);
$this->assertInstanceOf('\core\event\moodlenet_resource_exported', $event);
$this->assertEquals($USER->id, $event->userid);
if ($result['responsecode'] == 201) {
$description = "The user with id '{$USER->id}' successfully shared activities to MoodleNet with the " .
"following course module ids, from context with id '{$this->coursecontext->id}': '{$this->moduleinstance->cmid}'.";
} else {
$description = "The user with id '{$USER->id}' failed to share activities to MoodleNet with the " .
"following course module ids, from context with id '{$this->coursecontext->id}': '{$this->moduleinstance->cmid}'.";
}
$this->assertEquals($description, $event->get_description());
}
/**
* Provider for test share_resource().
*
* @return array Test data.
*/
public function share_resource_provider(): array {
return [
'Success' => [
'http_response' => new Response(
201,
['Content-Type' => 'application/json'],
json_encode([
'homepage' => 'https://moodlenet.example.com/drafts/view/activity_backup_1.mbz',
]),
),
'expected' => [
'response_code' => 201,
'resource_url' => 'https://moodlenet.example.com/drafts/view/activity_backup_1.mbz',
],
],
'Fail with 200 status code' => [
'http_response' => new Response(
200,
['Content-Type' => 'application/json'],
json_encode([
'homepage' => 'https://moodlenet.example.com/drafts/view/activity_backup_2.mbz',
]),
),
'expected' => [
'response_code' => 200,
'resource_url' => 'https://moodlenet.example.com/drafts/view/activity_backup_2.mbz',
],
],
'Fail with 401 status code' => [
'http_response' => new Response(
401,
),
'expected' => [
'response_code' => 401,
'resource_url' => '',
'exception' => 'Client error: ' .
'`POST https://moodlenet.example.com/.pkg/@moodlenet/ed-resource/basic/v1/create` ' .
'resulted in a `401 Unauthorized` response',
],
],
'Fail with 404 status code' => [
'http_response' => new Response(
404,
),
'expected' => [
'response_code' => 404,
'resource_url' => '',
'exception' => 'Client error: '.
'`POST https://moodlenet.example.com/.pkg/@moodlenet/ed-resource/basic/v1/create` ' .
'resulted in a `404 Not Found` response',
],
],
];
}
}