ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ.. 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; /** * Test coverage for moodlenet course sender. * * @package core * @copyright 2023 Safat Shahin * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @coversDefaultClass \core\moodlenet\course_sender */ class course_sender_test extends \advanced_testcase { /** @var testing_data_generator Data generator. */ private testing_data_generator $generator; /** @var stdClass Course object. */ private stdClass $course; /** @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(['shortname' => 'testcourse']); $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(); // Set get_file method accessibility. $method = new ReflectionMethod(course_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')); $httpclient = new http_client(); $moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient); $coursesender = new course_sender( random_int(5, 30), $USER->id, $moodlenetclient, $this->mockoauthclient, random_int(5, 30) ); $coursesender = $method->invoke(new course_sender( $this->course->id, $USER->id, $moodlenetclient, $this->mockoauthclient, resource_sender::SHARE_FORMAT_BACKUP )); // Test with valid share format and invalid course module. $package = $method->invoke(new course_sender( $this->course->id, $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 course_sender( $this->course->id, $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(); $course = $this->generator->create_course([ 'summary' => '

This is an example Moodle course description.

 

This is a formatted intro

 

This thing has many lines.

 

The last word of this sentence is in bold

' ]); // Set get_resource_description method accessibility. $method = new ReflectionMethod(course_sender::class, 'get_resource_description'); $method->setAccessible(true); // Test the processed description. $httpclient = new http_client(); $moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient); $processeddescription = $method->invoke(new course_sender( $course->id, $USER->id, $moodlenetclient, $this->mockoauthclient, resource_sender::SHARE_FORMAT_BACKUP ), $this->coursecontext); $this->assertEquals('This is an example Moodle course 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 sender. $moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient); $coursesender = new course_sender( $this->course->id, $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 = $coursesender->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 = end($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 course to MoodleNet with the " . "following course id, from context with id '{$this->coursecontext->id}': '{$this->course->id}'."; } else { $description = "The user with id '{$USER->id}' failed to share course to MoodleNet with the " . "following course id, from context with id '{$this->coursecontext->id}': '{$this->course->id}'."; } $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/testcourse_backup_1.mbz', ]), ), 'expected' => [ 'response_code' => 201, 'resource_url' => 'https://moodlenet.example.com/drafts/view/testcourse_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/testcourse_backup_2.mbz', ]), ), 'expected' => [ 'response_code' => 200, 'resource_url' => 'https://moodlenet.example.com/drafts/view/testcourse_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', ], ], ]; } }