Differences Between: [Versions 402 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core\moodlenet; 18 19 use context_course; 20 use core\http_client; 21 use core\oauth2\issuer; 22 use GuzzleHttp\Exception\ClientException; 23 use GuzzleHttp\Handler\MockHandler; 24 use GuzzleHttp\HandlerStack; 25 use GuzzleHttp\Psr7\Response; 26 use PHPUnit\Framework\MockObject\MockObject; 27 use Psr\Http\Message\ResponseInterface; 28 use ReflectionMethod; 29 use stdClass; 30 use testing_data_generator; 31 32 /** 33 * Unit tests for {@see activity_sender}. 34 * 35 * @coversDefaultClass \core\moodlenet\activity_sender 36 * @package core 37 * @copyright 2023 Huong Nguyen <huongnv13@gmail.com> 38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 */ 40 class activity_sender_test extends \advanced_testcase { 41 42 /** @var testing_data_generator Data generator. */ 43 private testing_data_generator $generator; 44 /** @var stdClass Course object. */ 45 private stdClass $course; 46 /** @var stdClass Activity object, */ 47 private stdClass $moduleinstance; 48 /** @var context_course Course context instance. */ 49 private context_course $coursecontext; 50 /** @var issuer $issuer Dummy issuer. */ 51 private issuer $issuer; 52 /** @var MockObject $mockoauthclient Mock OAuth client. */ 53 private MockObject $mockoauthclient; 54 55 public static function setUpBeforeClass(): void { 56 parent::setUpBeforeClass(); 57 58 require_once (__DIR__ . '/helpers.php'); 59 } 60 61 /** 62 * Set up function for tests. 63 */ 64 protected function setUp(): void { 65 parent::setUp(); 66 67 $this->resetAfterTest(); 68 // Get data generator. 69 $this->generator = $this->getDataGenerator(); 70 // Create course. 71 $this->course = $this->generator->create_course(); 72 $this->moduleinstance = $this->generator->create_module('assign', ['course' => $this->course->id]); 73 $this->coursecontext = context_course::instance($this->course->id); 74 // Create mock issuer. 75 $this->issuer = helpers::get_mock_issuer(1); 76 // Create mock builder for OAuth2 client. 77 $mockbuilder = $this->getMockBuilder('core\oauth2\client'); 78 $mockbuilder->onlyMethods(['get_issuer', 'is_logged_in', 'get_accesstoken']); 79 $mockbuilder->setConstructorArgs([$this->issuer, '', '']); 80 // Get the OAuth2 client mock. 81 $this->mockoauthclient = $mockbuilder->getMock(); 82 } 83 84 /** 85 * Test prepare_share_contents method. 86 * 87 * @covers ::prepare_share_contents 88 */ 89 public function test_prepare_share_contents(): void { 90 global $USER; 91 $this->setAdminUser(); 92 93 $httpclient = new http_client(); 94 $moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient); 95 96 // Set get_file method accessibility. 97 $method = new ReflectionMethod(activity_sender::class, 'prepare_share_contents'); 98 $method->setAccessible(true); 99 100 // Test with invalid share format. 101 $this->expectException(\moodle_exception::class); 102 $this->expectExceptionMessage(get_string('moodlenet:invalidshareformat', 'error')); 103 $package = $method->invoke(new activity_sender( 104 $this->moduleinstance->cmid, 105 $USER->id, 106 $moodlenetclient, 107 $this->mockoauthclient, 108 random_int(5, 30) 109 )); 110 111 // Test with valid share format and invalid course module. 112 $package = $method->invoke(new activity_sender( 113 random_int(5, 30), 114 $USER->id, 115 $moodlenetclient, 116 $this->mockoauthclient, 117 activity_sender::SHARE_FORMAT_BACKUP 118 )); 119 $this->assertEmpty($package); 120 121 // Test with valid share format and valid course module. 122 $package = $method->invoke(new activity_sender( 123 $this->moduleinstance->cmid, 124 $USER->id, 125 $moodlenetclient, 126 $this->mockoauthclient, 127 activity_sender::SHARE_FORMAT_BACKUP 128 )); 129 $this->assertNotEmpty($package); 130 131 // Confirm the expected stored_file object is returned. 132 $this->assertInstanceOf(\stored_file::class, $package); 133 } 134 135 /** 136 * Test get_resource_description method. 137 * 138 * @covers ::get_resource_description 139 */ 140 public function test_get_resource_description(): void { 141 global $USER; 142 $this->setAdminUser(); 143 144 $activity = $this->generator->create_module('assign', [ 145 'course' => $this->course->id, 146 'intro' => '<p>This is an example Moodle activity description.</p> 147 <p> </p> 148 <p>This is a formatted intro</p> 149 <p> </p> 150 <p>This thing has many lines.</p> 151 <p> </p> 152 <p>The last word of this sentence is in <strong>bold</strong></p>' 153 ]); 154 155 $httpclient = new http_client(); 156 $moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient); 157 158 // Set get_resource_description method accessibility. 159 $method = new ReflectionMethod(activity_sender::class, 'get_resource_description'); 160 $method->setAccessible(true); 161 162 // Test the processed description. 163 $processeddescription = $method->invoke(new activity_sender( 164 $activity->cmid, 165 $USER->id, 166 $moodlenetclient, 167 $this->mockoauthclient, 168 activity_sender::SHARE_FORMAT_BACKUP 169 ), $this->coursecontext); 170 171 $this->assertEquals('This is an example Moodle activity description. 172 173 This is a formatted intro 174 175 This thing has many lines. 176 177 The last word of this sentence is in bold', $processeddescription); 178 } 179 180 /** 181 * Test share_activity() method. 182 * 183 * @dataProvider share_activity_provider 184 * @covers ::share_activity 185 * @covers ::log_event 186 * @covers \core\moodlenet\moodlenet_client::create_resource_from_stored_file 187 * @covers \core\moodlenet\moodlenet_client::prepare_file_share_request_data 188 * @param ResponseInterface $httpresponse 189 * @param array $expected 190 */ 191 public function test_share_activity(ResponseInterface $httpresponse, array $expected): void { 192 global $CFG, $USER; 193 $this->setAdminUser(); 194 195 // Enable the experimental flag. 196 $CFG->enablesharingtomoodlenet = true; 197 198 // Set OAuth 2 service in the outbound setting to the dummy issuer. 199 set_config('oauthservice', $this->issuer->get('id'), 'moodlenet'); 200 201 // Generate access token for the mock. 202 $accesstoken = new stdClass(); 203 $accesstoken->token = random_string(64); 204 205 // Get the OAuth2 client mock and set the return value for necessary methods. 206 $this->mockoauthclient->method('get_issuer')->will($this->returnValue($this->issuer)); 207 $this->mockoauthclient->method('is_logged_in')->will($this->returnValue(true)); 208 $this->mockoauthclient->method('get_accesstoken')->will($this->returnValue($accesstoken)); 209 210 // Create Guzzle mock. 211 $mockguzzlehandler = new MockHandler([$httpresponse]); 212 $handlerstack = HandlerStack::create($mockguzzlehandler); 213 $httpclient = new http_client(['handler' => $handlerstack]); 214 215 // Create events sink. 216 $sink = $this->redirectEvents(); 217 218 // Create activity sender. 219 $moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient); 220 $activitysender = new activity_sender( 221 $this->moduleinstance->cmid, 222 $USER->id, 223 $moodlenetclient, 224 $this->mockoauthclient, 225 activity_sender::SHARE_FORMAT_BACKUP 226 ); 227 228 if (isset($expected['exception'])) { 229 $this->expectException(ClientException::class); 230 $this->expectExceptionMessage($expected['exception']); 231 } 232 // Call the API. 233 $result = $activitysender->share_activity(); 234 235 // Verify the result. 236 $this->assertEquals($expected['response_code'], $result['responsecode']); 237 $this->assertEquals($expected['resource_url'], $result['drafturl']); 238 239 // Verify the events. 240 $events = $sink->get_events(); 241 $event = reset($events); 242 $this->assertInstanceOf('\core\event\moodlenet_resource_exported', $event); 243 $this->assertEquals($USER->id, $event->userid); 244 245 if ($result['responsecode'] == 201) { 246 $description = "The user with id '{$USER->id}' successfully shared activities to MoodleNet with the " . 247 "following course module ids, from context with id '{$this->coursecontext->id}': '{$this->moduleinstance->cmid}'."; 248 } else { 249 $description = "The user with id '{$USER->id}' failed to share activities to MoodleNet with the " . 250 "following course module ids, from context with id '{$this->coursecontext->id}': '{$this->moduleinstance->cmid}'."; 251 } 252 $this->assertEquals($description, $event->get_description()); 253 } 254 255 /** 256 * Provider for test share_activity(). 257 * 258 * @return array Test data. 259 */ 260 public function share_activity_provider(): array { 261 return [ 262 'Success' => [ 263 'http_response' => new Response( 264 201, 265 ['Content-Type' => 'application/json'], 266 json_encode([ 267 'homepage' => 'https://moodlenet.example.com/drafts/view/activity_backup_1.mbz', 268 ]), 269 ), 270 'expected' => [ 271 'response_code' => 201, 272 'resource_url' => 'https://moodlenet.example.com/drafts/view/activity_backup_1.mbz', 273 ], 274 ], 275 'Fail with 200 status code' => [ 276 'http_response' => new Response( 277 200, 278 ['Content-Type' => 'application/json'], 279 json_encode([ 280 'homepage' => 'https://moodlenet.example.com/drafts/view/activity_backup_2.mbz', 281 ]), 282 ), 283 'expected' => [ 284 'response_code' => 200, 285 'resource_url' => 'https://moodlenet.example.com/drafts/view/activity_backup_2.mbz', 286 ], 287 ], 288 'Fail with 401 status code' => [ 289 'http_response' => new Response( 290 401, 291 ), 292 'expected' => [ 293 'response_code' => 401, 294 'resource_url' => '', 295 'exception' => 'Client error: ' . 296 '`POST https://moodlenet.example.com/.pkg/@moodlenet/ed-resource/basic/v1/create` ' . 297 'resulted in a `401 Unauthorized` response', 298 ], 299 ], 300 'Fail with 404 status code' => [ 301 'http_response' => new Response( 302 404, 303 ), 304 'expected' => [ 305 'response_code' => 404, 306 'resource_url' => '', 307 'exception' => 'Client error: '. 308 '`POST https://moodlenet.example.com/.pkg/@moodlenet/ed-resource/basic/v1/create` ' . 309 'resulted in a `404 Not Found` response', 310 ], 311 ], 312 ]; 313 } 314 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body